diff --git a/.codecov.yml b/.codecov.yml index 746db53608d..81e48fa0b3e 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,3 +1,11 @@ ignore: + # ignore examples + - "example" # ignore auto-generated code - "github/github-accessors.go" + # ignore experimental scrape package + - "scrape" + # ignore test + - "test" + # ignore tools + - "tools" diff --git a/.custom-gcl.yml b/.custom-gcl.yml new file mode 100644 index 00000000000..70c35988238 --- /dev/null +++ b/.custom-gcl.yml @@ -0,0 +1,14 @@ +version: v2.12.2 # this is the version of golangci-lint +plugins: + - module: "github.com/google/go-github/v90/tools/extraneousnew" + path: ./tools/extraneousnew + - module: "github.com/google/go-github/v90/tools/fmtpercentv" + path: ./tools/fmtpercentv + - module: "github.com/google/go-github/v90/tools/paramcheck" + path: ./tools/paramcheck + - module: "github.com/google/go-github/v90/tools/redundantptr" + path: ./tools/redundantptr + - module: "github.com/google/go-github/v90/tools/sliceofpointers" + path: ./tools/sliceofpointers + - module: "github.com/google/go-github/v90/tools/structfield" + path: ./tools/structfield diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..b2765ca3e9d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,44 @@ +version: 2 +updates: +- package-ecosystem: gomod + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 +- package-ecosystem: gomod + directory: otel + schedule: + interval: weekly + cooldown: + default-days: 7 +- package-ecosystem: gomod + directory: scrape + schedule: + interval: weekly + cooldown: + default-days: 7 +- package-ecosystem: gomod + directories: + - /tools + - /tools/** + schedule: + interval: weekly + cooldown: + default-days: 7 +- package-ecosystem: gomod + directory: example + schedule: + interval: weekly + cooldown: + default-days: 7 +- package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + groups: + actions: + patterns: + - "actions/*" + cooldown: + default-days: 7 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 00000000000..25445778659 --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,50 @@ +on: [push, pull_request] +name: linter + +permissions: + contents: read + +jobs: + check-generated: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: stable + cache-dependency-path: "**/go.sum" + - name: Check generated code + run: ./script/generate.sh --check + + golangci-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: stable + cache-dependency-path: "**/go.sum" + - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 + with: + version: v2.12.2 # sync with version in .custom-gcl.yml + experimental: "automatic-module-directories,no-run-logs-group" + + check-openapi: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: stable + cache-dependency-path: "**/go.sum" + - name: Check OpenAPI + run: ./script/metadata.sh update-openapi --validate + env: + CHECK_GITHUB_OPENAPI: 1 + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000000..0a27b651194 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,69 @@ +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: + - master + pull_request: + branches: + - master + +name: tests +env: + GO111MODULE: on + +permissions: + contents: read + +jobs: + test: + permissions: + contents: read + id-token: write + defaults: + run: + shell: bash + strategy: + matrix: + go-version: [stable, oldstable] # test with N and the .0 release of N-1 + platform: [ubuntu-latest] + include: + # include windows, but only with the latest Go version, since there + # is very little in the library that is platform specific + - go-version: stable + platform: windows-latest + + # only update test coverage stats with the most recent go version on linux + - go-version: stable + platform: ubuntu-latest + update-coverage: true + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: ${{ matrix.go-version }} + cache-dependency-path: "**/go.sum" + + - name: Run go test + run: | + if [ -n "${{ matrix.update-coverage }}" ]; then + script/test.sh -race -covermode atomic -coverprofile coverage.txt ./... + exit + fi + script/test.sh -race -covermode atomic ./... + + - name: Ensure integration tests build + # don't actually run tests since they hit live GitHub API + run: go test -v -tags=integration -run=^$ ./test/integration + + - name: Upload coverage to Codecov + if: ${{ matrix.update-coverage }} + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + use_oidc: true diff --git a/.gitignore b/.gitignore index 3515c4b9740..6b918ae17ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,21 @@ +*.sh +!/script/*.sh *.test +.*.local coverage.out +/bin +# intellij files +.idea/ +vendor/ +.DS_Store +.vscode +.claude/ +# vim temp files +*.swp + +# goenv local version. See https://github.com/syndbg/goenv/blob/master/COMMANDS.md#goenv-local for more info. +.go-version + +# golangci-lint -v custom generates the following local file: +custom-gcl +custom-gcl.exe diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000000..adb297b6ebd --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,542 @@ +version: "2" +run: + build-tags: + - integration + allow-parallel-runners: true +linters: + default: none + enable: + - canonicalheader + - dogsled + - dupl + - errcheck + - errorlint + - extraneousnew + - fmtpercentv + - forbidigo + - gocritic + - godot + - goheader + - gosec + - govet + - ineffassign + - misspell + - modernize + - musttag + - nakedret + - nolintlint + - paralleltest + - paramcheck + - perfsprint + - redundantptr + - revive + - sliceofpointers + - staticcheck + - structfield + - tparallel + - unconvert + - unparam + - unused + - usetesting + - whitespace + settings: + errorlint: + errorf: false + asserts: true + comparison: true + forbidigo: + forbid: + - pattern: ^fmt\.Print.*$ + msg: "Do not use fmt.Print statements." + - pattern: ^reflect\.DeepEqual$ + msg: "Use cmp.Equal instead of reflect.DeepEqual" + - pattern: "^http\\.Method[A-Z][a-z]*$" + msg: "Use string literals instead of http.Method constants (https://pkg.go.dev/net/http#pkg-constants)" + - pattern: ^sort\..*$ + msg: "Use sorting functions from the slices package instead." + gocritic: + disable-all: true + enabled-checks: + - commentedOutCode + - commentFormatting + - deprecatedComment + - paramTypeCombine + goheader: + values: + regexp: + CopyrightDate: 'Copyright \d{4} ' + template: |- + {{CopyrightDate}}The go-github AUTHORS. All rights reserved. + + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. + gosec: + excludes: + # duplicates errcheck + - G104 + # int(os.Stdin.Fd()) + - G115 + # false positive + - G704 + # false positive on valid struct fields "AccessToken", "ClientSecret", "Secret", "Password" + - G117 + govet: + enable-all: true + disable: + - fieldalignment + - shadow + ineffassign: + check-escaping-errors: true + misspell: + locale: US + # extra words from https://go.dev/wiki/Spelling + extra-words: + - typo: marshall + correction: marshal + - typo: marshalled + correction: marshaled + - typo: marshalling + correction: marshaling + - typo: unmarshall + correction: unmarshal + - typo: unmarshalling + correction: unmarshaling + - typo: unmarshalled + correction: unmarshaled + - typo: unmarshalling + correction: unmarshaling + ignore-rules: + - analyses # returned by the GitHub API + - cancelled # returned by the GitHub API + nolintlint: + allow-unused: true + require-specific: true + perfsprint: + errorf: true + strconcat: false + revive: + # Set below 0.8 to enable error-strings rule. + confidence: 0.6 + rules: + - name: blank-imports + - name: bool-literal-in-expr + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: early-return + - name: empty-block + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + arguments: + - disableChecksOnConstants + - disableChecksOnVariables + - disableStutteringCheck + - name: filename-format + arguments: + - ^[_a-z][_a-z0-9]*.go$ + - name: forbidden-call-in-wg-go + - name: identical-branches + - name: identical-ifelseif-branches + - name: identical-ifelseif-conditions + - name: identical-switch-branches + - name: identical-switch-conditions + - name: if-return + - name: increment-decrement + - name: indent-error-flow + - name: inefficient-map-lookup + - name: package-comments + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: superfluous-else + - name: time-equal + - name: time-naming + - name: unexported-naming + - name: unexported-return + - name: unnecessary-format + - name: unnecessary-if + - name: unnecessary-stmt + - name: unreachable-code + - name: unused-parameter + - name: use-any + - name: use-errors-new + - name: use-fmt-print + - name: use-slices-sort + - name: use-waitgroup-go + - name: var-declaration + - name: var-naming + staticcheck: + checks: + - "all" + - "-QF1008" # allow embedded field in selector + unparam: + check-exported: true + usetesting: + context-background: true + context-todo: true + os-chdir: true + os-mkdir-temp: true + os-setenv: true + os-create-temp: true + os-temp-dir: true + custom: + extraneousnew: + type: module + description: Reports problematic usage of 'new' or '&SomeStruct{}' when a more idiomatic 'var' pointer would be more appropriate. + original-url: github.com/google/go-github/v90/tools/extraneousnew + settings: + ignored-methods: + - RateLimitService.Get + - RepositoriesService.ReplaceAllTopics + fmtpercentv: + type: module + description: Reports usage of %d or %s in format strings. + original-url: github.com/google/go-github/v90/tools/fmtpercentv + paramcheck: + type: module + description: Reports parameter naming and type convention issues. + original-url: github.com/google/go-github/v90/tools/paramcheck + settings: + # Body type names exempt from the "pass by value, not by pointer" rule. + # TODO: fix and remove these exceptions. + body-allowed-pointer-types: + - ActionsVariable + - AddProjectItemOptions + - AuthorizationRequest + - CodeScanningAlertState + - CodespaceCreateForUserOptions + - ConfigApplyOptions + - ConfigSettings + - CreateCodespaceOptions + - CreateOrUpdateIssueTypesOptions + - CreateOrgInvitationOptions + - CreateUpdateEnvironment + - CreateWorkflowDispatchEventRequest + - CustomDeploymentProtectionRuleRequest + - CustomProperty + - DependabotAlertState + - DependabotEncryptedSecret + - DependencyGraphSnapshot + - EncryptedSecret + - EnterpriseSecurityAnalysisSettings + - Hook + - ImpersonateUserOptions + - Import + - InstallationTokenListRepoOptions + - InstallationTokenOptions + - IssueComment + - IssueImportRequest + - Key + - LockIssueOptions + - MaintenanceOptions + - Organization + - PagesUpdate + - PagesUpdateWithoutCNAME + - PendingDeploymentsRequest + - ProtectionRequest + - PublishCodespaceOptions + - PullRequestBranchUpdateOptions + - PullRequestComment + - PullRequestReviewRequest + - PullRequestReviewsEnforcementUpdate + - Repository + - RepositoryAddCollaboratorOptions + - RepositoryComment + - RepositoryContentFileOptions + - RepositoryCreateForkOptions + - RequiredStatusChecksRequest + - ReviewCustomDeploymentProtectionRuleRequest + - SCIMUserAttributes + - SecretScanningAlertUpdateOptions + - SecretScanningPatternConfigsUpdateOptions + - SourceImportAuthor + - Subscription + - TeamAddTeamMembershipOptions + - TeamAddTeamRepoOptions + - TeamProjectOptions + - UpdateCodespaceOptions + - UpdateDefaultSetupConfigurationOptions + - UpdateProjectItemOptions + - UserSuspendOptions + - WorkflowsPermissionsOpt + # Body type names exempt from the "Options" suffix rule. + # TODO: fix and remove these exceptions. + body-allowed-wrong-names: + - AddProjectItemOptions + - CheckSuitePreferenceOptions + - CodespaceCreateForUserOptions + - ConfigApplyOptions + - CreateCheckRunOptions + - CreateCheckSuiteOptions + - CreateCodespaceOptions + - CreateOrUpdateIssueTypesOptions + - CreateOrgInvitationOptions + - ImpersonateUserOptions + - InstallationTokenListRepoOptions + - InstallationTokenOptions + - LockIssueOptions + - MaintenanceOptions + - PublishCodespaceOptions + - PullRequestBranchUpdateOptions + - RepositoryAddCollaboratorOptions + - RepositoryContentFileOptions + - RepositoryCreateForkOptions + - SecretScanningAlertUpdateOptions + - SecretScanningPatternConfigsUpdateOptions + - TeamAddTeamMembershipOptions + - TeamAddTeamRepoOptions + - TeamProjectOptions + - UpdateCheckRunOptions + - UpdateCodespaceOptions + - UpdateDefaultSetupConfigurationOptions + - UpdateProjectItemOptions + - UserSuspendOptions + redundantptr: + type: module + description: Reports github.Ptr(x) calls that can be replaced with &x. + original-url: github.com/google/go-github/v90/tools/redundantptr + sliceofpointers: + type: module + description: Reports usage of []*string and slices of structs without pointers. + original-url: github.com/google/go-github/v90/tools/sliceofpointers + structfield: + type: module + description: Reports mismatches between Go field and JSON, URL tag names and types. + original-url: github.com/google/go-github/v90/tools/structfield + settings: + allowed-tag-names: + - ActionsCacheUsageList.RepoCacheUsage # TODO: RepoCacheUsages ? + - AuditEntry.ExternalIdentityNameID + - AuditEntry.Timestamp + - CheckSuite.AfterSHA + - CheckSuite.BeforeSHA + - CodeSearchResult.CodeResults + - CodeSearchResult.Total + - CommitAuthor.Login + - CommitsSearchResult.Commits + - CommitsSearchResult.Total + - CreateOrgInvitationOptions.TeamID # TODO: TeamIDs + - DependencyGraphSnapshot.Sha # TODO: SHA + - Discussion.DiscussionCategory # TODO: Category ? + - EditOwner.OwnerInfo + - EnterpriseLicensedUsers.GithubComSamlNameID # TODO: GithubComSAMLNameID + - Event.RawPayload + - HookRequest.RawPayload + - HookResponse.RawPayload + - Issue.PullRequestLinks # TODO: PullRequest + - IssueImportRequest.IssueImport # TODO: Issue + - IssuesSearchResult.Issues # TODO: Items + - IssuesSearchResult.Total + - LabelsSearchResult.Labels # TODO: Items + - LabelsSearchResult.Total + - ListCheckRunsResults.Total + - ListCheckSuiteResults.Total + - ListCustomDeploymentRuleIntegrationsResponse.AvailableIntegrations + - ListDeploymentProtectionRuleResponse.ProtectionRules + - ListIDPGroupsOptions.Query + - ListProjectsOptions.Query + - OrganizationCustomRepoRoles.CustomRepoRoles # TODO: CustomRoles + - OrganizationCustomRoles.CustomRepoRoles # TODO: Roles + - PreReceiveHook.ConfigURL + - ProjectV2ItemEvent.ProjectV2Item # TODO: ProjectsV2Item + - Protection.RequireLinearHistory # TODO: RequiredLinearHistory + - ProtectionRequest.RequireLinearHistory # TODO: RequiredLinearHistory + - PullRequestComment.InReplyTo # TODO: InReplyToID + - PullRequestReviewsEnforcementRequest.BypassPullRequestAllowancesRequest # TODO: BypassPullRequestAllowances + - PullRequestReviewsEnforcementRequest.DismissalRestrictionsRequest # TODO: DismissalRestrictions + - PullRequestReviewsEnforcementUpdate.BypassPullRequestAllowancesRequest # TODO: BypassPullRequestAllowances + - PullRequestReviewsEnforcementUpdate.DismissalRestrictionsRequest # TODO: DismissalRestrictions + - Reactions.MinusOne + - Reactions.PlusOne + - RepositoriesSearchResult.Repositories + - RepositoriesSearchResult.Total + - RepositoryVulnerabilityAlert.GitHubSecurityAdvisoryID + - SecretScanningAlertLocationDetails.Startline # TODO: StartLine + - SecretScanningPatternOverride.Bypassrate # TODO: BypassRate + - StarredRepository.Repository # TODO: Repo + - Timeline.Requester # TODO: ReviewRequester + - Timeline.Reviewer # TODO: RequestedReviewer + - TopicsSearchResult.Topics # TODO: Items + - TopicsSearchResult.Total + - TotalCacheUsage.TotalActiveCachesUsageSizeInBytes # TODO: TotalActiveCachesSizeInBytes + - TransferRequest.TeamID # TODO: TeamIDs + - Tree.Entries + - User.LdapDn # TODO: LDAPDN + - UsersSearchResult.Total + - UsersSearchResult.Users + - WeeklyStats.Additions + - WeeklyStats.Commits + - WeeklyStats.Deletions + - WeeklyStats.Week + allowed-tag-types: + - APIMetaArtifactAttestations.TrustDomain # TODO: Meta + - ActionsCacheListOptions.Direction + - ActionsCacheListOptions.Key + - ActionsCacheListOptions.Ref + - ActionsCacheListOptions.Sort + - ActiveCommittersListOptions.AdvancedSecurityProduct + - AnalysesListOptions.Ref + - AnalysesListOptions.SarifID + - BranchListOptions.Protected + - CodespaceGetDefaultAttributesOptions.ClientIP + - CodespaceGetDefaultAttributesOptions.Ref + - CommitsListOptions.Since # TODO: Repositories + - CommitsListOptions.Until # TODO: Repositories + - ConfigApplyEventsOptions.LastRequestID + - CreateTag.Message # TODO: Git + - CreateTag.Object # TODO: Git + - CreateTag.Tag # TODO: Git + - CreateTag.Type # TODO: Git + - DismissalRestrictionsRequest.Apps # TODO: Repositories + - DismissalRestrictionsRequest.Teams # TODO: Repositories + - DismissalRestrictionsRequest.Users # TODO: Repositories + - EncryptedSecret.SelectedRepositoryIDs # TODO: Actions + - EncryptedSecret.Visibility # TODO: Actions + - ErrorBlock.Reason # TODO: Common + - ErrorResponse.DocumentationURL # TODO: Common + - GetAuditLogOptions.Include + - GetAuditLogOptions.Order + - GetAuditLogOptions.Phrase + - GetProvisionedSCIMGroupEnterpriseOptions.ExcludedAttributes + - GistListOptions.Since # TODO: Gists + - IssueEvent.Action # TODO: Issues + - IssueListByOrgOptions.Since + - IssueListByRepoOptions.Since # TODO: Issues + - IssueListCommentsOptions.Direction + - IssueListCommentsOptions.Sort + - License.Conditions # TODO: Licenses + - License.Limitations # TODO: Licenses + - License.Permissions # TODO: Licenses + - ListAlertsOptions.Direction + - ListAlertsOptions.Ecosystem + - ListAlertsOptions.Package + - ListAlertsOptions.Scope + - ListAlertsOptions.Severity + - ListAlertsOptions.Sort + - ListAlertsOptions.State + - ListAllIssuesOptions.Since + - ListArtifactsOptions.Name + - ListCheckRunsOptions.AppID + - ListCheckRunsOptions.CheckName + - ListCheckRunsOptions.Filter + - ListCheckRunsOptions.Status + - ListCheckSuiteOptions.AppID + - ListCheckSuiteOptions.CheckName + - ListCostCenterOptions.State + - ListExternalGroupsOptions.DisplayName + - ListGlobalSecurityAdvisoriesOptions.Affects + - ListGlobalSecurityAdvisoriesOptions.CVEID + - ListGlobalSecurityAdvisoriesOptions.Ecosystem + - ListGlobalSecurityAdvisoriesOptions.GHSAID + - ListGlobalSecurityAdvisoriesOptions.IsWithdrawn + - ListGlobalSecurityAdvisoriesOptions.Modified + - ListGlobalSecurityAdvisoriesOptions.Published + - ListGlobalSecurityAdvisoriesOptions.Severity + - ListGlobalSecurityAdvisoriesOptions.Type + - ListGlobalSecurityAdvisoriesOptions.Updated + - ListLicensesOptions.Featured + - ListProvisionedSCIMGroupsEnterpriseOptions.Count # TODO: Enterprise + - ListProvisionedSCIMGroupsEnterpriseOptions.ExcludedAttributes # TODO: Enterprise + - ListProvisionedSCIMGroupsEnterpriseOptions.Filter # TODO: Enterprise + - ListProvisionedSCIMGroupsEnterpriseOptions.StartIndex # TODO: Enterprise + - ListProvisionedSCIMUsersEnterpriseOptions.Count + - ListProvisionedSCIMUsersEnterpriseOptions.Filter + - ListProvisionedSCIMUsersEnterpriseOptions.StartIndex + - ListRepoMachineTypesOptions.ClientIP + - ListRepoMachineTypesOptions.Location + - ListRepoMachineTypesOptions.Ref + - ListRunnersOptions.Name + - ListSCIMProvisionedIdentitiesOptions.Count + - ListSCIMProvisionedIdentitiesOptions.Filter + - ListSCIMProvisionedIdentitiesOptions.StartIndex + - ListUserIssuesOptions.Since + - LockIssueOptions.LockReason # TODO: Issues + - MarketplacePlan.Bullets # TODO: Marketplaces + - NodeQueryOptions.ClusterRoles + - NodeQueryOptions.UUID + - NotificationListOptions.Before # TODO: Activities + - NotificationListOptions.Since # TODO: Activities + - PackageListOptions.PackageType + - PackageListOptions.State + - PackageListOptions.Visibility + - PremiumRequestUsageReportOptions.Day + - PremiumRequestUsageReportOptions.Model + - PremiumRequestUsageReportOptions.Month + - PremiumRequestUsageReportOptions.Product + - PremiumRequestUsageReportOptions.User + - PremiumRequestUsageReportOptions.Year + - PullRequestListCommentsOptions.Since # TODO: PullRequests + - Rate.Resource # TODO: Common + - RepositoryAddCollaboratorOptions.Permission # TODO: Repositories + - RepositoryCreateForkOptions.DefaultBranchOnly # TODO: Repositories + - RepositoryCreateForkOptions.Name # TODO: Repositories + - RepositoryCreateForkOptions.Organization # TODO: Repositories + - RepositoryListRulesetsOptions.IncludesParents + - RequiredStatusChecks.Checks # TODO: Repositories + - RequiredStatusChecks.Contexts # TODO: Repositories + - SearchOptions.AdvancedSearch + - Secret.SelectedRepositoriesURL # TODO: Actions + - Secret.Visibility # TODO: Actions + - TeamAddTeamMembershipOptions.Role # TODO: Teams + - TeamAddTeamRepoOptions.Permission # TODO: Teams + - UpdateRuleParameters.UpdateAllowsFetchAndMerge # TODO: Rules + - UsageReportOptions.Day + - UsageReportOptions.Hour + - UsageReportOptions.Month + - UsageReportOptions.Year + - WorkflowRunAttemptOptions.ExcludePullRequests + exclusions: + rules: + - linters: + - dogsled + - dupl + - gosec + - unparam + path: _test\.go + + # Allow fmt.Print in examples, internal tools, and tests. + - linters: [forbidigo] + path: ^(example|tools|test|scrape)\/ + text: 'fmt\.Print' + + # We need to pass nil Context in order to test DoBare erroring on nil ctx. + - linters: [staticcheck] + path: _test\.go + text: 'SA1012: do not pass a nil Context' + + # We need to use sha1 for validating signatures + - linters: [gosec] + text: 'G505: Blocklisted import crypto/sha1: weak cryptographic primitive' + + # This is adapted from golangci-lint's default exclusions. It disables linting for error checks on + # os.RemoveAll, fmt.Fprint*, fmt.Scanf, and any function ending in "Close". + - linters: [errcheck] + text: Error return value of .(.*Close|fmt\.Fprint.*|fmt\.Scanf|os\.Remove(All)?). is not checked + + # We don't care about gosec issues in examples or internal tools. + - linters: [gosec] + path: ^(example|tools)\/ + text: '(G304|G703|G705|G706)' + + # We don't run parallel integration tests + - linters: [paralleltest, tparallel] + path: ^test/integration + + # Because fmt.Sprint(reset.Unix())) is more readable than strconv.FormatInt(reset.Unix(), 10). + - linters: [perfsprint] + text: fmt.Sprint.* can be replaced with faster strconv.FormatInt +issues: + max-issues-per-linter: 0 + max-same-issues: 0 + # paramcheck reports multiple distinct issues on the same parameter line. + uniq-by-line: false +formatters: + enable: + - gci + - gofumpt + settings: + gofumpt: + extra-rules: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b1eff617acc..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: go - -go: - - "1.x" - - "1.12.x" - - master - -matrix: - allow_failures: - - go: master - fast_finish: true - -cache: - directories: - - $HOME/.cache/go-build - - $HOME/gopath/pkg/mod - -env: - global: - - GO111MODULE=on - -script: - - diff -u <(echo -n) <(gofmt -d -s .) - - go generate -x ./... && git diff --exit-code; code=$?; git checkout -- .; (exit $code) # Check that go generate ./... produces a zero diff; clean up any changes afterwards. - - go vet ./... - - go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... - - go test -v -tags=integration -run=^$ ./test/integration # Check that integration test builds successfully, but don't run any of the tests (they hit live GitHub API). - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..42f9c51e198 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. + +See [CONTRIBUTING.md](./CONTRIBUTING.md) for everything you need, in particular: + +- [Submitting a patch](./CONTRIBUTING.md#submitting-a-patch) - workflow and the scripts to run before submitting (`script/fmt.sh`, `script/test.sh`, `script/lint.sh`, `script/generate.sh`). +- [Tips](./CONTRIBUTING.md#tips) - expectations for (AI-driven) PRs. +- [Code Guidelines](./CONTRIBUTING.md#code-guidelines) - file organization, naming conventions, type conventions, JSON tags, pagination, generated code, and testing patterns. diff --git a/AUTHORS b/AUTHORS index fa0f1abc935..2270852e562 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,245 +9,586 @@ # their employer, as appropriate). 178inaba +2BFL +413x +6543 <6543@obermui.de> +Aaron LaBrie +Abed Kibbe +Abhijit Hota Abhinav Gupta +abhishek +Abhishek Sharma +Abhishek Veeramalla +Abinand P +aboy +Adam Guthrie +Adam Kohring +Aditya Mahendrakar adrienzieba +afdesk +Ahmad Nurus S Ahmed Hagy +Aidan Aidan Steele Ainsley Chong +ajz01 Akeda Bagus Akhil Mohan Alec Thomas Aleks Clark Alex Bramley +Alex Ellis +Alex Orr +Alex Su +Alex Unger Alexander Harkness +Alexey Alekhin +Alexis Couvreur +Alexis Gauthiez +Ali Farooq +Alin Balutoiu +Allan Guwatudde Allen Sun +Aman Verma Amey Sakhadeo +Anders Janmyr +Andreas Deininger Andreas Garnæs +Andreas Jaggi +Andrew Gillis Andrew Ryabchun +Andrew Svoboda +Andriyun Andy Grunwald Andy Hume Andy Lindeman +angie pinilla +Anish Rajan anjanashenoy Anshuman Bhartiya Antoine Antoine Pelisse +Anton Nguyen Anubha Kushwaha appilon +aprp +apurwaj2 Aravind Arda Kuyumcu +Ary Arıl Bozoluk +Asier Marruedo +Austen Stone +Austin Burdine Austin Dizzy +Azuka Okuleye Ben Batha Benjamen Keroack +Benjamin Nater +Berkay Tacyildiz Beshr Kayali Beyang Liu +billnapier +Billy Keyes Billy Lynch +Bingtan Lu +Bjorn Neergaard Björn Häuser +Bo Huang +boljen +Bracken Brad Harris Brad Moylan Bradley Falzon +Bradley McAllister +Brandon Butler Brandon Cook +Brandon Everett +Brandon Stubbs +Brett Kuhlman +Brett Logan Brian Egizi Bryan Boreham +Bryan Peterson Cami Diez +Carl Johnson Carlos Alexandro Becker Carlos Tadeu Panato Junior +Casey +ChandanChainani chandresh-pancholi +Changyong Um Charles Fenwick Elliott Charlie Yan +Chmouel Boudjnah +Chris Allen Lane Chris King +Chris Mc +Chris Raborg Chris Roche Chris Schaefer chrisforrette +Christian Bargmann Christian Muehlhaeuser +Christoph Jerolimov Christoph Sassenberg +CI Monk +Claus Näveke +Clemens W +cointem +Colby Williams Colin Misare +Craig Gumbley Craig Peterson Cristian Maglie +Cyb3r Jak3 Daehyeok Mun +Dalton Hubble +Daniel Lanner Daniel Leavitt Daniel Nilsson +Daoq Dave Du Cros Dave Henderson +Dave Perrett +Dave Protasowski David Deng +David Gamba +David J. M. Karlsen David Jannotta David Ji +David Lopez Reyes Davide Zipeto Dennis Webb +Derek Jobst +DeviousLab +Dhananjay Mishra Dhi Aurrahman Diego Lapiduz +Diogo Vilela +Dion Gionet Mallet Dmitri Shuralyov dmnlk Don Petersen Doug Turner Drew Fradette +Dustin Deus +Dustin Lish +Eivind Eli Uriegas Elliott Beach Emerson Wood +Emil V +Emma Sax +Eng Zer Jun +Enrico Candino eperm Erick Fejta +Erik Elkins +Erik Nobel +Erwan Finot erwinvaneyk +Evan Anderson Evan Elias +Eyal Kapon +Fabian Holler Fabrice +Fatema-Moaiyadi +Federico Di Pierro Felix Geisendörfer Filippo Valsorda Florian Forster +Florian Maier +Florian Wagner Francesc Gil Francis +Francisco Guimarães +François de Metz Fredrik Jönsson +Gabriel +Gal Ofri Garrett Squire George Kontridze Georgy Buranov +Glen Mailer Gnahz Google Inc. Grachev Mikhail +Gregor Jasny +Gregory Oschwald griffin_stewie +guangwu Guillaume Jacquet Guz Alexander Guðmundur Bjarni Ólafsson Hanno Hecker Hari haran +Harikesh00 +haya14busa haya14busa +Henrik Lundström +Hiroki Ito +hnkƶ +Hubot Jr Huy Tr huydx i2bskn +Iain Steers +Iehana Fu +Ikko Ashimine +Ilia Choly +Ioannis Georgoulas Isao Jonas +ishan upadhyay isqua +Ivan Martos +Jacob Valdemar +Jake Krammer +Jake Scaltreto +Jake White Jameel Haffejee +James Alseth +James Bowes +James Cockbain +James Loh +James Maguire +James Turley +Jamie West +Jan Guth Jan Kosecki +Jan Niklas Dittmar +Jan Švábík +Jason Brill +Jason Field Javier Campanini +Jef LeCompte +Jeff Wenzbauer Jens Rantil Jeremy Morris +Jesse Haka Jesse Newland Jihoon Chung +Jille Timmermans Jimmi Dyson +Jiří Žižkovský Joan Saum +JoannaaKL Joe Tsai John Barton John Engelman +John Jones +John Liu Jordan Brockopp +Jordan Burandt Jordan Sussman +Jorge Ferrero +Jorge Gómez Reus Joshua Bezaleel Abednego +Joshua French +João Cerqueira JP Phillips jpbelanger-mtl +Juan Juan Basso Julien Garcia Gonzalez +Julien Midedji Julien Rostand Junya Kono Justin Abrahms +Justin Toh Jusung Lee jzhoucliqr +k0ral +k1rnt +kadern0 +Karthik Sundari Katrina Owen -Kautilya Tripathi < tripathi.kautilya@gmail.com> Kautilya Tripathi Keita Urashima Kevin Burke +Kevin Wang +Kevin Zhao +kgalli +Khanh Ngo +Kirill Konrad Malawski Kookheon Kwon +Krishna Indani Krzysztof Kowalczyk Kshitij Saraogi +Kumar Saurabh +Kyle Kurz kyokomi +Lachlan Cooper +Lars Lehtonen Laurent Verdoïa +Leonard Sheng Sheng Lee +leopoldwang Liam Galvin +Liam Stanley +Lluis Campos Lovro Mažgon +Loïs Postula +Luca Campese Lucas Alcantara +Lucas Martin-King +Luis Davim Luke Evers +Luke Hinds Luke Kysow Luke Roberts Luke Young +lynn [they] +Léo Salé +M. Ryan Rigdon +Magnus Kulke Maksim Zhylinski +Manas Sivakumar +Manav Sharma +Manuel Bergler +Marc Binder +Marcelo Carlos Mark Tareshawty +Martin Holman Martin-Louis Bright +Martins Sipenko Marwan Sulaiman Masayuki Izumi Mat Geist +Matheus Santos Araújo +MathewClegg +Mathieu Sévégny +Matija Horvat +Matin Rahmanian Matt Matt Brender +Matt Dainty Matt Gaunt Matt Landis +Matt Mencel +Matt Moore +Matt Simons +Matthew Reidy Maxime Bury +Michael Meng +Michael Recachinas Michael Spiegel Michael Tiller Michał Glapa +Michelangelo Morrillo +Miguel Elias dos Santos +Mike Ball +Mike Chen +Miles Crabill +Mishin Nikolai +Mohamad Al-Zawahreh +mohammad ali <2018cs92@student.uet.edu.pk> +Mohammed AlDujaili +Mohammed Nafees +Mudit +Mukundan Senthil +Munia Balayil +Mustafa Abban Nadav Kaner +Naoki Kanatani Nathan VanBenschoten Navaneeth Suresh +Nayeem Hasan +Neal Caffery Neil O'Toole +Nicholas Herring Nick Miyake +Nick Platt Nick Spragg +Nicolas Chapurlat Nikhita Raghunath +Nikita Pivkin +Nilesh Singh +Noah Hanjun Lee Noah Zoschke +Noble Varghese ns-cweber +nxya +Ole Orhagen Oleg Kovalov +Oleksandr Redko Ondřej Kupka +Ori Talmor +Osama Faqhruldin +oslowalk +Pablo Pérez Schröder Palash Nigam Panagiotis Moustafellos Parham Alvani +pari-27 Parker Moore parkhyukjun89 +Pat Alwell +Patrick DeVivo +Patrick Marabeas +Patrik Nordlén +Pavel Dvoinos Pavel Shtanko +Pavlos Tzianos +Pedja Muatovic Pete Wagner Petr Shevtsov +Pierce McEntagart Pierre Carrier Piotr Zurek +Piyush Chugh +Pj Meyer +Pratik Mallya +Qais Patankar +Quang Le Hong Quentin Leffray Quinn Slack Rackspace US, Inc. Radek Simko Radliński Ignacy +Rafael Aramizu Gomes +Raisa Kabir +Rajat Jindal Rajendra arora +Rajkumar +Ramesh Gaikwad Ranbir Singh +Ravi Shekhar Jethani RaviTeja Pothana rc1140 Red Hat, Inc. +Reetuparna Mukherjee +reeves122 +Reinier Timmer +Renjith R +Rez Moss +Riaje Ricco Førgaard +Richard de Vries Rob Figueiredo +Roger Peppe Rohit Upadhyay +Rojan Dinc +Roman Wu +Roming22 Ronak Jain +Ronan Pelliard +Ross Gustafson Ruben Vereecken +Rufina Talalaeva +Rupok Ghosh +Russell Boley Ryan Leung Ryan Lower +Ryan Skidmore +Ryo Nakao +Saaarah +Safwan Olaimat Sahil Dua +Sai Ravi Teja Chintakrindi saisi Sam Minnée Sandeep Sukhani +Sander Knape Sander van Harmelen +sanjay s Sanket Payghan +Sarah Funkhouser Sarasa Kisaragi +Sasha Melentyev +Scott Dawson Sean Wang Sebastian Mandrean Sebastian Mæland Pedersen +sellskin +Sergei Popinevskii Sergey Romanov +Sergio Garcia +Seth Vargo Sevki Shagun Khemka shakeelrao Shawn Catanzarite Shawn Smith +Shibasis Patel +Sho Okada Shrikrishna Singh +Silas Alberti +Simon Davis sona-tar +soniachikh SoundCloud, Ltd. Sridhar Mocherla SriVignessh Pss +Stefan Sedich +Steve Hipwell +Steve Teuber Stian Eikeland +Suhaib Mujahid +sushmita wable +Sven Palberg Szymon Kodrebski +Søren Hansen +T.J. Corrigan +Takashi Yoneuchi +Takayuki Watanabe +Taketoshi Fujiwara +Taketoshi Fujiwara +Takuma Kajikawa Tasya Aditya Rukmana +Theo Henson +Theofilos Petsios +Thomas Aidan Curran Thomas Bruyelle +Tim +Tim Rogers +Timothy O'Brien Timothée Peignier +Tingluo Huang +tkhandel +Tobias Gesellchen +Tom Bamford +Tom Payne +Tomasz Adam Skrzypczak +tomfeigin +Travis Tomsu Trey Tacon +tsbkw ttacon Vaibhav Singh Varadarajan Aravamudhan Victor Castell Victor Vrantchan +Victory Osikwemhe +vikkyomkar +Ville Skyttä +Vivek +Vivek Kumar Sahu Vlad Ungureanu Wasim Thabraze +Weslei Juan Moser Pereira +Wheeler Law Will Maier +Will Norris +Willem D'Haeseleer William Bailey William Cooke +Xabi xibz Yann Malet Yannick Utard +Yarden Shoham Yicheng Qin Yosuke Akatsuka Yumikiyo Osanai +Yurii Soldak +Yusef Mohamadi +Yusuke Kuoka +Yuto Kimura Zach Latta +Ze Peng +zhouhaibing089 +zyfy29 +六开箱 +缘生 +蒋航 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2a2b282db1..936c7c7349f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,9 @@ -# How to contribute # +# How to contribute -We'd love to accept your patches and contributions to this project. There are -a just a few small guidelines you need to follow. +We'd love to accept your patches and contributions to this project. +There are just a few small guidelines you need to follow. - -## Contributor License Agreement ## +## Contributor License Agreement Contributions to any Google project must be accompanied by a Contributor License Agreement. This is not a copyright **assignment**, it simply gives @@ -16,82 +15,657 @@ You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. - -## Reporting issues ## +## Reporting issues Bugs, feature requests, and development-related questions should be directed to -our [GitHub issue tracker](https://github.com/google/go-github/issues). If +our [GitHub issue tracker](https://github.com/google/go-github/issues). If reporting a bug, please try and provide as much context as possible such as your operating system, Go version, and anything else that might be relevant to -the bug. For feature requests, please explain what you're trying to do, and +the bug. For feature requests, please explain what you're trying to do, and how the requested feature would help you do that. Security related bugs can either be reported in the issue tracker, or if they are more sensitive, emailed to . -## Submitting a patch ## +## Reviewing PRs + +In addition to writing code, community projects also require community +contributions in other ways; one of these is reviewing code contributions. If +you are willing to review PRs please open a PR to add your GitHub username to +the [REVIEWERS](./REVIEWERS) file. By adding your GitHub username to the list +of reviewers you are giving contributors permission to request a review for a +PR that has already been approved by a maintainer. If you are asked to review a +PR and either do not have the time or do not think you are able to you should +feel comfortable politely saying no. + +If at any time you would like to remove your permission to be contacted for a +review you can open a PR to remove your name from the [REVIEWERS](./REVIEWERS) +file. + +## Submitting a patch - 1. It's generally best to start by opening a new issue describing the bug or - feature you're intending to fix. Even if you think it's relatively minor, - it's helpful to know what people are working on. Mention in the initial - issue that you are planning to work on that bug or feature so that it can - be assigned to you. +1. It's generally best to start by opening a new issue describing the bug or + feature you're intending to fix. Even if you think it's relatively minor, + it's helpful to know what people are working on. Mention in the initial issue + that you are planning to work on that bug or feature so that it can be + assigned to you. - 1. Follow the normal process of [forking][] the project, and setup a new - branch to work in. It's important that each group of changes be done in - separate branches in order to ensure that a pull request only includes the - commits related to that bug or feature. +2. Follow the normal process of [forking][] the project, and set up a new branch + to work in. It's important that each group of changes be done in separate + branches in order to ensure that a pull request only includes the commits + related to that bug or feature. - 1. Go makes it very simple to ensure properly formatted code, so always run - `go fmt` on your code before committing it. You should also run - [golint][] over your code. As noted in the [golint readme][], it's not - strictly necessary that your code be completely "lint-free", but this will - help you find common style issues. +3. Any significant changes should almost always be accompanied by tests. The + project already has good test coverage, so look at some of the existing tests + if you're unsure how to go about it. Coverage is [monitored by codecov.io][], + which flags pull requests that decrease test coverage. This doesn't + necessarily mean that PRs with decreased coverage won't be merged. Sometimes + a decrease in coverage makes sense, but if your PR is flagged, you should + either add tests to cover those lines or add a PR comment explaining the + untested lines. - 1. Any significant changes should almost always be accompanied by tests. The - project already has good test coverage, so look at some of the existing - tests if you're unsure how to go about it. [gocov][] and [gocov-html][] - are invaluable tools for seeing which parts of your code aren't being - exercised by your tests. +4. Run `script/fmt.sh`, `script/test.sh` and `script/lint.sh` to format your code and + check that it passes all tests and linters. `script/lint.sh` may also tell you + that generated files need to be updated. If so, run `script/generate.sh` to + update them. - 1. Please run: - * `go generate github.com/google/go-github/...` - * `go test github.com/google/go-github/...` - * `go vet github.com/google/go-github/...` +5. Do your best to have [well-formed commit messages][] for each change. This + provides consistency throughout the project, and ensures that commit messages + are able to be formatted properly by various git tools. See next section for + more details. - 1. Do your best to have [well-formed commit messages][] for each change. - This provides consistency throughout the project, and ensures that commit - messages are able to be formatted properly by various git tools. +6. Finally, push the commits to your fork and submit a [pull request][]. + **NOTE:** Please do not use force-push on PRs in this repo, as it makes it + more difficult for reviewers to see what has changed since the last code + review. We always perform "squash and merge" actions on PRs in this repo, so it doesn't + matter how many commits your PR has, as they will end up being a single commit after merging. + This is done to make a much cleaner `git log` history and helps to find regressions in the code + using existing tools such as `git bisect`. - 1. Finally, push the commits to your fork and submit a [pull request][]. + - If your PR needs additional reviews you can request one of the + [REVIEWERS][] takes a look by mentioning them in a PR comment. [forking]: https://help.github.com/articles/fork-a-repo -[golint]: https://github.com/golang/lint -[golint readme]: https://github.com/golang/lint/blob/master/README.md -[gocov]: https://github.com/axw/gocov -[gocov-html]: https://github.com/matm/gocov-html -[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html -[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits +[well-formed commit messages]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html [pull request]: https://help.github.com/articles/creating-a-pull-request +[monitored by codecov.io]: https://codecov.io/gh/google/go-github +[REVIEWERS]: ./REVIEWERS + +### Use proper commit messages and PR titles + +Effective Git commit messages and subject lines hold immense significance in comprehending alterations and enhancing the code's maintainability. + +Always commit the changes to your fork and push them to the corresponding original repo by sending a Pull Request (PR). Follow the best practices for writing commit messages/PR titles. + +1. Limit the subject line to 50 characters +2. Capitalize the subject line +3. Do not end the subject line with a period +4. Use the imperative mood in the subject line. A properly formed Git commit subject line should always be able to complete the following sentence: + If applied, this commit will `` + +(This above advice can be found all over the internet, but was copied from [here](https://learn-ballerina.github.io/best_practices/use_proper_titles.html).) + +5. You may optionally prefix the PR title with the type of PR it is, in lower case, + followed by a colon. For example, `feat:`, `chore:`, `fix:`, `docs:`, etc. + For breaking API changes, add an exclamation point. + For example, `feat!:`, `chore!:`, `fix!:`, etc. + +### Windows users + +Use Git Bash as a terminal or WSL instead of PowerShell. + +To avoid [issues][] with a few linters and formatters within golangci-lint, +make sure you check out files only with LF endings: + +```sh +git config core.autocrlf false +git config core.eol lf +``` + +To convert an existing cloned repo from CRLF to LF, use the following commands: + +```sh +git config core.autocrlf false +git rm --cached -r . +git reset --hard HEAD +``` + +[issues]: https://github.com/golangci/golangci-lint/discussions/5840 + +## Tips + +Although we have not (yet) banned AI-driven contributions to this repo (as many +other open source projects have), we encourage you to read and honor the following +tips (which are frequently ignored by AI-driven PRs): + +* Always review your own PRs using the same user interface that your actual + reviewers will be using with a critical eye, attempting to anticipate what your + reviewers will call out, _before_ asking anyone to review your PR. +* Come up with a short and appropriate PR title. +* Come up with a short, well-written, and appropriate PR description (we don't + need hundreds of lines of text here - keep it short and to-the-point so a + reviewer can _quickly_ determine what the PR is all about). +* If a PR involves bug fixes, it should certainly include a unit test (or tests) + that demonstrates the bug - without the PR changes, the new unit test would + fail, but with the included PR changes, the new test(s) pass. +* When possible, try to make smaller, focused PRs (which are easier to review + and easier for others to understand). +* When a reviewer leaves a comment on one occurrence of an issue, apply the + same fix to all similar occurrences throughout the entire PR - don't wait + for the reviewer to point out each one individually. + +### Assisted contributions + +Tools that help draft or generate code are allowed, but contributors remain +responsible for understanding and testing every line they submit. If an +AI/LLM tool helped with code, tests, or prose, briefly disclose what it did in +the PR description. Review the rendered diff and description yourself before +requesting review, remove irrelevant generated changes, and keep review +comments specific and actionable. + +## Code Guidelines + +This section documents common code patterns and conventions used throughout +the go-github repository. Following these guidelines helps maintain consistency +and makes the codebase easier to understand and maintain. + +### Code Comments + +Every exported method and type needs to have code comments that follow +[Go Doc Comments][]. A typical method's comments will look like this: + +```go +// Get fetches a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#get-a-repository +// +//meta:operation GET /repos/{owner}/{repo} +func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v", owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + // ... +} +``` +And the returned type `Repository` will have comments like this: + +```go +// Repository represents a GitHub repository. +type Repository struct { + ID *int64 `json:"id,omitempty"` + Owner *User `json:"owner,omitempty"` + // ... +} +``` + +The first line is the name of the method followed by a short description. This +could also be a longer description if needed, but there is no need to repeat any +details that are documented in GitHub's documentation because users are expected +to follow the documentation links to learn more. + +After the description comes a link to the GitHub API documentation. This is +added or fixed automatically when you run `script/generate.sh`, so you won't +need to set this yourself. + +Finally, the `//meta:operation` comment is a directive to the code generator +that maps the method to the corresponding OpenAPI operation. Once again, there +can be multiple directives for methods that call multiple +endpoints. `script/generate.sh` will normalize these directives for you, so if +you are adding a new method you can use the pattern from the `u := fmt.Sprintf` +line instead of looking up what the url parameters are called in the OpenAPI +description. + +[Go Doc Comments]: https://go.dev/doc/comment + +### File Organization + +Files are organized by service and API endpoint, following the pattern +`{service}_{api}.go`. For example: +- `repos_contents.go` - Repository contents API methods +- `users_ssh_signing_keys.go` - User SSH signing keys API methods +- `orgs_rules.go` - Organization rules API methods + +Test files follow the pattern `{service}_{api}_test.go`. + +These services map directly to how the [GitHub API documentation][] is +organized, so use that as your guide for where to put new methods. + +For example, methods defined at live in [repos_hooks.go](https://github.com/google/go-github/blob/master/github/repos_hooks.go). + +[GitHub API documentation]: https://docs.github.com/en/rest + +### Services + +The API is split into services, one per logical area of the GitHub API +(e.g. `RepositoriesService`, `UsersService`). Each service is a named type +over the shared `service` struct, which holds a back-reference to the `Client`: + +```go +type service struct { + client *Client +} + +type RepositoriesService service +``` + +All services share a single `service` value embedded in the `Client` as +`common`, so adding a service does not allocate. To add a new service: + +1. Add a field for it on the `Client` struct, keeping the list alphabetical +2. Wire it up in `newClient` (alongside the other `c.X = (*XService)(&c.common)` lines, also kept alphabetical). +3. Declare the service type in the service's file (e.g. `repos.go`) and hang the + methods off it, using `s` as the receiver (see [Receiver Names](#receiver-names)). + +### Naming Conventions + +#### Receiver Names + +Service method receivers consistently use the single letter `s`: + +```go +func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) { + // ... +} +``` + +#### Method Names + +Methods use descriptive names that clearly indicate their action. +The method name should not repeat the scope already defined by the service: + +```go +// On OrganizationsService, the scope is already "organization": +func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opts *OrganizationListMembersOptions) ([]*User, *Response, error) + +// On EnterpriseService, the scope is already "enterprise": +func (s *EnterpriseService) GetLicenseInfo(ctx context.Context) (*LicenseInfo, *Response, error) +``` + +Common method name prefixes: +- `Get` - Retrieve a single resource +- `List` - Retrieve multiple resources (supports pagination) +- `Create` - Create a new resource +- `Update` - Update an existing resource +- `Delete` - Delete a resource + +#### Common Variable Names + +- `ctx` - Context +- `u` - URL string +- `req` - HTTP request +- `resp` - HTTP response +- `result` - Result from API call +- `err` - Error +- `opts` - Options parameter +- `body` - Body parameter +- `owner` - Repository owner (username or organization) +- `repo` - Repository name +- `org` - Organization name +- `user` - Username +- `team` - Team name or slug +- `project` - Project name or number + +### Type Conventions + +#### Value vs Pointer Parameters + +Prefer value types over pointer types for parameters where the distinction +between "zero" and "unset" is not needed, or where the type is small and +cheap to copy (e.g., `string`, `int`, `int64`, `bool`). Use pointer types +when you need to distinguish between an unset value and a zero value. +See [#3644][] and [#3887][] for background discussion. + +[#3644]: https://github.com/google/go-github/pull/3644 +[#3887]: https://github.com/google/go-github/pull/3887 + +#### Creating Pointers + +Pointer fields are common because many request and response fields are +optional. Use the generic `Ptr` helper to take the address of a literal +instead of declaring an intermediate variable: + +```go +repo := &Repository{ + Name: Ptr("go-github"), + Private: Ptr(true), + ID: Ptr(int64(1)), +} +``` + +#### Request Option Types + +Request option types should be used for query parameters and named after the method they +modify, with the suffix `Options`: + +```go +type RepositoryListOptions struct { + Type string `url:"type,omitempty"` + Sort string `url:"sort,omitempty"` + Direction string `url:"direction,omitempty"` + ListOptions +} +``` + +#### Request Body Types + +Request body types for POST/PUT/PATCH requests should use the `Request` +suffix for create and update operations: + +```go +type CreateHostedRunnerRequest struct { + Name string `json:"name"` + RunnerGroupID int64 `json:"runner_group_id"` + MaximumRunners *int64 `json:"maximum_runners,omitempty"` + // ... +} +``` +#### Response Types -## Other notes on code organization ## +Response types are named after the resource they represent, typically without +any suffix: -Currently, everything is defined in the main `github` package, with API methods -broken into separate service objects. These services map directly to how -the [GitHub API documentation][] is organized, so use that as your guide for -where to put new methods. +```go +type Repository struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + FullName *string `json:"full_name,omitempty"` + Description *string `json:"description,omitempty"` + // ... +} +``` -Code is organized in files also based pretty closely on the GitHub API -documentation, following the format `{service}_{api}.go`. For example, methods -defined at live in -[repos_hooks.go][]. +#### Common Structs -[GitHub API documentation]: https://developer.github.com/v3/ -[repos_hooks.go]: https://github.com/google/go-github/blob/master/github/repos_hooks.go +- `ListOptions` - For offset-based pagination (page/per_page) +- `ListCursorOptions` - For cursor-based pagination +- `UploadOptions` - For file uploads +- `Response` - Wraps the HTTP response +### JSON Tags -## Maintainer's Guide ## +#### Request Bodies + +Required fields should be non-pointer types without `omitempty`. +Optional fields should be pointer types with `omitempty`. +Use `omitzero` for structs and `time.Time` where you want to omit +empty values (not just nil). For slices and maps, `omitzero` has the +opposite behavior: it keeps empty (non-nil) values and only omits nil +values. + +```go +type RepositoryRuleset struct { + // ID is optional. + ID *int64 `json:"id,omitempty"` + + // Name is required. + Name string `json:"name"` + + // Target is optional struct. + Target *RulesetTarget `json:"target,omitempty"` + + // BypassActors is optional. + BypassActors []*BypassActor `json:"bypass_actors,omitzero"` + + // CreatedAt is optional. + CreatedAt *Timestamp `json:"created_at,omitempty"` + + // ... +} +``` + +For optional boolean fields where you need to distinguish between `false` +and "not set", use `*bool` with `omitzero`. + +#### Response Bodies + +Follow the same conventions as request bodies for `omitempty` and +`omitzero` usage. Optional fields should use pointer types with +`omitempty`, and required fields should prefer non-pointer types. +See [Common Types](#common-types) for conventions on ID, Node ID, and Timestamp. + +### URL Tags for Query Parameters + +All fields should use `url` tags with `omitempty` to omit zero values +from the query string: + +```go +type RepositoryListOptions struct { + Type string `url:"type,omitempty"` + Sort string `url:"sort,omitempty"` + Direction string `url:"direction,omitempty"` + ListOptions +} +``` + +### Pagination + +The go-github library supports two types of pagination: + +#### Offset-based Pagination + +Use `ListOptions` for APIs that use page/per_page parameters: + +```go +type ListOptions struct { + Page int `url:"page,omitempty"` + PerPage int `url:"per_page,omitempty"` +} +``` + +#### Cursor-based Pagination + +Use `ListCursorOptions` for APIs that use cursor-based pagination: + +```go +type ListCursorOptions struct { + Page string `url:"page,omitempty"` + PerPage int `url:"per_page,omitempty"` + First int `url:"first,omitempty"` + Last int `url:"last,omitempty"` + After string `url:"after,omitempty"` + Before string `url:"before,omitempty"` + Cursor string `url:"cursor,omitempty"` +} +``` + +Embed the appropriate pagination options in your option structs +based on the API's pagination model: use `ListOptions` for +offset-based APIs and `ListCursorOptions` for cursor-based APIs. +The library automatically generates iterator methods (e.g., `ListIter`) +for methods that start with `List` and return a slice. + +For APIs with non-standard pagination behavior (e.g., methods that +return a wrapper struct containing multiple slices), configuration maps +in `gen-iterators.go` can be adjusted — including `useCursorPagination`, +`customNames`, `sliceToBeUsedForIteration`, and `customTestJSON`. + +### Common Types + +#### ID Fields + +GitHub API IDs are usually `int64`. Use non-pointer `int64` +if the ID is required and `*int64` if the ID is optional. + +```go +type CreateHostedRunnerRequest struct { + RunnerGroupID int64 `json:"runner_group_id"` + // ... +} +``` + +#### Node ID Fields + +Node IDs are usually `string`: + +```go +type IssueFieldValue struct { + NodeID string `json:"node_id"` + // ... +} +``` + +#### Timestamp Fields + +Use the `Timestamp` type for all date/time fields: + +```go +type Repository struct { + CreatedAt *Timestamp `json:"created_at,omitempty"` + // ... +} +``` + +### Generated Code + +Some files are generated and must never be edited by hand. +When you add or change a struct, run `script/generate.sh` to regenerate them. + +So after adding a field you typically only write the struct field itself; +the accessor and stringify code follow from `script/generate.sh`. +`script/lint.sh` will fail if these files are out of date. +Documentation links and `//meta:operation` directives are updated +by the same script (see [Code Comments](#code-comments)). + +### Testing + +Tests use a real `httptest` server rather than mocks. Call `setup` to get a +`Client` pointed at a test server plus the `mux` to register handlers on, then +assert on the request and the decoded response. A typical test looks like this: + +```go +func TestRepositoriesService_GetByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repositories/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1,"name":"n"}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.GetByID(ctx, 1) + if err != nil { + t.Fatalf("Repositories.GetByID returned error: %v", err) + } + + want := &Repository{ID: Ptr(int64(1)), Name: Ptr("n")} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.GetByID returned %+v, want %+v", got, want) + } + + const methodName = "GetByID" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetByID(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} +``` + +Conventions to follow: + +- Call `t.Parallel()` and use `t.Context()` for the request context. +- Register handlers on `mux` with relative paths (a leading `/`); the test + server fails requests built from absolute URLs to catch that mistake. +- Use the shared helpers instead of reimplementing assertions: + - `testMethod` - asserts the HTTP method. + - `testFormValues` - asserts query parameters. + - `testHeader` - asserts a request header. + - `testJSONBody` / `testPlainBody` - assert the request body. + - `testNewRequestAndDoFailure` - exercises the request-building and + request-doing error paths for a method. + - `testBadOptions` - asserts that invalid options return an error. +- Use `testJSONMarshal` to verify a type round-trips to and from the expected JSON. +- Compare values with `cmp.Equal` and construct optional fields with `Ptr` + (see [Creating Pointers](#creating-pointers)). + +## Metadata + +GitHub publishes [OpenAPI descriptions of their API][]. We use these +descriptions to keep documentation links up to date and to keep track of which +methods call which endpoints via the `//meta:operation` comments described +above. GitHub's descriptions are far too large to keep in this repository or to +pull down every time we generate code, so we keep only the metadata we need +in `openapi_operations.yaml`. + +### openapi_operations.yaml + +Most contributors won't need to interact with `openapi_operations.yaml`, but it +may be useful to know what it is. Its sections are: + +- `openapi_operations` - is the metadata that comes from GitHub's OpenAPI + descriptions. It is generated by `script/metadata.sh update-openapi` and + should not be edited by hand. In the rare case where it needs to be + overridden, use the `operation_overrides` section instead. + + An operation consists of `name`, `documentation_url`, + and `openapi_files`. `openapi_files` is the list of files where the operation + is described. In order or preference, values can be "api.github.com.json" for + operations available on the free plan, "ghec.json" for operations available on + GitHub Enterprise Cloud or "ghes-.json" for operations available on + GitHub Enterprise Server. When an operation is described in multiple ghes + files, only the most recent version is included. `documentation_url` is the + URL that should be linked from godoc. It is the documentation link found in + the first file listed in `openapi_files`. + +- `openapi_commit` - is the git commit that `script/metadata.sh update-openapi` + saw when it last updated `openapi_operations`. It is not necessarily the most + recent commit seen because `update-openapi` doesn't update the file when + there are no changes to `openapi_operations`. + +- `operations` - contains manually added metadata that is not in GitHub's + OpenAPI descriptions. There are only a few of these. Some have + documentation_urls that point to relevant GitHub documentation that is not in + the OpenAPI descriptions. Others have no documentation_url and result in a + note in the generated code that the documentation is missing. + +- `operation_overrides` - is where we override the documentation_url for + operations where the link in the OpenAPI descriptions is wrong. + +Please note that if your PR unit tests are failing due to an out-of-date +`openapi_operations.yaml` file, simply ask the maintainer(s) of this repo +to update it for you so that your PR doesn't need to include changes +to this auto-generated file. + +### tools/metadata + +The `tools/metadata` package is a command-line tool for working with metadata. +In a typical workflow, you won't use it directly, but you will use it indirectly +through `script/generate.sh` and `script/lint.sh`. + +Its subcommands are: + +- `update-openapi` - updates `openapi_operations.yaml` with the latest + information from GitHub's OpenAPI descriptions. With `--validate` it will + validate that the descriptions are correct as of the commit + in `openapi_commit`. `update-openapi --validate` is called + by `script/lint.sh`. + +- `update-go` - updates Go files with documentation URLs and formats comments. + It is used by `script/generate.sh`. + +- `format` - formats white space in `openapi_operations.yaml` and sorts its + arrays. It is used by `script/fmt.sh`. + +- `unused` - lists operations from `openapi_operations.yaml` that are not mapped + from any methods. + +[OpenAPI descriptions of their API]: https://github.com/github/rest-api-description + +## Scripts + +The `script` directory has shell scripts that help with common development +tasks: + +- `script/fmt.sh` formats all Go code in the repository. +- `script/generate.sh` runs code generators and `go mod tidy` on all modules. With `--check` it checks that the generated files are current. +- `script/lint.sh` runs linters on the project and checks generated files are current. +- `script/metadata.sh` runs `tools/metadata`. See the [Metadata](#metadata) section for more information. +- `script/test.sh` runs tests on all modules. + +## Maintainer's Guide (These notes are mostly only for people merging in pull requests.) @@ -124,3 +698,6 @@ this][modified-comment]. [git-aliases]: https://github.com/willnorris/dotfiles/blob/d640d010c23b1116bdb3d4dc12088ed26120d87d/git/.gitconfig#L13-L15 [rebase-comment]: https://github.com/google/go-github/pull/277#issuecomment-183035491 [modified-comment]: https://github.com/google/go-github/pull/280#issuecomment-184859046 + +**When creating a release, don't forget to update the `Version` constant in `github.go`.** This is used to +send the version in the `User-Agent` header to identify clients to the GitHub API. diff --git a/README.md b/README.md index 66503b244ae..6430571fb85 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,75 @@ # go-github # -[![GoDoc](https://godoc.org/github.com/google/go-github/github?status.svg)](https://godoc.org/github.com/google/go-github/github) [![Build Status](https://travis-ci.org/google/go-github.svg?branch=master)](https://travis-ci.org/google/go-github) [![Test Coverage](https://codecov.io/gh/google/go-github/branch/master/graph/badge.svg)](https://codecov.io/gh/google/go-github) [![Discuss at go-github@googlegroups.com](https://img.shields.io/badge/discuss-go--github%40googlegroups.com-blue.svg)](https://groups.google.com/group/go-github) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/796/badge)](https://bestpractices.coreinfrastructure.org/projects/796) +[![go-github release (latest SemVer)](https://img.shields.io/github/v/release/google/go-github?sort=semver)](https://github.com/google/go-github/releases) +[![Go Reference](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/google/go-github/v90/github) +[![Test Status](https://github.com/google/go-github/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/google/go-github/actions/workflows/tests.yml) +[![Test Coverage](https://codecov.io/gh/google/go-github/branch/master/graph/badge.svg)](https://codecov.io/gh/google/go-github) +[![Discuss at go-github@googlegroups.com](https://img.shields.io/badge/discuss-go--github%40googlegroups.com-blue.svg)](https://groups.google.com/group/go-github) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/796/badge)](https://bestpractices.coreinfrastructure.org/projects/796) go-github is a Go client library for accessing the [GitHub API v3][]. -Currently, **go-github requires Go version 1.9 or greater**. go-github tracks -[Go's version support policy][support-policy]. We do our best not to break -older versions of Go if we don't have to, but due to tooling constraints, we -don't always test older versions. +go-github tracks [Go's version support policy][support-policy] supporting any +minor version of the latest two major releases of Go and the go directive in +go.mod reflects that. +We do our best not to break older versions of Go if we don't have to, but we +don't explicitly test older versions and as of Go 1.23 the go directive in +go.mod declares a hard required _minimum_ version of Go to use with this module +and this _must_ be greater than or equal to the go line of all dependencies so +go-github will require the N-1 major release of Go by default. [support-policy]: https://golang.org/doc/devel/release.html#policy +## Development ## + If you're interested in using the [GraphQL API v4][], the recommended library is [shurcooL/githubv4][]. +## Installation ## + +go-github is compatible with modern Go releases in module mode, with Go installed: + +```bash +go get github.com/google/go-github/v90 +``` + +will resolve and add the package to the current development module, along with its dependencies. + +Alternatively the same can be achieved if you use import in a package: + +```go +import "github.com/google/go-github/v90/github" +``` + +and run `go get` without parameters. + +Finally, to use the top-of-trunk version of this repo, use the following command: + +```bash +go get github.com/google/go-github/v90@master +``` + +To discover all the changes that have occurred since a prior release, you can +first clone the repo, then run (for example): + +```bash +go run tools/gen-release-notes/main.go --tag v90.0.0 +``` + ## Usage ## ```go -import "github.com/google/go-github/v28/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) -import "github.com/google/go-github/github" // with go modules disabled +import "github.com/google/go-github/v90/github" ``` Construct a new GitHub client, then use the various services on the client to access different parts of the GitHub API. For example: ```go -client := github.NewClient(nil) +client, err := github.NewClient() +if err != nil { + // Handle error. +} // list all organizations for user "willnorris" orgs, _, err := client.Organizations.List(context.Background(), "willnorris", nil) @@ -34,7 +78,10 @@ orgs, _, err := client.Organizations.List(context.Background(), "willnorris", ni Some API methods have optional parameters that can be passed. For example: ```go -client := github.NewClient(nil) +client, err := github.NewClient() +if err != nil { + // Handle error. +} // list public repositories for org "github" opt := &github.RepositoryListByOrgOptions{Type: "public"} @@ -42,11 +89,10 @@ repos, _, err := client.Repositories.ListByOrg(context.Background(), "github", o ``` The services of a client divide the API into logical chunks and correspond to -the structure of the GitHub API documentation at -https://developer.github.com/v3/. +the structure of the [GitHub API documentation](https://docs.github.com/en/rest). -NOTE: Using the [context](https://godoc.org/context) package, one can easily -pass cancelation signals and deadlines to various services of the client for +NOTE: Using the [context](https://pkg.go.dev/context) package, one can easily +pass cancellation signals and deadlines to various services of the client for handling a request. In case there is no context available, then `context.Background()` can be used as a starting point. @@ -55,85 +101,186 @@ For more sample code snippets, head over to the ### Authentication ### -The go-github library does not directly handle authentication. Instead, when -creating a new client, pass an `http.Client` that can handle authentication for -you. The easiest and recommended way to do this is using the [oauth2][] -library, but you can always use any other library that provides an -`http.Client`. If you have an OAuth2 access token (for example, a [personal -API token][]), you can use it with the oauth2 library using: +Use the `github.WithAuthToken` options method to configure your client to authenticate using an +OAuth token (for example, a [personal access token][]). This is what is needed +for a majority of use cases aside from GitHub Apps. ```go -import "golang.org/x/oauth2" - -func main() { - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: "... your access token ..."}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) - - // list all repositories for the authenticated user - repos, _, err := client.Repositories.List(ctx, "", nil) +client, err := github.NewClient(github.WithAuthToken("... your access token ...")) +if err != nil { + // Handle error. } ``` +To support more advanced use cases; you can use the `github.WithTransport` option to provide a +custom `http.RoundTripper` that handles authentication for you, or the `github.WithHTTPClient` +option to provide a custom `http.Client`. As an example; you can use the `oauth2.Transport` +from the [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) package to handle OAuth +token refreshing for you. + Note that when using an authenticated Client, all calls made by the client will include the specified OAuth token. Therefore, authenticated clients should almost never be shared between different users. -See the [oauth2 docs][] for complete instructions on using that library. - For API methods that require HTTP Basic Authentication, use the -[`BasicAuthTransport`](https://godoc.org/github.com/google/go-github/github#BasicAuthTransport). +[`BasicAuthTransport`](https://pkg.go.dev/github.com/google/go-github/v90/github#BasicAuthTransport). + +#### As a GitHub App #### + +GitHub Apps authentication can be provided by different pkgs like [bradleyfalzon/ghinstallation](https://github.com/bradleyfalzon/ghinstallation) +or [jferrl/go-githubauth](https://github.com/jferrl/go-githubauth). + +> **Note**: Most endpoints (ex. [`GET /rate_limit`]) require access token authentication +> while a few others (ex. [`GET /app/hook/deliveries`]) require [JWT] authentication. -GitHub Apps authentication can be provided by the [ghinstallation](https://github.com/bradleyfalzon/ghinstallation) -package. +[`GET /rate_limit`]: https://docs.github.com/en/rest/rate-limit#get-rate-limit-status-for-the-authenticated-user +[`GET /app/hook/deliveries`]: https://docs.github.com/en/rest/apps/webhooks#list-deliveries-for-an-app-webhook +[JWT]: https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-a-github-app + +`ghinstallation` provides `Transport`, which implements `http.RoundTripper` to provide authentication as an installation for GitHub Apps. + +Here is an example of how to authenticate as a GitHub App using the `ghinstallation` package: ```go -import "github.com/bradleyfalzon/ghinstallation" +import ( + "net/http" + + "github.com/bradleyfalzon/ghinstallation/v2" + "github.com/google/go-github/v90/github" +) func main() { // Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99. itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem") + + // Or for endpoints that require JWT authentication + // itr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem") + if err != nil { // Handle error. } // Use installation transport with client. - client := github.NewClient(&http.Client{Transport: itr}) + client, err := github.NewClient(github.WithTransport(itr)) + if err != nil { + // Handle error. + } // Use client... } ``` -### Rate Limiting ### +`go-githubauth` implements a set of `oauth2.TokenSource` to be used with `oauth2.Client`. An `oauth2.Client` can be injected into the `github.Client` to authenticate requests. + +Another example using `go-githubauth`: + +```go +package main -GitHub imposes a rate limit on all API clients. Unauthenticated clients are -limited to 60 requests per hour, while authenticated clients can make up to -5,000 requests per hour. The Search API has a custom rate limit. Unauthenticated -clients are limited to 10 requests per minute, while authenticated clients -can make up to 30 requests per minute. To receive the higher rate limit when -making calls that are not issued on behalf of a user, -use `UnauthenticatedRateLimitedTransport`. +import ( + "context" + "fmt" + "os" + "strconv" -The returned `Response.Rate` value contains the rate limit information + "github.com/google/go-github/v90/github" + "github.com/jferrl/go-githubauth" + "golang.org/x/oauth2" +) + +func main() { + privateKey := []byte(os.Getenv("GITHUB_APP_PRIVATE_KEY")) + + appTokenSource, err := githubauth.NewApplicationTokenSource(1112, privateKey) + if err != nil { + fmt.Println("Error creating application token source:", err) + return + } + + installationTokenSource := githubauth.NewInstallationTokenSource(1113, appTokenSource) + + // oauth2.NewClient uses oauth2.ReuseTokenSource to reuse the token until it expires. + // The token will be automatically refreshed when it expires. + // InstallationTokenSource has the mechanism to refresh the token when it expires. + httpClient := oauth2.NewClient(context.Background(), installationTokenSource) + + client, err := github.NewClient(github.WithHTTPClient(httpClient)) + if err != nil { + // Handle error. + } +} +``` + +_Note_: In order to interact with certain APIs, for example writing a file to a repo, one must generate an installation token +using the installation ID of the GitHub app and authenticate with the OAuth method mentioned above. See the examples. + +### Rate Limiting ### + +GitHub imposes rate limits on all API clients. The [primary rate limit](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#about-primary-rate-limits) +is the limit to the number of REST API requests that a client can make within a +specific amount of time. This limit helps prevent abuse and denial-of-service +attacks, and ensures that the API remains available for all users. Some +endpoints, like the search endpoints, have more restrictive limits. +Unauthenticated clients may request public data but have a low rate limit, +while authenticated clients have rate limits based on the client +identity. + +In addition to primary rate limits, GitHub enforces [secondary rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#about-secondary-rate-limits) +in order to prevent abuse and keep the API available for all users. +Secondary rate limits generally limit the number of concurrent requests that a +client can make. + +The client returned `Response.Rate` value contains the rate limit information from the most recent API call. If a recent enough response isn't -available, you can use `RateLimits` to fetch the most up-to-date rate -limit data for the client. +available, you can use the client `RateLimits` service to fetch the most +up-to-date rate limit data for the client. -To detect an API rate limit error, you can check if its type is `*github.RateLimitError`: +To detect a primary API rate limit error, you can check if the error is a +`RateLimitError`. ```go repos, _, err := client.Repositories.List(ctx, "", nil) -if _, ok := err.(*github.RateLimitError); ok { - log.Println("hit rate limit") +var rateErr *github.RateLimitError +if errors.As(err, &rateErr) { + log.Printf("hit primary rate limit, used %v of %v\n", rateErr.Rate.Used, rateErr.Rate.Limit) } ``` -Learn more about GitHub rate limiting at -https://developer.github.com/v3/#rate-limiting. +To detect an API secondary rate limit error, you can check if the error is an +`AbuseRateLimitError`. + +```go +repos, _, err := client.Repositories.List(ctx, "", nil) +var rateErr *github.AbuseRateLimitError +if errors.As(err, &rateErr) { + log.Printf("hit secondary rate limit, retry after %v\n", rateErr.RetryAfter) +} +``` + +If you hit the primary rate limit, you can use the `SleepUntilPrimaryRateLimitResetWhenRateLimited` +method to block until the rate limit is reset. + +```go +repos, _, err := client.Repositories.List(context.WithValue(ctx, github.SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "", nil) +``` + +If you need to make a request even if the rate limit has been hit you can use +the `BypassRateLimitCheck` method to bypass the rate limit check and make the +request anyway. + +```go +repos, _, err := client.Repositories.List(context.WithValue(ctx, github.BypassRateLimitCheck, true), "", nil) +``` + +For more advanced use cases, you can use [gofri/go-github-ratelimit](https://github.com/gofri/go-github-ratelimit) +which provides a middleware (`http.RoundTripper`) that handles both the primary +rate limit and secondary rate limit for the GitHub API. In this case you can +set the client `DisableRateLimitCheck` to `true` so the client doesn't track the rate limit usage. + +If the client is an [OAuth app](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#primary-rate-limit-for-oauth-apps) +you can use the apps higher rate limit to request public data by using the +`UnauthenticatedRateLimitedTransport` to make calls as the app instead of as +the user. ### Accepted Status ### @@ -147,21 +294,39 @@ To detect this condition of error, you can check if its type is ```go stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo) -if _, ok := err.(*github.AcceptedError); ok { +if errors.As(err, new(*github.AcceptedError)) { log.Println("scheduled on GitHub side") } ``` ### Conditional Requests ### -The GitHub API has good support for conditional requests which will help -prevent you from burning through your rate limit, as well as help speed up your -application. `go-github` does not handle conditional requests directly, but is -instead designed to work with a caching `http.Transport`. We recommend using -https://github.com/gregjones/httpcache for that. +The GitHub REST API has good support for [conditional HTTP requests](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#use-conditional-requests-if-appropriate) +via the `ETag` header which will help prevent you from burning through your +rate limit, as well as help speed up your application. `go-github` does not +handle conditional requests directly, but is instead designed to work with a +caching `http.Transport`. -Learn more about GitHub conditional requests at -https://developer.github.com/v3/#conditional-requests. +Typically, an [RFC 9111](https://datatracker.ietf.org/doc/html/rfc9111) +compliant HTTP cache such as [bartventer/httpcache](https://github.com/bartventer/httpcache) +is recommended, ex: + +```go +import ( + "github.com/bartventer/httpcache" + _ "github.com/bartventer/httpcache/store/memcache" // Register the in-memory backend +) + +client, err := github.NewClient(github.WithHTTPClient(httpcache.NewClient("memcache://")), github.WithAuthToken(os.Getenv("GITHUB_TOKEN"))) +if err != nil { + // Handle error. +} +``` + +Alternatively, the [bored-engineer/github-conditional-http-transport](https://github.com/bored-engineer/github-conditional-http-transport) +package relies on (undocumented) GitHub specific cache logic and is +recommended when making requests using short-lived credentials such as a +[GitHub App installation token](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation). ### Creating and Updating Resources ### @@ -173,8 +338,8 @@ bool, and int values. For example: ```go // create a new private repository named "foo" repo := &github.Repository{ - Name: github.String("foo"), - Private: github.Bool(true), + Name: github.Ptr("foo"), + Private: github.Ptr(true), } client.Repositories.Create(ctx, "", repo) ``` @@ -184,14 +349,17 @@ Users who have worked with protocol buffers should find this pattern familiar. ### Pagination ### All requests for resource collections (repos, pull requests, issues, etc.) -support pagination. Pagination options are described in the +support pagination. Pagination options using page numbers are described in the `github.ListOptions` struct and passed to the list methods directly or as an embedded type of a more specific list options struct (for example `github.PullRequestListOptions`). Pages information is available via the `github.Response` struct. ```go -client := github.NewClient(nil) +client, err := github.NewClient() +if err != nil { + // Handle error. +} opt := &github.RepositoryListByOrgOptions{ ListOptions: github.ListOptions{PerPage: 10}, @@ -211,30 +379,108 @@ for { } ``` +Pagination options using string cursors are described in the `github.ListCursorOptions` +struct and passed to the list methods directly or as an +embedded type of a more specific list cursor options struct (for example +`github.ListGlobalSecurityAdvisoriesOptions`). Similarly, cursor and pages information +is available via the `github.Response` struct. + +#### Iterators #### + +Go v1.23 introduces the new `iter` package. + +The new `github/gen-iterators.go` file auto-generates "*Iter" methods in `github/github-iterators.go` +for all methods that support page number iteration (using the `NextPage` field in each response) +or string cursor iteration (using the `After` field in each response). +To handle rate limiting issues, make sure to use a rate-limiting transport. +(See [Rate Limiting](/#rate-limiting) above for more details.) +To use these methods, simply create an iterator and then range over it, for example: + +```go +client, err := github.NewClient() +if err != nil { + // Handle error. +} +var allRepos []*github.Repository + +// create an iterator and start looping through all the results +iter := client.Repositories.ListIter(ctx, "github", nil) +for repo, err := range iter { + if err != nil { + log.Fatal(err) + } + allRepos = append(allRepos, repo) +} +``` + +Alternatively, if you wish to use an external package, there is `enrichman/gh-iter`. +Its iterator will handle pagination for you, looping through all the available results. + +```go +client, err := github.NewClient() +if err != nil { + // Handle error. +} +var allRepos []*github.Repository + +// create an iterator and start looping through all the results +repos := ghiter.NewFromFn1(client.Repositories.ListByOrg, "github") +for repo := range repos.All() { + allRepos = append(allRepos, repo) +} +``` + +For complete usage of `enrichman/gh-iter`, see the full [package docs](https://github.com/enrichman/gh-iter). + +#### Middleware #### + +You can use [gofri/go-github-pagination](https://github.com/gofri/go-github-pagination) to handle +pagination for you. It supports both sync and async modes, as well as customizations. +By default, the middleware automatically paginates through all pages, aggregates results, and returns them as an array. +See `example/ratelimit/main.go` for usage. + +### Webhooks ### + +`go-github` provides structs for almost all [GitHub webhook events][] as well as functions to validate them and unmarshal JSON payloads from `http.Request` structs. + +```go +func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { + payload, err := github.ValidatePayload(r, s.webhookSecretKey) + if err != nil { ... } + event, err := github.ParseWebHook(github.WebHookType(r), payload) + if err != nil { ... } + switch event := event.(type) { + case *github.CommitCommentEvent: + processCommitCommentEvent(event) + case *github.CreateEvent: + processCreateEvent(event) + ... + } +} +``` + +Furthermore, there are libraries like [cbrgm/githubevents][] that build upon the example above and provide functions to subscribe callbacks to specific events. + For complete usage of go-github, see the full [package docs][]. -[GitHub API v3]: https://developer.github.com/v3/ -[oauth2]: https://github.com/golang/oauth2 -[oauth2 docs]: https://godoc.org/golang.org/x/oauth2 -[personal API token]: https://github.com/blog/1509-personal-api-tokens -[package docs]: https://godoc.org/github.com/google/go-github/github +[GitHub API v3]: https://docs.github.com/en/rest +[personal access token]: https://github.com/blog/1509-personal-api-tokens +[package docs]: https://pkg.go.dev/github.com/google/go-github/v90/github [GraphQL API v4]: https://developer.github.com/v4/ [shurcooL/githubv4]: https://github.com/shurcooL/githubv4 +[GitHub webhook events]: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads +[cbrgm/githubevents]: https://github.com/cbrgm/githubevents -### Integration Tests ### +### Testing code that uses `go-github` ### -You can run integration tests from the `test` directory. See the integration tests [README](test/README.md). +The repo [migueleliasweb/go-github-mock](https://github.com/migueleliasweb/go-github-mock) provides a way to mock responses. Check the repo for more details. -## Roadmap ## - -This library is being initially developed for an internal application at -Google, so API methods will likely be implemented in the order that they are -needed by that application. You can track the status of implementation in -[this Google spreadsheet][roadmap]. +### Integration Tests ### -[roadmap]: https://docs.google.com/spreadsheet/ccc?key=0ApoVX4GOiXr-dGNKN1pObFh6ek1DR2FKUjBNZ1FmaEE&usp=sharing +You can run integration tests from the `test` directory. See the integration tests [README](test/README.md). ## Contributing ## + I would like to cover the entire GitHub API and contributions are of course always welcome. The calling pattern is pretty well established, so adding new methods is relatively straightforward. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details. @@ -250,18 +496,55 @@ implementing preview features of the GitHub API, we've adopted the following versioning policy: * We increment the **major version** with any incompatible change to - non-preview functionality, including changes to the exported Go API surface - or behavior of the API. + non-preview functionality, including changes to the exported Go API surface + or behavior of the API. + * We increment the **minor version** with any backwards-compatible changes to - functionality, as well as any changes to preview functionality in the GitHub - API. GitHub makes no guarantee about the stability of preview functionality, - so neither do we consider it a stable part of the go-github API. + functionality, as well as any changes to preview functionality in the GitHub + API. GitHub makes no guarantee about the stability of preview functionality, + so neither do we consider it a stable part of the go-github API. + * We increment the **patch version** with any backwards-compatible bug fixes. Preview functionality may take the form of entire methods or simply additional data returned from an otherwise non-preview method. Refer to the GitHub API documentation for details on preview functionality. +### Calendar Versioning ### + +As of 2022-11-28, GitHub [has announced](https://github.blog/developer-skills/github/to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/) +that they are starting to version their v3 API based on "calendar-versioning". + +In practice, our goal is to make per-method version overrides (at +least in the core library) rare and temporary. + +Our understanding of the GitHub docs is that they will be revving the +entire API to each new date-based version, even if only a few methods +have breaking changes. Other methods will accept the new version with +their existing functionality. So when a new date-based version of the +GitHub API is released, we (the repo maintainers) plan to: + +* update each method that had breaking changes, overriding their + per-method API version header. This may happen in one or multiple + commits and PRs, and is all done in the main branch. + +* once all of the methods with breaking changes have been updated, + have a final commit that bumps the default API version, and remove + all of the per-method overrides. That would now get a major version + bump when the next go-github release is made. + +### Version Compatibility Table ### + +The following table identifies which version of the GitHub API is +supported by this (and past) versions of this repo (go-github). +Versions prior to 48.2.0 are not listed. + +| go-github Version | GitHub v3 API Version | +| ----------------- | --------------------- | +| 90.0.0 | 2022-11-28 | +| ... | 2022-11-28 | +| 48.2.0 | 2022-11-28 | + ## License ## This library is distributed under the BSD-style license found in the [LICENSE](./LICENSE) diff --git a/REVIEWERS b/REVIEWERS new file mode 100644 index 00000000000..116fd58dc3e --- /dev/null +++ b/REVIEWERS @@ -0,0 +1,3 @@ +stevehipwell +alexandear +Not-Dhananjay-Mishra diff --git a/example/actionpermissions/main.go b/example/actionpermissions/main.go new file mode 100644 index 00000000000..aff4ad89da8 --- /dev/null +++ b/example/actionpermissions/main.go @@ -0,0 +1,80 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The actionpermissions command utilizes go-github as a cli tool for +// changing GitHub Actions related permission settings for a repository. +package main + +import ( + "context" + "flag" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" +) + +var ( + name = flag.String("name", "", "repo to change Actions permissions.") + owner = flag.String("owner", "", "owner of targeted repo.") +) + +func main() { + flag.Parse() + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + log.Fatal("Unauthorized: No token present") + } + if *name == "" { + log.Fatal("No name: repo name must be given") + } + if *owner == "" { + log.Fatal("No owner: owner of repo must be given") + } + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } + + actionsPermissionsRepository, _, err := client.Repositories.GetActionsPermissions(ctx, *owner, *name) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Current ActionsPermissions %v\n", actionsPermissionsRepository) + + actionsPermissionsRepository = &github.ActionsPermissionsRepository{Enabled: github.Ptr(true), AllowedActions: github.Ptr("selected")} + _, _, err = client.Repositories.UpdateActionsPermissions(ctx, *owner, *name, *actionsPermissionsRepository) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Current ActionsPermissions %v\n", actionsPermissionsRepository) + + actionsAllowed, _, err := client.Repositories.GetActionsAllowed(ctx, *owner, *name) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Current ActionsAllowed %v\n", actionsAllowed) + + actionsAllowed = &github.ActionsAllowed{GithubOwnedAllowed: github.Ptr(true), VerifiedAllowed: github.Ptr(false), PatternsAllowed: []string{"a/b"}} + _, _, err = client.Repositories.EditActionsAllowed(ctx, *owner, *name, *actionsAllowed) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Current ActionsAllowed %v\n", actionsAllowed) + + actionsPermissionsRepository = &github.ActionsPermissionsRepository{Enabled: github.Ptr(true), AllowedActions: github.Ptr("all")} + _, _, err = client.Repositories.UpdateActionsPermissions(ctx, *owner, *name, *actionsPermissionsRepository) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Current ActionsPermissions %v\n", actionsPermissionsRepository) +} diff --git a/example/appengine/app.go b/example/appengine/app.go deleted file mode 100644 index 8f567496bb5..00000000000 --- a/example/appengine/app.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package demo provides an app that shows how to use the github package on -// Google App Engine. -package demo - -import ( - "fmt" - "net/http" - "os" - - "github.com/google/go-github/v28/github" - "golang.org/x/oauth2" - "google.golang.org/appengine" - "google.golang.org/appengine/log" -) - -func init() { - http.HandleFunc("/", handler) -} - -func handler(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.NotFound(w, r) - return - } - - ctx := appengine.NewContext(r) - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: os.Getenv("GITHUB_AUTH_TOKEN")}, - ) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) - - commits, _, err := client.Repositories.ListCommits(ctx, "google", "go-github", nil) - if err != nil { - log.Errorf(ctx, "ListCommits: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - for _, commit := range commits { - fmt.Fprintln(w, commit.GetHTMLURL()) - } -} diff --git a/example/appengine/app.yaml b/example/appengine/app.yaml deleted file mode 100644 index dca235faeeb..00000000000 --- a/example/appengine/app.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2017 The go-github AUTHORS. All rights reserved. -# -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -runtime: go -api_version: go1 - -handlers: -- url: /.* - script: _go_app - -env_variables: - GITHUB_AUTH_TOKEN: "-your-auth-token-here-" diff --git a/example/auditlogstream/main.go b/example/auditlogstream/main.go new file mode 100644 index 00000000000..7ecf2a5f6b5 --- /dev/null +++ b/example/auditlogstream/main.go @@ -0,0 +1,185 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The auditlogstream command demonstrates managing enterprise audit log +// streams for Azure Blob Storage using the go-github library. +// +// The GitHub API base URL is read from the GITHUB_API_URL environment +// variable. When running inside a GitHub Actions workflow this is set +// automatically. +// +// Usage — create: +// +// export GITHUB_AUTH_TOKEN= +// export GITHUB_API_URL=https://api..ghe.com/ or https://domain/api/v3/ +// go run main.go create \ +// -enterprise=my-enterprise \ +// -container=my-container \ +// -sas-url= +// +// Usage — delete: +// +// export GITHUB_AUTH_TOKEN= +// export GITHUB_API_URL=https://api..ghe.com/ or https://domain/api/v3/ +// go run main.go delete \ +// -enterprise=my-enterprise \ +// -stream-id=42 +package main + +import ( + "context" + "crypto/rand" + "encoding/base64" + "flag" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" + "golang.org/x/crypto/nacl/box" +) + +// encryptSecret encrypts a plain-text secret using libsodium's sealed box +// (crypto_box_seal), which is what GitHub's API expects for encrypted credentials. +func encryptSecret(publicKeyB64, secret string) (string, error) { + publicKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyB64) + if err != nil { + return "", fmt.Errorf("decoding public key: %w", err) + } + if len(publicKeyBytes) != 32 { + return "", fmt.Errorf("public key must be 32 bytes, got %v", len(publicKeyBytes)) + } + publicKey := [32]byte(publicKeyBytes) + + encrypted, err := box.SealAnonymous(nil, []byte(secret), &publicKey, rand.Reader) + if err != nil { + return "", fmt.Errorf("encrypting secret: %w", err) + } + + return base64.StdEncoding.EncodeToString(encrypted), nil +} + +func main() { + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "Usage: %v [flags]\n", os.Args[0]) + os.Exit(1) + } + + switch os.Args[1] { + case "create": + runCreate(os.Args[2:]) + case "delete": + runDelete(os.Args[2:]) + default: + fmt.Fprintf(os.Stderr, "Unknown command %q. Must be one of: create, delete\n", os.Args[1]) + os.Exit(1) + } +} + +// newFlagSet creates a FlagSet with the common -enterprise flag pre-registered. +func newFlagSet(name string) (*flag.FlagSet, *string) { + fs := flag.NewFlagSet(name, flag.ExitOnError) + enterprise := fs.String("enterprise", "", "Enterprise slug (required).") + return fs, enterprise +} + +// parseAndInit parses the FlagSet, validates the enterprise flag, reads +// environment variables, and returns a ready-to-use context, client, and +// enterprise slug. +func parseAndInit(fs *flag.FlagSet, enterprise *string, args []string) (context.Context, *github.Client, string) { + if err := fs.Parse(args); err != nil { + log.Fatalf("Error parsing flags: %v", err) + } + + requireFlag("enterprise", *enterprise) + + token := requireEnv("GITHUB_AUTH_TOKEN") + apiURL := requireEnv("GITHUB_API_URL") + + return context.Background(), newClient(token, apiURL), *enterprise +} + +func runCreate(args []string) { + fs, enterprise := newFlagSet("create") + container := fs.String("container", "", "Azure Blob Storage container name (required).") + sasURL := fs.String("sas-url", "", "Plain-text Azure SAS URL to encrypt and submit (required).") + enabled := fs.Bool("enabled", true, "Whether the stream should be enabled immediately.") + + ctx, client, ent := parseAndInit(fs, enterprise, args) + requireFlag("container", *container) + requireFlag("sas-url", *sasURL) + + streamKey, _, err := client.Enterprise.GetAuditLogStreamKey(ctx, ent) + if err != nil { + log.Fatalf("Error fetching audit log stream key: %v", err) + } + fmt.Printf("Retrieved stream key ID: %v\n", streamKey.KeyID) + + encryptedSASURL, err := encryptSecret(streamKey.Key, *sasURL) + if err != nil { + log.Fatalf("Error encrypting SAS URL: %v", err) + } + fmt.Println("SAS URL encrypted successfully.") + + config := github.NewAzureBlobStreamConfig(*enabled, &github.AzureBlobConfig{ + KeyID: streamKey.KeyID, + Container: *container, + EncryptedSASURL: encryptedSASURL, + }) + + stream, _, err := client.Enterprise.CreateAuditLogStream(ctx, ent, *config) + if err != nil { + log.Fatalf("Error creating audit log stream: %v", err) + } + + fmt.Println("Successfully created audit log stream:") + fmt.Printf(" ID: %v\n", stream.ID) + fmt.Printf(" Type: %v\n", stream.StreamType) + fmt.Printf(" Enabled: %v\n", stream.Enabled) + fmt.Printf(" Created at: %v\n", stream.CreatedAt) +} + +func runDelete(args []string) { + fs, enterprise := newFlagSet("delete") + streamID := fs.Int64("stream-id", 0, "ID of the audit log stream to delete (required).") + + ctx, client, ent := parseAndInit(fs, enterprise, args) + requireIntFlag("stream-id", *streamID) + + _, err := client.Enterprise.DeleteAuditLogStream(ctx, ent, *streamID) + if err != nil { + log.Fatalf("Error deleting audit log stream: %v", err) + } + + fmt.Printf("Successfully deleted audit log stream %v.\n", *streamID) +} + +func newClient(token, apiURL string) *github.Client { + client, err := github.NewClient(github.WithAuthToken(token), github.WithEnterpriseURLs(apiURL, apiURL)) + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + return client +} + +func requireEnv(name string) string { + val := os.Getenv(name) + if val == "" { + log.Fatalf("environment variable %v is not set", name) + } + return val +} + +func requireFlag(name, val string) { + if val == "" { + log.Fatalf("flag -%v is required", name) + } +} + +func requireIntFlag(name string, val int64) { + if val == 0 { + log.Fatalf("flag -%v is required", name) + } +} diff --git a/example/basicauth/main.go b/example/basicauth/main.go index 33b118ba61c..d4af5fbd70c 100644 --- a/example/basicauth/main.go +++ b/example/basicauth/main.go @@ -6,18 +6,25 @@ // The basicauth command demonstrates using the github.BasicAuthTransport, // including handling two-factor authentication. This won't currently work for // accounts that use SMS to receive one-time passwords. +// +// Deprecation Notice: GitHub will discontinue password authentication to the API. +// You must now authenticate to the GitHub API with an API token, such as an OAuth access token, +// GitHub App installation access token, or personal access token, depending on what you need to do with the token. +// Password authentication to the API will be removed on November 13, 2020. +// See the tokenauth example for details. package main import ( "bufio" "context" + "errors" "fmt" + "log" "os" "strings" - "syscall" - "github.com/google/go-github/v28/github" - "golang.org/x/crypto/ssh/terminal" + "github.com/google/go-github/v90/github" + "golang.org/x/term" ) func main() { @@ -26,20 +33,22 @@ func main() { username, _ := r.ReadString('\n') fmt.Print("GitHub Password: ") - bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin)) - password := string(bytePassword) + password, _ := term.ReadPassword(int(os.Stdin.Fd())) tp := github.BasicAuthTransport{ Username: strings.TrimSpace(username), - Password: strings.TrimSpace(password), + Password: strings.TrimSpace(string(password)), } - client := github.NewClient(tp.Client()) + client, err := github.NewClient(github.WithHTTPClient(tp.Client())) + if err != nil { + log.Fatalf("error: %v", err) + } ctx := context.Background() user, _, err := client.Users.Get(ctx, "") // Is this a two-factor auth error? If so, prompt for OTP and try again. - if _, ok := err.(*github.TwoFactorAuthError); ok { + if errors.As(err, new(*github.TwoFactorAuthError)) { fmt.Print("\nGitHub OTP: ") otp, _ := r.ReadString('\n') tp.OTP = strings.TrimSpace(otp) @@ -47,8 +56,7 @@ func main() { } if err != nil { - fmt.Printf("\nerror: %v\n", err) - return + log.Fatalf("error: %v", err) } fmt.Printf("\n%v\n", github.Stringify(user)) diff --git a/example/codespaces/newreposecretwithxcrypto/main.go b/example/codespaces/newreposecretwithxcrypto/main.go new file mode 100644 index 00000000000..0a13710af08 --- /dev/null +++ b/example/codespaces/newreposecretwithxcrypto/main.go @@ -0,0 +1,166 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// newreposecretwithxcrypto creates a new secret in GitHub for a given owner/repo. +// newreposecretwithxcrypto uses x/crypto/nacl/box instead of sodium. +// It does not depend on any native libraries and is easier to cross-compile for different platforms. +// Quite possibly there is a performance penalty due to this. +// +// newreposecretwithxcrypto has two required flags for owner and repo, and takes in one argument for the name of the secret to add. +// The secret value is pulled from an environment variable based on the secret name. +// To authenticate with GitHub, provide your token via an environment variable GITHUB_AUTH_TOKEN. +// +// To verify the new secret, navigate to GitHub Repository > Settings > left side options bar > Secrets. +// +// Usage: +// +// export GITHUB_AUTH_TOKEN= +// export SECRET_VARIABLE= +// go run main.go -owner -repo SECRET_VARIABLE +// +// Example: +// +// export GITHUB_AUTH_TOKEN=0000000000000000 +// export SECRET_VARIABLE="my-secret" +// go run main.go -owner google -repo go-github SECRET_VARIABLE +package main + +import ( + "context" + crypto_rand "crypto/rand" + "encoding/base64" + "errors" + "flag" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" + "golang.org/x/crypto/nacl/box" +) + +var ( + repo = flag.String("repo", "", "The repo that the secret should be added to, ex. go-github") + owner = flag.String("owner", "", "The owner of there repo this should be added to, ex. google") +) + +func main() { + flag.Parse() + + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + log.Fatal("please provide a GitHub API token via env variable GITHUB_AUTH_TOKEN") + } + + if *repo == "" { + log.Fatal("please provide required flag --repo to specify GitHub repository ") + } + + if *owner == "" { + log.Fatal("please provide required flag --owner to specify GitHub user/org owner") + } + + secretName, err := getSecretName() + if err != nil { + log.Fatal(err) + } + + secretValue, err := getSecretValue(secretName) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } + + if err := addRepoSecret(ctx, client, *owner, *repo, secretName, secretValue); err != nil { + log.Fatal(err) + } + + fmt.Printf("Added secret %q to the repo %v/%v\n", secretName, *owner, *repo) +} + +func getSecretName() (string, error) { + secretName := flag.Arg(0) + if secretName == "" { + return "", errors.New("missing argument secret name") + } + return secretName, nil +} + +func getSecretValue(secretName string) (string, error) { + secretValue := os.Getenv(secretName) + if secretValue == "" { + return "", fmt.Errorf("secret value not found under env variable %q", secretName) + } + return secretValue, nil +} + +// addRepoSecret will add a secret to a GitHub repo for use in GitHub Codespaces. +// +// The secretName and secretValue determine the name of the secret added and its corresponding value. +// +// The actual transmission of the secret value to GitHub using the API requires that the secret value is encrypted +// using the public key of the target repo. This encryption is done using x/crypto/nacl/box. +// +// First, the public key of the repo is retrieved. The public key comes base64 +// encoded, so it must be decoded prior to use. +// +// Second, the decoded key is converted into a fixed-size byte array. +// +// Third, the secret value is converted into a slice of bytes. +// +// Fourth, the secret is encrypted with box.SealAnonymous using the repo's decoded public key. +// +// Fifth, the encrypted secret is encoded as a base64 string to be used in a github.EncryptedSecret type. +// +// Sixth, the other two properties of the github.EncryptedSecret type are determined: the name of the secret to be added +// (string not base64), and the KeyID of the public key used to encrypt the secret. +// This can be retrieved via the public key's GetKeyID method. +// +// Finally, the github.EncryptedSecret is passed into the GitHub client.Codespaces.CreateOrUpdateRepoSecret method to +// populate the secret in the GitHub repo. +func addRepoSecret(ctx context.Context, client *github.Client, owner, repo, secretName, secretValue string) error { + publicKey, _, err := client.Codespaces.GetRepoPublicKey(ctx, owner, repo) + if err != nil { + return err + } + + encryptedSecret, err := encryptSecretWithPublicKey(publicKey, secretName, secretValue) + if err != nil { + return err + } + + if _, err := client.Codespaces.CreateOrUpdateRepoSecret(ctx, owner, repo, encryptedSecret); err != nil { + return fmt.Errorf("client.Codespaces.CreateOrUpdateRepoSecret returned error: %v", err) + } + + return nil +} + +func encryptSecretWithPublicKey(publicKey *github.PublicKey, secretName, secretValue string) (*github.EncryptedSecret, error) { + decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKey.GetKey()) + if err != nil { + return nil, fmt.Errorf("unable to decode public key: %v", err) + } + + boxKey := [32]byte(decodedPublicKey) + encryptedBytes, err := box.SealAnonymous([]byte{}, []byte(secretValue), &boxKey, crypto_rand.Reader) + if err != nil { + return nil, fmt.Errorf("box.SealAnonymous failed with error %w", err) + } + + encryptedString := base64.StdEncoding.EncodeToString(encryptedBytes) + keyID := publicKey.GetKeyID() + encryptedSecret := &github.EncryptedSecret{ + Name: secretName, + KeyID: keyID, + EncryptedValue: encryptedString, + } + return encryptedSecret, nil +} diff --git a/example/codespaces/newusersecretwithxcrypto/main.go b/example/codespaces/newusersecretwithxcrypto/main.go new file mode 100644 index 00000000000..584795b6cde --- /dev/null +++ b/example/codespaces/newusersecretwithxcrypto/main.go @@ -0,0 +1,173 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// newusersecretwithxcrypto creates a new secret in GitHub for a given user. +// newusersecretwithxcrypto uses x/crypto/nacl/box instead of sodium. +// It does not depend on any native libraries and is easier to cross-compile for different platforms. +// Quite possibly there is a performance penalty due to this. +// +// newusersecretwithxcrypto takes in one argument for the name of the secret to add, and 2 flags owner, repo. +// If owner/repo are defined then it adds the secret to that repository +// The secret value is pulled from an environment variable based on the secret name. +// To authenticate with GitHub, provide your token via an environment variable GITHUB_AUTH_TOKEN. +// +// To verify the new secret, navigate to GitHub User > Settings > left side options bar > Codespaces > Secrets. +// +// Usage: +// +// export GITHUB_AUTH_TOKEN= +// export SECRET_VARIABLE= +// go run main.go SECRET_VARIABLE +// +// Example: +// +// export GITHUB_AUTH_TOKEN=0000000000000000 +// export SECRET_VARIABLE="my-secret" +// go run main.go SECRET_VARIABLE +package main + +import ( + "context" + crypto_rand "crypto/rand" + "encoding/base64" + "errors" + "flag" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" + "golang.org/x/crypto/nacl/box" +) + +var ( + repo = flag.String("repo", "", "The repo that the secret should be added to, ex. go-github") + owner = flag.String("owner", "", "The owner of there repo this should be added to, ex. google") +) + +func main() { + flag.Parse() + + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + log.Fatal("please provide a GitHub API token via env variable GITHUB_AUTH_TOKEN") + } + + secretName, err := getSecretName() + if err != nil { + log.Fatal(err) + } + + secretValue, err := getSecretValue(secretName) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } + + if err := addUserSecret(ctx, client, secretName, secretValue, *owner, *repo); err != nil { + log.Fatal(err) + } + + fmt.Printf("Added secret %q to the authenticated user\n", secretName) +} + +func getSecretName() (string, error) { + secretName := flag.Arg(0) + if secretName == "" { + return "", errors.New("missing argument secret name") + } + return secretName, nil +} + +func getSecretValue(secretName string) (string, error) { + secretValue := os.Getenv(secretName) + if secretValue == "" { + return "", fmt.Errorf("secret value not found under env variable %q", secretName) + } + return secretValue, nil +} + +// addUserSecret will add a secret to a GitHub user for use in GitHub Codespaces. +// +// The secretName and secretValue determine the name of the secret added and its corresponding value. +// +// The actual transmission of the secret value to GitHub using the API requires that the secret value is encrypted +// using the public key of the target user. This encryption is done using x/crypto/nacl/box. +// +// First, the public key of the user is retrieved. The public key comes base64 +// encoded, so it must be decoded prior to use. +// +// Second, the decoded key is converted into a fixed-size byte array. +// +// Third, the secret value is converted into a slice of bytes. +// +// Fourth, the secret is encrypted with box.SealAnonymous using the user's decoded public key. +// +// Fifth, the encrypted secret is encoded as a base64 string to be used in a github.EncryptedSecret type. +// +// Sixth, the other two properties of the github.EncryptedSecret type are determined: the name of the secret to be added +// (string not base64), and the KeyID of the public key used to encrypt the secret. +// This can be retrieved via the public key's GetKeyID method. +// +// Seventh, the github.EncryptedSecret is passed into the GitHub client.Codespaces.CreateOrUpdateUserSecret method to +// populate the secret in the GitHub user. +// +// Finally, if a repo and owner are passed in, it adds the repo to the user secret. +func addUserSecret(ctx context.Context, client *github.Client, secretName, secretValue, owner, repo string) error { + publicKey, _, err := client.Codespaces.GetUserPublicKey(ctx) + if err != nil { + return err + } + + encryptedSecret, err := encryptSecretWithPublicKey(publicKey, secretName, secretValue) + if err != nil { + return err + } + + if _, err := client.Codespaces.CreateOrUpdateUserSecret(ctx, encryptedSecret); err != nil { + return fmt.Errorf("client.Codespaces.CreateOrUpdateUserSecret returned error: %v", err) + } + + if owner != "" && repo != "" { + r, _, err := client.Repositories.Get(ctx, owner, repo) + if err != nil { + return fmt.Errorf("client.Repositories.Get returned error: %v", err) + } + _, err = client.Codespaces.AddSelectedRepoToUserSecret(ctx, encryptedSecret.Name, r) + if err != nil { + return fmt.Errorf("client.Codespaces.AddSelectedRepoToUserSecret returned error: %v", err) + } + fmt.Printf("Added secret %q to %v/%v\n", secretName, owner, repo) + } + + return nil +} + +func encryptSecretWithPublicKey(publicKey *github.PublicKey, secretName, secretValue string) (*github.EncryptedSecret, error) { + decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKey.GetKey()) + if err != nil { + return nil, fmt.Errorf("base64.StdEncoding.DecodeString was unable to decode public key: %v", err) + } + + boxKey := [32]byte(decodedPublicKey) + encryptedBytes, err := box.SealAnonymous([]byte{}, []byte(secretValue), &boxKey, crypto_rand.Reader) + if err != nil { + return nil, fmt.Errorf("box.SealAnonymous failed with error %w", err) + } + + encryptedString := base64.StdEncoding.EncodeToString(encryptedBytes) + keyID := publicKey.GetKeyID() + encryptedSecret := &github.EncryptedSecret{ + Name: secretName, + KeyID: keyID, + EncryptedValue: encryptedString, + } + return encryptedSecret, nil +} diff --git a/example/commitpr/main.go b/example/commitpr/main.go index 53061841aab..ec4c980112e 100644 --- a/example/commitpr/main.go +++ b/example/commitpr/main.go @@ -13,7 +13,7 @@ // // Note, if you want to push a single file, you probably prefer to use the // content API. An example is available here: -// https://godoc.org/github.com/google/go-github/github#example-RepositoriesService-CreateFile +// https://pkg.go.dev/github.com/google/go-github/v90/github#example-RepositoriesService-CreateFile // // Note, for this to work at least 1 commit is needed, so you if you use this // after creating a repository you might want to make sure you set `AutoInit` to @@ -21,18 +21,19 @@ package main import ( + "bytes" "context" "errors" "flag" "fmt" - "io/ioutil" + "io" "log" "os" "strings" "time" - "github.com/google/go-github/v28/github" - "golang.org/x/oauth2" + "github.com/ProtonMail/go-crypto/openpgp" + "github.com/google/go-github/v90/github" ) var ( @@ -40,6 +41,7 @@ var ( sourceRepo = flag.String("source-repo", "", "Name of repo to create the commit in.") commitMessage = flag.String("commit-message", "", "Content of the commit message.") commitBranch = flag.String("commit-branch", "", "Name of branch to create the commit in. If it does not already exists, it will be created using the `base-branch` parameter") + repoBranch = flag.String("repo-branch", "", "Name of the repository where the changes in the pull request were made. This field is required for cross-repository pull requests if both repositories are owned by the same organization") baseBranch = flag.String("base-branch", "master", "Name of branch to create the `commit-branch` from.") prRepoOwner = flag.String("merge-repo-owner", "", "Name of the owner (user or org) of the repo to create the PR against. If not specified, the value of the `-source-owner` flag will be used.") prRepo = flag.String("merge-repo", "", "Name of repo to create the PR against. If not specified, the value of the `-source-repo` flag will be used.") @@ -52,50 +54,58 @@ If the file should be in the same location with the same name, you can just put Example: README.md,main.go:github/examples/commitpr/main.go`) authorName = flag.String("author-name", "", "Name of the author of the commit.") authorEmail = flag.String("author-email", "", "Email of the author of the commit.") + privateKey = flag.String("private-key", "", "Path to the private key to use to sign the commit.") ) -var client *github.Client -var ctx = context.Background() +var ( + client *github.Client + ctx = context.Background() +) // getRef returns the commit branch reference object if it exists or creates it // from the base branch before returning it. func getRef() (ref *github.Reference, err error) { - if ref, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, "refs/heads/"+*commitBranch); err == nil { + if ref, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, branchRef(*commitBranch)); err == nil { return ref, nil } // We consider that an error means the branch has not been found and needs to // be created. if *commitBranch == *baseBranch { - return nil, errors.New("The commit branch does not exist but `-base-branch` is the same as `-commit-branch`") + return nil, errors.New("the commit branch does not exist but `-base-branch` is the same as `-commit-branch`") } if *baseBranch == "" { - return nil, errors.New("The `-base-branch` should not be set to an empty string when the branch specified by `-commit-branch` does not exists") + return nil, errors.New("the `-base-branch` should not be set to an empty string when the branch specified by `-commit-branch` does not exists") } var baseRef *github.Reference - if baseRef, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, "refs/heads/"+*baseBranch); err != nil { + if baseRef, _, err = client.Git.GetRef(ctx, *sourceOwner, *sourceRepo, branchRef(*baseBranch)); err != nil { return nil, err } - newRef := &github.Reference{Ref: github.String("refs/heads/" + *commitBranch), Object: &github.GitObject{SHA: baseRef.Object.SHA}} + newRef := github.CreateRef{Ref: branchRef(*commitBranch), SHA: *baseRef.Object.SHA} ref, _, err = client.Git.CreateRef(ctx, *sourceOwner, *sourceRepo, newRef) return ref, err } +// branchRef generates the fully qualified git reference for the given branch name. +func branchRef(name string) string { + return "refs/heads/" + name +} + // getTree generates the tree to commit based on the given files and the commit // of the ref you got in getRef. func getTree(ref *github.Reference) (tree *github.Tree, err error) { // Create a tree with what to commit. - entries := []github.TreeEntry{} + entries := []*github.TreeEntry{} // Load each file into the tree. - for _, fileArg := range strings.Split(*sourceFiles, ",") { + for fileArg := range strings.SplitSeq(*sourceFiles, ",") { file, content, err := getFileContent(fileArg) if err != nil { return nil, err } - entries = append(entries, github.TreeEntry{Path: github.String(file), Type: github.String("blob"), Content: github.String(string(content)), Mode: github.String("100644")}) + entries = append(entries, &github.TreeEntry{Path: &file, Type: github.Ptr("blob"), Content: github.Ptr(string(content)), Mode: github.Ptr("100644")}) } tree, _, err = client.Git.CreateTree(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA, entries) @@ -118,14 +128,14 @@ func getFileContent(fileArg string) (targetName string, b []byte, err error) { targetName = files[1] } - b, err = ioutil.ReadFile(localFile) + b, err = os.ReadFile(localFile) return targetName, b, err } -// createCommit creates the commit in the given reference using the given tree. +// pushCommit creates the commit in the given reference using the given tree. func pushCommit(ref *github.Reference, tree *github.Tree) (err error) { // Get the parent commit to attach the commit to. - parent, _, err := client.Repositories.GetCommit(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA) + parent, _, err := client.Repositories.GetCommit(ctx, *sourceOwner, *sourceRepo, *ref.Object.SHA, nil) if err != nil { return err } @@ -134,27 +144,48 @@ func pushCommit(ref *github.Reference, tree *github.Tree) (err error) { // Create the commit using the tree. date := time.Now() - author := &github.CommitAuthor{Date: &date, Name: authorName, Email: authorEmail} - commit := &github.Commit{Author: author, Message: commitMessage, Tree: tree, Parents: []github.Commit{*parent.Commit}} - newCommit, _, err := client.Git.CreateCommit(ctx, *sourceOwner, *sourceRepo, commit) + author := &github.CommitAuthor{Date: &github.Timestamp{Time: date}, Name: authorName, Email: authorEmail} + commit := github.Commit{Author: author, Message: commitMessage, Tree: tree, Parents: []*github.Commit{parent.Commit}} + opts := github.CreateCommitOptions{} + if *privateKey != "" { + armoredBlock, e := os.ReadFile(*privateKey) + if e != nil { + return e + } + keyring, e := openpgp.ReadArmoredKeyRing(bytes.NewReader(armoredBlock)) + if e != nil { + return e + } + if len(keyring) != 1 { + return errors.New("expected exactly one key in the keyring") + } + key := keyring[0] + opts.Signer = github.MessageSignerFunc(func(w io.Writer, r io.Reader) error { + return openpgp.ArmoredDetachSign(w, key, r, nil) + }) + } + newCommit, _, err := client.Git.CreateCommit(ctx, *sourceOwner, *sourceRepo, commit, &opts) if err != nil { return err } // Attach the commit to the master branch. ref.Object.SHA = newCommit.SHA - _, _, err = client.Git.UpdateRef(ctx, *sourceOwner, *sourceRepo, ref, false) + _, _, err = client.Git.UpdateRef(ctx, *sourceOwner, *sourceRepo, *ref.Ref, github.UpdateRef{ + SHA: *newCommit.SHA, + Force: github.Ptr(false), + }) return err } -// createPR creates a pull request. Based on: https://godoc.org/github.com/google/go-github/github#example-PullRequestsService-Create +// createPR creates a pull request. Based on: https://pkg.go.dev/github.com/google/go-github/v90/github#example-PullRequestsService-Create func createPR() (err error) { if *prSubject == "" { return errors.New("missing `-pr-title` flag; skipping PR creation") } if *prRepoOwner != "" && *prRepoOwner != *sourceOwner { - *commitBranch = fmt.Sprintf("%s:%s", *sourceOwner, *commitBranch) + *commitBranch = fmt.Sprintf("%v:%v", *sourceOwner, *commitBranch) } else { prRepoOwner = sourceOwner } @@ -163,12 +194,13 @@ func createPR() (err error) { prRepo = sourceRepo } - newPR := &github.NewPullRequest{ + newPR := github.CreatePullRequest{ Title: prSubject, - Head: commitBranch, - Base: prBranch, + Head: *commitBranch, + HeadRepo: repoBranch, + Base: *prBranch, Body: prDescription, - MaintainerCanModify: github.Bool(true), + MaintainerCanModify: github.Ptr(true), } pr, _, err := client.PullRequests.Create(ctx, *prRepoOwner, *prRepo, newPR) @@ -176,7 +208,7 @@ func createPR() (err error) { return err } - fmt.Printf("PR created: %s\n", pr.GetHTMLURL()) + fmt.Printf("PR created: %v\n", pr.GetHTMLURL()) return nil } @@ -189,28 +221,30 @@ func main() { if *sourceOwner == "" || *sourceRepo == "" || *commitBranch == "" || *sourceFiles == "" || *authorName == "" || *authorEmail == "" { log.Fatal("You need to specify a non-empty value for the flags `-source-owner`, `-source-repo`, `-commit-branch`, `-files`, `-author-name` and `-author-email`") } - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - tc := oauth2.NewClient(ctx, ts) - client = github.NewClient(tc) + c, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } + client = c ref, err := getRef() if err != nil { - log.Fatalf("Unable to get/create the commit reference: %s\n", err) + log.Fatalf("Unable to get/create the commit reference: %v", err) } if ref == nil { - log.Fatalf("No error where returned but the reference is nil") + log.Fatal("No error where returned but the reference is nil") } tree, err := getTree(ref) if err != nil { - log.Fatalf("Unable to create the tree based on the provided files: %s\n", err) + log.Fatalf("Unable to create the tree based on the provided files: %v", err) } if err := pushCommit(ref, tree); err != nil { - log.Fatalf("Unable to create the commit: %s\n", err) + log.Fatalf("Unable to create the commit: %v", err) } if err := createPR(); err != nil { - log.Fatalf("Error while creating the pull request: %s", err) + log.Fatalf("Error while creating the pull request: %v", err) } } diff --git a/example/contents/main.go b/example/contents/main.go new file mode 100644 index 00000000000..56a8ffb9421 --- /dev/null +++ b/example/contents/main.go @@ -0,0 +1,76 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The contents command utilizes go-github as a CLI tool for +// downloading the contents of a file in a repository. +// It takes an inputs of the repository owner, repository name, path to the +// file in the repository, reference (branch, tag or commit SHA), and output +// path for the downloaded file. It then uses the Repositories.DownloadContents +// method to download the file and saves it to the specified output path. +package main + +import ( + "bufio" + "context" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" + + "github.com/google/go-github/v90/github" +) + +func main() { + fmt.Println("This example will download the contents of a file from a GitHub repository.") + + r := bufio.NewReader(os.Stdin) + + fmt.Print("Repository Owner: ") + owner, _ := r.ReadString('\n') + owner = strings.TrimSpace(owner) + + fmt.Print("Repository Name: ") + repo, _ := r.ReadString('\n') + repo = strings.TrimSpace(repo) + + fmt.Print("Repository Path: ") + repoPath, _ := r.ReadString('\n') + repoPath = strings.TrimSpace(repoPath) + + fmt.Print("Reference (branch, tag or commit SHA): ") + ref, _ := r.ReadString('\n') + ref = strings.TrimSpace(ref) + + fmt.Print("Output Path: ") + outputPath, _ := r.ReadString('\n') + outputPath = filepath.Clean(strings.TrimSpace(outputPath)) + + fmt.Printf("\nDownloading %v/%v/%v at ref %v to %v...\n", owner, repo, repoPath, ref, outputPath) + + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + rc, _, err := client.Repositories.DownloadContents(context.Background(), owner, repo, repoPath, &github.RepositoryContentGetOptions{Ref: ref}) + if err != nil { + log.Fatalf("Error downloading contents: %v", err) + } + defer rc.Close() + + f, err := os.Create(outputPath) //#nosec G703 -- path is validated above + if err != nil { + log.Fatalf("Error creating output file: %v", err) + } + defer f.Close() + + if _, err := io.Copy(f, rc); err != nil { + log.Fatalf("Error writing to output file: %v", err) + } + + fmt.Println("Download completed.") +} diff --git a/example/go.mod b/example/go.mod new file mode 100644 index 00000000000..eb331030a5a --- /dev/null +++ b/example/go.mod @@ -0,0 +1,96 @@ +module github.com/google/go-github/v90/example + +go 1.25.8 + +require ( + github.com/ProtonMail/go-crypto v1.4.1 + github.com/bradleyfalzon/ghinstallation/v2 v2.19.0 + github.com/gofri/go-github-pagination v1.0.1 + github.com/gofri/go-github-ratelimit/v2 v2.0.2 + github.com/google/go-github/otel/v90 v90.0.0 + github.com/google/go-github/v90 v90.0.0 + github.com/sigstore/sigstore-go v1.2.2 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0 + go.opentelemetry.io/otel/sdk v1.45.0 + golang.org/x/crypto v0.54.0 + golang.org/x/term v0.45.0 +) + +require ( + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/blang/semver v3.5.1+incompatible // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudflare/circl v1.6.3 // indirect + github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect + github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect + github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/analysis v0.25.2 // indirect + github.com/go-openapi/errors v0.22.8 // indirect + github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/jsonreference v0.21.6 // indirect + github.com/go-openapi/loads v0.24.0 // indirect + github.com/go-openapi/runtime v0.32.4 // indirect + github.com/go-openapi/runtime/server-middleware v0.30.0 // indirect + github.com/go-openapi/spec v0.22.6 // indirect + github.com/go-openapi/strfmt v0.26.4 // indirect + github.com/go-openapi/swag v0.26.1 // indirect + github.com/go-openapi/swag/cmdutils v0.26.1 // indirect + github.com/go-openapi/swag/conv v0.27.0 // indirect + github.com/go-openapi/swag/fileutils v0.26.1 // indirect + github.com/go-openapi/swag/jsonname v0.26.1 // indirect + github.com/go-openapi/swag/jsonutils v0.26.1 // indirect + github.com/go-openapi/swag/loading v0.26.1 // indirect + github.com/go-openapi/swag/mangling v0.26.1 // indirect + github.com/go-openapi/swag/netutils v0.26.1 // indirect + github.com/go-openapi/swag/stringutils v0.26.1 // indirect + github.com/go-openapi/swag/typeutils v0.27.0 // indirect + github.com/go-openapi/swag/yamlutils v0.26.1 // indirect + github.com/go-openapi/validate v0.26.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/google/certificate-transparency-go v1.3.3 // indirect + github.com/google/go-containerregistry v0.21.7 // indirect + github.com/google/go-github/v88 v88.0.0 // indirect + github.com/google/go-querystring v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect + github.com/in-toto/attestation v1.2.0 // indirect + github.com/in-toto/in-toto-golang v0.11.0 // indirect + github.com/oklog/ulid/v2 v2.1.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.11.0 // indirect + github.com/shibumi/go-pathspec v1.3.0 // indirect + github.com/sigstore/protobuf-specs v0.5.1 // indirect + github.com/sigstore/rekor v1.5.3 // indirect + github.com/sigstore/rekor-tiles/v2 v2.3.0 // indirect + github.com/sigstore/sigstore v1.10.8 // indirect + github.com/sigstore/timestamp-authority/v2 v2.1.2 // indirect + github.com/theupdateframework/go-tuf/v2 v2.4.2 // indirect + github.com/transparency-dev/formats v0.1.1 // indirect + github.com/transparency-dev/merkle v0.0.2 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.45.0 // indirect + go.opentelemetry.io/otel/metric v1.45.0 // indirect + go.opentelemetry.io/otel/trace v1.45.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 // indirect + google.golang.org/grpc v1.82.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect + k8s.io/klog/v2 v2.140.0 // indirect +) + +// Use version at HEAD, not the latest published. +replace github.com/google/go-github/v90 => ../ + +replace github.com/google/go-github/otel/v90 => ../otel diff --git a/example/go.sum b/example/go.sum new file mode 100644 index 00000000000..bd8728d233e --- /dev/null +++ b/example/go.sum @@ -0,0 +1,389 @@ +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.11.0 h1:KieQ9Pb+LLPak1O3Rv3GgCxhnmkYf7Xyh0P5HfF1jFM= +cloud.google.com/go/iam v1.11.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= +cloud.google.com/go/kms v1.31.0 h1:LS8N92OxFDgOLg5NCo3OmbvjtQAIVT5gUHVLKIDHaFE= +cloud.google.com/go/kms v1.31.0/go.mod h1:YIyXZym11R5uovJJt4oN5eUL3oPmirF3yKeIh6QAf4U= +cloud.google.com/go/longrunning v1.0.0 h1:lwzWEYD8+NkYV7dhexOz6kmlvajZA70+bW/xMhRVVdY= +cloud.google.com/go/longrunning v1.0.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +filippo.io/mldsa v0.0.0-20260215214346-43d0283efc3e h1:VsUbObBMxXlc23Eb9VeeJYE4jvTs87qa5RqSN2U5FJU= +filippo.io/mldsa v0.0.0-20260215214346-43d0283efc3e/go.mod h1:32qQ5yj3R24Eu03iWFWchdC3OB653wPvoepWejkefbY= +github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= +github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 h1:jHb/wfvRikGdxMXYV3QG/SzUOPYN9KEUUuC0Yd0/vC0= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1/go.mod h1:pzBXCYn05zvYIrwLgtK8Ap8QcjRg+0i76tMQdWN6wOk= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.5.0 h1:MaKvxE6D0KkjOg6Wd9M00iqP5PR0kUxCfiezes4JweM= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.5.0/go.mod h1:i2h9fsTFKZorh8RdV2IcSUf/Qj98GlTkrTvUbX/s8as= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0 h1:4iB+IesclUXdP0ICgAabvq2FYLXrJWKx1fJQ+GxSo3Y= +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go-v2 v1.41.9 h1:/rYeyO2+HrMztAmxAq9++XJtFMqSIpSsNA0yDGALYq4= +github.com/aws/aws-sdk-go-v2 v1.41.9/go.mod h1:+HsoOEX80qAVUitj1A2DhCNTjmb3edVyuDypb6LNEeo= +github.com/aws/aws-sdk-go-v2/config v1.32.20 h1:8VMDnWc/kEzxsI/1ngGM9mG81a8IGmIHD8KLcYGwagc= +github.com/aws/aws-sdk-go-v2/config v1.32.20/go.mod h1:PuwEpciweIXGULWeOeSTXtSbH4CW9mWdWrhdCKQI1sM= +github.com/aws/aws-sdk-go-v2/credentials v1.19.19 h1:yuFzSV1U0aRNYCQGVaTY2zW2M/L93pYHnXnrJUphYhU= +github.com/aws/aws-sdk-go-v2/credentials v1.19.19/go.mod h1:7y63L1kGzeoDlJaQ3Z578KrnmfBut96JjvJUzGwR+YE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.25 h1:0w6dCiO8iez+YKwRhRBlL1CH/E3GTfdkuzrwj1by8vo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.25/go.mod h1:9FDWUothyr5RCRAHc45XOiVCzUR8n/IhCYX+uVqw6vk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.25 h1:Uii3frf9ztec/ABM2/FSH9/z7PLzxfpG8h4RpkUFflQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.25/go.mod h1:G6kntsA2GorAxDPbap6xgB2F+amSLUF8GJTi7PUoX44= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.25 h1:r1+/l6m+WaUJF9HISEsNOLHSNj5EXYQxK8VX6Cz9NlA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.25/go.mod h1:cKf+D+NMDK1LndD7BowHbBZPgR9V0/5HubH0PFWvA+c= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.26 h1:A1PmWU2zfkIm9EyFlJncFXL4W4phML+h8KjltUsCvNQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.26/go.mod h1:dY4MRzXEizrD4hqtpKvWVGPX7QleSGGVY+EBolo1RmM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.10 h1:d5/908OJ4bXg8lyjeMPvXetEKqoDoLi5Owy1zNue3yg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.10/go.mod h1:a57l7Hwh+FWI+we50g5NPJHYUKeJKfXbc4w8SyXu8Ig= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.25 h1:dD3dhHNglpd98gs72my22Ndqi1hqQGllFFg1F+twfxg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.25/go.mod h1:0yAbjPfd64gG7mj85RW+fMEYdfBgCRZw8g/oWcL1pjc= +github.com/aws/aws-sdk-go-v2/service/kms v1.52.0 h1:QNtg+Mtj1zmepk568+UKBD5DFfqh+ESTUUqQT27JkQc= +github.com/aws/aws-sdk-go-v2/service/kms v1.52.0/go.mod h1:Y0+uxvxz6ib4KktRdK0V4X45Vcs/JyYoz8H71pO8xeI= +github.com/aws/aws-sdk-go-v2/service/signin v1.1.1 h1:1VwbP3qMNfxUDEXWki4rCE5iA+44VA1lokTz9HasGzw= +github.com/aws/aws-sdk-go-v2/service/signin v1.1.1/go.mod h1:vUtyoSj0OPji3kjIVSc/GlKuWEiL33f/WFxl6dmpy/A= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.19 h1:N6pIsdFOW1Kd9S4KyFKXdGRBojPPxkP32+uHFWLv4Hc= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.19/go.mod h1:3gt5WJArFooNmyLONS+h/R4J+o86II8du38IgCwj9dE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.2 h1:hc+lBYiiTr8Zk4MTzIsQ92MeDWCIDvWGmzKUWOaBcOg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.2/go.mod h1:hU6fqB3OJA6/ePheD47LQnxvjYk6br6PtQxs+Q9ojvk= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.3 h1:ErklX/7uhSbkAAeyQD/Y1OoQ9hO3SJXQNEgksORW3Js= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.3/go.mod h1:ULe4HCzfKPiR6R3HEurE3b1upEkuk8AkMrOKtaOxKO8= +github.com/aws/smithy-go v1.26.0 h1:9ouqbi+NyKP7fV3Te7UElCwdAb6Y8uk7LGwPE5tVe/s= +github.com/aws/smithy-go v1.26.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bradleyfalzon/ghinstallation/v2 v2.19.0 h1:KQfD+43pRw9NUJhGycGrFr9vF1MubZacksKol1gomFI= +github.com/bradleyfalzon/ghinstallation/v2 v2.19.0/go.mod h1:fe5ECIhCdEnxwLiBlNTxx9CP455wt42BELnlDVMvaAA= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= +github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= +github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= +github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= +github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= +github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= +github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= +github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I= +github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM= +github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/analysis v0.25.2 h1:I0vy4n3alz+DHTiN1PRhCb7QZxkK6g5YmswZKv2TKuw= +github.com/go-openapi/analysis v0.25.2/go.mod h1:Uhs1t/2XR10EnwONYILGEzw8gcfGIG5Xk5K2AxnhqDo= +github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= +github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= +github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= +github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y= +github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY= +github.com/go-openapi/loads v0.24.0 h1:4LLorXRPTzIN9V6ngMUZbAscsBOUBk3Oa8cClu/bFrQ= +github.com/go-openapi/loads v0.24.0/go.mod h1:xQMgX+hw5xRAhGrcDXxeMw78IFqUpIzhleu3HqPhyF4= +github.com/go-openapi/runtime v0.32.4 h1:8ElGj/3goG0itt0nBPP6Cm57ehcYyuHoI3O20nxgvkw= +github.com/go-openapi/runtime v0.32.4/go.mod h1:Bz6keOZw1NX4T6f+m42OoT1MBPDt6Re13dbccHyGH/4= +github.com/go-openapi/runtime/server-middleware v0.30.0 h1:8rPoJ/xv7JL8BsovaqboKETlpWBArVh8n+0L/GyePog= +github.com/go-openapi/runtime/server-middleware v0.30.0/go.mod h1:OYNT/TxNvB/VK5oe4htM2jDTwlEXuejVJmu0DVZfAMs= +github.com/go-openapi/spec v0.22.6 h1:Tyy1pLaNCM8GBCFLoGYLonjJi6zykqyLCjXLc19ZPic= +github.com/go-openapi/spec v0.22.6/go.mod h1:HZvTHat+iH0PALQRWhrqIHtU/PEqxqd89fu0MxGlMeM= +github.com/go-openapi/strfmt v0.26.4 h1:yI6IAEfcWow459BD5UzFY430KUwXZwBHrYusPFkhWlc= +github.com/go-openapi/strfmt v0.26.4/go.mod h1:hNJi6nb5ETD6i7A1yRo03M9S6ZoTPPoWff1iUexmfUc= +github.com/go-openapi/swag v0.26.1 h1:l5sVEyVpwj+DDYeZyo7wQI/Ebn/mKYIyGB/pFwAfGoQ= +github.com/go-openapi/swag v0.26.1/go.mod h1:yNY38BbIVthxbkDtq1UHBCGasBqjakW3lCR6ANzdBEw= +github.com/go-openapi/swag/cmdutils v0.26.1 h1:f2iE1ijYaJ3nuu5PaEMx3zpEhzhZFgivCJObWEObLIQ= +github.com/go-openapi/swag/cmdutils v0.26.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.27.0 h1:EKOH4feXrvdo8DbSsXSAqRT8fz1epEnS5O2IfXUOzE8= +github.com/go-openapi/swag/conv v0.27.0/go.mod h1:pfiv0uKQTbaGApk8Zs/lZV3uSjmSpa2FO1y183YngN8= +github.com/go-openapi/swag/fileutils v0.26.1 h1:K1XCM2CGhfNsc6YDt6v7Q5+1e59rftYWdcu/isZhvFw= +github.com/go-openapi/swag/fileutils v0.26.1/go.mod h1:mYUgxQAKX4ShS3qvvySx+/9yrlUnDhjiD1CalaQl8lQ= +github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE= +github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc= +github.com/go-openapi/swag/jsonutils v0.26.1 h1:2hdBfFkHg+7Wrz2VsCbeyR6hzkRDs7AztnMR2u84yOY= +github.com/go-openapi/swag/jsonutils v0.26.1/go.mod h1:U+RMJH3wa+6BRiphuRtIyI8fW9HPFqFQ4sHk2oRx0UQ= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1 h1:1CD7NiLLb/TXl3tOnFYU4b+mNfb5rtgHkaA+q7RMYYQ= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1/go.mod h1:ZWafc8nMdYzTE3uYY6W86f0n46+IF0g4uUyRhJw/kXc= +github.com/go-openapi/swag/loading v0.26.1 h1:E9K4wqXeROlhjFQ13K9zMz6ojFGXIggGe+ad1odrK9w= +github.com/go-openapi/swag/loading v0.26.1/go.mod h1:3qvRIlWzWdq1HvmldwmuJ2ohpcAryN6xVt2OTKd0/7E= +github.com/go-openapi/swag/mangling v0.26.1 h1:gpYI4WuPKFJJVjV5cDLGlDVJhFIxYjQc7yN5eEb4CqM= +github.com/go-openapi/swag/mangling v0.26.1/go.mod h1:POETDH01hqAdASXfw7ISEd9bCOE6xBHOt8NHmGZRmYM= +github.com/go-openapi/swag/netutils v0.26.1 h1:BNctoc39WTAUMxyAs355fExOPzMZtPbZ0ZZ1Am2FR5M= +github.com/go-openapi/swag/netutils v0.26.1/go.mod h1:y02vByhZhQPAVwOX+0KipXFZ/hUbk6G/Enhf5rGaOkQ= +github.com/go-openapi/swag/stringutils v0.26.1 h1:f88uYyTso7TnHrKM/bUBsQ5e2wKf37cpgo6pvbzd9yU= +github.com/go-openapi/swag/stringutils v0.26.1/go.mod h1:Sc6d3bU8fgk5AyZR8/8jEQ+Is/Ald+TD/IIggPN8UJk= +github.com/go-openapi/swag/typeutils v0.27.0 h1:aCf4MSGo8NLwZP8Q6t32DWLJSvl/WwNqgmEG+xJ6v2o= +github.com/go-openapi/swag/typeutils v0.27.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= +github.com/go-openapi/swag/yamlutils v0.26.1 h1:0TSLK+lXs9vfIhAWzBeI/lOzEnIoot6WTCO1aAeWFTk= +github.com/go-openapi/swag/yamlutils v0.26.1/go.mod h1:7W5b7PRX9MxwL7TjeG7H8HkyBGRsIDRObhyMWFgBI2M= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk= +github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= +github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/validate v0.26.0 h1:dxWzQ3F+vb1SajqUxHjwb5T4mTpSHmdrtv5Bi7+ZNhw= +github.com/go-openapi/validate v0.26.0/go.mod h1:b4o00uq7fJeJA+wWhVFCJpKTctzeFwzZImGGmHsl2JA= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofri/go-github-pagination v1.0.1 h1:j5uJRx65i/Ta2M0QSgiPcyokY69JnCQglt4n9pspFhY= +github.com/gofri/go-github-pagination v1.0.1/go.mod h1:Qij55Fb4fNPjam3SB+8cLnqp4pgR8RGMyIspYXcyHX0= +github.com/gofri/go-github-ratelimit/v2 v2.0.2 h1:gS8wAS1jTmlWGdTjAM7KIpsLjwY1S0S/gKK5hthfSXM= +github.com/gofri/go-github-ratelimit/v2 v2.0.2/go.mod h1:YBQt4gTbdcbMjJFT05YFEaECwH78P5b0IwrnbLiHGdE= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/certificate-transparency-go v1.3.3 h1:hq/rSxztSkXN2tx/3jQqF6Xc0O565UQPdHrOWvZwybo= +github.com/google/certificate-transparency-go v1.3.3/go.mod h1:iR17ZgSaXRzSa5qvjFl8TnVD5h8ky2JMVio+dzoKMgA= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.21.7 h1:/vPFuVXDjtFREsVArW+0h1CIl5urnOhzei4X2DMW9IU= +github.com/google/go-containerregistry v0.21.7/go.mod h1:kjSbt7/zMsKLWfnHrIvKvhXHUw91jbe9DNjPPJ32gXE= +github.com/google/go-github/v88 v88.0.0 h1:dZA9IKkPK1eXZj4ypngnpRj5FwdpTv4whix2PrQMP7M= +github.com/google/go-github/v88 v88.0.0/go.mod h1:rufTDgn2N45wjhukLTyxmvc9nilSp3mr3Rgtt6b1MPw= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/trillian v1.7.3 h1:hziW+vo4czis48tzx2GK5xRBl/ZxBA9B0/UR5avXOro= +github.com/google/trillian v1.7.3/go.mod h1:qh8iy4x/GvnVXUBd5pK4oncuT1Y9vVYfibQVsR/WpKg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.16 h1:F/VPrx0YPBdksZJQdCAp0WUsqnNmZpUZszzfYt0M5Dw= +github.com/googleapis/enterprise-certificate-proxy v0.3.16/go.mod h1:9Yb0eAkH/Xqhvv3zbeKf/+wMJqCeocWc6KIhDvEAuYE= +github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= +github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= +github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= +github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= +github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= +github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= +github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= +github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= +github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/in-toto/attestation v1.2.0 h1:aPRUZ3azbqD7yEBD5fP3TD8Dszf+YHo284SOcpahjQk= +github.com/in-toto/attestation v1.2.0/go.mod h1:r79G45gOmzPismgObLSL+rZTFxUgZLOQJI6LofTZgXk= +github.com/in-toto/in-toto-golang v0.11.0 h1:nfidMYBFx+E0lnmX5KUnN2Pdm8zdNKal1ayjJuzzRoA= +github.com/in-toto/in-toto-golang v0.11.0/go.mod h1:u3PjTnwFKjp5a1YCcw8SJg0G+tMeKfVoWsWeFMDCMtw= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8= +github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQmNrgofl+IY/8L+n20H6E6PWBBTokdsv+q49j0QhsU= +github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= +github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/letsencrypt/boulder v0.20260309.0 h1:kZynrxK3QfqLGx6hhoz+Rfs3hgltJs1p9Mp+4+VwnY0= +github.com/letsencrypt/boulder v0.20260309.0/go.mod h1:yG8lj8pNPZ8taq3oNdTpfBS+eC74IaEuiewqzVpXiWE= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= +github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= +github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= +github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= +github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= +github.com/secure-systems-lab/go-securesystemslib v0.11.0 h1:iuCR9kcMFD4QurdKrGvPLoKZLv9YvwPYVr0473BdtFs= +github.com/secure-systems-lab/go-securesystemslib v0.11.0/go.mod h1:+PMOTjUGwHj2vcZ+TFKlb1tXRbrdWE1LYDT5i9JC80Q= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= +github.com/sigstore/protobuf-specs v0.5.1 h1:/5OPaNuolRJmQfeZLayJGFXMpsRJEdgC6ah1/+7Px7U= +github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats= +github.com/sigstore/rekor v1.5.3 h1:0Tyolw3zreRgm7PUW8dccFLXGBThi08278jI8EXNSr4= +github.com/sigstore/rekor v1.5.3/go.mod h1:h3GK5dDqCcWJJZUJwdpKGSSmEV2GEjPUjJy3WTjBwzA= +github.com/sigstore/rekor-tiles/v2 v2.3.0 h1:HhMgH61UP0t899V8Fjt7pz1YdgOBptbaQdnCF+79cdc= +github.com/sigstore/rekor-tiles/v2 v2.3.0/go.mod h1:DEFiKSyQ4nF75QRVNdOPaIH3cmvMkO2B6xDZjNYngPc= +github.com/sigstore/sigstore v1.10.8 h1:1Mgkxvkw4AXMfIP1DOjc6kw0GkUgA8pGVpveN/EfOq4= +github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M= +github.com/sigstore/sigstore-go v1.2.2 h1:xAJ8hxaoecC0HKBYVbrwUjkeAI+GJYu6vLqbxDlD2Q0= +github.com/sigstore/sigstore-go v1.2.2/go.mod h1:MIFwBxAHJD+/lKgZzt9n/4Zhq/3T2+EuGX8iGrIsZgU= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.8 h1:tofVQ+UWJgad/69I5zbqxdFCN5gpIn9tRQP7iBzIpBw= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.8/go.mod h1:73AfJE8H6w5KGCFPBu4x/OG+i1Yxgmh0L/FtV7prd88= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.8 h1:8Mt7J36GcUEmbiJaiFhz2tud5ZIgkfVVCe2H/WJCHmw= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.8/go.mod h1:YiTpAsxoWXhF9KlLOVWCh7BckN5cYO8X01WufDq1ido= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.8 h1:MxpAIMZVzn0Tpbarc9ax1I498oQBp7oYSMgoMSsOmKI= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.8/go.mod h1:bnAUEkFNam6STvkVZhptVwWzWR5pS24CEtQ+lhxu7S0= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.8 h1:1DGe4/clcdOnkz5MINEczWlmEvjUtZd+AjPPT/cBhQ8= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.8/go.mod h1:6IDFhpgxtzqbnzrFkyegbj7RfWwKeRrb3/+xAD1Wp+Y= +github.com/sigstore/timestamp-authority/v2 v2.1.2 h1:7DDhnknLL4w8VwomyvW2W8qblOS9LDR8oihna+jc7Ls= +github.com/sigstore/timestamp-authority/v2 v2.1.2/go.mod h1:o6rAVZceFyejClIj/uStRNIemP16bVMZtbMmhk6pr0U= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= +github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= +github.com/theupdateframework/go-tuf/v2 v2.4.2 h1:w7976/W8uTwlsegP5nRymlpjPgrwSh+AXUf85is6nJk= +github.com/theupdateframework/go-tuf/v2 v2.4.2/go.mod h1:JqBrIUnNLAaNq/8GmBcEMFWfAFBbqp/MkJEJseXKbks= +github.com/tink-crypto/tink-go-awskms/v3 v3.0.0 h1:XSohRhCkXAVI0iaCnWB/GS05TEmpnKurQmzaY1jzt3Y= +github.com/tink-crypto/tink-go-awskms/v3 v3.0.0/go.mod h1:+7MXsShLzVbSQ6dI0Pe4JuZM52jD1jQ1itAygd/MDsA= +github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= +github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= +github.com/tink-crypto/tink-go-hcvault/v2 v2.5.0 h1:eXuNqgrcYelxU1MVikOJDP3wTS5lvihM4ntoAbAMfvs= +github.com/tink-crypto/tink-go-hcvault/v2 v2.5.0/go.mod h1:3RhcxAqek6xUlRFmJifvU4CYLZN60KMQdIKqpZAZJG0= +github.com/tink-crypto/tink-go/v2 v2.6.0 h1:+KHNBHhWH33Vn+igZWcsgdEPUxKwBMEe0QC60t388v4= +github.com/tink-crypto/tink-go/v2 v2.6.0/go.mod h1:2WbBA6pfNsAfBwDCggboaHeB2X29wkU8XHtGwh2YIk8= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= +github.com/transparency-dev/formats v0.1.1 h1:4bVHJc+KdBgpA1OJD1yjI+g0i5Z1graCppTMH8lWKJI= +github.com/transparency-dev/formats v0.1.1/go.mod h1:qtZ8goRuJ8FTBG9c9+Bj0rn2rUG7eG/AUTkr+Aw3jFw= +github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= +github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= +github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0 h1:lsA/S1bxgdbyFGkTj+3meEdJ6ADVU7QoFstV6MXgE68= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0/go.mod h1:L7u+MirGoB1bjeLH66+xDykF4RC8C3RN7lIFpBiewUo= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= +go.step.sm/crypto v0.77.7 h1:6azC+pD678Vjju8yXnMDHCZJ+HzFaEmL3sCryiezTIA= +go.step.sm/crypto v0.77.7/go.mod h1:OW/2sEHwTtDKq70PvSQ5B0JGy/CrLyDKOiVy3YvZMTQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik= +google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= +google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= +software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= +software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/example/listenvironments/main.go b/example/listenvironments/main.go new file mode 100644 index 00000000000..7353a42bab5 --- /dev/null +++ b/example/listenvironments/main.go @@ -0,0 +1,53 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// listenvironments is an example of how to use ListEnvironments method with EnvironmentListOptions. +// It's runnable with the following command: +// +// export GITHUB_AUTH_TOKEN=your_token +// export GITHUB_REPOSITORY_OWNER=your_owner +// export GITHUB_REPOSITORY_NAME=your_repo +// go run . +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" +) + +func main() { + token := os.Getenv("GITHUB_AUTH_TOKEN") + repo := os.Getenv("GITHUB_REPOSITORY_NAME") + owner := os.Getenv("GITHUB_REPOSITORY_OWNER") + + if token == "" { + log.Fatal("Unauthorized: No token present") + } + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } + + expectedPageSize := 2 + + opts := &github.EnvironmentListOptions{ListOptions: github.ListOptions{PerPage: expectedPageSize}} + envResponse, _, err := client.Repositories.ListEnvironments(ctx, owner, repo, opts) + if err != nil { + log.Fatal(err) + } + + if len(envResponse.Environments) != expectedPageSize { + log.Fatal("Unexpected number of environments returned") + } + + // The number of environments here should be equal to expectedPageSize + fmt.Printf("%v environments returned\n", len(envResponse.Environments)) +} diff --git a/example/migrations/main.go b/example/migrations/main.go index e0d30530758..5944375b843 100644 --- a/example/migrations/main.go +++ b/example/migrations/main.go @@ -11,28 +11,26 @@ package main import ( "context" "fmt" + "log" - "github.com/google/go-github/v28/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v90/github" ) func fetchAllUserMigrations() ([]*github.UserMigration, error) { ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: ""}, - ) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client, err := github.NewClient(github.WithAuthToken("")) + if err != nil { + return nil, err + } - migrations, _, err := client.Migrations.ListUserMigrations(ctx) + migrations, _, err := client.Migrations.ListUserMigrations(ctx, &github.ListOptions{Page: 1}) return migrations, err } func main() { migrations, err := fetchAllUserMigrations() if err != nil { - fmt.Printf("Error %v\n", err) - return + log.Fatalf("Error: %v", err) } for i, m := range migrations { diff --git a/example/newfilewithappauth/main.go b/example/newfilewithappauth/main.go new file mode 100644 index 00000000000..6e4a5776291 --- /dev/null +++ b/example/newfilewithappauth/main.go @@ -0,0 +1,85 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// newfilewithappauth demonstrates the functionality of GitHub's app authentication +// methods by fetching an installation access token as a GitHub App and then +// using that token to authenticate with the GitHub API. +package main + +import ( + "context" + "log" + "net/http" + "os" + "time" + + "github.com/bradleyfalzon/ghinstallation/v2" + "github.com/google/go-github/v90/github" +) + +func main() { + const gitHost = "https://git.api.com" + + privatePem, err := os.ReadFile("path/to/pem") + if err != nil { + log.Fatalf("failed to read pem: %v", err) + } + + itr, err := ghinstallation.NewAppsTransport(http.DefaultTransport, 10, privatePem) + if err != nil { + log.Fatalf("failed to create app transport: %v", err) + } + itr.BaseURL = gitHost + + // create git client with app transport + client, err := github.NewClient(github.WithHTTPClient(&http.Client{ + Transport: itr, + Timeout: time.Second * 30, + }), github.WithEnterpriseURLs(gitHost, gitHost)) + if err != nil { + log.Fatalf("failed to create git client for app: %v", err) + } + + installations, _, err := client.Apps.ListInstallations(context.Background(), &github.ListOptions{}) + if err != nil { + log.Fatalf("failed to list installations: %v", err) + } + + // capture our installationId for our app + // we need this for the access token + var installID int64 + for _, val := range installations { + installID = val.GetID() + } + + token, _, err := client.Apps.CreateInstallationToken( + context.Background(), + installID, + &github.InstallationTokenOptions{}) + if err != nil { + log.Fatalf("failed to create installation token: %v", err) + } + + apiClient, err := github.NewClient(github.WithAuthToken(token.GetToken()), github.WithEnterpriseURLs(gitHost, gitHost)) + if err != nil { + log.Fatalf("failed to create new git client with token: %v", err) + } + + _, resp, err := apiClient.Repositories.CreateFile( + context.Background(), + "repoOwner", + "sample-repo", + "example/foo.txt", + &github.RepositoryContentFileOptions{ + Content: []byte("foo"), + Message: github.Ptr("sample commit"), + SHA: nil, + }) + if err != nil { + log.Fatalf("failed to create new file: %v", err) + } + + log.Printf("file written status code: %v", resp.StatusCode) +} diff --git a/example/newrepo/main.go b/example/newrepo/main.go index 82eccf5eedd..c820b0e84a7 100644 --- a/example/newrepo/main.go +++ b/example/newrepo/main.go @@ -16,14 +16,14 @@ import ( "log" "os" - "github.com/google/go-github/v28/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v90/github" ) var ( name = flag.String("name", "", "Name of repo to create in authenticated user's GitHub account.") description = flag.String("description", "", "Description of created repo.") private = flag.Bool("private", false, "Will created repo be private.") + autoInit = flag.Bool("auto-init", false, "Pass true to create an initial commit with empty README.") ) func main() { @@ -36,11 +36,12 @@ func main() { log.Fatal("No name: New repos must be given a name") } ctx := context.Background() - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } - r := &github.Repository{Name: name, Private: private, Description: description} + r := &github.Repository{Name: name, Private: private, Description: description, AutoInit: autoInit} repo, _, err := client.Repositories.Create(ctx, "", r) if err != nil { log.Fatal(err) diff --git a/example/newreposecretwithxcrypto/main.go b/example/newreposecretwithxcrypto/main.go new file mode 100644 index 00000000000..8443646c894 --- /dev/null +++ b/example/newreposecretwithxcrypto/main.go @@ -0,0 +1,157 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// newreposecretwithxcrypto creates a new secret in GitHub for a given owner/repo. +// newreposecretwithxcrypto uses x/crypto/nacl/box instead of sodium. +// It does not depend on any native libraries and is easier to cross-compile for different platforms. +// Quite possibly there is a performance penalty due to this. +// +// newreposecretwithxcrypto has two required flags for owner and repo, and takes in one argument for the name of the secret to add. +// The secret value is pulled from an environment variable based on the secret name. +// To authenticate with GitHub, provide your token via an environment variable GITHUB_AUTH_TOKEN. +// +// To verify the new secret, navigate to GitHub Repository > Settings > left side options bar > Secrets. +// +// Usage: +// +// export GITHUB_AUTH_TOKEN= +// export SECRET_VARIABLE= +// go run main.go -owner -repo SECRET_VARIABLE +// +// Example: +// +// export GITHUB_AUTH_TOKEN=0000000000000000 +// export SECRET_VARIABLE="my-secret" +// go run main.go -owner google -repo go-github SECRET_VARIABLE +package main + +import ( + "context" + crypto_rand "crypto/rand" + "encoding/base64" + "errors" + "flag" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" + "golang.org/x/crypto/nacl/box" +) + +var ( + repo = flag.String("repo", "", "The repo that the secret should be added to, ex. go-github") + owner = flag.String("owner", "", "The owner of there repo this should be added to, ex. google") +) + +func main() { + flag.Parse() + + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + log.Fatal("please provide a GitHub API token via env variable GITHUB_AUTH_TOKEN") + } + + if *repo == "" { + log.Fatal("please provide required flag --repo to specify GitHub repository ") + } + + if *owner == "" { + log.Fatal("please provide required flag --owner to specify GitHub user/org owner") + } + + secretName, err := getSecretName() + if err != nil { + log.Fatal(err) + } + + secretValue, err := getSecretValue(secretName) + if err != nil { + log.Fatal(err) + } + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatal(err) + } + + if err := addRepoSecret(ctx, client, *owner, *repo, secretName, secretValue); err != nil { + log.Fatal(err) + } + + fmt.Printf("Added secret %q to the repo %v/%v\n", secretName, *owner, *repo) +} + +func getSecretName() (string, error) { + secretName := flag.Arg(0) + if secretName == "" { + return "", errors.New("missing argument secret name") + } + return secretName, nil +} + +func getSecretValue(secretName string) (string, error) { + secretValue := os.Getenv(secretName) + if secretValue == "" { + return "", fmt.Errorf("secret value not found under env variable %q", secretName) + } + return secretValue, nil +} + +// addRepoSecret will add a secret to a GitHub repo for use in GitHub Actions. +// +// The secretName and secretValue determine the name of the secret added and its corresponding value. +// +// The actual transmission of the secret value to GitHub using the API requires that the secret value is encrypted +// using the public key of the target repo. This encryption is done using x/crypto/nacl/box. +// +// First, the public key of the repo is retrieved. The public key comes base64 +// encoded, so it must be decoded prior to use. +// +// Second, the decoded key is converted into a fixed-size byte array. +// +// Third, the secret value is converted into a slice of bytes. +// +// Fourth, the secret is encrypted with box.SealAnonymous using the repo's decoded public key. +// +// Fifth, the encrypted secret is encoded as a base64 string to be used in a github.EncryptedSecret type. +// +// Sixth, the other two properties of the github.EncryptedSecret type are determined: the name of the secret to be added +// (string not base64), and the KeyID of the public key used to encrypt the secret. +// This can be retrieved via the public key's GetKeyID method. +// +// Finally, the github.EncryptedSecret is passed into the GitHub client.Actions.CreateOrUpdateRepoSecret method to +// populate the secret in the GitHub repo. +func addRepoSecret(ctx context.Context, client *github.Client, owner, repo, secretName, secretValue string) error { + publicKey, _, err := client.Actions.GetRepoPublicKey(ctx, owner, repo) + if err != nil { + return err + } + + decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKey.GetKey()) + if err != nil { + return fmt.Errorf("base64.StdEncoding.DecodeString was unable to decode public key: %v", err) + } + + boxKey := [32]byte(decodedPublicKey) + encryptedBytes, err := box.SealAnonymous([]byte{}, []byte(secretValue), &boxKey, crypto_rand.Reader) + if err != nil { + return fmt.Errorf("box.SealAnonymous failed with error %w", err) + } + + encryptedString := base64.StdEncoding.EncodeToString(encryptedBytes) + keyID := publicKey.GetKeyID() + req := github.SecretRequest{ + KeyID: keyID, + EncryptedValue: encryptedString, + } + + if _, err := client.Actions.CreateOrUpdateRepoSecret(ctx, owner, repo, secretName, req); err != nil { + return fmt.Errorf("client.Actions.CreateOrUpdateRepoSecret returned error: %v", err) + } + + return nil +} diff --git a/example/otel/main.go b/example/otel/main.go new file mode 100644 index 00000000000..8c258c43709 --- /dev/null +++ b/example/otel/main.go @@ -0,0 +1,64 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This example demonstrates how to use the otel transport to instrument +// the go-github client with OpenTelemetry tracing. +package main + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/google/go-github/otel/v90" + "github.com/google/go-github/v90/github" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/sdk/trace" +) + +func main() { + // Initialize stdout exporter to see traces in console + exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint()) + if err != nil { + log.Fatalf("failed to initialize stdouttrace exporter: %v", err) + } + + tp := trace.NewTracerProvider( + trace.WithBatcher(exporter), + ) + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Fatal(err) + } + }() + + // Configure OTel transport + t := otel.NewTransport( + http.DefaultTransport, + otel.WithTracerProvider(tp), + ) + + client, err := github.NewClient(github.WithTransport(t)) + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + // Make a request (Get Rate Limits is public and cheap) + limits, resp, err := client.RateLimit.Get(context.Background()) + if err != nil { + log.Printf("Error fetching rate limits: %v", err) + } else { + fmt.Printf("Core Rate Limit: %v/%v (Resets at %v)\n", + limits.GetCore().Remaining, + limits.GetCore().Limit, + limits.GetCore().Reset) + } + + // Print the HTTP response status when available. + if resp != nil { + fmt.Printf("Response Status: %v\n", resp.Status) + } +} diff --git a/example/ratelimit/main.go b/example/ratelimit/main.go new file mode 100644 index 00000000000..e005e209803 --- /dev/null +++ b/example/ratelimit/main.go @@ -0,0 +1,55 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The ratelimit command demonstrates using github_ratelimit and github_pagination. +// By using the waiters, the client automatically sleeps and retries requests +// when it hits secondary rate limits. +// It also prevents the client from abusing the API in case of a primary rate limit. +package main + +import ( + "context" + "fmt" + "log" + + "github.com/gofri/go-github-pagination/githubpagination" + "github.com/gofri/go-github-ratelimit/v2/github_ratelimit" + "github.com/gofri/go-github-ratelimit/v2/github_ratelimit/github_primary_ratelimit" + "github.com/gofri/go-github-ratelimit/v2/github_ratelimit/github_secondary_ratelimit" + "github.com/google/go-github/v90/github" +) + +func main() { + var username string + fmt.Print("Enter GitHub username: ") + fmt.Scanf("%s", &username) + + rateLimiter := github_ratelimit.New(nil, + github_primary_ratelimit.WithLimitDetectedCallback(func(ctx *github_primary_ratelimit.CallbackContext) { + fmt.Printf("Primary rate limit detected: category %v, reset time: %v\n", ctx.Category, ctx.ResetTime) + }), + github_secondary_ratelimit.WithLimitDetectedCallback(func(ctx *github_secondary_ratelimit.CallbackContext) { + fmt.Printf("Secondary rate limit detected: reset time: %v, total sleep time: %v\n", ctx.ResetTime, ctx.TotalSleepTime) + }), + ) + + paginator := githubpagination.NewClient(rateLimiter, + githubpagination.WithPerPage(100), // default to 100 results per page + ) + client, err := github.NewClient(github.WithHTTPClient(paginator)) + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + // Example usage of the client + repos, _, err := client.Repositories.ListByUser(context.Background(), username, nil) + if err != nil { + log.Fatalf("Error: %v", err) + } + + for i, repo := range repos { + fmt.Printf("%v. %v\n", i+1, repo.GetName()) + } +} diff --git a/example/simple/main.go b/example/simple/main.go index b10be06b9eb..59cd942f4e0 100644 --- a/example/simple/main.go +++ b/example/simple/main.go @@ -11,14 +11,17 @@ package main import ( "context" "fmt" + "log" - "github.com/google/go-github/v28/github" + "github.com/google/go-github/v90/github" ) // Fetch all the public organizations' membership of a user. -// -func FetchOrganizations(username string) ([]*github.Organization, error) { - client := github.NewClient(nil) +func fetchOrganizations(username string) ([]*github.Organization, error) { + client, err := github.NewClient() + if err != nil { + return nil, err + } orgs, _, err := client.Organizations.List(context.Background(), username, nil) return orgs, err } @@ -28,10 +31,9 @@ func main() { fmt.Print("Enter GitHub username: ") fmt.Scanf("%s", &username) - organizations, err := FetchOrganizations(username) + organizations, err := fetchOrganizations(username) if err != nil { - fmt.Printf("Error: %v\n", err) - return + log.Fatalf("Error: %v", err) } for i, organization := range organizations { diff --git a/example/tokenauth/main.go b/example/tokenauth/main.go new file mode 100644 index 00000000000..95d612baff9 --- /dev/null +++ b/example/tokenauth/main.go @@ -0,0 +1,47 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The tokenauth command demonstrates using a Personal Access Token (PAT) to +// authenticate with GitHub. +// You can test out a GitHub Personal Access Token using this simple example. +// You can generate them here: https://github.com/settings/tokens +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" + "golang.org/x/term" +) + +func main() { + fmt.Print("GitHub Token: ") + token, _ := term.ReadPassword(int(os.Stdin.Fd())) + fmt.Println() + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(string(token))) + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + user, resp, err := client.Users.Get(ctx, "") + if err != nil { + log.Fatalf("Error fetching user: %v", err) + } + + // Rate.Limit should most likely be 5000 when authorized. + log.Printf("Rate: %#v\n", resp.Rate) + + // If a Token Expiration has been set, it will be displayed. + if !resp.TokenExpiration.IsZero() { + log.Printf("Token Expiration: %v\n", resp.TokenExpiration) + } + + fmt.Printf("\n%v\n", github.Stringify(user)) +} diff --git a/example/topics/main.go b/example/topics/main.go new file mode 100644 index 00000000000..20a3f33e3dc --- /dev/null +++ b/example/topics/main.go @@ -0,0 +1,43 @@ +// Copyright 2019 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The topics command demonstrates the functionality that +// prompts the user for a GitHub topic and lists all the entities +// that are related to the specified topic or subject. +package main + +import ( + "context" + "fmt" + "log" + + "github.com/google/go-github/v90/github" +) + +// Fetch and lists all the public topics associated with the specified GitHub topic. +func fetchTopics(topic string) (*github.TopicsSearchResult, error) { + client, err := github.NewClient() + if err != nil { + return nil, err + } + + topics, _, err := client.Search.Topics(context.Background(), topic, nil) + return topics, err +} + +func main() { + var topic string + fmt.Print("Enter GitHub topic: ") + fmt.Scanf("%s", &topic) + + topics, err := fetchTopics(topic) + if err != nil { + log.Fatalf("Error: %v", err) + } + + for _, topic := range topics.Topics { + fmt.Println(*topic.Name) + } +} diff --git a/example/uploadreleaseassetfromrelease/main.go b/example/uploadreleaseassetfromrelease/main.go new file mode 100644 index 00000000000..70a201c86f2 --- /dev/null +++ b/example/uploadreleaseassetfromrelease/main.go @@ -0,0 +1,64 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The uploadreleaseassetfromrelease example demonstrates how to upload +// a release asset using the UploadReleaseAssetFromRelease helper. +package main + +import ( + "bytes" + "context" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" +) + +func main() { + token := os.Getenv("GITHUB_AUTH_TOKEN") + if token == "" { + log.Fatal("GITHUB_AUTH_TOKEN not set") + } + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + owner := "OWNER" + repo := "REPO" + releaseID := int64(1) + + // Fetch the release (UploadURL is populated by the API) + release, _, err := client.Repositories.GetRelease(ctx, owner, repo, releaseID) + if err != nil { + log.Fatalf("GetRelease failed: %v", err) + } + + // Asset content + data := []byte("Hello from go-github!\n") + reader := bytes.NewReader(data) + size := int64(len(data)) + + opts := &github.UploadOptions{ + Name: "example.txt", + Label: "Example asset", + } + + asset, _, err := client.Repositories.UploadReleaseAssetFromRelease( + ctx, + release, + opts, + reader, + size, + ) + if err != nil { + log.Fatalf("UploadReleaseAssetFromRelease failed: %v", err) + } + + fmt.Printf("Uploaded asset ID: %v\n", asset.GetID()) +} diff --git a/example/verifyartifact/main.go b/example/verifyartifact/main.go new file mode 100644 index 00000000000..73896933d4f --- /dev/null +++ b/example/verifyartifact/main.go @@ -0,0 +1,200 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a simple example of how to verify artifact attestations +// hosted on GitHub using the sigstore-go library. +// This is a very barebones example drawn from the sigstore-go +// library's examples and should not be used in production. +package main + +import ( + "context" + "encoding/hex" + "encoding/json" + "flag" + "fmt" + "log" + "os" + + "github.com/google/go-github/v90/github" + "github.com/sigstore/sigstore-go/pkg/bundle" + "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/verify" +) + +var ( + owner = flag.String("owner", "cli", "GitHub organization or user to scope attestation lookup by") + // You can use a utility like openssl or sha256sum to + // compute the digest. + artifactDigest = flag.String("artifact-digest", "", "The digest of the artifact") + // The algorithm used to compute the digest of the artifact. + // Note that the GitHub API currently only supports querying + // by sha256 digest. + artifactDigestAlgorithm = flag.String("artifact-digest-algorithm", "sha256", "The algorithm used to compute the digest of the artifact") + // Attestations produced by GitHub Actions use ID tokens + // issued by GitHub. + expectedIssuer = flag.String("expected-issuer", "https://token.actions.githubusercontent.com", "Issuer of the OIDC token") + // Subject Alternative Name is set to the calling workflow file. + // This value will vary from repository to repository. + expectedSAN = flag.String("expected-san", "https://github.com/cli/cli/.github/workflows/deployment.yml@refs/heads/trunk", "The expected Subject Alternative Name (SAN) of the certificate used to sign the attestation") + // Attestations produced by GitHub Actions use the public + // good trust root maintained by Sigstore. + // A copy is included in this repo for convenience. + // + // https://github.com/sigstore/root-signing/raw/refs/heads/main/targets/trusted_root.json + trustedRootJSONPath = flag.String("trusted-root-json-path", "verifyartifact/trusted-root-public-good.json", "Path to the trusted root JSON file") +) + +func usage() { + fmt.Fprintln(os.Stderr, "This is an example of how to verify the provenance of an artifact using GitHub Attestations and the sigstore-go library.") + fmt.Fprintf(os.Stderr, "\nUsage: %v [flags]\n", os.Args[0]) + fmt.Fprint(os.Stderr, "\nThe flags are:\n") + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, ` +Example: +Verifying a GitHub CLI artifact + %v -owner cli \ + -artifact-digest 2ce2e480e3c3f7ca0af83418d3ebaeedacee135dbac94bd946d7d84edabcdb64 \ + -expected-san https://github.com/cli/cli/.github/workflows/deployment.yml@refs/heads/trunk + + See https://github.com/cli/cli/attestations/2543768 for a summary of the attestation. +`, os.Args[0]) +} + +func main() { + flag.Parse() + if *artifactDigest == "" { + fmt.Fprintln(os.Stderr, "artifact-digest is required.") + usage() + os.Exit(1) + } + + token := os.Getenv("GITHUB_AUTH_TOKEN") + + if token == "" { + log.Fatal("Unauthorized: No token present. Please set the GITHUB_AUTH_TOKEN environment variable to a valid token with `attestations:read` permission.") + } + + ctx := context.Background() + client, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + // Fetch attestations from the GitHub API. + // The attestations API doesn't differentiate between users and orgs, + // so we can use the OrganizationsService to fetch attestations for both. + attestations, _, err := client.Organizations.ListAttestations(ctx, *owner, fmt.Sprintf("%v:%v", *artifactDigestAlgorithm, *artifactDigest), nil) + if err != nil { + log.Fatal(err) + } + + if len(attestations.Attestations) == 0 { + log.Fatal("No attestations found.") + } + + sev, err := getSignedEntityVerifier() + if err != nil { + log.Fatal(err) + } + + pb, err := getPolicyBuilder() + if err != nil { + log.Fatal(err) + } + + var b *bundle.Bundle + for _, attestation := range attestations.Attestations { + if err := json.Unmarshal(attestation.Bundle, &b); err != nil { + log.Fatal(err) + } + + err := runVerification(sev, pb, b) + if err != nil { + log.Fatal(err) + } + } +} + +func getTrustedMaterial() (root.TrustedMaterialCollection, error) { + trustedRootJSON, err := os.ReadFile(*trustedRootJSONPath) + if err != nil { + return nil, fmt.Errorf("failed to read %v: %w", *trustedRootJSONPath, err) + } + + trustedRoot, err := root.NewTrustedRootFromJSON(trustedRootJSON) + if err != nil { + return nil, err + } + + trustedMaterial := root.TrustedMaterialCollection{ + trustedRoot, + } + + return trustedMaterial, nil +} + +func getIdentityPolicies() ([]verify.PolicyOption, error) { + certID, err := verify.NewShortCertificateIdentity(*expectedIssuer, "", *expectedSAN, "") + if err != nil { + return nil, err + } + + return []verify.PolicyOption{ + verify.WithCertificateIdentity(certID), + }, nil +} + +func getSignedEntityVerifier() (*verify.Verifier, error) { + // Set up the verifier + verifierConfig := []verify.VerifierOption{ + verify.WithSignedCertificateTimestamps(1), + verify.WithObserverTimestamps(1), + verify.WithTransparencyLog(1), + } + + // Set up the trusted material + trustedMaterial, err := getTrustedMaterial() + if err != nil { + return nil, err + } + + return verify.NewVerifier(trustedMaterial, verifierConfig...) +} + +func getPolicyBuilder() (*verify.PolicyBuilder, error) { + // Set up the identity policy + identityPolicies, err := getIdentityPolicies() + if err != nil { + return nil, err + } + + // Set up the artifact policy + artifactDigestBytes, err := hex.DecodeString(*artifactDigest) + if err != nil { + return nil, err + } + artifactPolicy := verify.WithArtifactDigest(*artifactDigestAlgorithm, artifactDigestBytes) + + pb := verify.NewPolicy(artifactPolicy, identityPolicies...) + return &pb, nil +} + +func runVerification(sev *verify.Verifier, pb *verify.PolicyBuilder, b *bundle.Bundle) error { + res, err := sev.Verify(b, *pb) + if err != nil { + return err + } + + fmt.Fprint(os.Stderr, "Verification successful!\n") + + marshaled, err := json.MarshalIndent(res, "", " ") + if err != nil { + return err + } + + fmt.Println(string(marshaled)) + return nil +} diff --git a/example/verifyartifact/trusted-root-public-good.json b/example/verifyartifact/trusted-root-public-good.json new file mode 100644 index 00000000000..5ed62811e17 --- /dev/null +++ b/example/verifyartifact/trusted-root-public-good.json @@ -0,0 +1,114 @@ +{ + "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", + "tlogs": [ + { + "baseUrl": "https://rekor.sigstore.dev", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2021-01-12T11:53:27.000Z" + } + }, + "logId": { + "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" + } + } + ], + "certificateAuthorities": [ + { + "subject": { + "organization": "sigstore.dev", + "commonName": "sigstore" + }, + "uri": "https://fulcio.sigstore.dev", + "certChain": { + "certificates": [ + { + "rawBytes": "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==" + } + ] + }, + "validFor": { + "start": "2021-03-07T03:20:29.000Z", + "end": "2022-12-31T23:59:59.999Z" + } + }, + { + "subject": { + "organization": "sigstore.dev", + "commonName": "sigstore" + }, + "uri": "https://fulcio.sigstore.dev", + "certChain": { + "certificates": [ + { + "rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=" + }, + { + "rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ" + } + ] + }, + "validFor": { + "start": "2022-04-13T20:06:15.000Z" + } + } + ], + "ctlogs": [ + { + "baseUrl": "https://ctfe.sigstore.dev/test", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2021-03-14T00:00:00.000Z", + "end": "2022-10-31T23:59:59.999Z" + } + }, + "logId": { + "keyId": "CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=" + } + }, + { + "baseUrl": "https://ctfe.sigstore.dev/2022", + "hashAlgorithm": "SHA2_256", + "publicKey": { + "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==", + "keyDetails": "PKIX_ECDSA_P256_SHA_256", + "validFor": { + "start": "2022-10-20T00:00:00.000Z" + } + }, + "logId": { + "keyId": "3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4=" + } + } + ], + "timestampAuthorities": [ + { + "subject": { + "organization": "GitHub, Inc.", + "commonName": "Internal Services Root" + }, + "certChain": { + "certificates": [ + { + "rawBytes": "MIIB3DCCAWKgAwIBAgIUchkNsH36Xa04b1LqIc+qr9DVecMwCgYIKoZIzj0EAwMwMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMB4XDTIzMDQxNDAwMDAwMFoXDTI0MDQxMzAwMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgVGltZXN0YW1waW5nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUD5ZNbSqYMd6r8qpOOEX9ibGnZT9GsuXOhr/f8U9FJugBGExKYp40OULS0erjZW7xV9xV52NnJf5OeDq4e5ZKqNWMFQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUaW1RudOgVt0leqY0WKYbuPr47wAwCgYIKoZIzj0EAwMDaAAwZQIwbUH9HvD4ejCZJOWQnqAlkqURllvu9M8+VqLbiRK+zSfZCZwsiljRn8MQQRSkXEE5AjEAg+VxqtojfVfu8DhzzhCx9GKETbJHb19iV72mMKUbDAFmzZ6bQ8b54Zb8tidy5aWe" + }, + { + "rawBytes": "MIICEDCCAZWgAwIBAgIUX8ZO5QXP7vN4dMQ5e9sU3nub8OgwCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDQxNDAwMDAwMFoXDTI4MDQxMjAwMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEvMLY/dTVbvIJYANAuszEwJnQE1llftynyMKIMhh48HmqbVr5ygybzsLRLVKbBWOdZ21aeJz+gZiytZetqcyF9WlER5NEMf6JV7ZNojQpxHq4RHGoGSceQv/qvTiZxEDKo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaW1RudOgVt0leqY0WKYbuPr47wAwHwYDVR0jBBgwFoAU9NYYlobnAG4c0/qjxyH/lq/wz+QwCgYIKoZIzj0EAwMDaQAwZgIxAK1B185ygCrIYFlIs3GjswjnwSMG6LY8woLVdakKDZxVa8f8cqMs1DhcxJ0+09w95QIxAO+tBzZk7vjUJ9iJgD4R6ZWTxQWKqNm74jO99o+o9sv4FI/SZTZTFyMn0IJEHdNmyA==" + }, + { + "rawBytes": "MIIB9DCCAXqgAwIBAgIUa/JAkdUjK4JUwsqtaiRJGWhqLSowCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDQxNDAwMDAwMFoXDTMzMDQxMTAwMDAwMFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf9jFAXxz4kx68AHRMOkFBhflDcMTvzaXz4x/FCcXjJ/1qEKon/qPIGnaURskDtyNbNDOpeJTDDFqt48iMPrnzpx6IZwqemfUJN4xBEZfza+pYt/iyod+9tZr20RRWSv/o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQU9NYYlobnAG4c0/qjxyH/lq/wz+QwCgYIKoZIzj0EAwMDaAAwZQIxALZLZ8BgRXzKxLMMN9VIlO+e4hrBnNBgF7tz7Hnrowv2NetZErIACKFymBlvWDvtMAIwZO+ki6ssQ1bsZo98O8mEAf2NZ7iiCgDDU0Vwjeco6zyeh0zBTs9/7gV6AHNQ53xD" + } + ] + }, + "validFor": { + "start": "2023-04-14T00:00:00.000Z" + } + } + ] +} diff --git a/github/actions.go b/github/actions.go new file mode 100644 index 00000000000..575a637a2a9 --- /dev/null +++ b/github/actions.go @@ -0,0 +1,12 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +// ActionsService handles communication with the actions related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/actions?apiVersion=2022-11-28 +type ActionsService service diff --git a/github/actions_artifacts.go b/github/actions_artifacts.go new file mode 100644 index 00000000000..2a345edbc9c --- /dev/null +++ b/github/actions_artifacts.go @@ -0,0 +1,224 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "net/url" +) + +// ArtifactWorkflowRun represents a GitHub artifact's workflow run. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28 +type ArtifactWorkflowRun struct { + ID *int64 `json:"id,omitempty"` + RepositoryID *int64 `json:"repository_id,omitempty"` + HeadRepositoryID *int64 `json:"head_repository_id,omitempty"` + HeadBranch *string `json:"head_branch,omitempty"` + HeadSHA *string `json:"head_sha,omitempty"` +} + +// Artifact represents a GitHub artifact. Artifacts allow sharing +// data between jobs in a workflow and provide storage for data +// once a workflow is complete. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28 +type Artifact struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + SizeInBytes *int64 `json:"size_in_bytes,omitempty"` + URL *string `json:"url,omitempty"` + ArchiveDownloadURL *string `json:"archive_download_url,omitempty"` + Expired *bool `json:"expired,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` + // Digest is the SHA256 digest of the artifact. + // This field will only be populated on artifacts uploaded with upload-artifact v4 or newer. + // For older versions, this field will be null. + Digest *string `json:"digest,omitempty"` + WorkflowRun *ArtifactWorkflowRun `json:"workflow_run,omitempty"` +} + +// ArtifactList represents a list of GitHub artifacts. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28#artifacts +type ArtifactList struct { + TotalCount *int64 `json:"total_count,omitempty"` + Artifacts []*Artifact `json:"artifacts,omitempty"` +} + +// ListArtifactsOptions specifies the optional parameters to the +// ActionsService.ListArtifacts method. +type ListArtifactsOptions struct { + // Name represents the name field of an artifact. + // When specified, only artifacts with this name will be returned. + Name *string `url:"name,omitempty"` + + ListOptions +} + +// ArtifactPeriod represents the period for which the artifact and +// log of a workflow run is retained. +type ArtifactPeriod struct { + Days *int `json:"days,omitempty"` + MaximumAllowedDays *int `json:"maximum_allowed_days,omitempty"` +} + +func (a ArtifactPeriod) String() string { return Stringify(a) } + +// ArtifactPeriodOpt is used to specify the retention period of +// artifacts and logs in a workflow run. +type ArtifactPeriodOpt struct { + Days *int `json:"days,omitempty"` +} + +// ListArtifacts lists all artifacts that belong to a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28#list-artifacts-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/artifacts +func (s *ActionsService) ListArtifacts(ctx context.Context, owner, repo string, opts *ListArtifactsOptions) (*ArtifactList, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/artifacts", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var artifactList *ArtifactList + resp, err := s.client.Do(req, &artifactList) + if err != nil { + return nil, resp, err + } + + return artifactList, resp, nil +} + +// ListWorkflowRunArtifacts lists all artifacts that belong to a workflow run. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts +func (s *ActionsService) ListWorkflowRunArtifacts(ctx context.Context, owner, repo string, runID int64, opts *ListOptions) (*ArtifactList, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/artifacts", owner, repo, runID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var artifactList *ArtifactList + resp, err := s.client.Do(req, &artifactList) + if err != nil { + return nil, resp, err + } + + return artifactList, resp, nil +} + +// GetArtifact gets a specific artifact for a workflow run. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28#get-an-artifact +// +//meta:operation GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id} +func (s *ActionsService) GetArtifact(ctx context.Context, owner, repo string, artifactID int64) (*Artifact, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v", owner, repo, artifactID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var artifact *Artifact + resp, err := s.client.Do(req, &artifact) + if err != nil { + return nil, resp, err + } + + return artifact, resp, nil +} + +// DownloadArtifact gets a redirect URL to download an archive for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact +// +//meta:operation GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format} +func (s *ActionsService) DownloadArtifact(ctx context.Context, owner, repo string, artifactID int64, maxRedirects int) (*url.URL, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v/zip", owner, repo, artifactID) + + if s.client.rateLimitRedirectionalEndpoints { + return s.downloadArtifactWithRateLimit(ctx, u, maxRedirects) + } + + return s.downloadArtifactWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) downloadArtifactWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusFound { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) + } + + parsedURL, err := url.Parse(resp.Header.Get("Location")) + if err != nil { + return nil, newResponse(resp), err + } + + return parsedURL, newResponse(resp), nil +} + +func (s *ActionsService) downloadArtifactWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} + +// DeleteArtifact deletes a workflow run artifact. +// +// GitHub API docs: https://docs.github.com/rest/actions/artifacts?apiVersion=2022-11-28#delete-an-artifact +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id} +func (s *ActionsService) DeleteArtifact(ctx context.Context, owner, repo string, artifactID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v", owner, repo, artifactID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_artifacts_test.go b/github/actions_artifacts_test.go new file mode 100644 index 00000000000..4ae31c47ff6 --- /dev/null +++ b/github/actions_artifacts_test.go @@ -0,0 +1,588 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListArtifacts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "name": "TheArtifact"}) + fmt.Fprint(w, + `{ + "total_count":1, + "artifacts":[{"id":1}] + }`, + ) + }) + + opts := &ListArtifactsOptions{ + Name: Ptr("TheArtifact"), + ListOptions: ListOptions{Page: 2}, + } + ctx := t.Context() + artifacts, _, err := client.Actions.ListArtifacts(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListArtifacts returned error: %v", err) + } + + want := &ArtifactList{TotalCount: Ptr(int64(1)), Artifacts: []*Artifact{{ID: Ptr(int64(1))}}} + if !cmp.Equal(artifacts, want) { + t.Errorf("Actions.ListArtifacts returned %+v, want %+v", artifacts, want) + } + + const methodName = "ListArtifacts" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListArtifacts(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListArtifacts(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListArtifacts_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListArtifacts(ctx, "%", "r", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListArtifacts_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListArtifacts(ctx, "o", "%", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListArtifacts_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + artifacts, resp, err := client.Actions.ListArtifacts(ctx, "o", "r", nil) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.ListArtifacts return status %v, want %v", got, want) + } + if artifacts != nil { + t.Errorf("Actions.ListArtifacts return %+v, want nil", artifacts) + } +} + +func TestActionsService_ListWorkflowRunArtifacts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/1/artifacts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, + `{ + "total_count":1, + "artifacts":[{"id":1}] + }`, + ) + }) + + opts := &ListOptions{Page: 2} + ctx := t.Context() + artifacts, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "r", 1, opts) + if err != nil { + t.Errorf("Actions.ListWorkflowRunArtifacts returned error: %v", err) + } + + want := &ArtifactList{TotalCount: Ptr(int64(1)), Artifacts: []*Artifact{{ID: Ptr(int64(1))}}} + if !cmp.Equal(artifacts, want) { + t.Errorf("Actions.ListWorkflowRunArtifacts returned %+v, want %+v", artifacts, want) + } + + const methodName = "ListWorkflowRunArtifacts" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListWorkflowRunArtifacts(ctx, "\n", "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "r", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListWorkflowRunArtifacts_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, "%", "r", 1, nil) + testURLParseError(t, err) +} + +func TestActionsService_ListWorkflowRunArtifacts_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "%", 1, nil) + testURLParseError(t, err) +} + +func TestActionsService_ListWorkflowRunArtifacts_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/1/artifacts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + artifacts, resp, err := client.Actions.ListWorkflowRunArtifacts(ctx, "o", "r", 1, nil) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.ListWorkflowRunArtifacts return status %v, want %v", got, want) + } + if artifacts != nil { + t.Errorf("Actions.ListWorkflowRunArtifacts return %+v, want nil", artifacts) + } +} + +func TestActionsService_GetArtifact(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id":1, + "node_id":"xyz", + "name":"a", + "size_in_bytes":5, + "archive_download_url":"u" + }`) + }) + + ctx := t.Context() + artifact, _, err := client.Actions.GetArtifact(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Actions.GetArtifact returned error: %v", err) + } + + want := &Artifact{ + ID: Ptr(int64(1)), + NodeID: Ptr("xyz"), + Name: Ptr("a"), + SizeInBytes: Ptr(int64(5)), + ArchiveDownloadURL: Ptr("u"), + } + if !cmp.Equal(artifact, want) { + t.Errorf("Actions.GetArtifact returned %+v, want %+v", artifact, want) + } + + const methodName = "GetArtifact" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetArtifact(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetArtifact(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetArtifact_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.GetArtifact(ctx, "%", "r", 1) + testURLParseError(t, err) +} + +func TestActionsService_GetArtifact_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.GetArtifact(ctx, "o", "%", 1) + testURLParseError(t, err) +} + +func TestActionsService_GetArtifact_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + artifact, resp, err := client.Actions.GetArtifact(ctx, "o", "r", 1) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetArtifact return status %v, want %v", got, want) + } + if artifact != nil { + t.Errorf("Actions.GetArtifact return %+v, want nil", artifact) + } +} + +func TestActionsService_DownloadArtifact(t *testing.T) { + t.Parallel() + + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/artifact", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1) + if err != nil { + t.Errorf("Actions.DownloadArtifact returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.DownloadArtifact returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + + want := "https://github.com/artifact" + if url.String() != want { + t.Errorf("Actions.DownloadArtifact returned %+v, want %+v", url, want) + } + + const methodName = "DownloadArtifact" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.DownloadArtifact(ctx, "\n", "\n", -1, 1) + return err + }) + + // Create "bad" client with custom round tripper + badClient, err := client.Clone(WithTransport(roundTripperFunc(func(*http.Request) (*http.Response, error) { + return nil, errors.New("failed to download artifact") + }))) + if err != nil { + t.Fatalf("failed to clone client: %v", err) + } + testBadOptions(t, methodName, func() (err error) { + _, _, err = badClient.Actions.DownloadArtifact(ctx, "o", "r", 1, 1) + return err + }) + }) + } +} + +func TestActionsService_DownloadArtifact_invalidOwner(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + ctx := t.Context() + _, _, err := client.Actions.DownloadArtifact(ctx, "%", "r", 1, 1) + testURLParseError(t, err) + }) + } +} + +func TestActionsService_DownloadArtifact_invalidRepo(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + ctx := t.Context() + _, _, err := client.Actions.DownloadArtifact(ctx, "o", "%", 1, 1) + testURLParseError(t, err) + }) + } +} + +func TestActionsService_DownloadArtifact_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/artifact", http.StatusMovedPermanently) + }) + + ctx := t.Context() + _, resp, _ := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 0) + if resp.StatusCode != http.StatusMovedPermanently { + t.Errorf("Actions.DownloadArtifact return status %v, want %v", resp.StatusCode, http.StatusMovedPermanently) + } + }) + } +} + +func TestActionsService_DownloadArtifact_StatusMovedPermanently_followRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/artifact", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1) + if err != nil { + t.Errorf("Actions.DownloadArtifact return error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.DownloadArtifact return status %v, want %v", resp.StatusCode, http.StatusFound) + } + want := "https://github.com/artifact" + if url.String() != want { + t.Errorf("Actions.DownloadArtifact returned %+v, want %+v", url, want) + } + }) + } +} + +func TestActionsService_DownloadArtifact_unexpectedCode(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + url, resp, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, 1) + if err == nil { + t.Fatal("Actions.DownloadArtifact should return error on unexpected code") + } + if !strings.Contains(err.Error(), "unexpected status code") { + t.Error("Actions.DownloadArtifact should return unexpected status code") + } + if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want { + t.Errorf("Actions.DownloadArtifact return status %v, want %v", got, want) + } + if url != nil { + t.Errorf("Actions.DownloadArtifact return %+v, want nil", url) + } + }) + } +} + +func TestActionsService_DeleteArtifact(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteArtifact(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Actions.DeleteArtifact return error: %v", err) + } + + const methodName = "DeleteArtifact" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteArtifact(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteArtifact(ctx, "o", "r", 1) + }) +} + +func TestActionsService_DeleteArtifact_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Actions.DeleteArtifact(ctx, "%", "r", 1) + testURLParseError(t, err) +} + +func TestActionsService_DeleteArtifact_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Actions.DeleteArtifact(ctx, "o", "%", 1) + testURLParseError(t, err) +} + +func TestActionsService_DeleteArtifact_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, err := client.Actions.DeleteArtifact(ctx, "o", "r", 1) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.DeleteArtifact return status %v, want %v", got, want) + } +} diff --git a/github/actions_cache.go b/github/actions_cache.go new file mode 100644 index 00000000000..73e82700839 --- /dev/null +++ b/github/actions_cache.go @@ -0,0 +1,249 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ActionsCache represents a GitHub action cache. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#about-the-cache-api +type ActionsCache struct { + ID *int64 `json:"id,omitempty" url:"-"` + Ref *string `json:"ref,omitempty" url:"ref"` + Key *string `json:"key,omitempty" url:"key"` + Version *string `json:"version,omitempty" url:"-"` + LastAccessedAt *Timestamp `json:"last_accessed_at,omitempty" url:"-"` + CreatedAt *Timestamp `json:"created_at,omitempty" url:"-"` + SizeInBytes *int64 `json:"size_in_bytes,omitempty" url:"-"` +} + +// ActionsCacheList represents a list of GitHub actions Cache. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository +type ActionsCacheList struct { + TotalCount int `json:"total_count"` + ActionsCaches []*ActionsCache `json:"actions_caches,omitempty"` +} + +// ActionsCacheUsage represents a GitHub Actions Cache Usage object. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-a-repository +type ActionsCacheUsage struct { + FullName string `json:"full_name"` + ActiveCachesSizeInBytes int64 `json:"active_caches_size_in_bytes"` + ActiveCachesCount int `json:"active_caches_count"` +} + +// ActionsCacheUsageList represents a list of repositories with GitHub Actions cache usage for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-a-repository +type ActionsCacheUsageList struct { + TotalCount int `json:"total_count"` + RepoCacheUsage []*ActionsCacheUsage `json:"repository_cache_usages,omitempty"` +} + +// TotalCacheUsage represents total GitHub actions cache usage of an organization or enterprise. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-an-enterprise +type TotalCacheUsage struct { + TotalActiveCachesUsageSizeInBytes int64 `json:"total_active_caches_size_in_bytes"` + TotalActiveCachesCount int `json:"total_active_caches_count"` +} + +// ActionsCacheListOptions represents a list of all possible optional Query parameters for ListCaches method. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository +type ActionsCacheListOptions struct { + ListOptions + // The Git reference for the results you want to list. + // The ref for a branch can be formatted either as refs/heads/ + // or simply . To reference a pull request use refs/pull//merge + Ref *string `url:"ref,omitempty"` + Key *string `url:"key,omitempty"` + // Can be one of: "created_at", "last_accessed_at", "size_in_bytes". Default: "last_accessed_at" + Sort *string `url:"sort,omitempty"` + // Can be one of: "asc", "desc" Default: desc + Direction *string `url:"direction,omitempty"` +} + +// ListCaches lists the GitHub Actions caches for a repository. +// You must authenticate using an access token with the repo scope to use this endpoint. +// +// Permissions: must have the actions:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#list-github-actions-caches-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/caches +func (s *ActionsService) ListCaches(ctx context.Context, owner, repo string, opts *ActionsCacheListOptions) (*ActionsCacheList, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/caches", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var actionCacheList *ActionsCacheList + resp, err := s.client.Do(req, &actionCacheList) + if err != nil { + return nil, resp, err + } + + return actionCacheList, resp, nil +} + +// DeleteCachesByKey deletes one or more GitHub Actions caches for a repository, using a complete cache key. +// By default, all caches that match the provided key are deleted, but you can optionally provide +// a Git ref to restrict deletions to caches that match both the provided key and the Git ref. +// The ref for a branch can be formatted either as "refs/heads/" or simply "". +// To reference a pull request use "refs/pull//merge". If you don't want to use ref just pass nil in parameter. +// +// Permissions: You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have the actions:write permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#delete-github-actions-caches-for-a-repository-using-a-cache-key +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/caches +func (s *ActionsService) DeleteCachesByKey(ctx context.Context, owner, repo, key string, ref *string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/caches", owner, repo) + u, err := addOptions(u, &ActionsCache{Key: &key, Ref: ref}) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteCachesByID deletes a GitHub Actions cache for a repository, using a cache ID. +// +// Permissions: You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have the actions:write permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/caches/{cache_id} +func (s *ActionsService) DeleteCachesByID(ctx context.Context, owner, repo string, cacheID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/caches/%v", owner, repo, cacheID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetCacheUsageForRepo gets GitHub Actions cache usage for a repository. The data fetched using this API is refreshed approximately every 5 minutes, +// so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: Anyone with read access to the repository can use this endpoint. If the repository is private, you must use an +// access token with the repo scope. GitHub Apps must have the actions:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/cache/usage +func (s *ActionsService) GetCacheUsageForRepo(ctx context.Context, owner, repo string) (*ActionsCacheUsage, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/cache/usage", owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cacheUsage *ActionsCacheUsage + res, err := s.client.Do(req, &cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} + +// ListCacheUsageByRepoForOrg lists repositories and their GitHub Actions cache usage for an organization. The data fetched using this API is +// refreshed approximately every 5 minutes, so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: You must authenticate using an access token with the read:org scope to use this endpoint. +// GitHub Apps must have the organization_administration:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#list-repositories-with-github-actions-cache-usage-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/cache/usage-by-repository +func (s *ActionsService) ListCacheUsageByRepoForOrg(ctx context.Context, org string, opts *ListOptions) (*ActionsCacheUsageList, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/cache/usage-by-repository", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cacheUsage *ActionsCacheUsageList + res, err := s.client.Do(req, &cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} + +// GetTotalCacheUsageForOrg gets the total GitHub Actions cache usage for an organization. The data fetched using this API is refreshed approximately every +// 5 minutes, so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: You must authenticate using an access token with the read:org scope to use this endpoint. +// GitHub Apps must have the organization_administration:read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/cache/usage +func (s *ActionsService) GetTotalCacheUsageForOrg(ctx context.Context, org string) (*TotalCacheUsage, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/cache/usage", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cacheUsage *TotalCacheUsage + res, err := s.client.Do(req, &cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} + +// GetTotalCacheUsageForEnterprise gets the total GitHub Actions cache usage for an enterprise. The data fetched using this API is refreshed approximately every 5 minutes, +// so values returned from this endpoint may take at least 5 minutes to get updated. +// +// Permissions: You must authenticate using an access token with the "admin:enterprise" scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/cache?apiVersion=2022-11-28#get-github-actions-cache-usage-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/cache/usage +func (s *ActionsService) GetTotalCacheUsageForEnterprise(ctx context.Context, enterprise string) (*TotalCacheUsage, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/cache/usage", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cacheUsage *TotalCacheUsage + res, err := s.client.Do(req, &cacheUsage) + if err != nil { + return nil, res, err + } + + return cacheUsage, res, err +} diff --git a/github/actions_cache_test.go b/github/actions_cache_test.go new file mode 100644 index 00000000000..14a8ad194d6 --- /dev/null +++ b/github/actions_cache_test.go @@ -0,0 +1,517 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListCaches(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/caches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, + `{ + "total_count":1, + "actions_caches":[{"id":1}] + }`, + ) + }) + + opts := &ActionsCacheListOptions{ListOptions: ListOptions{Page: 2}} + ctx := t.Context() + cacheList, _, err := client.Actions.ListCaches(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListCaches returned error: %v", err) + } + + want := &ActionsCacheList{TotalCount: 1, ActionsCaches: []*ActionsCache{{ID: Ptr(int64(1))}}} + if !cmp.Equal(cacheList, want) { + t.Errorf("Actions.ListCaches returned %+v, want %+v", cacheList, want) + } + + const methodName = "ListCaches" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListCaches(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListCaches(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListCaches_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListCaches(ctx, "%", "r", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListCaches_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListCaches(ctx, "o", "%", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListCaches_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/caches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + caches, resp, err := client.Actions.ListCaches(ctx, "o", "r", nil) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.ListCaches return status %v, want %v", got, want) + } + if caches != nil { + t.Errorf("Actions.ListCaches return %+v, want nil", caches) + } +} + +func TestActionsService_DeleteCachesByKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/caches", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testFormValues(t, r, values{"key": "1", "ref": "main"}) + }) + + ctx := t.Context() + _, err := client.Actions.DeleteCachesByKey(ctx, "o", "r", "1", Ptr("main")) + if err != nil { + t.Errorf("Actions.DeleteCachesByKey return error: %v", err) + } + + const methodName = "DeleteCachesByKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteCachesByKey(ctx, "\n", "\n", "\n", Ptr("\n")) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteCachesByKey(ctx, "o", "r", "1", Ptr("main")) + }) +} + +func TestActionsService_DeleteCachesByKey_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Actions.DeleteCachesByKey(ctx, "%", "r", "1", Ptr("main")) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByKey_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Actions.DeleteCachesByKey(ctx, "o", "%", "1", Ptr("main")) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByKey_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/artifacts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, err := client.Actions.DeleteCachesByKey(ctx, "o", "r", "1", Ptr("main")) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.DeleteCachesByKey return status %v, want %v", got, want) + } +} + +func TestActionsService_DeleteCachesByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/caches/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteCachesByID(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Actions.DeleteCachesByID return error: %v", err) + } + + const methodName = "DeleteCachesByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteCachesByID(ctx, "\n", "\n", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteCachesByID(ctx, "o", "r", 1) + }) +} + +func TestActionsService_DeleteCachesByID_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Actions.DeleteCachesByID(ctx, "%", "r", 1) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByID_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Actions.DeleteCachesByID(ctx, "o", "%", 1) + testURLParseError(t, err) +} + +func TestActionsService_DeleteCachesByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("repos/o/r/actions/caches/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, err := client.Actions.DeleteCachesByID(ctx, "o", "r", 1) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.DeleteCachesByID return status %v, want %v", got, want) + } +} + +func TestActionsService_GetCacheUsageForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, + `{ + "full_name":"test-cache", + "active_caches_size_in_bytes":1000, + "active_caches_count":1 + }`, + ) + }) + + ctx := t.Context() + cacheUse, _, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetCacheUsageForRepo returned error: %v", err) + } + + want := &ActionsCacheUsage{FullName: "test-cache", ActiveCachesSizeInBytes: 1000, ActiveCachesCount: 1} + if !cmp.Equal(cacheUse, want) { + t.Errorf("Actions.GetCacheUsageForRepo returned %+v, want %+v", cacheUse, want) + } + + const methodName = "GetCacheUsageForRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetCacheUsageForRepo(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetCacheUsageForRepo_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.GetCacheUsageForRepo(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForRepo_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "%") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForRepo_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + caches, resp, err := client.Actions.GetCacheUsageForRepo(ctx, "o", "r") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetCacheUsageForRepo return status %v, want %v", got, want) + } + if caches != nil { + t.Errorf("Actions.GetCacheUsageForRepo return %+v, want nil", caches) + } +} + +func TestActionsService_ListCacheUsageByRepoForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/cache/usage-by-repository", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "1"}) + fmt.Fprint(w, + `{ + "total_count":1, + "repository_cache_usages":[{"full_name":"test-cache","active_caches_size_in_bytes":1000,"active_caches_count":1}] + }`, + ) + }) + + opts := &ListOptions{PerPage: 1, Page: 2} + ctx := t.Context() + cacheList, _, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListCacheUsageByRepoForOrg returned error: %v", err) + } + + want := &ActionsCacheUsageList{TotalCount: 1, RepoCacheUsage: []*ActionsCacheUsage{{FullName: "test-cache", ActiveCachesSizeInBytes: 1000, ActiveCachesCount: 1}}} + if !cmp.Equal(cacheList, want) { + t.Errorf("Actions.ListCacheUsageByRepoForOrg returned %+v, want %+v", cacheList, want) + } + + const methodName = "ListCacheUsageByRepoForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListCacheUsageByRepoForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListCacheUsageByRepoForOrg_invalidOrganization(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestActionsService_ListCacheUsageByRepoForOrg_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/cache/usage-by-repository", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + caches, resp, err := client.Actions.ListCacheUsageByRepoForOrg(ctx, "o", nil) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.ListCacheUsageByRepoForOrg return status %v, want %v", got, want) + } + if caches != nil { + t.Errorf("Actions.ListCacheUsageByRepoForOrg return %+v, want nil", caches) + } +} + +func TestActionsService_GetCacheUsageForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, + `{ + "total_active_caches_size_in_bytes":1000, + "total_active_caches_count":1 + }`, + ) + }) + + ctx := t.Context() + cache, _, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "o") + if err != nil { + t.Errorf("Actions.GetTotalCacheUsageForOrg returned error: %v", err) + } + + want := &TotalCacheUsage{TotalActiveCachesUsageSizeInBytes: 1000, TotalActiveCachesCount: 1} + if !cmp.Equal(cache, want) { + t.Errorf("Actions.GetTotalCacheUsageForOrg returned %+v, want %+v", cache, want) + } + + const methodName = "GetTotalCacheUsageForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetTotalCacheUsageForOrg(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetCacheUsageForOrg_invalidOrganization(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "%") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForOrg_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + caches, resp, err := client.Actions.GetTotalCacheUsageForOrg(ctx, "o") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetTotalCacheUsageForOrg return status %v, want %v", got, want) + } + if caches != nil { + t.Errorf("Actions.GetTotalCacheUsageForOrg return %+v, want nil", caches) + } +} + +func TestActionsService_GetCacheUsageForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, + `{ + "total_active_caches_size_in_bytes":1000, + "total_active_caches_count":1 + }`, + ) + }) + + ctx := t.Context() + cache, _, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise returned error: %v", err) + } + + want := &TotalCacheUsage{TotalActiveCachesUsageSizeInBytes: 1000, TotalActiveCachesCount: 1} + if !cmp.Equal(cache, want) { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise returned %+v, want %+v", cache, want) + } + + const methodName = "GetTotalCacheUsageForEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetTotalCacheUsageForEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetCacheUsageForEnterprise_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "%") + testURLParseError(t, err) +} + +func TestActionsService_GetCacheUsageForEnterprise_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/cache/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + caches, resp, err := client.Actions.GetTotalCacheUsageForEnterprise(ctx, "o") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise return status %v, want %v", got, want) + } + if caches != nil { + t.Errorf("Actions.GetTotalCacheUsageForEnterprise return %+v, want nil", caches) + } +} diff --git a/github/actions_hosted_runners.go b/github/actions_hosted_runners.go new file mode 100644 index 00000000000..abc4decfb4d --- /dev/null +++ b/github/actions_hosted_runners.go @@ -0,0 +1,514 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" +) + +// HostedRunnerPublicIP represents the details of a public IP for GitHub-hosted runner. +type HostedRunnerPublicIP struct { + Enabled bool `json:"enabled"` // Whether public IP is enabled. + Prefix string `json:"prefix"` // The prefix for the public IP. Example: 20.80.208.150 + Length int `json:"length"` // The length of the IP prefix. Example: 28 +} + +// HostedRunnerMachineSpec represents the details of a particular machine specification for GitHub-hosted runner. +type HostedRunnerMachineSpec struct { + ID string `json:"id"` // The ID used for the `size` parameter when creating a new runner. Example: 8-core + CPUCores int `json:"cpu_cores"` // The number of cores. Example: 8 + MemoryGB int `json:"memory_gb"` // The available RAM for the machine spec. Example: 32 + StorageGB int `json:"storage_gb"` // The available SSD storage for the machine spec. Example: 300 +} + +// HostedRunner represents a single GitHub-hosted runner with additional details. +type HostedRunner struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + RunnerGroupID *int64 `json:"runner_group_id,omitempty"` + Platform *string `json:"platform,omitempty"` + ImageDetails *HostedRunnerImageDetail `json:"image_details,omitempty"` + MachineSizeDetails *HostedRunnerMachineSpec `json:"machine_size_details,omitempty"` + Status *string `json:"status,omitempty"` + MaximumRunners *int64 `json:"maximum_runners,omitempty"` + PublicIPEnabled *bool `json:"public_ip_enabled,omitempty"` + PublicIPs []*HostedRunnerPublicIP `json:"public_ips,omitempty"` + LastActiveOn *Timestamp `json:"last_active_on,omitempty"` +} + +// HostedRunnerImageDetail represents the image details of a GitHub-hosted runners. +type HostedRunnerImageDetail struct { + ID *string `json:"id"` // The ID of the image. Use this ID for the `image` parameter when creating a new larger runner. Example: ubuntu-20.04 + SizeGB *int64 `json:"size_gb"` // Image size in GB. Example: 86 + DisplayName *string `json:"display_name"` // Display name for this image. Example: 20.04 + Source *string `json:"source"` // The image provider. Example: github, partner, custom + Version *string `json:"version"` // The image version of the hosted runner pool. Example: latest +} + +// HostedRunners represents a collection of GitHub-hosted runners for an organization. +type HostedRunners struct { + TotalCount int `json:"total_count"` + Runners []*HostedRunner `json:"runners"` +} + +// ListHostedRunners lists all the GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#list-github-hosted-runners-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners +func (s *ActionsService) ListHostedRunners(ctx context.Context, org string, opts *ListOptions) (*HostedRunners, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *HostedRunners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// HostedRunnerImage represents the image of GitHub-hosted runners. +type HostedRunnerImage struct { + // The unique identifier of the runner image. + ID string `json:"id"` + // The source of the runner image. Can be one of: github, partner, custom. + Source string `json:"source"` + // The version of the runner image to deploy. This is relevant only for runners using custom images. + Version *string `json:"version,omitempty"` +} + +// CreateHostedRunnerRequest specifies body parameters to create Hosted Runner configuration. +type CreateHostedRunnerRequest struct { + Name string `json:"name"` + Image HostedRunnerImage `json:"image"` + Size string `json:"size"` + RunnerGroupID int64 `json:"runner_group_id"` + MaximumRunners *int64 `json:"maximum_runners,omitempty"` + EnableStaticIP *bool `json:"enable_static_ip,omitempty"` + ImageGen *bool `json:"image_gen,omitempty"` +} + +// UpdateHostedRunnerRequest specifies body parameters to update Hosted Runner configuration. +type UpdateHostedRunnerRequest struct { + Name *string `json:"name,omitempty"` + RunnerGroupID *int64 `json:"runner_group_id,omitempty"` + MaximumRunners *int64 `json:"maximum_runners,omitempty"` + EnableStaticIP *bool `json:"enable_static_ip,omitempty"` + Size *string `json:"size,omitempty"` + ImageID *string `json:"image_id,omitempty"` + ImageVersion *string `json:"image_version,omitempty"` +} + +// validateCreateHostedRunnerRequest validates the provided CreateHostedRunnerRequest to ensure +// that all required fields are properly set and that no invalid fields are present for hosted runner create request. +// +// If any of these conditions are violated, an appropriate error message is returned. +// Otherwise, nil is returned, indicating the request is valid. +func validateCreateHostedRunnerRequest(request *CreateHostedRunnerRequest) error { + if request.Name == "" { + return errors.New("name is required for creating a hosted runner") + } + if request.Image == (HostedRunnerImage{}) { + return errors.New("image is required for creating a hosted runner") + } + if request.Size == "" { + return errors.New("size is required for creating a hosted runner") + } + if request.RunnerGroupID == 0 { + return errors.New("runner group ID is required for creating a hosted runner") + } + return nil +} + +// CreateHostedRunner creates a GitHub-hosted runner for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#create-a-github-hosted-runner-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/hosted-runners +func (s *ActionsService) CreateHostedRunner(ctx context.Context, org string, body CreateHostedRunnerRequest) (*HostedRunner, *Response, error) { + if err := validateCreateHostedRunnerRequest(&body); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/actions/hosted-runners", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// HostedRunnerCustomImage represents a custom image definition for GitHub-hosted runners. +type HostedRunnerCustomImage struct { + ID int64 `json:"id"` + Platform string `json:"platform"` + Name string `json:"name"` + Source string `json:"source"` + VersionsCount int `json:"versions_count"` + TotalVersionsSize int `json:"total_versions_size"` + LatestVersion string `json:"latest_version"` + State string `json:"state"` +} + +// HostedRunnerCustomImages represents a collection of custom images for GitHub-hosted runners. +type HostedRunnerCustomImages struct { + TotalCount int `json:"total_count"` + Images []*HostedRunnerCustomImage `json:"images"` +} + +// HostedRunnerCustomImageVersion represents a version of a custom image for GitHub-hosted runners. +type HostedRunnerCustomImageVersion struct { + Version string `json:"version"` + SizeGB int `json:"size_gb"` + State string `json:"state"` + StateDetails string `json:"state_details"` + CreatedOn Timestamp `json:"created_on"` +} + +// HostedRunnerCustomImageVersions represents a collection of versions of a custom image. +type HostedRunnerCustomImageVersions struct { + TotalCount int `json:"total_count"` + ImageVersions []*HostedRunnerCustomImageVersion `json:"image_versions"` +} + +// HostedRunnerImageSpecs represents the details of a GitHub-hosted runner image. +type HostedRunnerImageSpecs struct { + ID string `json:"id"` + Platform string `json:"platform"` + SizeGB int `json:"size_gb"` + DisplayName string `json:"display_name"` + Source string `json:"source"` +} + +// HostedRunnerImages represents the response containing the total count and details of runner images. +type HostedRunnerImages struct { + TotalCount int `json:"total_count"` + Images []*HostedRunnerImageSpecs `json:"images"` +} + +// GetHostedRunnerGitHubOwnedImages gets the list of GitHub-owned images available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-github-owned-images-for-github-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/github-owned +func (s *ActionsService) GetHostedRunnerGitHubOwnedImages(ctx context.Context, org string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/github-owned", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunnerImages *HostedRunnerImages + resp, err := s.client.Do(req, &hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// GetHostedRunnerPartnerImages gets the list of partner images available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-partner-images-for-github-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/partner +func (s *ActionsService) GetHostedRunnerPartnerImages(ctx context.Context, org string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/partner", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunnerImages *HostedRunnerImages + resp, err := s.client.Do(req, &hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// HostedRunnerPublicIPLimits represents the static public IP limits for GitHub-hosted runners. +type HostedRunnerPublicIPLimits struct { + PublicIPs *PublicIPUsage `json:"public_ips"` +} + +// PublicIPUsage provides details of static public IP limits for GitHub-hosted runners. +type PublicIPUsage struct { + Maximum int64 `json:"maximum"` // The maximum number of static public IP addresses that can be used for Hosted Runners. Example: 50 + CurrentUsage int64 `json:"current_usage"` // The current number of static public IP addresses in use by Hosted Runners. Example: 17 +} + +// GetHostedRunnerLimits gets the GitHub-hosted runners Static public IP Limits for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-limits-on-github-hosted-runners-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/limits +func (s *ActionsService) GetHostedRunnerLimits(ctx context.Context, org string) (*HostedRunnerPublicIPLimits, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/limits", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var publicIPLimits *HostedRunnerPublicIPLimits + resp, err := s.client.Do(req, &publicIPLimits) + if err != nil { + return nil, resp, err + } + + return publicIPLimits, resp, nil +} + +// HostedRunnerMachineSpecs represents the response containing the total count and details of machine specs for GitHub-hosted runners. +type HostedRunnerMachineSpecs struct { + TotalCount int `json:"total_count"` + MachineSpecs []*HostedRunnerMachineSpec `json:"machine_specs"` +} + +// GetHostedRunnerMachineSpecs gets the list of machine specs available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-github-hosted-runners-machine-specs-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/machine-sizes +func (s *ActionsService) GetHostedRunnerMachineSpecs(ctx context.Context, org string) (*HostedRunnerMachineSpecs, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/machine-sizes", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var machineSpecs *HostedRunnerMachineSpecs + resp, err := s.client.Do(req, &machineSpecs) + if err != nil { + return nil, resp, err + } + + return machineSpecs, resp, nil +} + +// HostedRunnerPlatforms represents the response containing the total count and platforms for GitHub-hosted runners. +type HostedRunnerPlatforms struct { + TotalCount int `json:"total_count"` + Platforms []string `json:"platforms"` +} + +// GetHostedRunnerPlatforms gets list of platforms available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-platforms-for-github-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/platforms +func (s *ActionsService) GetHostedRunnerPlatforms(ctx context.Context, org string) (*HostedRunnerPlatforms, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/platforms", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var platforms *HostedRunnerPlatforms + resp, err := s.client.Do(req, &platforms) + if err != nil { + return nil, resp, err + } + + return platforms, resp, nil +} + +// GetHostedRunner gets a GitHub-hosted runner in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-a-github-hosted-runner-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/{hosted_runner_id} +func (s *ActionsService) GetHostedRunner(ctx context.Context, org string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/%v", org, runnerID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// UpdateHostedRunner updates a GitHub-hosted runner for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#update-a-github-hosted-runner-for-an-organization +// +//meta:operation PATCH /orgs/{org}/actions/hosted-runners/{hosted_runner_id} +func (s *ActionsService) UpdateHostedRunner(ctx context.Context, org string, runnerID int64, body UpdateHostedRunnerRequest) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/%v", org, runnerID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// DeleteHostedRunner deletes GitHub-hosted runner from an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#delete-a-github-hosted-runner-for-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/hosted-runners/{hosted_runner_id} +func (s *ActionsService) DeleteHostedRunner(ctx context.Context, org string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/%v", org, runnerID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// ListHostedRunnerCustomImages lists custom images for GitHub-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#list-custom-images-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/custom +func (s *ActionsService) ListHostedRunnerCustomImages(ctx context.Context, org string) (*HostedRunnerCustomImages, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/custom", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var images *HostedRunnerCustomImages + resp, err := s.client.Do(req, &images) + if err != nil { + return nil, resp, err + } + + return images, resp, nil +} + +// GetHostedRunnerCustomImage gets a custom image definition for GitHub-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-a-custom-image-definition-for-github-actions-hosted-runners +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id} +func (s *ActionsService) GetHostedRunnerCustomImage(ctx context.Context, org string, imageDefinitionID int64) (*HostedRunnerCustomImage, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/custom/%v", org, imageDefinitionID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var image *HostedRunnerCustomImage + resp, err := s.client.Do(req, &image) + if err != nil { + return nil, resp, err + } + + return image, resp, nil +} + +// DeleteHostedRunnerCustomImage deletes a custom image from the organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#delete-a-custom-image-from-the-organization +// +//meta:operation DELETE /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id} +func (s *ActionsService) DeleteHostedRunnerCustomImage(ctx context.Context, org string, imageDefinitionID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/custom/%v", org, imageDefinitionID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListHostedRunnerCustomImageVersions lists image versions of a custom image for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#list-image-versions-of-a-custom-image-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id}/versions +func (s *ActionsService) ListHostedRunnerCustomImageVersions(ctx context.Context, org string, imageDefinitionID int64) (*HostedRunnerCustomImageVersions, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/custom/%v/versions", org, imageDefinitionID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var versions *HostedRunnerCustomImageVersions + resp, err := s.client.Do(req, &versions) + if err != nil { + return nil, resp, err + } + + return versions, resp, nil +} + +// GetHostedRunnerCustomImageVersion gets an image version of a custom image for GitHub-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#get-an-image-version-of-a-custom-image-for-github-actions-hosted-runners +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} +func (s *ActionsService) GetHostedRunnerCustomImageVersion(ctx context.Context, org string, imageDefinitionID int64, version string) (*HostedRunnerCustomImageVersion, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/custom/%v/versions/%v", org, imageDefinitionID, version) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var imageVersion *HostedRunnerCustomImageVersion + resp, err := s.client.Do(req, &imageVersion) + if err != nil { + return nil, resp, err + } + + return imageVersion, resp, nil +} + +// DeleteHostedRunnerCustomImageVersion deletes an image version of a custom image from the organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners?apiVersion=2022-11-28#delete-an-image-version-of-custom-image-from-the-organization +// +//meta:operation DELETE /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} +func (s *ActionsService) DeleteHostedRunnerCustomImageVersion(ctx context.Context, org string, imageDefinitionID int64, version string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/custom/%v/versions/%v", org, imageDefinitionID, version) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_hosted_runners_test.go b/github/actions_hosted_runners_test.go new file mode 100644 index 00000000000..b4cc96a61ba --- /dev/null +++ b/github/actions_hosted_runners_test.go @@ -0,0 +1,1161 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListHostedRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 2, + "runners": [ + { + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }, + { + "id": 7, + "name": "My hosted Windows runner", + "runner_group_id": 2, + "platform": "win-x64", + "image_details": { + "id": "windows-latest", + "size_gb": 256 + }, + "machine_size_details": { + "id": "8-core", + "cpu_cores": 8, + "memory_gb": 32, + "storage_gb": 300 + }, + "status": "Ready", + "maximum_runners": 20, + "public_ip_enabled": false, + "public_ips": [], + "last_active_on": `+referenceTimeStr+` + } + ] + }`) + }) + opts := &ListOptions{Page: 1, PerPage: 1} + ctx := t.Context() + hostedRunners, _, err := client.Actions.ListHostedRunners(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListHostedRunners returned error: %v", err) + } + + want := &HostedRunners{ + TotalCount: 2, + Runners: []*HostedRunner{ + { + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + }, + { + ID: Ptr(int64(7)), + Name: Ptr("My hosted Windows runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("win-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("windows-latest"), + SizeGB: Ptr(int64(256)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "8-core", + CPUCores: 8, + MemoryGB: 32, + StorageGB: 300, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(20)), + PublicIPEnabled: Ptr(false), + PublicIPs: []*HostedRunnerPublicIP{}, + LastActiveOn: &referenceTimestamp, + }, + }, + } + if !cmp.Equal(hostedRunners, want) { + t.Errorf("Actions.ListHostedRunners returned %+v, want %+v", hostedRunners, want) + } + + const methodName = "ListHostedRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListHostedRunners(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListHostedRunners(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + + validReq := CreateHostedRunnerRequest{ + Name: "My Hosted runner", + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + RunnerGroupID: 1, + Size: "4-core", + MaximumRunners: Ptr(int64(50)), + EnableStaticIP: Ptr(false), + ImageGen: Ptr(true), + } + hostedRunner, _, err := client.Actions.CreateHostedRunner(ctx, "o", validReq) + if err != nil { + t.Errorf("Actions.CreateHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Actions.CreateHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + // Validation tests + testCases := []struct { + name string + request CreateHostedRunnerRequest + expectedError string + }{ + { + name: "Missing Size", + request: CreateHostedRunnerRequest{ + Name: "My Hosted runner", + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + RunnerGroupID: 1, + }, + expectedError: "validation failed: size is required for creating a hosted runner", + }, + { + name: "Missing Image", + request: CreateHostedRunnerRequest{ + Name: "My Hosted runner", + RunnerGroupID: 1, + Size: "4-core", + }, + expectedError: "validation failed: image is required for creating a hosted runner", + }, + { + name: "Missing Name", + request: CreateHostedRunnerRequest{ + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + RunnerGroupID: 1, + Size: "4-core", + }, + expectedError: "validation failed: name is required for creating a hosted runner", + }, + { + name: "Missing RunnerGroupID", + request: CreateHostedRunnerRequest{ + Name: "My Hosted runner", + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + Size: "4-core", + }, + expectedError: "validation failed: runner group ID is required for creating a hosted runner", + }, + } + + for _, tt := range testCases { + _, _, err := client.Actions.CreateHostedRunner(ctx, "o", tt.request) + if err == nil || err.Error() != tt.expectedError { + t.Errorf("expected error: %v, got: %v", tt.expectedError, err) + } + } + + const methodName = "CreateHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateHostedRunner(ctx, "\n", validReq) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateHostedRunner(ctx, "o", validReq) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerGitHubOwnedImages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/github-owned", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "images": [ + { + "id": "ubuntu-20.04", + "platform": "linux-x64", + "size_gb": 86, + "display_name": "20.04", + "source": "github" + } + ] + }`) + }) + + ctx := t.Context() + hostedRunnerImages, _, err := client.Actions.GetHostedRunnerGitHubOwnedImages(ctx, "o") + if err != nil { + t.Errorf("Actions.GetHostedRunnerGitHubOwnedImages returned error: %v", err) + } + + want := &HostedRunnerImages{ + TotalCount: 1, + Images: []*HostedRunnerImageSpecs{ + { + ID: "ubuntu-20.04", + Platform: "linux-x64", + SizeGB: 86, + DisplayName: "20.04", + Source: "github", + }, + }, + } + + if !cmp.Equal(hostedRunnerImages, want) { + t.Errorf("Actions.GetHostedRunnerGitHubOwnedImages returned %+v, want %+v", hostedRunnerImages, want) + } + + const methodName = "GetHostedRunnerGitHubOwnedImages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerGitHubOwnedImages(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerGitHubOwnedImages(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerPartnerImages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/partner", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "images": [ + { + "id": "ubuntu-20.04", + "platform": "linux-x64", + "size_gb": 86, + "display_name": "20.04", + "source": "partner" + } + ] + }`) + }) + + ctx := t.Context() + hostedRunnerImages, _, err := client.Actions.GetHostedRunnerPartnerImages(ctx, "o") + if err != nil { + t.Errorf("Actions.GetHostedRunnerPartnerImages returned error: %v", err) + } + + want := &HostedRunnerImages{ + TotalCount: 1, + Images: []*HostedRunnerImageSpecs{ + { + ID: "ubuntu-20.04", + Platform: "linux-x64", + SizeGB: 86, + DisplayName: "20.04", + Source: "partner", + }, + }, + } + + if !cmp.Equal(hostedRunnerImages, want) { + t.Errorf("Actions.GetHostedRunnerPartnerImages returned %+v, want %+v", hostedRunnerImages, want) + } + + const methodName = "GetHostedRunnerPartnerImages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerPartnerImages(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerPartnerImages(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerLimits(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/limits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "public_ips": { + "current_usage": 17, + "maximum": 50 + } + }`) + }) + + ctx := t.Context() + publicIPLimits, _, err := client.Actions.GetHostedRunnerLimits(ctx, "o") + if err != nil { + t.Errorf("Actions.GetHostedRunnerLimits returned error: %v", err) + } + + want := &HostedRunnerPublicIPLimits{ + PublicIPs: &PublicIPUsage{ + CurrentUsage: 17, + Maximum: 50, + }, + } + + if !cmp.Equal(publicIPLimits, want) { + t.Errorf("Actions.GetHostedRunnerLimits returned %+v, want %+v", publicIPLimits, want) + } + + const methodName = "GetHostedRunnerLimits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerLimits(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerLimits(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerMachineSpecs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/machine-sizes", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "machine_specs": [ + { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + } + ] + }`) + }) + + ctx := t.Context() + machineSpecs, _, err := client.Actions.GetHostedRunnerMachineSpecs(ctx, "o") + if err != nil { + t.Errorf("Actions.GetHostedRunnerMachineSpecs returned error: %v", err) + } + want := &HostedRunnerMachineSpecs{ + TotalCount: 1, + MachineSpecs: []*HostedRunnerMachineSpec{ + { + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + }, + } + + if !cmp.Equal(machineSpecs, want) { + t.Errorf("Actions.GetHostedRunnerMachineSpecs returned %+v, want %+v", machineSpecs, want) + } + + const methodName = "GetHostedRunnerMachineSpecs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerMachineSpecs(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerMachineSpecs(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerPlatforms(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/platforms", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "platforms": [ + "linux-x64", + "win-x64" + ] + }`) + }) + + ctx := t.Context() + platforms, _, err := client.Actions.GetHostedRunnerPlatforms(ctx, "o") + if err != nil { + t.Errorf("Actions.GetHostedRunnerPlatforms returned error: %v", err) + } + want := &HostedRunnerPlatforms{ + TotalCount: 1, + Platforms: []string{ + "linux-x64", + "win-x64", + }, + } + + if !cmp.Equal(platforms, want) { + t.Errorf("Actions.GetHostedRunnerPlatforms returned %+v, want %+v", platforms, want) + } + + const methodName = "GetHostedRunnerPlatforms" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerPlatforms(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerPlatforms(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + hostedRunner, _, err := client.Actions.GetHostedRunner(ctx, "o", 23) + if err != nil { + t.Errorf("Actions.GetHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Actions.GetHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + const methodName = "GetHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunner(ctx, "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunner(ctx, "o", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + validReq := UpdateHostedRunnerRequest{ + Name: Ptr("My larger runner"), + RunnerGroupID: Ptr(int64(1)), + MaximumRunners: Ptr(int64(50)), + EnableStaticIP: Ptr(false), + ImageVersion: Ptr("1.0.0"), + } + hostedRunner, _, err := client.Actions.UpdateHostedRunner(ctx, "o", 23, validReq) + if err != nil { + t.Errorf("Actions.UpdateHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Actions.UpdateHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + const methodName = "UpdateHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateHostedRunner(ctx, "\n", 23, validReq) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateHostedRunner(ctx, "o", 23, validReq) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_DeleteHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + hostedRunner, _, err := client.Actions.DeleteHostedRunner(ctx, "o", 23) + if err != nil { + t.Errorf("Actions.DeleteHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Actions.DeleteHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + const methodName = "DeleteHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.DeleteHostedRunner(ctx, "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.DeleteHostedRunner(ctx, "o", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListHostedRunnerCustomImages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/custom", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 2, + "images": [ + { + "id": 1, + "platform": "linux-x64", + "name": "CustomImage1", + "source": "custom", + "versions_count": 4, + "total_versions_size": 200, + "latest_version": "1.3.0", + "state": "Ready" + }, + { + "id": 2, + "platform": "linux-x64", + "name": "CustomImage2", + "source": "custom", + "versions_count": 2, + "total_versions_size": 150, + "latest_version": "1.0.0", + "state": "Ready" + } + ] + }`) + }) + + ctx := t.Context() + images, _, err := client.Actions.ListHostedRunnerCustomImages(ctx, "o") + if err != nil { + t.Errorf("Actions.ListHostedRunnerCustomImages returned error: %v", err) + } + + want := &HostedRunnerCustomImages{ + TotalCount: 2, + Images: []*HostedRunnerCustomImage{ + { + ID: 1, + Platform: "linux-x64", + Name: "CustomImage1", + Source: "custom", + VersionsCount: 4, + TotalVersionsSize: 200, + LatestVersion: "1.3.0", + State: "Ready", + }, + { + ID: 2, + Platform: "linux-x64", + Name: "CustomImage2", + Source: "custom", + VersionsCount: 2, + TotalVersionsSize: 150, + LatestVersion: "1.0.0", + State: "Ready", + }, + }, + } + + if !cmp.Equal(images, want) { + t.Errorf("Actions.ListHostedRunnerCustomImages returned %+v, want %+v", images, want) + } + + const methodName = "ListHostedRunnerCustomImages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListHostedRunnerCustomImages(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListHostedRunnerCustomImages(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerCustomImage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/custom/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 1, + "platform": "linux-x64", + "name": "CustomImage", + "source": "custom", + "versions_count": 4, + "total_versions_size": 200, + "latest_version": "1.3.0", + "state": "Ready" + }`) + }) + + ctx := t.Context() + image, _, err := client.Actions.GetHostedRunnerCustomImage(ctx, "o", 1) + if err != nil { + t.Errorf("Actions.GetHostedRunnerCustomImage returned error: %v", err) + } + + want := &HostedRunnerCustomImage{ + ID: 1, + Platform: "linux-x64", + Name: "CustomImage", + Source: "custom", + VersionsCount: 4, + TotalVersionsSize: 200, + LatestVersion: "1.3.0", + State: "Ready", + } + + if !cmp.Equal(image, want) { + t.Errorf("Actions.GetHostedRunnerCustomImage returned %+v, want %+v", image, want) + } + + const methodName = "GetHostedRunnerCustomImage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerCustomImage(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerCustomImage(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_DeleteHostedRunnerCustomImage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/custom/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteHostedRunnerCustomImage(ctx, "o", 1) + if err != nil { + t.Errorf("Actions.DeleteHostedRunnerCustomImage returned error: %v", err) + } + + const methodName = "DeleteHostedRunnerCustomImage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteHostedRunnerCustomImage(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteHostedRunnerCustomImage(ctx, "o", 1) + }) +} + +func TestActionsService_ListHostedRunnerCustomImageVersions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/custom/1/versions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 2, + "image_versions": [ + { + "version": "1.1.0", + "size_gb": 75, + "state": "Ready", + "state_details": "None", + "created_on": `+referenceTimeStr+` + }, + { + "version": "1.0.0", + "size_gb": 75, + "state": "Ready", + "state_details": "None", + "created_on": `+referenceTimeStr+` + } + ] + }`) + }) + + ctx := t.Context() + versions, _, err := client.Actions.ListHostedRunnerCustomImageVersions(ctx, "o", 1) + if err != nil { + t.Errorf("Actions.ListHostedRunnerCustomImageVersions returned error: %v", err) + } + + want := &HostedRunnerCustomImageVersions{ + TotalCount: 2, + ImageVersions: []*HostedRunnerCustomImageVersion{ + { + Version: "1.1.0", + SizeGB: 75, + State: "Ready", + StateDetails: "None", + CreatedOn: referenceTimestamp, + }, + { + Version: "1.0.0", + SizeGB: 75, + State: "Ready", + StateDetails: "None", + CreatedOn: referenceTimestamp, + }, + }, + } + + if !cmp.Equal(versions, want) { + t.Errorf("Actions.ListHostedRunnerCustomImageVersions returned %+v, want %+v", versions, want) + } + + const methodName = "ListHostedRunnerCustomImageVersions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListHostedRunnerCustomImageVersions(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListHostedRunnerCustomImageVersions(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetHostedRunnerCustomImageVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/custom/1/versions/1.0.0", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "version": "1.0.0", + "size_gb": 75, + "state": "Ready", + "state_details": "None", + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + version, _, err := client.Actions.GetHostedRunnerCustomImageVersion(ctx, "o", 1, "1.0.0") + if err != nil { + t.Errorf("Actions.GetHostedRunnerCustomImageVersion returned error: %v", err) + } + + want := &HostedRunnerCustomImageVersion{ + Version: "1.0.0", + SizeGB: 75, + State: "Ready", + StateDetails: "None", + CreatedOn: referenceTimestamp, + } + + if !cmp.Equal(version, want) { + t.Errorf("Actions.GetHostedRunnerCustomImageVersion returned %+v, want %+v", version, want) + } + + const methodName = "GetHostedRunnerCustomImageVersion" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetHostedRunnerCustomImageVersion(ctx, "\n", 1, "1.0.0") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetHostedRunnerCustomImageVersion(ctx, "o", 1, "1.0.0") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_DeleteHostedRunnerCustomImageVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/hosted-runners/images/custom/1/versions/1.0.0", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteHostedRunnerCustomImageVersion(ctx, "o", 1, "1.0.0") + if err != nil { + t.Errorf("Actions.DeleteHostedRunnerCustomImageVersion returned error: %v", err) + } + + const methodName = "DeleteHostedRunnerCustomImageVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteHostedRunnerCustomImageVersion(ctx, "\n", 1, "1.0.0") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteHostedRunnerCustomImageVersion(ctx, "o", 1, "1.0.0") + }) +} diff --git a/github/actions_oidc.go b/github/actions_oidc.go new file mode 100644 index 00000000000..d0cf78ffaa7 --- /dev/null +++ b/github/actions_oidc.go @@ -0,0 +1,201 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// OIDCSubjectClaimCustomTemplate represents an OIDC subject claim customization template. +type OIDCSubjectClaimCustomTemplate struct { + UseDefault *bool `json:"use_default,omitempty"` + IncludeClaimKeys []string `json:"include_claim_keys,omitempty"` + UseImmutableSubject *bool `json:"use_immutable_subject,omitempty"` + SubClaimPrefix *string `json:"sub_claim_prefix,omitempty"` +} + +// GetOrgOIDCSubjectClaimCustomTemplate gets the subject claim customization template for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#get-the-customization-template-for-an-oidc-subject-claim-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/oidc/customization/sub +func (s *ActionsService) GetOrgOIDCSubjectClaimCustomTemplate(ctx context.Context, org string) (*OIDCSubjectClaimCustomTemplate, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/sub", org) + return s.getOIDCSubjectClaimCustomTemplate(ctx, u) +} + +// GetRepoOIDCSubjectClaimCustomTemplate gets the subject claim customization template for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#get-the-customization-template-for-an-oidc-subject-claim-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/oidc/customization/sub +func (s *ActionsService) GetRepoOIDCSubjectClaimCustomTemplate(ctx context.Context, owner, repo string) (*OIDCSubjectClaimCustomTemplate, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/oidc/customization/sub", owner, repo) + return s.getOIDCSubjectClaimCustomTemplate(ctx, u) +} + +func (s *ActionsService) getOIDCSubjectClaimCustomTemplate(ctx context.Context, url string) (*OIDCSubjectClaimCustomTemplate, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var tmpl *OIDCSubjectClaimCustomTemplate + resp, err := s.client.Do(req, &tmpl) + if err != nil { + return nil, resp, err + } + + return tmpl, resp, nil +} + +// SetOrgOIDCSubjectClaimCustomTemplate sets the subject claim customization for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#set-the-customization-template-for-an-oidc-subject-claim-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/oidc/customization/sub +func (s *ActionsService) SetOrgOIDCSubjectClaimCustomTemplate(ctx context.Context, org string, body OIDCSubjectClaimCustomTemplate) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/sub", org) + return s.setOIDCSubjectClaimCustomTemplate(ctx, u, body) +} + +// SetRepoOIDCSubjectClaimCustomTemplate sets the subject claim customization for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/oidc/customization/sub +func (s *ActionsService) SetRepoOIDCSubjectClaimCustomTemplate(ctx context.Context, owner, repo string, body OIDCSubjectClaimCustomTemplate) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/oidc/customization/sub", owner, repo) + return s.setOIDCSubjectClaimCustomTemplate(ctx, u, body) +} + +func (s *ActionsService) setOIDCSubjectClaimCustomTemplate(ctx context.Context, url string, body OIDCSubjectClaimCustomTemplate) (*Response, error) { + req, err := s.client.NewRequest(ctx, "PUT", url, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// InclusionSource represents whether the custom oidc property claim was defined at the organization or enterprise level. +type InclusionSource string + +// InclusionSource constants represent whether the inclusion was defined at the organization or enterprise level. +const ( + InclusionSourceEnterprise InclusionSource = "enterprise" + InclusionSourceOrganization InclusionSource = "organization" +) + +// OIDCCustomPropertyClaim represents an OIDC custom property claim for GitHub Actions. +type OIDCCustomPropertyClaim struct { + CustomPropertyName string `json:"custom_property_name"` +} + +// OIDCCustomPropertyClaimResponse represents the OIDC custom property claim along with the inclusion source (enterprise or organization) for GitHub Actions. +type OIDCCustomPropertyClaimResponse struct { + OIDCCustomPropertyClaim + InclusionSource InclusionSource `json:"inclusion_source"` +} + +// ListEnterpriseOIDCCustomPropertyClaims lists the custom property claims in oidc for enterprise actions. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#list-oidc-custom-property-inclusions-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/oidc/customization/properties/repo +func (s *ActionsService) ListEnterpriseOIDCCustomPropertyClaims(ctx context.Context, enterprise string) ([]*OIDCCustomPropertyClaimResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/oidc/customization/properties/repo", enterprise) + return s.listOIDCCustomPropertyClaims(ctx, u) +} + +// ListOrgOIDCCustomPropertyClaims lists the custom property claims in oidc for organization actions. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#list-oidc-custom-property-inclusions-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/oidc/customization/properties/repo +func (s *ActionsService) ListOrgOIDCCustomPropertyClaims(ctx context.Context, org string) ([]*OIDCCustomPropertyClaimResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/properties/repo", org) + return s.listOIDCCustomPropertyClaims(ctx, u) +} + +func (s *ActionsService) listOIDCCustomPropertyClaims(ctx context.Context, url string) ([]*OIDCCustomPropertyClaimResponse, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var props []*OIDCCustomPropertyClaimResponse + resp, err := s.client.Do(req, &props) + if err != nil { + return nil, resp, err + } + + return props, resp, nil +} + +// SetEnterpriseOIDCCustomPropertyClaim sets a new custom property claim in oidc for enterprise actions. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#create-an-oidc-custom-property-inclusion-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/actions/oidc/customization/properties/repo +func (s *ActionsService) SetEnterpriseOIDCCustomPropertyClaim(ctx context.Context, enterprise string, body OIDCCustomPropertyClaim) (*OIDCCustomPropertyClaim, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/oidc/customization/properties/repo", enterprise) + return s.setOIDCCustomPropertyClaim(ctx, u, body) +} + +// SetOrgOIDCCustomPropertyClaim sets a new custom property claim in oidc for organization actions. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#create-an-oidc-custom-property-inclusion-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/oidc/customization/properties/repo +func (s *ActionsService) SetOrgOIDCCustomPropertyClaim(ctx context.Context, org string, body OIDCCustomPropertyClaim) (*OIDCCustomPropertyClaim, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/properties/repo", org) + return s.setOIDCCustomPropertyClaim(ctx, u, body) +} + +func (s *ActionsService) setOIDCCustomPropertyClaim(ctx context.Context, url string, body OIDCCustomPropertyClaim) (*OIDCCustomPropertyClaim, *Response, error) { + req, err := s.client.NewRequest(ctx, "POST", url, body) + if err != nil { + return nil, nil, err + } + + var customProperty *OIDCCustomPropertyClaim + resp, err := s.client.Do(req, &customProperty) + if err != nil { + return nil, resp, err + } + return customProperty, resp, err +} + +// DeleteEnterpriseOIDCCustomPropertyClaim deletes a custom property claim in oidc for enterprise actions. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#delete-an-oidc-custom-property-inclusion-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/oidc/customization/properties/repo/{custom_property_name} +func (s *ActionsService) DeleteEnterpriseOIDCCustomPropertyClaim(ctx context.Context, enterprise, customProperty string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/oidc/customization/properties/repo/%v", enterprise, customProperty) + return s.deleteOIDCCustomPropertyClaim(ctx, u) +} + +// DeleteOrgOIDCCustomPropertyClaim deletes a custom property claim in oidc for organization actions. +// +// GitHub API docs: https://docs.github.com/rest/actions/oidc?apiVersion=2022-11-28#delete-an-oidc-custom-property-inclusion-for-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/oidc/customization/properties/repo/{custom_property_name} +func (s *ActionsService) DeleteOrgOIDCCustomPropertyClaim(ctx context.Context, enterprise, customProperty string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/oidc/customization/properties/repo/%v", enterprise, customProperty) + return s.deleteOIDCCustomPropertyClaim(ctx, u) +} + +func (s *ActionsService) deleteOIDCCustomPropertyClaim(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "DELETE", url, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_oidc_test.go b/github/actions_oidc_test.go new file mode 100644 index 00000000000..710012bf469 --- /dev/null +++ b/github/actions_oidc_test.go @@ -0,0 +1,398 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_GetOrgOIDCSubjectClaimCustomTemplate(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"include_claim_keys":["repo","context"],"use_immutable_subject":true}`) + }) + + ctx := t.Context() + template, _, err := client.Actions.GetOrgOIDCSubjectClaimCustomTemplate(ctx, "o") + if err != nil { + t.Errorf("Actions.GetOrgOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + want := &OIDCSubjectClaimCustomTemplate{IncludeClaimKeys: []string{"repo", "context"}, UseImmutableSubject: Ptr(true)} + if !cmp.Equal(template, want) { + t.Errorf("Actions.GetOrgOIDCSubjectClaimCustomTemplate returned %+v, want %+v", template, want) + } + + const methodName = "GetOrgOIDCSubjectClaimCustomTemplate" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrgOIDCSubjectClaimCustomTemplate(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrgOIDCSubjectClaimCustomTemplate(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRepoOIDCSubjectClaimCustomTemplate(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"use_default":false,"include_claim_keys":["repo","context"],"use_immutable_subject":true,"sub_claim_prefix":"repo:o/r"}`) + }) + + ctx := t.Context() + template, _, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetRepoOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + want := &OIDCSubjectClaimCustomTemplate{UseDefault: Ptr(false), IncludeClaimKeys: []string{"repo", "context"}, UseImmutableSubject: Ptr(true), SubClaimPrefix: Ptr("repo:o/r")} + if !cmp.Equal(template, want) { + t.Errorf("Actions.GetRepoOIDCSubjectClaimCustomTemplate returned %+v, want %+v", template, want) + } + + const methodName = "GetRepoOIDCSubjectClaimCustomTemplate" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetOrgOIDCSubjectClaimCustomTemplate(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := OIDCSubjectClaimCustomTemplate{ + IncludeClaimKeys: []string{"repo", "context"}, + UseImmutableSubject: Ptr(true), + } + + mux.HandleFunc("/orgs/o/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.SetOrgOIDCSubjectClaimCustomTemplate(ctx, "o", input) + if err != nil { + t.Errorf("Actions.SetOrgOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + const methodName = "SetOrgOIDCSubjectClaimCustomTemplate" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetOrgOIDCSubjectClaimCustomTemplate(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetOrgOIDCSubjectClaimCustomTemplate(ctx, "o", input) + }) +} + +func TestActionsService_SetRepoOIDCSubjectClaimCustomTemplate(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := OIDCSubjectClaimCustomTemplate{ + UseDefault: Ptr(false), + IncludeClaimKeys: []string{"repo", "context"}, + } + + mux.HandleFunc("/repos/o/r/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.SetRepoOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + const methodName = "SetRepoOIDCSubjectClaimCustomTemplate" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + }) +} + +func TestActionsService_SetRepoOIDCSubjectClaimCustomTemplateToDefault(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := OIDCSubjectClaimCustomTemplate{ + UseDefault: Ptr(true), + } + + mux.HandleFunc("/repos/o/r/actions/oidc/customization/sub", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.SetRepoOIDCSubjectClaimCustomTemplate returned error: %v", err) + } + + const methodName = "SetRepoOIDCSubjectClaimCustomTemplate" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRepoOIDCSubjectClaimCustomTemplate(ctx, "o", "r", input) + }) +} + +func TestActionsService_ListEnterpriseOIDCCustomPropertyClaims(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/oidc/customization/properties/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"custom_property_name": "environment","inclusion_source": "enterprise"},{"custom_property_name": "lane","inclusion_source": "enterprise"}]`) + }) + + ctx := t.Context() + claims, _, err := client.Actions.ListEnterpriseOIDCCustomPropertyClaims(ctx, "e") + if err != nil { + t.Errorf("Actions.ListEnterpriseOIDCCustomPropertyClaims returned error: %v", err) + } + + want := []*OIDCCustomPropertyClaimResponse{ + {OIDCCustomPropertyClaim: OIDCCustomPropertyClaim{"environment"}, InclusionSource: InclusionSourceEnterprise}, + {OIDCCustomPropertyClaim: OIDCCustomPropertyClaim{"lane"}, InclusionSource: InclusionSourceEnterprise}, + } + + if !cmp.Equal(claims, want) { + t.Errorf("Actions.ListEnterpriseOIDCCustomPropertyClaims returned %+v, want %+v", claims, want) + } + + const methodName = "ListEnterpriseOIDCCustomPropertyClaims" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListEnterpriseOIDCCustomPropertyClaims(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListEnterpriseOIDCCustomPropertyClaims(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListOrgOIDCCustomPropertyClaims(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/oidc/customization/properties/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"custom_property_name": "environment","inclusion_source": "organization"},{"custom_property_name": "lane","inclusion_source": "organization"}]`) + }) + + ctx := t.Context() + claims, _, err := client.Actions.ListOrgOIDCCustomPropertyClaims(ctx, "o") + if err != nil { + t.Errorf("Actions.ListOrgOIDCCustomPropertyClaims returned error: %v", err) + } + + want := []*OIDCCustomPropertyClaimResponse{ + {OIDCCustomPropertyClaim: OIDCCustomPropertyClaim{"environment"}, InclusionSource: InclusionSourceOrganization}, + {OIDCCustomPropertyClaim: OIDCCustomPropertyClaim{"lane"}, InclusionSource: InclusionSourceOrganization}, + } + + if !cmp.Equal(claims, want) { + t.Errorf("Actions.ListOrgOIDCCustomPropertyClaims returned %+v, want %+v", claims, want) + } + + const methodName = "ListOrgOIDCCustomPropertyClaims" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrgOIDCCustomPropertyClaims(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrgOIDCCustomPropertyClaims(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetEnterpriseOIDCCustomPropertyClaim(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := OIDCCustomPropertyClaim{ + CustomPropertyName: "environment", + } + + mux.HandleFunc("/enterprises/e/actions/oidc/customization/properties/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"custom_property_name": "environment"}`) + }) + + ctx := t.Context() + property, _, err := client.Actions.SetEnterpriseOIDCCustomPropertyClaim(ctx, "e", input) + if err != nil { + t.Errorf("Actions.SetEnterpriseOIDCCustomPropertyClaim returned error: %v", err) + } + + want := &OIDCCustomPropertyClaim{CustomPropertyName: "environment"} + if !cmp.Equal(property, want) { + t.Errorf("Actions.SetEnterpriseOIDCCustomPropertyClaim returned %+v, want %+v", property, want) + } + + const methodName = "SetEnterpriseOIDCCustomPropertyClaim" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.SetEnterpriseOIDCCustomPropertyClaim(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.SetEnterpriseOIDCCustomPropertyClaim(ctx, "e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetOrgOIDCCustomPropertyClaim(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := OIDCCustomPropertyClaim{ + CustomPropertyName: "environment", + } + + mux.HandleFunc("/orgs/o/actions/oidc/customization/properties/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"custom_property_name": "environment"}`) + }) + + ctx := t.Context() + property, _, err := client.Actions.SetOrgOIDCCustomPropertyClaim(ctx, "o", input) + if err != nil { + t.Errorf("Actions.SetOrgOIDCCustomPropertyClaim returned error: %v", err) + } + + want := &OIDCCustomPropertyClaim{CustomPropertyName: "environment"} + if !cmp.Equal(property, want) { + t.Errorf("Actions.SetOrgOIDCCustomPropertyClaim returned %+v, want %+v", property, want) + } + + const methodName = "SetOrgOIDCCustomPropertyClaim" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.SetOrgOIDCCustomPropertyClaim(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.SetOrgOIDCCustomPropertyClaim(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_DeleteEnterpriseOIDCCustomPropertyClaim(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/oidc/customization/properties/repo/environment", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteEnterpriseOIDCCustomPropertyClaim(ctx, "e", "environment") + if err != nil { + t.Errorf("Actions.DeleteEnterpriseOIDCCustomPropertyClaim return error: %v", err) + } + + const methodName = "DeleteEnterpriseOIDCCustomPropertyClaim" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteEnterpriseOIDCCustomPropertyClaim(ctx, "\n", "environment") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteEnterpriseOIDCCustomPropertyClaim(ctx, "e", "r") + }) +} + +func TestActionsService_DeleteOrgOIDCCustomPropertyClaim(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/oidc/customization/properties/repo/environment", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteOrgOIDCCustomPropertyClaim(ctx, "o", "environment") + if err != nil { + t.Errorf("Actions.DeleteOrgOIDCCustomPropertyClaim return error: %v", err) + } + + const methodName = "DeleteOrgOIDCCustomPropertyClaim" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteOrgOIDCCustomPropertyClaim(ctx, "\n", "environment") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteOrgOIDCCustomPropertyClaim(ctx, "o", "r") + }) +} diff --git a/github/actions_permissions_enterprise.go b/github/actions_permissions_enterprise.go new file mode 100644 index 00000000000..1f903e9bf83 --- /dev/null +++ b/github/actions_permissions_enterprise.go @@ -0,0 +1,414 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ActionsEnabledOnEnterpriseRepos represents all the repositories in an enterprise for which Actions is enabled. +type ActionsEnabledOnEnterpriseRepos struct { + TotalCount int `json:"total_count"` + Organizations []*Organization `json:"organizations"` +} + +// ActionsPermissionsEnterprise represents a policy for allowed actions in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28 +type ActionsPermissionsEnterprise struct { + EnabledOrganizations *string `json:"enabled_organizations,omitempty"` + AllowedActions *string `json:"allowed_actions,omitempty"` + SelectedActionsURL *string `json:"selected_actions_url,omitempty"` +} + +func (a ActionsPermissionsEnterprise) String() string { + return Stringify(a) +} + +// DefaultWorkflowPermissionEnterprise represents the default permissions for GitHub Actions workflows for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28 +type DefaultWorkflowPermissionEnterprise struct { + DefaultWorkflowPermissions *string `json:"default_workflow_permissions,omitempty"` + CanApprovePullRequestReviews *bool `json:"can_approve_pull_request_reviews,omitempty"` +} + +// SelfHostRunnerPermissionsEnterprise represents the settings for whether organizations in the enterprise are allowed to manage self-hosted runners at the repository level. +type SelfHostRunnerPermissionsEnterprise struct { + DisableSelfHostedRunnersForAllOrgs *bool `json:"disable_self_hosted_runners_for_all_orgs,omitempty"` +} + +func (a SelfHostRunnerPermissionsEnterprise) String() string { + return Stringify(a) +} + +// GetActionsPermissionsInEnterprise gets the GitHub Actions permissions policy for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-github-actions-permissions-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions +func (s *ActionsService) GetActionsPermissionsInEnterprise(ctx context.Context, enterprise string) (*ActionsPermissionsEnterprise, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *ActionsPermissionsEnterprise + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateActionsPermissionsInEnterprise sets the permissions policy in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-github-actions-permissions-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions +func (s *ActionsService) UpdateActionsPermissionsInEnterprise(ctx context.Context, enterprise string, body ActionsPermissionsEnterprise) (*ActionsPermissionsEnterprise, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *ActionsPermissionsEnterprise + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// ListEnabledOrgsInEnterprise lists the selected organizations that are enabled for GitHub Actions in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#list-selected-organizations-enabled-for-github-actions-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/organizations +func (s *ActionsService) ListEnabledOrgsInEnterprise(ctx context.Context, owner string, opts *ListOptions) (*ActionsEnabledOnEnterpriseRepos, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/organizations", owner) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var orgs *ActionsEnabledOnEnterpriseRepos + resp, err := s.client.Do(req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// SetEnabledOrgsInEnterprise replaces the list of selected organizations that are enabled for GitHub Actions in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-selected-organizations-enabled-for-github-actions-in-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/organizations +func (s *ActionsService) SetEnabledOrgsInEnterprise(ctx context.Context, owner string, organizationIDs []int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/organizations", owner) + + req, err := s.client.NewRequest(ctx, "PUT", u, struct { + IDs []int64 `json:"selected_organization_ids"` + }{IDs: organizationIDs}) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AddEnabledOrgInEnterprise adds an organization to the list of selected organizations that are enabled for GitHub Actions in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#enable-a-selected-organization-for-github-actions-in-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/organizations/{org_id} +func (s *ActionsService) AddEnabledOrgInEnterprise(ctx context.Context, owner string, organizationID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/organizations/%v", owner, organizationID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// RemoveEnabledOrgInEnterprise removes an organization from the list of selected organizations that are enabled for GitHub Actions in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#disable-a-selected-organization-for-github-actions-in-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/permissions/organizations/{org_id} +func (s *ActionsService) RemoveEnabledOrgInEnterprise(ctx context.Context, owner string, organizationID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/organizations/%v", owner, organizationID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// GetActionsAllowedInEnterprise gets the actions that are allowed in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-allowed-actions-and-reusable-workflows-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/selected-actions +func (s *ActionsService) GetActionsAllowedInEnterprise(ctx context.Context, enterprise string) (*ActionsAllowed, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/selected-actions", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var actionsAllowed *ActionsAllowed + resp, err := s.client.Do(req, &actionsAllowed) + if err != nil { + return nil, resp, err + } + + return actionsAllowed, resp, nil +} + +// UpdateActionsAllowedInEnterprise sets the actions that are allowed in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-allowed-actions-and-reusable-workflows-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/selected-actions +func (s *ActionsService) UpdateActionsAllowedInEnterprise(ctx context.Context, enterprise string, body ActionsAllowed) (*ActionsAllowed, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/selected-actions", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *ActionsAllowed + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// GetDefaultWorkflowPermissionsInEnterprise gets the GitHub Actions default workflow permissions for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-default-workflow-permissions-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/workflow +func (s *ActionsService) GetDefaultWorkflowPermissionsInEnterprise(ctx context.Context, enterprise string) (*DefaultWorkflowPermissionEnterprise, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/workflow", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *DefaultWorkflowPermissionEnterprise + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateDefaultWorkflowPermissionsInEnterprise sets the GitHub Actions default workflow permissions for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-default-workflow-permissions-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/workflow +func (s *ActionsService) UpdateDefaultWorkflowPermissionsInEnterprise(ctx context.Context, enterprise string, body DefaultWorkflowPermissionEnterprise) (*DefaultWorkflowPermissionEnterprise, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/workflow", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *DefaultWorkflowPermissionEnterprise + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// GetArtifactAndLogRetentionPeriodInEnterprise gets the artifact and log retention period for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-artifact-and-log-retention-settings-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/artifact-and-log-retention +func (s *ActionsService) GetArtifactAndLogRetentionPeriodInEnterprise(ctx context.Context, enterprise string) (*ArtifactPeriod, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/artifact-and-log-retention", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var arp *ArtifactPeriod + resp, err := s.client.Do(req, &arp) + if err != nil { + return nil, resp, err + } + + return arp, resp, nil +} + +// UpdateArtifactAndLogRetentionPeriodInEnterprise sets the artifact and log retention period for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-artifact-and-log-retention-settings-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/artifact-and-log-retention +func (s *ActionsService) UpdateArtifactAndLogRetentionPeriodInEnterprise(ctx context.Context, enterprise string, body ArtifactPeriodOpt) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/artifact-and-log-retention", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetSelfHostedRunnerPermissionsInEnterprise gets the self-hosted runner permissions for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-self-hosted-runners-permissions-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/self-hosted-runners +func (s *ActionsService) GetSelfHostedRunnerPermissionsInEnterprise(ctx context.Context, enterprise string) (*SelfHostRunnerPermissionsEnterprise, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/self-hosted-runners", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *SelfHostRunnerPermissionsEnterprise + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateSelfHostedRunnerPermissionsInEnterprise sets the self-hosted runner permissions for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-self-hosted-runners-permissions-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/self-hosted-runners +func (s *ActionsService) UpdateSelfHostedRunnerPermissionsInEnterprise(ctx context.Context, enterprise string, body SelfHostRunnerPermissionsEnterprise) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/self-hosted-runners", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetPrivateRepoForkPRWorkflowSettingsInEnterprise gets the settings for whether workflows from fork pull requests can run on private repositories in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-private-repo-fork-pr-workflow-settings-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/fork-pr-workflows-private-repos +func (s *ActionsService) GetPrivateRepoForkPRWorkflowSettingsInEnterprise(ctx context.Context, enterprise string) (*WorkflowsPermissions, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/fork-pr-workflows-private-repos", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *WorkflowsPermissions + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise sets the settings for whether workflows from fork pull requests can run on private repositories in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-private-repo-fork-pr-workflow-settings-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/fork-pr-workflows-private-repos +func (s *ActionsService) UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise(ctx context.Context, enterprise string, body *WorkflowsPermissionsOpt) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/fork-pr-workflows-private-repos", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetEnterpriseForkPRContributorApprovalPermissions gets the fork PR contributor approval policy for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#get-fork-pr-contributor-approval-permissions-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/permissions/fork-pr-contributor-approval +func (s *ActionsService) GetEnterpriseForkPRContributorApprovalPermissions(ctx context.Context, enterprise string) (*ContributorApprovalPermissions, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/fork-pr-contributor-approval", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var policy *ContributorApprovalPermissions + resp, err := s.client.Do(req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// UpdateEnterpriseForkPRContributorApprovalPermissions sets the fork PR contributor approval policy for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions?apiVersion=2022-11-28#set-fork-pr-contributor-approval-permissions-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/permissions/fork-pr-contributor-approval +func (s *ActionsService) UpdateEnterpriseForkPRContributorApprovalPermissions(ctx context.Context, enterprise string, body ContributorApprovalPermissions) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/permissions/fork-pr-contributor-approval", enterprise) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_permissions_enterprise_test.go b/github/actions_permissions_enterprise_test.go new file mode 100644 index 00000000000..e56d6b11960 --- /dev/null +++ b/github/actions_permissions_enterprise_test.go @@ -0,0 +1,644 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_GetActionsPermissionsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled_organizations": "all", "allowed_actions": "all"}`) + }) + + ctx := t.Context() + ent, _, err := client.Actions.GetActionsPermissionsInEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetActionsPermissionsInEnterprise returned error: %v", err) + } + want := &ActionsPermissionsEnterprise{EnabledOrganizations: Ptr("all"), AllowedActions: Ptr("all")} + if !cmp.Equal(ent, want) { + t.Errorf("Actions.GetActionsPermissionsInEnterprise returned %+v, want %+v", ent, want) + } + + const methodName = "GetActionsPermissionsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetActionsPermissionsInEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetActionsPermissionsInEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateActionsPermissionsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsPermissionsEnterprise{EnabledOrganizations: Ptr("all"), AllowedActions: Ptr("selected")} + + mux.HandleFunc("/enterprises/e/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"enabled_organizations": "all", "allowed_actions": "selected"}`) + }) + + ctx := t.Context() + ent, _, err := client.Actions.UpdateActionsPermissionsInEnterprise(ctx, "e", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsPermissionsInEnterprise returned error: %v", err) + } + + want := &ActionsPermissionsEnterprise{EnabledOrganizations: Ptr("all"), AllowedActions: Ptr("selected")} + if !cmp.Equal(ent, want) { + t.Errorf("Actions.UpdateActionsPermissionsInEnterprise returned %+v, want %+v", ent, want) + } + + const methodName = "UpdateActionsPermissionsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateActionsPermissionsInEnterprise(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateActionsPermissionsInEnterprise(ctx, "e", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListEnabledOrgsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/organizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + }) + fmt.Fprint(w, `{"total_count":2,"organizations":[{"id":2}, {"id":3}]}`) + }) + + ctx := t.Context() + opt := &ListOptions{ + Page: 1, + } + got, _, err := client.Actions.ListEnabledOrgsInEnterprise(ctx, "e", opt) + if err != nil { + t.Errorf("Actions.ListEnabledOrgsInEnterprise returned error: %v", err) + } + + want := &ActionsEnabledOnEnterpriseRepos{TotalCount: int(2), Organizations: []*Organization{ + {ID: Ptr(int64(2))}, + {ID: Ptr(int64(3))}, + }} + if !cmp.Equal(got, want) { + t.Errorf("Actions.ListEnabledOrgsInEnterprise returned %+v, want %+v", got, want) + } + + const methodName = "ListEnabledOrgsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListEnabledOrgsInEnterprise(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListEnabledOrgsInEnterprise(ctx, "e", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetEnabledOrgsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{123, 1234} + + mux.HandleFunc("/enterprises/e/actions/permissions/organizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + IDs []int64 `json:"selected_organization_ids"` + }{ + IDs: input, + }) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.SetEnabledOrgsInEnterprise(ctx, "e", input) + if err != nil { + t.Errorf("Actions.SetEnabledOrgsInEnterprise returned error: %v", err) + } + + const methodName = "SetEnabledOrgsInEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetEnabledOrgsInEnterprise(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetEnabledOrgsInEnterprise(ctx, "e", input) + }) +} + +func TestActionsService_AddEnabledOrgInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/organizations/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.AddEnabledOrgInEnterprise(ctx, "e", 123) + if err != nil { + t.Errorf("Actions.AddEnabledOrgInEnterprise returned error: %v", err) + } + + const methodName = "AddEnabledOrgInEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddEnabledOrgInEnterprise(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddEnabledOrgInEnterprise(ctx, "e", 123) + }) +} + +func TestActionsService_RemoveEnabledOrgInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/organizations/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.RemoveEnabledOrgInEnterprise(ctx, "e", 123) + if err != nil { + t.Errorf("Actions.RemoveEnabledOrgInEnterprise returned error: %v", err) + } + + const methodName = "RemoveEnabledOrgInEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveEnabledOrgInEnterprise(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveEnabledOrgInEnterprise(ctx, "e", 123) + }) +} + +func TestActionsService_GetActionsAllowedInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + ent, _, err := client.Actions.GetActionsAllowedInEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetActionsAllowedInEnterprise returned error: %v", err) + } + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(ent, want) { + t.Errorf("Actions.GetActionsAllowedInEnterprise returned %+v, want %+v", ent, want) + } + + const methodName = "GetActionsAllowedInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetActionsAllowedInEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetActionsAllowedInEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateActionsAllowedInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + + mux.HandleFunc("/enterprises/e/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + ent, _, err := client.Actions.UpdateActionsAllowedInEnterprise(ctx, "e", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsAllowedInEnterprise returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(ent, want) { + t.Errorf("Actions.UpdateActionsAllowedInEnterprise returned %+v, want %+v", ent, want) + } + + const methodName = "UpdateActionsAllowedInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateActionsAllowedInEnterprise(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateActionsAllowedInEnterprise(ctx, "e", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetDefaultWorkflowPermissionsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`) + }) + + ctx := t.Context() + ent, _, err := client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetDefaultWorkflowPermissionsInEnterprise returned error: %v", err) + } + want := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + if !cmp.Equal(ent, want) { + t.Errorf("Actions.GetDefaultWorkflowPermissionsInEnterprise returned %+v, want %+v", ent, want) + } + + const methodName = "GetDefaultWorkflowPermissionsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateDefaultWorkflowPermissionsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + + mux.HandleFunc("/enterprises/e/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`) + }) + + ctx := t.Context() + ent, _, err := client.Actions.UpdateDefaultWorkflowPermissionsInEnterprise(ctx, "e", *input) + if err != nil { + t.Errorf("Actions.UpdateDefaultWorkflowPermissionsInEnterprise returned error: %v", err) + } + + want := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + if !cmp.Equal(ent, want) { + t.Errorf("Actions.UpdateDefaultWorkflowPermissionsInEnterprise returned %+v, want %+v", ent, want) + } + + const methodName = "UpdateDefaultWorkflowPermissionsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateDefaultWorkflowPermissionsInEnterprise(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateDefaultWorkflowPermissionsInEnterprise(ctx, "e", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetArtifactAndLogRetentionPeriodInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/artifact-and-log-retention", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"days": 90, "maximum_allowed_days": 365}`) + }) + + ctx := t.Context() + period, _, err := client.Actions.GetArtifactAndLogRetentionPeriodInEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetArtifactAndLogRetentionPeriodInEnterprise returned error: %v", err) + } + + want := &ArtifactPeriod{ + Days: Ptr(90), + MaximumAllowedDays: Ptr(365), + } + if !cmp.Equal(period, want) { + t.Errorf("Actions.GetArtifactAndLogRetentionPeriodInEnterprise = %+v, want %+v", period, want) + } + + const methodName = "GetArtifactAndLogRetentionPeriodInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetArtifactAndLogRetentionPeriodInEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetArtifactAndLogRetentionPeriodInEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateArtifactAndLogRetentionPeriodInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ArtifactPeriodOpt{Days: Ptr(90)} + + mux.HandleFunc("/enterprises/e/actions/permissions/artifact-and-log-retention", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateArtifactAndLogRetentionPeriodInEnterprise(ctx, "e", *input) + if err != nil { + t.Errorf("Actions.UpdateArtifactAndLogRetentionPeriodInEnterprise returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateArtifactAndLogRetentionPeriodInEnterprise = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateArtifactAndLogRetentionPeriodInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateArtifactAndLogRetentionPeriodInEnterprise(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateArtifactAndLogRetentionPeriodInEnterprise(ctx, "e", *input) + }) +} + +func TestActionsService_GetSelfHostedRunnerPermissionsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/self-hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"disable_self_hosted_runners_for_all_orgs": true}`) + }) + + ctx := t.Context() + permissions, _, err := client.Actions.GetSelfHostedRunnerPermissionsInEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetSelfHostedRunnerPermissionsInEnterprise returned error: %v", err) + } + want := &SelfHostRunnerPermissionsEnterprise{DisableSelfHostedRunnersForAllOrgs: Ptr(true)} + if !cmp.Equal(permissions, want) { + t.Errorf("Actions.GetSelfHostedRunnerPermissionsInEnterprise returned %+v, want %+v", permissions, want) + } + + const methodName = "GetSelfHostedRunnerPermissionsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetSelfHostedRunnerPermissionsInEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetSelfHostedRunnerPermissionsInEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateSelfHostedRunnerPermissionsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SelfHostRunnerPermissionsEnterprise{DisableSelfHostedRunnersForAllOrgs: Ptr(false)} + + mux.HandleFunc("/enterprises/e/actions/permissions/self-hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateSelfHostedRunnerPermissionsInEnterprise(ctx, "e", *input) + if err != nil { + t.Errorf("Actions.UpdateSelfHostedRunnerPermissionsInEnterprise returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateSelfHostedRunnerPermissionsInEnterprise = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateSelfHostedRunnerPermissionsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateSelfHostedRunnerPermissionsInEnterprise(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateSelfHostedRunnerPermissionsInEnterprise(ctx, "e", *input) + }) +} + +func TestActionsService_GetPrivateRepoForkPRWorkflowSettingsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/fork-pr-workflows-private-repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"run_workflows_from_fork_pull_requests": true, "send_write_tokens_to_workflows": false, "send_secrets_and_variables": true, "require_approval_for_fork_pr_workflows": false}`) + }) + + ctx := t.Context() + permissions, _, err := client.Actions.GetPrivateRepoForkPRWorkflowSettingsInEnterprise(ctx, "e") + if err != nil { + t.Errorf("Actions.GetPrivateRepoForkPRWorkflowSettingsInEnterprise returned error: %v", err) + } + want := &WorkflowsPermissions{ + RunWorkflowsFromForkPullRequests: Ptr(true), + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(true), + RequireApprovalForForkPRWorkflows: Ptr(false), + } + if !cmp.Equal(permissions, want) { + t.Errorf("Actions.GetPrivateRepoForkPRWorkflowSettingsInEnterprise returned %+v, want %+v", permissions, want) + } + + const methodName = "GetPrivateRepoForkPRWorkflowSettingsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetPrivateRepoForkPRWorkflowSettingsInEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetPrivateRepoForkPRWorkflowSettingsInEnterprise(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &WorkflowsPermissionsOpt{ + RunWorkflowsFromForkPullRequests: true, + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(true), + } + + mux.HandleFunc("/enterprises/e/actions/permissions/fork-pr-workflows-private-repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise(ctx, "e", input) + if err != nil { + t.Errorf("Actions.UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdatePrivateRepoForkPRWorkflowSettingsInEnterprise(ctx, "e", input) + }) +} + +func TestActionsService_GetEnterpriseForkPRContributorApprovalPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/permissions/fork-pr-contributor-approval", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"approval_policy": "require_approval"}`) + }) + + ctx := t.Context() + policy, _, err := client.Actions.GetEnterpriseForkPRContributorApprovalPermissions(ctx, "e") + if err != nil { + t.Errorf("Actions.GetEnterpriseForkPRContributorApprovalPermissions returned error: %v", err) + } + want := &ContributorApprovalPermissions{ApprovalPolicy: "require_approval"} + if !cmp.Equal(policy, want) { + t.Errorf("Actions.GetEnterpriseForkPRContributorApprovalPermissions returned %+v, want %+v", policy, want) + } + + const methodName = "GetEnterpriseForkPRContributorApprovalPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetEnterpriseForkPRContributorApprovalPermissions(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetEnterpriseForkPRContributorApprovalPermissions(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateEnterpriseForkPRContributorApprovalPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ContributorApprovalPermissions{ApprovalPolicy: "require_approval"} + + mux.HandleFunc("/enterprises/e/actions/permissions/fork-pr-contributor-approval", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateEnterpriseForkPRContributorApprovalPermissions(ctx, "e", input) + if err != nil { + t.Errorf("Actions.UpdateEnterpriseForkPRContributorApprovalPermissions returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateEnterpriseForkPRContributorApprovalPermissions = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateEnterpriseForkPRContributorApprovalPermissions" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateEnterpriseForkPRContributorApprovalPermissions(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateEnterpriseForkPRContributorApprovalPermissions(ctx, "e", input) + }) +} diff --git a/github/actions_permissions_orgs.go b/github/actions_permissions_orgs.go new file mode 100644 index 00000000000..88bfd87812d --- /dev/null +++ b/github/actions_permissions_orgs.go @@ -0,0 +1,526 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ActionsPermissions represents a policy for repositories and allowed actions in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28 +type ActionsPermissions struct { + EnabledRepositories *string `json:"enabled_repositories,omitempty"` + AllowedActions *string `json:"allowed_actions,omitempty"` + SelectedActionsURL *string `json:"selected_actions_url,omitempty"` + SHAPinningRequired *bool `json:"sha_pinning_required,omitempty"` +} + +func (a ActionsPermissions) String() string { + return Stringify(a) +} + +// ActionsEnabledOnOrgRepos represents all the repositories in an organization for which Actions is enabled. +type ActionsEnabledOnOrgRepos struct { + TotalCount int `json:"total_count"` + Repositories []*Repository `json:"repositories"` +} + +// ActionsAllowed represents selected actions that are allowed. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28 +type ActionsAllowed struct { + GithubOwnedAllowed *bool `json:"github_owned_allowed,omitempty"` + VerifiedAllowed *bool `json:"verified_allowed,omitempty"` + // PatternsAllowed uses `omitzero` to allow an empty array to be sent in the request body in order to + // configure the organization to allow no 3rd-party actions. It needs to still omit a `nil` value so + // that it's possible to patch the other values without changing the allowed actions. + PatternsAllowed []string `json:"patterns_allowed,omitzero"` +} + +func (a ActionsAllowed) String() string { + return Stringify(a) +} + +// DefaultWorkflowPermissionOrganization represents the default permissions for GitHub Actions workflows for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28 +type DefaultWorkflowPermissionOrganization struct { + DefaultWorkflowPermissions *string `json:"default_workflow_permissions,omitempty"` + CanApprovePullRequestReviews *bool `json:"can_approve_pull_request_reviews,omitempty"` +} + +// SelfHostedRunnersSettingsOrganization represents the self-hosted runners permissions settings for repositories in an organization. +type SelfHostedRunnersSettingsOrganization struct { + EnabledRepositories *string `json:"enabled_repositories,omitempty"` + SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` +} + +func (s SelfHostedRunnersSettingsOrganization) String() string { + return Stringify(s) +} + +// SelfHostedRunnersSettingsOrganizationOpt specifies the self-hosted runners permissions settings for repositories in an organization. +type SelfHostedRunnersSettingsOrganizationOpt struct { + EnabledRepositories *string `json:"enabled_repositories,omitempty"` +} + +// GetActionsPermissions gets the GitHub Actions permissions policy for repositories and allowed actions in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-github-actions-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions +func (s *ActionsService) GetActionsPermissions(ctx context.Context, org string) (*ActionsPermissions, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *ActionsPermissions + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateActionsPermissions sets the permissions policy for repositories and allowed actions in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-github-actions-permissions-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions +func (s *ActionsService) UpdateActionsPermissions(ctx context.Context, org string, body ActionsPermissions) (*ActionsPermissions, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *ActionsPermissions + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// ListEnabledReposInOrg lists the selected repositories that are enabled for GitHub Actions in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#list-selected-repositories-enabled-for-github-actions-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/repositories +func (s *ActionsService) ListEnabledReposInOrg(ctx context.Context, owner string, opts *ListOptions) (*ActionsEnabledOnOrgRepos, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/repositories", owner) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repos *ActionsEnabledOnOrgRepos + resp, err := s.client.Do(req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// SetEnabledReposInOrg replaces the list of selected repositories that are enabled for GitHub Actions in an organization.. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-selected-repositories-enabled-for-github-actions-in-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/repositories +func (s *ActionsService) SetEnabledReposInOrg(ctx context.Context, owner string, repositoryIDs []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/repositories", owner) + + req, err := s.client.NewRequest(ctx, "PUT", u, struct { + IDs []int64 `json:"selected_repository_ids"` + }{IDs: repositoryIDs}) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AddEnabledReposInOrg adds a repository to the list of selected repositories that are enabled for GitHub Actions in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#enable-a-selected-repository-for-github-actions-in-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/repositories/{repository_id} +func (s *ActionsService) AddEnabledReposInOrg(ctx context.Context, owner string, repositoryID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/repositories/%v", owner, repositoryID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// RemoveEnabledReposInOrg removes a single repository from the list of enabled repos for GitHub Actions in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#disable-a-selected-repository-for-github-actions-in-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/permissions/repositories/{repository_id} +func (s *ActionsService) RemoveEnabledReposInOrg(ctx context.Context, owner string, repositoryID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/repositories/%v", owner, repositoryID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// GetActionsAllowed gets the actions that are allowed in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-allowed-actions-and-reusable-workflows-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/selected-actions +func (s *ActionsService) GetActionsAllowed(ctx context.Context, org string) (*ActionsAllowed, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/selected-actions", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var actionsAllowed *ActionsAllowed + resp, err := s.client.Do(req, &actionsAllowed) + if err != nil { + return nil, resp, err + } + + return actionsAllowed, resp, nil +} + +// UpdateActionsAllowed sets the actions that are allowed in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-allowed-actions-and-reusable-workflows-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/selected-actions +func (s *ActionsService) UpdateActionsAllowed(ctx context.Context, org string, body ActionsAllowed) (*ActionsAllowed, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/selected-actions", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *ActionsAllowed + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// GetDefaultWorkflowPermissionsInOrganization gets the GitHub Actions default workflow permissions for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-default-workflow-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/workflow +func (s *ActionsService) GetDefaultWorkflowPermissionsInOrganization(ctx context.Context, org string) (*DefaultWorkflowPermissionOrganization, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/workflow", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *DefaultWorkflowPermissionOrganization + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateDefaultWorkflowPermissionsInOrganization sets the GitHub Actions default workflow permissions for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-default-workflow-permissions-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/workflow +func (s *ActionsService) UpdateDefaultWorkflowPermissionsInOrganization(ctx context.Context, org string, body DefaultWorkflowPermissionOrganization) (*DefaultWorkflowPermissionOrganization, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/workflow", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *DefaultWorkflowPermissionOrganization + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// GetArtifactAndLogRetentionPeriodInOrganization gets the artifact and log retention period for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-artifact-and-log-retention-settings-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/artifact-and-log-retention +func (s *ActionsService) GetArtifactAndLogRetentionPeriodInOrganization(ctx context.Context, org string) (*ArtifactPeriod, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/artifact-and-log-retention", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var arp *ArtifactPeriod + resp, err := s.client.Do(req, &arp) + if err != nil { + return nil, resp, err + } + + return arp, resp, nil +} + +// UpdateArtifactAndLogRetentionPeriodInOrganization sets the artifact and log retention period for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-artifact-and-log-retention-settings-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/artifact-and-log-retention +func (s *ActionsService) UpdateArtifactAndLogRetentionPeriodInOrganization(ctx context.Context, org string, body ArtifactPeriodOpt) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/artifact-and-log-retention", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetSelfHostedRunnersSettingsInOrganization gets the self-hosted runners permissions settings for repositories in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-self-hosted-runners-settings-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/self-hosted-runners +func (s *ActionsService) GetSelfHostedRunnersSettingsInOrganization(ctx context.Context, org string) (*SelfHostedRunnersSettingsOrganization, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/self-hosted-runners", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var settings *SelfHostedRunnersSettingsOrganization + resp, err := s.client.Do(req, &settings) + if err != nil { + return nil, resp, err + } + + return settings, resp, nil +} + +// UpdateSelfHostedRunnersSettingsInOrganization sets the self-hosted runners permissions settings for repositories in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-self-hosted-runners-settings-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/self-hosted-runners +func (s *ActionsService) UpdateSelfHostedRunnersSettingsInOrganization(ctx context.Context, org string, body SelfHostedRunnersSettingsOrganizationOpt) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/self-hosted-runners", org) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// SelfHostedRunnersAllowedRepos represents the repositories that are allowed to use self-hosted runners in an organization. +type SelfHostedRunnersAllowedRepos struct { + TotalCount int `json:"total_count"` + Repositories []*Repository `json:"repositories"` +} + +// ListRepositoriesSelfHostedRunnersAllowedInOrganization lists the repositories that are allowed to use self-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#list-repositories-allowed-to-use-self-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/self-hosted-runners/repositories +func (s *ActionsService) ListRepositoriesSelfHostedRunnersAllowedInOrganization(ctx context.Context, org string, opts *ListOptions) (*SelfHostedRunnersAllowedRepos, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/self-hosted-runners/repositories", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var settings *SelfHostedRunnersAllowedRepos + resp, err := s.client.Do(req, &settings) + if err != nil { + return nil, resp, err + } + + return settings, resp, nil +} + +// SetRepositoriesSelfHostedRunnersAllowedInOrganization allows the list of repositories to use self-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-repositories-allowed-to-use-self-hosted-runners-in-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/self-hosted-runners/repositories +func (s *ActionsService) SetRepositoriesSelfHostedRunnersAllowedInOrganization(ctx context.Context, org string, repositoryIDs []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/self-hosted-runners/repositories", org) + + req, err := s.client.NewRequest(ctx, "PUT", u, struct { + IDs []int64 `json:"selected_repository_ids"` + }{IDs: repositoryIDs}) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddRepositorySelfHostedRunnersAllowedInOrganization adds a repository to the list of repositories that are allowed to use self-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#add-a-repository-to-the-list-of-repositories-allowed-to-use-self-hosted-runners-in-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/self-hosted-runners/repositories/{repository_id} +func (s *ActionsService) AddRepositorySelfHostedRunnersAllowedInOrganization(ctx context.Context, org string, repositoryID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/self-hosted-runners/repositories/%v", org, repositoryID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveRepositorySelfHostedRunnersAllowedInOrganization removes a repository from the list of repositories that are allowed to use self-hosted runners in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#remove-a-repository-from-the-list-of-repositories-allowed-to-use-self-hosted-runners-in-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/permissions/self-hosted-runners/repositories/{repository_id} +func (s *ActionsService) RemoveRepositorySelfHostedRunnersAllowedInOrganization(ctx context.Context, org string, repositoryID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/self-hosted-runners/repositories/%v", org, repositoryID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// GetPrivateRepoForkPRWorkflowSettingsInOrganization gets the settings for whether workflows from fork pull requests can run on private repositories in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-private-repo-fork-pr-workflow-settings-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/fork-pr-workflows-private-repos +func (s *ActionsService) GetPrivateRepoForkPRWorkflowSettingsInOrganization(ctx context.Context, org string) (*WorkflowsPermissions, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/fork-pr-workflows-private-repos", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *WorkflowsPermissions + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdatePrivateRepoForkPRWorkflowSettingsInOrganization sets the settings for whether workflows from fork pull requests can run on private repositories in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-private-repo-fork-pr-workflow-settings-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/fork-pr-workflows-private-repos +func (s *ActionsService) UpdatePrivateRepoForkPRWorkflowSettingsInOrganization(ctx context.Context, org string, body *WorkflowsPermissionsOpt) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/fork-pr-workflows-private-repos", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetOrganizationForkPRContributorApprovalPermissions gets the fork PR contributor approval policy for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-fork-pr-contributor-approval-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/fork-pr-contributor-approval +func (s *ActionsService) GetOrganizationForkPRContributorApprovalPermissions(ctx context.Context, org string) (*ContributorApprovalPermissions, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/fork-pr-contributor-approval", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var policy *ContributorApprovalPermissions + resp, err := s.client.Do(req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// UpdateOrganizationForkPRContributorApprovalPermissions sets the fork PR contributor approval policy for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-fork-pr-contributor-approval-permissions-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/fork-pr-contributor-approval +func (s *ActionsService) UpdateOrganizationForkPRContributorApprovalPermissions(ctx context.Context, org string, body ContributorApprovalPermissions) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/permissions/fork-pr-contributor-approval", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_permissions_orgs_test.go b/github/actions_permissions_orgs_test.go new file mode 100644 index 00000000000..b8dddddba72 --- /dev/null +++ b/github/actions_permissions_orgs_test.go @@ -0,0 +1,886 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_GetActionsPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled_repositories": "all", "allowed_actions": "all", "sha_pinning_required": true}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.GetActionsPermissions(ctx, "o") + if err != nil { + t.Errorf("Actions.GetActionsPermissions returned error: %v", err) + } + want := &ActionsPermissions{EnabledRepositories: Ptr("all"), AllowedActions: Ptr("all"), SHAPinningRequired: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Actions.GetActionsPermissions returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetActionsPermissions(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetActionsPermissions(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateActionsPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsPermissions{EnabledRepositories: Ptr("all"), AllowedActions: Ptr("selected"), SHAPinningRequired: Ptr(true)} + + mux.HandleFunc("/orgs/o/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"enabled_repositories": "all", "allowed_actions": "selected", "sha_pinning_required": true}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.UpdateActionsPermissions(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsPermissions returned error: %v", err) + } + + want := &ActionsPermissions{EnabledRepositories: Ptr("all"), AllowedActions: Ptr("selected"), SHAPinningRequired: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Actions.UpdateActionsPermissions returned %+v, want %+v", org, want) + } + + const methodName = "UpdateActionsPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateActionsPermissions(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateActionsPermissions(ctx, "o", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListEnabledReposInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + }) + fmt.Fprint(w, `{"total_count":2,"repositories":[{"id":2}, {"id": 3}]}`) + }) + + ctx := t.Context() + opt := &ListOptions{ + Page: 1, + } + got, _, err := client.Actions.ListEnabledReposInOrg(ctx, "o", opt) + if err != nil { + t.Errorf("Actions.ListEnabledReposInOrg returned error: %v", err) + } + + want := &ActionsEnabledOnOrgRepos{TotalCount: int(2), Repositories: []*Repository{ + {ID: Ptr(int64(2))}, + {ID: Ptr(int64(3))}, + }} + if !cmp.Equal(got, want) { + t.Errorf("Actions.ListEnabledReposInOrg returned %+v, want %+v", got, want) + } + + const methodName = "ListEnabledRepos" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListEnabledReposInOrg(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListEnabledReposInOrg(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetEnabledReposInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{123, 1234} + + mux.HandleFunc("/orgs/o/actions/permissions/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + IDs []int64 `json:"selected_repository_ids"` + }{ + IDs: input, + }) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.SetEnabledReposInOrg(ctx, "o", input) + if err != nil { + t.Errorf("Actions.SetEnabledReposInOrg returned error: %v", err) + } + + const methodName = "SetEnabledRepos" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetEnabledReposInOrg(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetEnabledReposInOrg(ctx, "o", input) + }) +} + +func TestActionsService_AddEnabledReposInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/repositories/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.AddEnabledReposInOrg(ctx, "o", 123) + if err != nil { + t.Errorf("Actions.AddEnabledReposInOrg returned error: %v", err) + } + + const methodName = "AddEnabledReposInOrg" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddEnabledReposInOrg(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddEnabledReposInOrg(ctx, "o", 123) + }) +} + +func TestActionsService_RemoveEnabledReposInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/repositories/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.RemoveEnabledReposInOrg(ctx, "o", 123) + if err != nil { + t.Errorf("Actions.RemoveEnabledReposInOrg returned error: %v", err) + } + + const methodName = "RemoveEnabledReposInOrg" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveEnabledReposInOrg(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveEnabledReposInOrg(ctx, "o", 123) + }) +} + +func TestActionsService_GetActionsAllowed(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.GetActionsAllowed(ctx, "o") + if err != nil { + t.Errorf("Actions.GetActionsAllowed returned error: %v", err) + } + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Actions.GetActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetActionsAllowed(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetActionsAllowed(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetActionsAllowed_emptyPatterns(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":[]}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.GetActionsAllowed(ctx, "o") + if err != nil { + t.Errorf("Actions.GetActionsAllowed returned error: %v", err) + } + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{}} + if !cmp.Equal(org, want) { + t.Errorf("Actions.GetActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetActionsAllowed(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetActionsAllowed(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateActionsAllowed(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.UpdateActionsAllowed(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsAllowed returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Actions.UpdateActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "UpdateActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateActionsAllowed(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateActionsAllowed(ctx, "o", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateActionsAllowed_emptyPatterns(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{}} + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":[]}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.UpdateActionsAllowed(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsAllowed returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{}} + if !cmp.Equal(org, want) { + t.Errorf("Actions.UpdateActionsAllowed returned %+v, want %+v", org, want) + } +} + +func TestActionsService_UpdateActionsAllowed_nilPatterns(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: nil} + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.UpdateActionsAllowed(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsAllowed returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Actions.UpdateActionsAllowed returned %+v, want %+v", org, want) + } +} + +func TestActionsService_UpdateActionsAllowed_omittedPatterns(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false)} + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Actions.UpdateActionsAllowed(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateActionsAllowed returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Actions.UpdateActionsAllowed returned %+v, want %+v", org, want) + } +} + +func TestActionsService_GetDefaultWorkflowPermissionsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`) + }) + + ctx := t.Context() + org, _, err := client.Actions.GetDefaultWorkflowPermissionsInOrganization(ctx, "o") + if err != nil { + t.Errorf("Actions.GetDefaultWorkflowPermissionsInOrganization returned error: %v", err) + } + want := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Actions.GetDefaultWorkflowPermissionsInOrganization returned %+v, want %+v", org, want) + } + + const methodName = "GetDefaultWorkflowPermissionsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetDefaultWorkflowPermissionsInOrganization(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetDefaultWorkflowPermissionsInOrganization(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateDefaultWorkflowPermissionsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + + mux.HandleFunc("/orgs/o/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`) + }) + + ctx := t.Context() + org, _, err := client.Actions.UpdateDefaultWorkflowPermissionsInOrganization(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateDefaultWorkflowPermissionsInOrganization returned error: %v", err) + } + + want := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Actions.UpdateDefaultWorkflowPermissionsInOrganization returned %+v, want %+v", org, want) + } + + const methodName = "UpdateDefaultWorkflowPermissionsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateDefaultWorkflowPermissionsInOrganization(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateDefaultWorkflowPermissionsInOrganization(ctx, "o", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetArtifactAndLogRetentionPeriodInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/artifact-and-log-retention", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"days": 90, "maximum_allowed_days": 365}`) + }) + + ctx := t.Context() + period, _, err := client.Actions.GetArtifactAndLogRetentionPeriodInOrganization(ctx, "o") + if err != nil { + t.Errorf("Actions.GetArtifactAndLogRetentionPeriodInOrganization returned error: %v", err) + } + + want := &ArtifactPeriod{ + Days: Ptr(90), + MaximumAllowedDays: Ptr(365), + } + if !cmp.Equal(period, want) { + t.Errorf("Actions.GetArtifactAndLogRetentionPeriodInOrganization = %+v, want %+v", period, want) + } + + const methodName = "GetArtifactAndLogRetentionPeriodInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetArtifactAndLogRetentionPeriodInOrganization(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetArtifactAndLogRetentionPeriodInOrganization(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateArtifactAndLogRetentionPeriodInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ArtifactPeriodOpt{Days: Ptr(90)} + + mux.HandleFunc("/orgs/o/actions/permissions/artifact-and-log-retention", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateArtifactAndLogRetentionPeriodInOrganization(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateArtifactAndLogRetentionPeriodInOrganization returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateArtifactAndLogRetentionPeriodInOrganization = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateArtifactAndLogRetentionPeriodInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateArtifactAndLogRetentionPeriodInOrganization(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateArtifactAndLogRetentionPeriodInOrganization(ctx, "o", *input) + }) +} + +func TestActionsService_GetSelfHostedRunnersSettingsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/self-hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled_repositories": "all", "selected_repositories_url": "https://api.github.com/orgs/octo-org/actions/permissions/self-hosted-runners/repositories"}`) + }) + + ctx := t.Context() + settings, _, err := client.Actions.GetSelfHostedRunnersSettingsInOrganization(ctx, "o") + if err != nil { + t.Errorf("Actions.GetSelfHostedRunnersSettingsInOrganization returned error: %v", err) + } + want := &SelfHostedRunnersSettingsOrganization{ + EnabledRepositories: Ptr("all"), + SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/permissions/self-hosted-runners/repositories"), + } + if !cmp.Equal(settings, want) { + t.Errorf("Actions.GetSelfHostedRunnersSettingsInOrganization returned %+v, want %+v", settings, want) + } + + const methodName = "GetSelfHostedRunnersSettingsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetSelfHostedRunnersSettingsInOrganization(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetSelfHostedRunnersSettingsInOrganization(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateSelfHostedRunnersSettingsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SelfHostedRunnersSettingsOrganizationOpt{EnabledRepositories: Ptr("selected")} + + mux.HandleFunc("/orgs/o/actions/permissions/self-hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateSelfHostedRunnersSettingsInOrganization(ctx, "o", *input) + if err != nil { + t.Errorf("Actions.UpdateSelfHostedRunnersSettingsInOrganization returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateSelfHostedRunnersSettingsInOrganization = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateSelfHostedRunnersSettingsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateSelfHostedRunnersSettingsInOrganization(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateSelfHostedRunnersSettingsInOrganization(ctx, "o", *input) + }) +} + +func TestActionsService_ListRepositoriesSelfHostedRunnersAllowedInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/self-hosted-runners/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + }) + fmt.Fprint(w, `{"total_count":2,"repositories":[{"id":2}, {"id": 3}]}`) + }) + + ctx := t.Context() + opt := &ListOptions{ + Page: 1, + } + got, _, err := client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, "o", opt) + if err != nil { + t.Errorf("Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganization returned error: %v", err) + } + + want := &SelfHostedRunnersAllowedRepos{TotalCount: int(2), Repositories: []*Repository{ + {ID: Ptr(int64(2))}, + {ID: Ptr(int64(3))}, + }} + if !cmp.Equal(got, want) { + t.Errorf("Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganization returned %+v, want %+v", got, want) + } + + const methodName = "ListRepositoriesSelfHostedRunnersAllowedInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetRepositoriesSelfHostedRunnersAllowedInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{123, 1234} + + mux.HandleFunc("/orgs/o/actions/permissions/self-hosted-runners/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + IDs []int64 `json:"selected_repository_ids"` + }{ + IDs: input, + }) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.SetRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, "o", input) + if err != nil { + t.Errorf("Actions.SetRepositoriesSelfHostedRunnersAllowedInOrganization returned error: %v", err) + } + + const methodName = "SetRepositoriesSelfHostedRunnersAllowedInOrganization" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, "o", input) + }) +} + +func TestActionsService_AddRepositorySelfHostedRunnersAllowedInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/self-hosted-runners/repositories/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.AddRepositorySelfHostedRunnersAllowedInOrganization(ctx, "o", 123) + if err != nil { + t.Errorf("Actions.AddRepositorySelfHostedRunnersAllowedInOrganization returned error: %v", err) + } + + const methodName = "AddRepositorySelfHostedRunnersAllowedInOrganization" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddRepositorySelfHostedRunnersAllowedInOrganization(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddRepositorySelfHostedRunnersAllowedInOrganization(ctx, "o", 123) + }) +} + +func TestActionsService_RemoveRepositorySelfHostedRunnersAllowedInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/self-hosted-runners/repositories/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.RemoveRepositorySelfHostedRunnersAllowedInOrganization(ctx, "o", 123) + if err != nil { + t.Errorf("Actions.RemoveRepositorySelfHostedRunnersAllowedInOrganization returned error: %v", err) + } + + const methodName = "RemoveRepositorySelfHostedRunnersAllowedInOrganization" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveRepositorySelfHostedRunnersAllowedInOrganization(ctx, "\n", 123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveRepositorySelfHostedRunnersAllowedInOrganization(ctx, "o", 123) + }) +} + +func TestActionsService_GetPrivateRepoForkPRWorkflowSettingsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/fork-pr-workflows-private-repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"run_workflows_from_fork_pull_requests": true, "send_write_tokens_to_workflows": false, "send_secrets_and_variables": true, "require_approval_for_fork_pr_workflows": false}`) + }) + + ctx := t.Context() + permissions, _, err := client.Actions.GetPrivateRepoForkPRWorkflowSettingsInOrganization(ctx, "o") + if err != nil { + t.Errorf("Actions.GetPrivateRepoForkPRWorkflowSettingsInOrganization returned error: %v", err) + } + want := &WorkflowsPermissions{ + RunWorkflowsFromForkPullRequests: Ptr(true), + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(true), + RequireApprovalForForkPRWorkflows: Ptr(false), + } + if !cmp.Equal(permissions, want) { + t.Errorf("Actions.GetPrivateRepoForkPRWorkflowSettingsInOrganization returned %+v, want %+v", permissions, want) + } + + const methodName = "GetPrivateRepoForkPRWorkflowSettingsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetPrivateRepoForkPRWorkflowSettingsInOrganization(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetPrivateRepoForkPRWorkflowSettingsInOrganization(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdatePrivateRepoForkPRWorkflowSettingsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &WorkflowsPermissionsOpt{ + RunWorkflowsFromForkPullRequests: true, + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(true), + } + + mux.HandleFunc("/orgs/o/actions/permissions/fork-pr-workflows-private-repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdatePrivateRepoForkPRWorkflowSettingsInOrganization(ctx, "o", input) + if err != nil { + t.Errorf("Actions.UpdatePrivateRepoForkPRWorkflowSettingsInOrganization returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdatePrivateRepoForkPRWorkflowSettingsInOrganization = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdatePrivateRepoForkPRWorkflowSettingsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdatePrivateRepoForkPRWorkflowSettingsInOrganization(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdatePrivateRepoForkPRWorkflowSettingsInOrganization(ctx, "o", input) + }) +} + +func TestActionsService_GetOrganizationForkPRContributorApprovalPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/fork-pr-contributor-approval", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"approval_policy": "require_approval"}`) + }) + + ctx := t.Context() + policy, _, err := client.Actions.GetOrganizationForkPRContributorApprovalPermissions(ctx, "o") + if err != nil { + t.Errorf("Actions.GetOrganizationForkPRContributorApprovalPermissions returned error: %v", err) + } + want := &ContributorApprovalPermissions{ApprovalPolicy: "require_approval"} + if !cmp.Equal(policy, want) { + t.Errorf("Actions.GetOrganizationForkPRContributorApprovalPermissions returned %+v, want %+v", policy, want) + } + + const methodName = "GetOrganizationForkPRContributorApprovalPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrganizationForkPRContributorApprovalPermissions(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrganizationForkPRContributorApprovalPermissions(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateOrganizationForkPRContributorApprovalPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ContributorApprovalPermissions{ApprovalPolicy: "require_approval"} + + mux.HandleFunc("/orgs/o/actions/permissions/fork-pr-contributor-approval", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateOrganizationForkPRContributorApprovalPermissions(ctx, "o", input) + if err != nil { + t.Errorf("Actions.UpdateOrganizationForkPRContributorApprovalPermissions returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateOrganizationForkPRContributorApprovalPermissions = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateOrganizationForkPRContributorApprovalPermissions" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateOrganizationForkPRContributorApprovalPermissions(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateOrganizationForkPRContributorApprovalPermissions(ctx, "o", input) + }) +} diff --git a/github/actions_runner_groups.go b/github/actions_runner_groups.go new file mode 100644 index 00000000000..40da3fd5a0b --- /dev/null +++ b/github/actions_runner_groups.go @@ -0,0 +1,368 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RunnerGroup represents a self-hosted runner group configured in an organization. +type RunnerGroup struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Visibility *string `json:"visibility,omitempty"` + Default *bool `json:"default,omitempty"` + SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` + RunnersURL *string `json:"runners_url,omitempty"` + HostedRunnersURL *string `json:"hosted_runners_url,omitempty"` + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` + Inherited *bool `json:"inherited,omitempty"` + AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` + RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"` + SelectedWorkflows []string `json:"selected_workflows,omitempty"` + WorkflowRestrictionsReadOnly *bool `json:"workflow_restrictions_read_only,omitempty"` +} + +// RunnerGroups represents a collection of self-hosted runner groups configured for an organization. +type RunnerGroups struct { + TotalCount int `json:"total_count"` + RunnerGroups []*RunnerGroup `json:"runner_groups"` +} + +// CreateRunnerGroupRequest represents a request to create a Runner group for an organization. +type CreateRunnerGroupRequest struct { + Name *string `json:"name,omitempty"` + Visibility *string `json:"visibility,omitempty"` + // List of repository IDs that can access the runner group. + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` + // Runners represent a list of runner IDs to add to the runner group. + Runners []int64 `json:"runners,omitempty"` + // If set to True, public repos can use this runner group + AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` + // If true, the runner group will be restricted to running only the workflows specified in the SelectedWorkflows slice. + RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"` + // List of workflows the runner group should be allowed to run. This setting will be ignored unless RestrictedToWorkflows is set to true. + SelectedWorkflows []string `json:"selected_workflows,omitempty"` + // The identifier of a hosted compute network configuration. + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` +} + +// UpdateRunnerGroupRequest represents a request to update a Runner group for an organization. +type UpdateRunnerGroupRequest struct { + Name *string `json:"name,omitempty"` + Visibility *string `json:"visibility,omitempty"` + AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` + RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"` + SelectedWorkflows []string `json:"selected_workflows,omitempty"` + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` +} + +// SetRepoAccessRunnerGroupRequest represents a request to replace the list of repositories +// that can access a self-hosted runner group configured in an organization. +type SetRepoAccessRunnerGroupRequest struct { + // Updated list of repository IDs that should be given access to the runner group. + SelectedRepositoryIDs []int64 `json:"selected_repository_ids"` +} + +// SetRunnerGroupRunnersRequest represents a request to replace the list of +// self-hosted runners that are part of an organization runner group. +type SetRunnerGroupRunnersRequest struct { + // Updated list of runner IDs that should be given access to the runner group. + Runners []int64 `json:"runners"` +} + +// ListOrgRunnerGroupOptions extend ListOptions to have the optional parameters VisibleToRepository. +type ListOrgRunnerGroupOptions struct { + ListOptions + + // Only return runner groups that are allowed to be used by this repository. + VisibleToRepository string `url:"visible_to_repository,omitempty"` +} + +// ListOrganizationRunnerGroups lists all self-hosted runner groups configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-self-hosted-runner-groups-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runner-groups +func (s *ActionsService) ListOrganizationRunnerGroups(ctx context.Context, org string, opts *ListOrgRunnerGroupOptions) (*RunnerGroups, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var groups *RunnerGroups + resp, err := s.client.Do(req, &groups) + if err != nil { + return nil, resp, err + } + + return groups, resp, nil +} + +// GetOrganizationRunnerGroup gets a specific self-hosted runner group for an organization using its RunnerGroup ID. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#get-a-self-hosted-runner-group-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runner-groups/{runner_group_id} +func (s *ActionsService) GetOrganizationRunnerGroup(ctx context.Context, org string, groupID int64) (*RunnerGroup, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v", org, groupID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runnerGroup *RunnerGroup + resp, err := s.client.Do(req, &runnerGroup) + if err != nil { + return nil, resp, err + } + + return runnerGroup, resp, nil +} + +// DeleteOrganizationRunnerGroup deletes a self-hosted runner group from an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#delete-a-self-hosted-runner-group-from-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/runner-groups/{runner_group_id} +func (s *ActionsService) DeleteOrganizationRunnerGroup(ctx context.Context, org string, groupID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v", org, groupID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateOrganizationRunnerGroup creates a new self-hosted runner group for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#create-a-self-hosted-runner-group-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/runner-groups +func (s *ActionsService) CreateOrganizationRunnerGroup(ctx context.Context, org string, body CreateRunnerGroupRequest) (*RunnerGroup, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var runnerGroup *RunnerGroup + resp, err := s.client.Do(req, &runnerGroup) + if err != nil { + return nil, resp, err + } + + return runnerGroup, resp, nil +} + +// UpdateOrganizationRunnerGroup updates a self-hosted runner group for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#update-a-self-hosted-runner-group-for-an-organization +// +//meta:operation PATCH /orgs/{org}/actions/runner-groups/{runner_group_id} +func (s *ActionsService) UpdateOrganizationRunnerGroup(ctx context.Context, org string, groupID int64, body UpdateRunnerGroupRequest) (*RunnerGroup, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v", org, groupID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var runnerGroup *RunnerGroup + resp, err := s.client.Do(req, &runnerGroup) + if err != nil { + return nil, resp, err + } + + return runnerGroup, resp, nil +} + +// ListRepositoryAccessRunnerGroup lists the repositories with access to a self-hosted runner group configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-repository-access-to-a-self-hosted-runner-group-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories +func (s *ActionsService) ListRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID int64, opts *ListOptions) (*ListRepositories, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/repositories", org, groupID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repos *ListRepositories + resp, err := s.client.Do(req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// SetRepositoryAccessRunnerGroup replaces the list of repositories that have access to a self-hosted runner group configured in an organization +// with a new List of repositories. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#set-repository-access-for-a-self-hosted-runner-group-in-an-organization +// +//meta:operation PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories +func (s *ActionsService) SetRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID int64, body SetRepoAccessRunnerGroupRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/repositories", org, groupID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddRepositoryAccessRunnerGroup adds a repository to the list of selected repositories that can access a self-hosted runner group. +// The runner group must have visibility set to 'selected'. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#add-repository-access-to-a-self-hosted-runner-group-in-an-organization +// +//meta:operation PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories/{repository_id} +func (s *ActionsService) AddRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/repositories/%v", org, groupID, repoID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveRepositoryAccessRunnerGroup removes a repository from the list of selected repositories that can access a self-hosted runner group. +// The runner group must have visibility set to 'selected'. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#remove-repository-access-to-a-self-hosted-runner-group-in-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories/{repository_id} +func (s *ActionsService) RemoveRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/repositories/%v", org, groupID, repoID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListRunnerGroupHostedRunners lists the GitHub-hosted runners in an organization runner group. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-github-hosted-runners-in-a-group-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runner-groups/{runner_group_id}/hosted-runners +func (s *ActionsService) ListRunnerGroupHostedRunners(ctx context.Context, org string, groupID int64, opts *ListOptions) (*HostedRunners, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/hosted-runners", org, groupID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *HostedRunners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// ListRunnerGroupRunners lists self-hosted runners that are in a specific organization group. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-self-hosted-runners-in-a-group-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners +func (s *ActionsService) ListRunnerGroupRunners(ctx context.Context, org string, groupID int64, opts *ListOptions) (*Runners, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/runners", org, groupID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *Runners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// SetRunnerGroupRunners replaces the list of self-hosted runners that are part of an organization runner group +// with a new list of runners. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#set-self-hosted-runners-in-a-group-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/runners +func (s *ActionsService) SetRunnerGroupRunners(ctx context.Context, org string, groupID int64, body SetRunnerGroupRunnersRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/runners", org, groupID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddRunnerGroupRunners adds a self-hosted runner to a runner group configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#add-a-self-hosted-runner-to-a-group-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/runners/{runner_id} +func (s *ActionsService) AddRunnerGroupRunners(ctx context.Context, org string, groupID, runnerID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/runners/%v", org, groupID, runnerID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveRunnerGroupRunners removes a self-hosted runner from a group configured in an organization. +// The runner is then returned to the default group. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#remove-a-self-hosted-runner-from-a-group-for-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/runner-groups/{runner_group_id}/runners/{runner_id} +func (s *ActionsService) RemoveRunnerGroupRunners(ctx context.Context, org string, groupID, runnerID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/runners/%v", org, groupID, runnerID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_runner_groups_test.go b/github/actions_runner_groups_test.go new file mode 100644 index 00000000000..51578b1ee20 --- /dev/null +++ b/github/actions_runner_groups_test.go @@ -0,0 +1,680 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListOrganizationRunnerGroups(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"runner_groups":[{"id":1,"name":"Default","visibility":"all","default":true,"runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/1/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":true,"selected_workflows":["a","b"]},{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories","runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners","inherited":true,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]},{"id":3,"name":"expensive-hardware","visibility":"private","default":false,"runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/3/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}]}`) + }) + + opts := &ListOrgRunnerGroupOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + groups, _, err := client.Actions.ListOrganizationRunnerGroups(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListOrganizationRunnerGroups returned error: %v", err) + } + + want := &RunnerGroups{ + TotalCount: 3, + RunnerGroups: []*RunnerGroup{ + {ID: Ptr(int64(1)), Name: Ptr("Default"), Visibility: Ptr("all"), Default: Ptr(true), RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/1/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(true), SelectedWorkflows: []string{"a", "b"}}, + {ID: Ptr(int64(2)), Name: Ptr("octo-runner-group"), Visibility: Ptr("selected"), Default: Ptr(false), SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories"), RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners"), Inherited: Ptr(true), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + {ID: Ptr(int64(3)), Name: Ptr("expensive-hardware"), Visibility: Ptr("private"), Default: Ptr(false), RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/3/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Actions.ListOrganizationRunnerGroups returned %+v, want %+v", groups, want) + } + + const methodName = "ListOrganizationRunnerGroups" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrganizationRunnerGroups(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrganizationRunnerGroups(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListOrganizationRunnerGroupsVisibleToRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2", "visible_to_repository": "github"}) + fmt.Fprint(w, `{"total_count":3,"runner_groups":[{"id":1,"name":"Default","visibility":"all","default":true,"runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/1/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]},{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories","runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners","inherited":true,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]},{"id":3,"name":"expensive-hardware","visibility":"private","default":false,"runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/3/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}]}`) + }) + + opts := &ListOrgRunnerGroupOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}, VisibleToRepository: "github"} + ctx := t.Context() + groups, _, err := client.Actions.ListOrganizationRunnerGroups(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListOrganizationRunnerGroups returned error: %v", err) + } + + want := &RunnerGroups{ + TotalCount: 3, + RunnerGroups: []*RunnerGroup{ + {ID: Ptr(int64(1)), Name: Ptr("Default"), Visibility: Ptr("all"), Default: Ptr(true), RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/1/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + {ID: Ptr(int64(2)), Name: Ptr("octo-runner-group"), Visibility: Ptr("selected"), Default: Ptr(false), SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories"), RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners"), Inherited: Ptr(true), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + {ID: Ptr(int64(3)), Name: Ptr("expensive-hardware"), Visibility: Ptr("private"), Default: Ptr(false), RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/3/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Actions.ListOrganizationRunnerGroups returned %+v, want %+v", groups, want) + } + + const methodName = "ListOrganizationRunnerGroups" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrganizationRunnerGroups(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrganizationRunnerGroups(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetOrganizationRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories","runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners","hosted_runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/hosted-runners","network_configuration_id":"EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}`) + }) + + ctx := t.Context() + group, _, err := client.Actions.GetOrganizationRunnerGroup(ctx, "o", 2) + if err != nil { + t.Errorf("Actions.GetOrganizationRunnerGroup returned error: %v", err) + } + + want := &RunnerGroup{ + ID: Ptr(int64(2)), + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + Default: Ptr(false), + SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories"), + RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners"), + HostedRunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/hosted-runners"), + NetworkConfigurationID: Ptr("EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA"), + Inherited: Ptr(false), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + + if !cmp.Equal(group, want) { + t.Errorf("Actions.GetOrganizationRunnerGroup returned %+v, want %+v", group, want) + } + + const methodName = "GetOrganizationRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrganizationRunnerGroup(ctx, "\n", 2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrganizationRunnerGroup(ctx, "o", 2) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_DeleteOrganizationRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteOrganizationRunnerGroup(ctx, "o", 2) + if err != nil { + t.Errorf("Actions.DeleteOrganizationRunnerGroup returned error: %v", err) + } + + const methodName = "DeleteOrganizationRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteOrganizationRunnerGroup(ctx, "\n", 2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteOrganizationRunnerGroup(ctx, "o", 2) + }) +} + +func TestActionsService_CreateOrganizationRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories","runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners","hosted_runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/hosted-runners","network_configuration_id":"EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}`) + }) + + ctx := t.Context() + req := CreateRunnerGroupRequest{ + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + NetworkConfigurationID: Ptr("EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA"), + } + group, _, err := client.Actions.CreateOrganizationRunnerGroup(ctx, "o", req) + if err != nil { + t.Errorf("Actions.CreateOrganizationRunnerGroup returned error: %v", err) + } + + want := &RunnerGroup{ + ID: Ptr(int64(2)), + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + Default: Ptr(false), + SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories"), + RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners"), + HostedRunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/hosted-runners"), + NetworkConfigurationID: Ptr("EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA"), + Inherited: Ptr(false), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + + if !cmp.Equal(group, want) { + t.Errorf("Actions.CreateOrganizationRunnerGroup returned %+v, want %+v", group, want) + } + + const methodName = "CreateOrganizationRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateOrganizationRunnerGroup(ctx, "\n", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateOrganizationRunnerGroup(ctx, "o", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateOrganizationRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories","runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners","hosted_runners_url":"https://api.github.com/orgs/octo-org/actions/runner_groups/2/hosted-runners","network_configuration_id":"EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}`) + }) + + ctx := t.Context() + req := UpdateRunnerGroupRequest{ + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + NetworkConfigurationID: Ptr("EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA"), + } + group, _, err := client.Actions.UpdateOrganizationRunnerGroup(ctx, "o", 2, req) + if err != nil { + t.Errorf("Actions.UpdateOrganizationRunnerGroup returned error: %v", err) + } + + want := &RunnerGroup{ + ID: Ptr(int64(2)), + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + Default: Ptr(false), + SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/repositories"), + RunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/runners"), + HostedRunnersURL: Ptr("https://api.github.com/orgs/octo-org/actions/runner_groups/2/hosted-runners"), + NetworkConfigurationID: Ptr("EC486D5D793175D7E3B29C27318D5C1AAE49A7833FC85F2E82C3D2C54AC7D3BA"), + Inherited: Ptr(false), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + + if !cmp.Equal(group, want) { + t.Errorf("Actions.UpdateOrganizationRunnerGroup returned %+v, want %+v", group, want) + } + + const methodName = "UpdateOrganizationRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.UpdateOrganizationRunnerGroup(ctx, "\n", 2, req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.UpdateOrganizationRunnerGroup(ctx, "o", 2, req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListRepositoryAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "1", "page": "1"}) + fmt.Fprint(w, `{"total_count": 1, "repositories": [{"id": 43, "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", "name": "Hello-World", "full_name": "octocat/Hello-World"}]}`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 1} + groups, _, err := client.Actions.ListRepositoryAccessRunnerGroup(ctx, "o", 2, opts) + if err != nil { + t.Errorf("Actions.ListRepositoryAccessRunnerGroup returned error: %v", err) + } + + want := &ListRepositories{ + TotalCount: Ptr(1), + Repositories: []*Repository{ + {ID: Ptr(int64(43)), NodeID: Ptr("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"), Name: Ptr("Hello-World"), FullName: Ptr("octocat/Hello-World")}, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Actions.ListRepositoryAccessRunnerGroup returned %+v, want %+v", groups, want) + } + + const methodName = "ListRepositoryAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepositoryAccessRunnerGroup(ctx, "\n", 2, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepositoryAccessRunnerGroup(ctx, "o", 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetRepositoryAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + req := SetRepoAccessRunnerGroupRequest{ + SelectedRepositoryIDs: []int64{ + 1, + 2, + }, + } + + ctx := t.Context() + _, err := client.Actions.SetRepositoryAccessRunnerGroup(ctx, "o", 2, req) + if err != nil { + t.Errorf("Actions.SetRepositoryAccessRunnerGroup returned error: %v", err) + } + + const methodName = "SetRepositoryAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRepositoryAccessRunnerGroup(ctx, "\n", 2, req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRepositoryAccessRunnerGroup(ctx, "o", 2, req) + }) +} + +func TestActionsService_AddRepositoryAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/repositories/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + ctx := t.Context() + _, err := client.Actions.AddRepositoryAccessRunnerGroup(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Actions.AddRepositoryAccessRunnerGroup returned error: %v", err) + } + + const methodName = "AddRepositoryAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddRepositoryAccessRunnerGroup(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddRepositoryAccessRunnerGroup(ctx, "o", 2, 42) + }) +} + +func TestActionsService_RemoveRepositoryAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/repositories/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.RemoveRepositoryAccessRunnerGroup(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Actions.RemoveRepositoryAccessRunnerGroup returned error: %v", err) + } + + const methodName = "RemoveRepositoryAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveRepositoryAccessRunnerGroup(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveRepositoryAccessRunnerGroup(ctx, "o", 2, 42) + }) +} + +func TestActionsService_ListRunnerGroupHostedRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{ + "total_count": 2, + "runners": [ + { + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }, + { + "id": 7, + "name": "My hosted Windows runner", + "runner_group_id": 2, + "platform": "win-x64", + "image_details": { + "id": "windows-latest", + "size_gb": 256 + }, + "machine_size_details": { + "id": "8-core", + "cpu_cores": 8, + "memory_gb": 32, + "storage_gb": 300 + }, + "status": "Ready", + "maximum_runners": 20, + "public_ip_enabled": false, + "public_ips": [], + "last_active_on": `+referenceTimeStr+` + } + ] + }`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + runners, _, err := client.Actions.ListRunnerGroupHostedRunners(ctx, "o", 2, opts) + if err != nil { + t.Errorf("Actions.ListRunnerGroupHostedRunners returned error: %v", err) + } + + want := &HostedRunners{ + TotalCount: 2, + Runners: []*HostedRunner{ + { + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + }, + { + ID: Ptr(int64(7)), + Name: Ptr("My hosted Windows runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("win-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("windows-latest"), + SizeGB: Ptr(int64(256)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "8-core", + CPUCores: 8, + MemoryGB: 32, + StorageGB: 300, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(20)), + PublicIPEnabled: Ptr(false), + PublicIPs: []*HostedRunnerPublicIP{}, + LastActiveOn: &referenceTimestamp, + }, + }, + } + if !cmp.Equal(runners, want) { + t.Errorf("Actions.ListRunnerGroupHostedRunners returned %+v, want %+v", runners, want) + } + + const methodName = "ListRunnerGroupHostedRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRunnerGroupHostedRunners(ctx, "\n", 2, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRunnerGroupHostedRunners(ctx, "o", 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":2,"runners":[{"id":23,"name":"MBP","os":"macos","status":"online"},{"id":24,"name":"iMac","os":"macos","status":"offline"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + runners, _, err := client.Actions.ListRunnerGroupRunners(ctx, "o", 2, opts) + if err != nil { + t.Errorf("Actions.ListRunnerGroupRunners returned error: %v", err) + } + + want := &Runners{ + TotalCount: 2, + Runners: []*Runner{ + {ID: Ptr(int64(23)), Name: Ptr("MBP"), OS: Ptr("macos"), Status: Ptr("online")}, + {ID: Ptr(int64(24)), Name: Ptr("iMac"), OS: Ptr("macos"), Status: Ptr("offline")}, + }, + } + if !cmp.Equal(runners, want) { + t.Errorf("Actions.ListRunnerGroupRunners returned %+v, want %+v", runners, want) + } + + const methodName = "ListRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRunnerGroupRunners(ctx, "\n", 2, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRunnerGroupRunners(ctx, "o", 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/runners", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + req := SetRunnerGroupRunnersRequest{ + Runners: []int64{ + 1, + 2, + }, + } + + ctx := t.Context() + _, err := client.Actions.SetRunnerGroupRunners(ctx, "o", 2, req) + if err != nil { + t.Errorf("Actions.SetRunnerGroupRunners returned error: %v", err) + } + + const methodName = "SetRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetRunnerGroupRunners(ctx, "\n", 2, req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetRunnerGroupRunners(ctx, "o", 2, req) + }) +} + +func TestActionsService_AddRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/runners/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + ctx := t.Context() + _, err := client.Actions.AddRunnerGroupRunners(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Actions.AddRunnerGroupRunners returned error: %v", err) + } + + const methodName = "AddRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddRunnerGroupRunners(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddRunnerGroupRunners(ctx, "o", 2, 42) + }) +} + +func TestActionsService_RemoveRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runner-groups/2/runners/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.RemoveRunnerGroupRunners(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Actions.RemoveRunnerGroupRunners returned error: %v", err) + } + + const methodName = "RemoveRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveRunnerGroupRunners(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveRunnerGroupRunners(ctx, "o", 2, 42) + }) +} diff --git a/github/actions_runners.go b/github/actions_runners.go new file mode 100644 index 00000000000..6113adce48e --- /dev/null +++ b/github/actions_runners.go @@ -0,0 +1,380 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RunnerApplicationDownload represents a binary for the self-hosted runner application that can be downloaded. +type RunnerApplicationDownload struct { + OS *string `json:"os,omitempty"` + Architecture *string `json:"architecture,omitempty"` + DownloadURL *string `json:"download_url,omitempty"` + Filename *string `json:"filename,omitempty"` + TempDownloadToken *string `json:"temp_download_token,omitempty"` + SHA256Checksum *string `json:"sha256_checksum,omitempty"` +} + +// ListRunnerApplicationDownloads lists self-hosted runner application binaries that can be downloaded and run. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-runner-applications-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/runners/downloads +func (s *ActionsService) ListRunnerApplicationDownloads(ctx context.Context, owner, repo string) ([]*RunnerApplicationDownload, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/downloads", owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rads []*RunnerApplicationDownload + resp, err := s.client.Do(req, &rads) + if err != nil { + return nil, resp, err + } + + return rads, resp, nil +} + +// CreateJITConfigRequest specifies body parameters to CreateOrgJITConfig, CreateRepoJITConfig, and CreateJITConfig. +type CreateJITConfigRequest struct { + Name string `json:"name"` + RunnerGroupID int64 `json:"runner_group_id"` + WorkFolder *string `json:"work_folder,omitempty"` + + // Labels represents the names of the custom labels to add to the runner. + // Minimum items: 1. Maximum items: 100. + Labels []string `json:"labels"` +} + +// JITRunnerConfig represents encoded JIT configuration that can be used to bootstrap a self-hosted runner. +type JITRunnerConfig struct { + Runner *Runner `json:"runner,omitempty"` + EncodedJITConfig *string `json:"encoded_jit_config,omitempty"` +} + +// CreateOrgJITConfig creates a just-in-time configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-configuration-for-a-just-in-time-runner-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/runners/generate-jitconfig +func (s *ActionsService) CreateOrgJITConfig(ctx context.Context, org string, body CreateJITConfigRequest) (*JITRunnerConfig, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/generate-jitconfig", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var jitConfig *JITRunnerConfig + resp, err := s.client.Do(req, &jitConfig) + if err != nil { + return nil, resp, err + } + + return jitConfig, resp, nil +} + +// CreateRepoJITConfig creates a just-in-time configuration for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-configuration-for-a-just-in-time-runner-for-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/actions/runners/generate-jitconfig +func (s *ActionsService) CreateRepoJITConfig(ctx context.Context, owner, repo string, body CreateJITConfigRequest) (*JITRunnerConfig, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/generate-jitconfig", owner, repo) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var jitConfig *JITRunnerConfig + resp, err := s.client.Do(req, &jitConfig) + if err != nil { + return nil, resp, err + } + + return jitConfig, resp, nil +} + +// RegistrationToken represents a token that can be used to add a self-hosted runner to a repository. +type RegistrationToken struct { + Token *string `json:"token,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` +} + +// CreateRegistrationToken creates a token that can be used to add a self-hosted runner. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/actions/runners/registration-token +func (s *ActionsService) CreateRegistrationToken(ctx context.Context, owner, repo string) (*RegistrationToken, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/registration-token", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var registrationToken *RegistrationToken + resp, err := s.client.Do(req, ®istrationToken) + if err != nil { + return nil, resp, err + } + + return registrationToken, resp, nil +} + +// Runner represents a self-hosted runner registered with a repository. +type Runner struct { + ID *int64 `json:"id,omitempty"` + RunnerGroupID *int64 `json:"runner_group_id,omitempty"` + Name *string `json:"name,omitempty"` + OS *string `json:"os,omitempty"` + Status *string `json:"status,omitempty"` + Busy *bool `json:"busy,omitempty"` + Labels []*RunnerLabels `json:"labels,omitempty"` + Ephemeral *bool `json:"ephemeral,omitempty"` + Version *string `json:"version,omitempty"` +} + +// RunnerLabels represents a collection of labels attached to each runner. +type RunnerLabels struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` +} + +// Runners represents a collection of self-hosted runners for a repository. +type Runners struct { + TotalCount int `json:"total_count"` + Runners []*Runner `json:"runners"` +} + +// ListRunnersOptions specifies the optional parameters to the ListRunners and ListOrganizationRunners methods. +type ListRunnersOptions struct { + Name *string `url:"name,omitempty"` + ListOptions +} + +// ListRunners lists all the self-hosted runners for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/runners +func (s *ActionsService) ListRunners(ctx context.Context, owner, repo string, opts *ListRunnersOptions) (*Runners, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *Runners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// GetRunner gets a specific self-hosted runner for a repository using its runner ID. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#get-a-self-hosted-runner-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/runners/{runner_id} +func (s *ActionsService) GetRunner(ctx context.Context, owner, repo string, runnerID int64) (*Runner, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/%v", owner, repo, runnerID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runner *Runner + resp, err := s.client.Do(req, &runner) + if err != nil { + return nil, resp, err + } + + return runner, resp, nil +} + +// RemoveToken represents a token that can be used to remove a self-hosted runner from a repository. +type RemoveToken struct { + Token *string `json:"token,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` +} + +// CreateRemoveToken creates a token that can be used to remove a self-hosted runner from a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-remove-token-for-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/actions/runners/remove-token +func (s *ActionsService) CreateRemoveToken(ctx context.Context, owner, repo string) (*RemoveToken, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/remove-token", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var removeToken *RemoveToken + resp, err := s.client.Do(req, &removeToken) + if err != nil { + return nil, resp, err + } + + return removeToken, resp, nil +} + +// RemoveRunner forces the removal of a self-hosted runner in a repository using the runner id. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#delete-a-self-hosted-runner-from-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/runners/{runner_id} +func (s *ActionsService) RemoveRunner(ctx context.Context, owner, repo string, runnerID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runners/%v", owner, repo, runnerID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListOrganizationRunnerApplicationDownloads lists self-hosted runner application binaries that can be downloaded and run. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-runner-applications-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runners/downloads +func (s *ActionsService) ListOrganizationRunnerApplicationDownloads(ctx context.Context, org string) ([]*RunnerApplicationDownload, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/downloads", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rads []*RunnerApplicationDownload + resp, err := s.client.Do(req, &rads) + if err != nil { + return nil, resp, err + } + + return rads, resp, nil +} + +// CreateOrganizationRegistrationToken creates a token that can be used to add a self-hosted runner to an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/runners/registration-token +func (s *ActionsService) CreateOrganizationRegistrationToken(ctx context.Context, org string) (*RegistrationToken, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/registration-token", org) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var registrationToken *RegistrationToken + resp, err := s.client.Do(req, ®istrationToken) + if err != nil { + return nil, resp, err + } + + return registrationToken, resp, nil +} + +// ListOrganizationRunners lists all the self-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runners +func (s *ActionsService) ListOrganizationRunners(ctx context.Context, org string, opts *ListRunnersOptions) (*Runners, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *Runners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// GetOrganizationRunner gets a specific self-hosted runner for an organization using its runner ID. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#get-a-self-hosted-runner-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/runners/{runner_id} +func (s *ActionsService) GetOrganizationRunner(ctx context.Context, org string, runnerID int64) (*Runner, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/%v", org, runnerID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runner *Runner + resp, err := s.client.Do(req, &runner) + if err != nil { + return nil, resp, err + } + + return runner, resp, nil +} + +// CreateOrganizationRemoveToken creates a token that can be used to remove a self-hosted runner from an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-remove-token-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/runners/remove-token +func (s *ActionsService) CreateOrganizationRemoveToken(ctx context.Context, org string) (*RemoveToken, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/remove-token", org) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var removeToken *RemoveToken + resp, err := s.client.Do(req, &removeToken) + if err != nil { + return nil, resp, err + } + + return removeToken, resp, nil +} + +// RemoveOrganizationRunner forces the removal of a self-hosted runner from an organization using the runner id. +// +// GitHub API docs: https://docs.github.com/rest/actions/self-hosted-runners?apiVersion=2022-11-28#delete-a-self-hosted-runner-from-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/runners/{runner_id} +func (s *ActionsService) RemoveOrganizationRunner(ctx context.Context, org string, runnerID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/runners/%v", org, runnerID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_runners_test.go b/github/actions_runners_test.go new file mode 100644 index 00000000000..c29bab5c096 --- /dev/null +++ b/github/actions_runners_test.go @@ -0,0 +1,538 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListRunnerApplicationDownloads(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runners/downloads", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"os":"osx","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-osx-x64-2.164.0.tar.gz","filename":"actions-runner-osx-x64-2.164.0.tar.gz"},{"os":"linux","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz","filename":"actions-runner-linux-x64-2.164.0.tar.gz"},{"os": "linux","architecture":"arm","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm-2.164.0.tar.gz","filename":"actions-runner-linux-arm-2.164.0.tar.gz"},{"os":"win","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-win-x64-2.164.0.zip","filename":"actions-runner-win-x64-2.164.0.zip"},{"os":"linux","architecture":"arm64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm64-2.164.0.tar.gz","filename":"actions-runner-linux-arm64-2.164.0.tar.gz"}]`) + }) + + ctx := t.Context() + downloads, _, err := client.Actions.ListRunnerApplicationDownloads(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.ListRunnerApplicationDownloads returned error: %v", err) + } + + want := []*RunnerApplicationDownload{ + {OS: Ptr("osx"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-osx-x64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-osx-x64-2.164.0.tar.gz")}, + {OS: Ptr("linux"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-x64-2.164.0.tar.gz")}, + {OS: Ptr("linux"), Architecture: Ptr("arm"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-arm-2.164.0.tar.gz")}, + {OS: Ptr("win"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-win-x64-2.164.0.zip"), Filename: Ptr("actions-runner-win-x64-2.164.0.zip")}, + {OS: Ptr("linux"), Architecture: Ptr("arm64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-arm64-2.164.0.tar.gz")}, + } + if !cmp.Equal(downloads, want) { + t.Errorf("Actions.ListRunnerApplicationDownloads returned %+v, want %+v", downloads, want) + } + + const methodName = "ListRunnerApplicationDownloads" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRunnerApplicationDownloads(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRunnerApplicationDownloads(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrgJITConfig(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateJITConfigRequest{Name: "test", RunnerGroupID: 1, Labels: []string{"one", "two"}} + + mux.HandleFunc("/orgs/o/actions/runners/generate-jitconfig", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"encoded_jit_config":"foo"}`) + }) + + ctx := t.Context() + jitConfig, _, err := client.Actions.CreateOrgJITConfig(ctx, "o", input) + if err != nil { + t.Errorf("Actions.CreateOrgJITConfig returned error: %v", err) + } + + want := &JITRunnerConfig{EncodedJITConfig: Ptr("foo")} + if !cmp.Equal(jitConfig, want) { + t.Errorf("Actions.CreateOrgJITConfig returned %+v, want %+v", jitConfig, want) + } + + const methodName = "CreateOrgJITConfig" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateOrgJITConfig(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateOrgJITConfig(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateRepoJITConfig(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateJITConfigRequest{Name: "test", RunnerGroupID: 1, Labels: []string{"one", "two"}} + + mux.HandleFunc("/repos/o/r/actions/runners/generate-jitconfig", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"encoded_jit_config":"foo"}`) + }) + + ctx := t.Context() + jitConfig, _, err := client.Actions.CreateRepoJITConfig(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.CreateRepoJITConfig returned error: %v", err) + } + + want := &JITRunnerConfig{EncodedJITConfig: Ptr("foo")} + if !cmp.Equal(jitConfig, want) { + t.Errorf("Actions.CreateRepoJITConfig returned %+v, want %+v", jitConfig, want) + } + + const methodName = "CreateRepoJITConfig" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateRepoJITConfig(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateRepoJITConfig(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateRegistrationToken(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runners/registration-token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"token":"LLBF3JGZDX3P5PMEXLND6TS6FCWO6","expires_at":`+referenceTimeStr+`}`) + }) + + ctx := t.Context() + token, _, err := client.Actions.CreateRegistrationToken(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.CreateRegistrationToken returned error: %v", err) + } + + want := &RegistrationToken{ + Token: Ptr("LLBF3JGZDX3P5PMEXLND6TS6FCWO6"), + ExpiresAt: &referenceTimestamp, + } + if !cmp.Equal(token, want) { + t.Errorf("Actions.CreateRegistrationToken returned %+v, want %+v", token, want) + } + + const methodName = "CreateRegistrationToken" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateRegistrationToken(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateRegistrationToken(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"name": "MBP", "per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":1,"runners":[{"id":23,"name":"MBP","os":"macos","status":"online"}]}`) + }) + + opts := &ListRunnersOptions{ + Name: Ptr("MBP"), + ListOptions: ListOptions{Page: 2, PerPage: 2}, + } + ctx := t.Context() + runners, _, err := client.Actions.ListRunners(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRunners returned error: %v", err) + } + + want := &Runners{ + TotalCount: 1, + Runners: []*Runner{ + {ID: Ptr(int64(23)), Name: Ptr("MBP"), OS: Ptr("macos"), Status: Ptr("online")}, + }, + } + if !cmp.Equal(runners, want) { + t.Errorf("Actions.ListRunners returned %+v, want %+v", runners, want) + } + + const methodName = "ListRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRunners(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRunners(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":23,"name":"MBP","os":"macos","status":"online"}`) + }) + + ctx := t.Context() + runner, _, err := client.Actions.GetRunner(ctx, "o", "r", 23) + if err != nil { + t.Errorf("Actions.GetRunner returned error: %v", err) + } + + want := &Runner{ + ID: Ptr(int64(23)), + Name: Ptr("MBP"), + OS: Ptr("macos"), + Status: Ptr("online"), + } + if !cmp.Equal(runner, want) { + t.Errorf("Actions.GetRunner returned %+v, want %+v", runner, want) + } + + const methodName = "GetRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRunner(ctx, "\n", "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRunner(ctx, "o", "r", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateRemoveToken(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runners/remove-token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"token":"AABF3JGZDX3P5PMEXLND6TS6FCWO6","expires_at":`+referenceTimeStr+`}`) + }) + + ctx := t.Context() + token, _, err := client.Actions.CreateRemoveToken(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.CreateRemoveToken returned error: %v", err) + } + + want := &RemoveToken{Token: Ptr("AABF3JGZDX3P5PMEXLND6TS6FCWO6"), ExpiresAt: &referenceTimestamp} + if !cmp.Equal(token, want) { + t.Errorf("Actions.CreateRemoveToken returned %+v, want %+v", token, want) + } + + const methodName = "CreateRemoveToken" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateRemoveToken(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateRemoveToken(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_RemoveRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runners/21", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.RemoveRunner(ctx, "o", "r", 21) + if err != nil { + t.Errorf("Actions.RemoveRunner returned error: %v", err) + } + + const methodName = "RemoveRunner" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveRunner(ctx, "\n", "\n", 21) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveRunner(ctx, "o", "r", 21) + }) +} + +func TestActionsService_ListOrganizationRunnerApplicationDownloads(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runners/downloads", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"os":"osx","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-osx-x64-2.164.0.tar.gz","filename":"actions-runner-osx-x64-2.164.0.tar.gz"},{"os":"linux","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz","filename":"actions-runner-linux-x64-2.164.0.tar.gz"},{"os": "linux","architecture":"arm","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm-2.164.0.tar.gz","filename":"actions-runner-linux-arm-2.164.0.tar.gz"},{"os":"win","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-win-x64-2.164.0.zip","filename":"actions-runner-win-x64-2.164.0.zip"},{"os":"linux","architecture":"arm64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm64-2.164.0.tar.gz","filename":"actions-runner-linux-arm64-2.164.0.tar.gz"}]`) + }) + + ctx := t.Context() + downloads, _, err := client.Actions.ListOrganizationRunnerApplicationDownloads(ctx, "o") + if err != nil { + t.Errorf("Actions.ListOrganizationRunnerApplicationDownloads returned error: %v", err) + } + + want := []*RunnerApplicationDownload{ + {OS: Ptr("osx"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-osx-x64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-osx-x64-2.164.0.tar.gz")}, + {OS: Ptr("linux"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-x64-2.164.0.tar.gz")}, + {OS: Ptr("linux"), Architecture: Ptr("arm"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-arm-2.164.0.tar.gz")}, + {OS: Ptr("win"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-win-x64-2.164.0.zip"), Filename: Ptr("actions-runner-win-x64-2.164.0.zip")}, + {OS: Ptr("linux"), Architecture: Ptr("arm64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-arm64-2.164.0.tar.gz")}, + } + if !cmp.Equal(downloads, want) { + t.Errorf("Actions.ListOrganizationRunnerApplicationDownloads returned %+v, want %+v", downloads, want) + } + + const methodName = "ListOrganizationRunnerApplicationDownloads" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrganizationRunnerApplicationDownloads(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrganizationRunnerApplicationDownloads(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrganizationRegistrationToken(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runners/registration-token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"token":"LLBF3JGZDX3P5PMEXLND6TS6FCWO6","expires_at":`+referenceTimeStr+`}`) + }) + + ctx := t.Context() + token, _, err := client.Actions.CreateOrganizationRegistrationToken(ctx, "o") + if err != nil { + t.Errorf("Actions.CreateOrganizationRegistrationToken returned error: %v", err) + } + + want := &RegistrationToken{ + Token: Ptr("LLBF3JGZDX3P5PMEXLND6TS6FCWO6"), + ExpiresAt: &referenceTimestamp, + } + if !cmp.Equal(token, want) { + t.Errorf("Actions.CreateOrganizationRegistrationToken returned %+v, want %+v", token, want) + } + + const methodName = "CreateOrganizationRegistrationToken" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateOrganizationRegistrationToken(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateOrganizationRegistrationToken(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListOrganizationRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":2,"runners":[{"id":23,"name":"MBP","os":"macos","status":"online"},{"id":24,"name":"iMac","os":"macos","status":"offline"}]}`) + }) + + opts := &ListRunnersOptions{ + ListOptions: ListOptions{Page: 2, PerPage: 2}, + } + ctx := t.Context() + runners, _, err := client.Actions.ListOrganizationRunners(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListOrganizationRunners returned error: %v", err) + } + + want := &Runners{ + TotalCount: 2, + Runners: []*Runner{ + {ID: Ptr(int64(23)), Name: Ptr("MBP"), OS: Ptr("macos"), Status: Ptr("online")}, + {ID: Ptr(int64(24)), Name: Ptr("iMac"), OS: Ptr("macos"), Status: Ptr("offline")}, + }, + } + if !cmp.Equal(runners, want) { + t.Errorf("Actions.ListOrganizationRunners returned %+v, want %+v", runners, want) + } + + const methodName = "ListOrganizationRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrganizationRunners(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrganizationRunners(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetOrganizationRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":23,"name":"MBP","os":"macos","status":"online"}`) + }) + + ctx := t.Context() + runner, _, err := client.Actions.GetOrganizationRunner(ctx, "o", 23) + if err != nil { + t.Errorf("Actions.GetOrganizationRunner returned error: %v", err) + } + + want := &Runner{ + ID: Ptr(int64(23)), + Name: Ptr("MBP"), + OS: Ptr("macos"), + Status: Ptr("online"), + } + if !cmp.Equal(runner, want) { + t.Errorf("Actions.GetOrganizationRunner returned %+v, want %+v", runner, want) + } + + const methodName = "GetOrganizationRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrganizationRunner(ctx, "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrganizationRunner(ctx, "o", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrganizationRemoveToken(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runners/remove-token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"token":"AABF3JGZDX3P5PMEXLND6TS6FCWO6","expires_at":`+referenceTimeStr+`}`) + }) + + ctx := t.Context() + token, _, err := client.Actions.CreateOrganizationRemoveToken(ctx, "o") + if err != nil { + t.Errorf("Actions.CreateOrganizationRemoveToken returned error: %v", err) + } + + want := &RemoveToken{Token: Ptr("AABF3JGZDX3P5PMEXLND6TS6FCWO6"), ExpiresAt: &referenceTimestamp} + if !cmp.Equal(token, want) { + t.Errorf("Actions.CreateOrganizationRemoveToken returned %+v, want %+v", token, want) + } + + const methodName = "CreateOrganizationRemoveToken" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateOrganizationRemoveToken(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateOrganizationRemoveToken(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_RemoveOrganizationRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/runners/21", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.RemoveOrganizationRunner(ctx, "o", 21) + if err != nil { + t.Errorf("Actions.RemoveOrganizationRunner returned error: %v", err) + } + + const methodName = "RemoveOrganizationRunner" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveOrganizationRunner(ctx, "\n", 21) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveOrganizationRunner(ctx, "o", 21) + }) +} diff --git a/github/actions_secrets.go b/github/actions_secrets.go new file mode 100644 index 00000000000..2426a10d98c --- /dev/null +++ b/github/actions_secrets.go @@ -0,0 +1,501 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "strconv" +) + +// PublicKey represents the public key that should be used to encrypt secrets. +type PublicKey struct { + KeyID *string `json:"key_id"` + Key *string `json:"key"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// This ensures GitHub Enterprise versions which return a numeric key id +// do not error out when unmarshaling. +func (p *PublicKey) UnmarshalJSON(data []byte) error { + var pk struct { + KeyID any `json:"key_id"` + Key *string `json:"key"` + } + + if err := json.Unmarshal(data, &pk); err != nil { + return err + } + + p.Key = pk.Key + + switch v := pk.KeyID.(type) { + case nil: + return nil + case string: + p.KeyID = &v + case float64: + p.KeyID = Ptr(strconv.FormatFloat(v, 'f', -1, 64)) + default: + return fmt.Errorf("unable to unmarshal %T as a string", v) + } + + return nil +} + +func (s *ActionsService) getPublicKey(ctx context.Context, url string) (*PublicKey, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var pubKey *PublicKey + resp, err := s.client.Do(req, &pubKey) + if err != nil { + return nil, resp, err + } + + return pubKey, resp, nil +} + +// GetRepoPublicKey gets a public key that should be used for secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#get-a-repository-public-key +// +//meta:operation GET /repos/{owner}/{repo}/actions/secrets/public-key +func (s *ActionsService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/actions/secrets/public-key", owner, repo) + return s.getPublicKey(ctx, url) +} + +// GetOrgPublicKey gets a public key that should be used for secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#get-an-organization-public-key +// +//meta:operation GET /orgs/{org}/actions/secrets/public-key +func (s *ActionsService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) { + url := fmt.Sprintf("orgs/%v/actions/secrets/public-key", org) + return s.getPublicKey(ctx, url) +} + +// GetEnvPublicKey gets a public key that should be used for secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#get-an-environment-public-key +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/secrets/public-key +func (s *ActionsService) GetEnvPublicKey(ctx context.Context, owner, repo, env string) (*PublicKey, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/environments/%v/secrets/public-key", owner, repo, env) + return s.getPublicKey(ctx, url) +} + +// Secret represents a repository action secret. +type Secret struct { + Name string `json:"name"` + CreatedAt Timestamp `json:"created_at"` + UpdatedAt Timestamp `json:"updated_at"` + Visibility string `json:"visibility,omitempty"` + SelectedRepositoriesURL string `json:"selected_repositories_url,omitempty"` +} + +// Secrets represents one item from the ListSecrets response. +type Secrets struct { + TotalCount int `json:"total_count"` + Secrets []*Secret `json:"secrets"` +} + +// ListRepoSecrets lists all secrets available in a repository +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#list-repository-secrets +// +//meta:operation GET /repos/{owner}/{repo}/actions/secrets +func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/secrets", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// ListRepoOrgSecrets lists all organization secrets available in a repository +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#list-repository-organization-secrets +// +//meta:operation GET /repos/{owner}/{repo}/actions/organization-secrets +func (s *ActionsService) ListRepoOrgSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/organization-secrets", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// ListOrgSecrets lists all secrets available in an organization +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#list-organization-secrets +// +//meta:operation GET /orgs/{org}/actions/secrets +func (s *ActionsService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// ListEnvSecrets lists all secrets available in an environment. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#list-environment-secrets +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/secrets +func (s *ActionsService) ListEnvSecrets(ctx context.Context, owner, repo, env string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/secrets", owner, repo, env) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// GetRepoSecret gets a single repository secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#get-a-repository-secret +// +//meta:operation GET /repos/{owner}/{repo}/actions/secrets/{secret_name} +func (s *ActionsService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// GetOrgSecret gets a single organization secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#get-an-organization-secret +// +//meta:operation GET /orgs/{org}/actions/secrets/{secret_name} +func (s *ActionsService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// GetEnvSecret gets a single environment secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#get-an-environment-secret +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} +func (s *ActionsService) GetEnvSecret(ctx context.Context, owner, repo, env, secretName string) (*Secret, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/secrets/%v", owner, repo, env, secretName) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// SelectedRepoIDs are the repository IDs that have access to the actions secrets. +type SelectedRepoIDs []int64 + +// EncryptedSecret represents a secret that is encrypted using a public key. +// +// The value of EncryptedValue must be your secret, encrypted with +// LibSodium (see documentation here: https://libsodium.gitbook.io/doc/bindings_for_other_languages) +// using the public key retrieved using the GetPublicKey method. +type EncryptedSecret struct { + Name string `json:"-"` + KeyID string `json:"key_id"` + EncryptedValue string `json:"encrypted_value"` + Visibility string `json:"visibility,omitempty"` + SelectedRepositoryIDs SelectedRepoIDs `json:"selected_repository_ids,omitempty"` +} + +// SecretOrgRequest represents a request to create or update a secret for an +// organization. +// +// The value of EncryptedValue must be your secret, encrypted with +// LibSodium (see documentation here: https://libsodium.gitbook.io/doc/bindings_for_other_languages) +// using the public key retrieved using the GetPublicKey method. +type SecretOrgRequest struct { + KeyID string `json:"key_id"` + EncryptedValue string `json:"encrypted_value"` + Visibility string `json:"visibility"` + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitzero"` +} + +// SecretRequest represents a request to create or update a secret for a +// repository or a repository environment. +// +// The value of EncryptedValue must be your secret, encrypted with +// LibSodium (see documentation here: https://libsodium.gitbook.io/doc/bindings_for_other_languages) +// using the public key retrieved using the GetPublicKey method. +type SecretRequest struct { + KeyID string `json:"key_id"` + EncryptedValue string `json:"encrypted_value"` +} + +// CreateOrUpdateRepoSecret creates or updates a repository secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret +// +//meta:operation PUT /repos/{owner}/{repo}/actions/secrets/{secret_name} +func (s *ActionsService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo, name string, body SecretRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateOrUpdateOrgSecret creates or updates an organization secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret +// +//meta:operation PUT /orgs/{org}/actions/secrets/{secret_name} +func (s *ActionsService) CreateOrUpdateOrgSecret(ctx context.Context, org, name string, body SecretOrgRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateOrUpdateEnvSecret creates or updates a single environment secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-an-environment-secret +// +//meta:operation PUT /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} +func (s *ActionsService) CreateOrUpdateEnvSecret(ctx context.Context, owner, repo, env, name string, body SecretRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/secrets/%v", owner, repo, env, name) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteRepoSecret deletes a secret in a repository using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#delete-a-repository-secret +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name} +func (s *ActionsService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteOrgSecret deletes a secret in an organization using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#delete-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/actions/secrets/{secret_name} +func (s *ActionsService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteEnvSecret deletes a secret in an environment using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#delete-an-environment-secret +// +//meta:operation DELETE /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} +func (s *ActionsService) DeleteEnvSecret(ctx context.Context, owner, repo, env, secretName string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/secrets/%v", owner, repo, env, secretName) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// SelectedReposList represents the list of repositories selected for an organization secret. +type SelectedReposList struct { + TotalCount *int `json:"total_count,omitempty"` + Repositories []*Repository `json:"repositories,omitempty"` +} + +// ListSelectedReposForOrgSecret lists all repositories that have access to a secret. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#list-selected-repositories-for-an-organization-secret +// +//meta:operation GET /orgs/{org}/actions/secrets/{secret_name}/repositories +func (s *ActionsService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *SelectedReposList + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// SetSelectedReposForOrgSecret sets the repositories that have access to a secret. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#set-selected-repositories-for-an-organization-secret +// +//meta:operation PUT /orgs/{org}/actions/secrets/{secret_name}/repositories +func (s *ActionsService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name) + + type repoIDs struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + } + + req, err := s.client.NewRequest(ctx, "PUT", u, repoIDs{SelectedIDs: ids}) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddSelectedRepoToOrgSecret adds a repository to an organization secret. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#add-selected-repository-to-an-organization-secret +// +//meta:operation PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id} +func (s *ActionsService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, repoID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveSelectedRepoFromOrgSecret removes a repository from an organization secret. +// +// GitHub API docs: https://docs.github.com/rest/actions/secrets?apiVersion=2022-11-28#remove-selected-repository-from-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id} +func (s *ActionsService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, repoID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_secrets_test.go b/github/actions_secrets_test.go new file mode 100644 index 00000000000..e1e00c721b7 --- /dev/null +++ b/github/actions_secrets_test.go @@ -0,0 +1,883 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestPublicKey_UnmarshalJSON(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + data []byte + wantPublicKey PublicKey + wantErr bool + }{ + "Empty": { + data: []byte("{}"), + wantPublicKey: PublicKey{}, + wantErr: false, + }, + "Invalid JSON": { + data: []byte("{"), + wantPublicKey: PublicKey{}, + wantErr: true, + }, + "Numeric KeyID": { + data: []byte(`{"key_id":1234,"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`), + wantPublicKey: PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}, + wantErr: false, + }, + "String KeyID": { + data: []byte(`{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`), + wantPublicKey: PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}, + wantErr: false, + }, + "Invalid KeyID": { + data: []byte(`{"key_id":["1234"],"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`), + wantPublicKey: PublicKey{KeyID: nil, Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}, + wantErr: true, + }, + "Invalid Key": { + data: []byte(`{"key":123}`), + wantPublicKey: PublicKey{KeyID: nil, Key: nil}, + wantErr: true, + }, + "Nil": { + data: nil, + wantPublicKey: PublicKey{KeyID: nil, Key: nil}, + wantErr: true, + }, + "Empty String": { + data: []byte(""), + wantPublicKey: PublicKey{KeyID: nil, Key: nil}, + wantErr: true, + }, + "Missing Key": { + data: []byte(`{"key_id":"1234"}`), + wantPublicKey: PublicKey{KeyID: Ptr("1234")}, + wantErr: false, + }, + "Missing KeyID": { + data: []byte(`{"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`), + wantPublicKey: PublicKey{Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")}, + wantErr: false, + }, + } + + for name, tt := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + pk := PublicKey{} + err := json.Unmarshal(tt.data, &pk) + if err == nil && tt.wantErr { + t.Error("PublicKey.UnmarshalJSON returned nil instead of an error") + } + if err != nil && !tt.wantErr { + t.Errorf("PublicKey.UnmarshalJSON returned an unexpected error: %+v", err) + } + if !cmp.Equal(tt.wantPublicKey, pk) { + t.Errorf("PublicKey.UnmarshalJSON expected public key %+v, got %+v", tt.wantPublicKey, pk) + } + }) + } +} + +func TestActionsService_GetRepoPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Actions.GetRepoPublicKey(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetRepoPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Actions.GetRepoPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetRepoPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoPublicKey(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoPublicKey(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRepoPublicKeyNumeric(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":1234,"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Actions.GetRepoPublicKey(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetRepoPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Actions.GetRepoPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetRepoPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoPublicKey(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoPublicKey(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListRepoSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Actions.ListRepoSecrets(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRepoSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Actions.ListRepoSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListRepoSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepoSecrets(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepoSecrets(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListRepoOrgSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/organization-secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Actions.ListRepoOrgSecrets(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRepoOrgSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Actions.ListRepoOrgSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListRepoOrgSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepoOrgSecrets(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepoOrgSecrets(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + secret, _, err := client.Actions.GetRepoSecret(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Actions.GetRepoSecret returned error: %v", err) + } + + want := &Secret{ + Name: "NAME", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + } + if !cmp.Equal(secret, want) { + t.Errorf("Actions.GetRepoSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoSecret(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoSecret(ctx, "o", "r", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrUpdateRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretRequest{ + KeyID: "1234", + EncryptedValue: "QIv=", + } + + mux.HandleFunc("/repos/o/r/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := SecretRequest{ + KeyID: "1234", + EncryptedValue: "QIv=", + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.CreateOrUpdateRepoSecret(ctx, "o", "r", "NAME", input) + if err != nil { + t.Errorf("Actions.CreateOrUpdateRepoSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateOrUpdateRepoSecret(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateOrUpdateRepoSecret(ctx, "o", "r", "NAME", input) + }) +} + +func TestActionsService_DeleteRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteRepoSecret(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Actions.DeleteRepoSecret returned error: %v", err) + } + + const methodName = "DeleteRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteRepoSecret(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteRepoSecret(ctx, "o", "r", "NAME") + }) +} + +func TestActionsService_GetOrgPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"012345678","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Actions.GetOrgPublicKey(ctx, "o") + if err != nil { + t.Errorf("Actions.GetOrgPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("012345678"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Actions.GetOrgPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetOrgPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrgPublicKey(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrgPublicKey(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListOrgSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"secrets":[{"name":"GIST_ID","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"private"},{"name":"DEPLOY_TOKEN","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`,"visibility":"all"},{"name":"GH_TOKEN","created_at":`+refTimeStr(1136178004)+`,"updated_at":`+refTimeStr(1136178005)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/secrets/SUPER_SECRET/repositories"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Actions.ListOrgSecrets(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListOrgSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 3, + Secrets: []*Secret{ + {Name: "GIST_ID", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001), Visibility: "private"}, + {Name: "DEPLOY_TOKEN", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003), Visibility: "all"}, + {Name: "GH_TOKEN", CreatedAt: *refTimestamp(1136178004), UpdatedAt: *refTimestamp(1136178005), Visibility: "selected", SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/actions/secrets/SUPER_SECRET/repositories"}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Actions.ListOrgSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListOrgSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrgSecrets(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrgSecrets(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/secrets/SUPER_SECRET/repositories"}`) + }) + + ctx := t.Context() + secret, _, err := client.Actions.GetOrgSecret(ctx, "o", "NAME") + if err != nil { + t.Errorf("Actions.GetOrgSecret returned error: %v", err) + } + + want := &Secret{ + Name: "NAME", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + Visibility: "selected", + SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/actions/secrets/SUPER_SECRET/repositories", + } + if !cmp.Equal(secret, want) { + t.Errorf("Actions.GetOrgSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrgSecret(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrgSecret(ctx, "o", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrUpdateOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretOrgRequest{ + KeyID: "1234", + EncryptedValue: "QIv=", + Visibility: "selected", + SelectedRepositoryIDs: []int64{1296269, 1269280}, + } + + mux.HandleFunc("/orgs/o/actions/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := SecretOrgRequest{ + KeyID: "1234", + EncryptedValue: "QIv=", + Visibility: "selected", + SelectedRepositoryIDs: []int64{1296269, 1269280}, + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.CreateOrUpdateOrgSecret(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Actions.CreateOrUpdateOrgSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateOrUpdateOrgSecret(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateOrUpdateOrgSecret(ctx, "o", "NAME", input) + }) +} + +func TestActionsService_ListSelectedReposForOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + repos, _, err := client.Actions.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + if err != nil { + t.Errorf("Actions.ListSelectedReposForOrgSecret returned error: %v", err) + } + + want := &SelectedReposList{ + TotalCount: Ptr(1), + Repositories: []*Repository{ + {ID: Ptr(int64(1))}, + }, + } + if !cmp.Equal(repos, want) { + t.Errorf("Actions.ListSelectedReposForOrgSecret returned %+v, want %+v", repos, want) + } + + const methodName = "ListSelectedReposForOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListSelectedReposForOrgSecret(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetSelectedReposForOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{64780797} + + mux.HandleFunc("/orgs/o/actions/secrets/NAME/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + }{ + SelectedIDs: input, + }) + }) + + ctx := t.Context() + _, err := client.Actions.SetSelectedReposForOrgSecret(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Actions.SetSelectedReposForOrgSecret returned error: %v", err) + } + + const methodName = "SetSelectedReposForOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetSelectedReposForOrgSecret(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetSelectedReposForOrgSecret(ctx, "o", "NAME", input) + }) +} + +func TestActionsService_AddSelectedRepoToOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + repoID := int64(1234) + ctx := t.Context() + _, err := client.Actions.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repoID) + if err != nil { + t.Errorf("Actions.AddSelectedRepoToOrgSecret returned error: %v", err) + } + + const methodName = "AddSelectedRepoToOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", 0) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddSelectedRepoToOrgSecret(ctx, "\n", "\n", repoID) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repoID) + }) +} + +func TestActionsService_RemoveSelectedRepoFromOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + repoID := int64(1234) + ctx := t.Context() + _, err := client.Actions.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repoID) + if err != nil { + t.Errorf("Actions.RemoveSelectedRepoFromOrgSecret returned error: %v", err) + } + + const methodName = "RemoveSelectedRepoFromOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", 0) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveSelectedRepoFromOrgSecret(ctx, "\n", "\n", repoID) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repoID) + }) +} + +func TestActionsService_DeleteOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteOrgSecret(ctx, "o", "NAME") + if err != nil { + t.Errorf("Actions.DeleteOrgSecret returned error: %v", err) + } + + const methodName = "DeleteOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteOrgSecret(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteOrgSecret(ctx, "o", "NAME") + }) +} + +func TestActionsService_GetEnvPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Actions.GetEnvPublicKey(ctx, "o", "r", "e") + if err != nil { + t.Errorf("Actions.GetEnvPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Actions.GetEnvPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetEnvPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetEnvPublicKey(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetEnvPublicKey(ctx, "o", "r", "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetEnvPublicKeyNumeric(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":1234,"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Actions.GetEnvPublicKey(ctx, "o", "r", "e") + if err != nil { + t.Errorf("Actions.GetEnvPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Actions.GetEnvPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetEnvPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetEnvPublicKey(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetEnvPublicKey(ctx, "o", "r", "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListEnvSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Actions.ListEnvSecrets(ctx, "o", "r", "e", opts) + if err != nil { + t.Errorf("Actions.ListEnvSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Actions.ListEnvSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListEnvSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListEnvSecrets(ctx, "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListEnvSecrets(ctx, "o", "r", "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetEnvSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/secrets/secret", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"secret","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + secret, _, err := client.Actions.GetEnvSecret(ctx, "o", "r", "e", "secret") + if err != nil { + t.Errorf("Actions.GetEnvSecret returned error: %v", err) + } + + want := &Secret{ + Name: "secret", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + } + if !cmp.Equal(secret, want) { + t.Errorf("Actions.GetEnvSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetEnvSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetEnvSecret(ctx, "\n", "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetEnvSecret(ctx, "o", "r", "e", "secret") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrUpdateEnvSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretRequest{ + KeyID: "1234", + EncryptedValue: "QIv=", + } + + mux.HandleFunc("/repos/o/r/environments/e/secrets/secret", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := SecretRequest{ + KeyID: "1234", + EncryptedValue: "QIv=", + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.CreateOrUpdateEnvSecret(ctx, "o", "r", "e", "secret", input) + if err != nil { + t.Errorf("Actions.CreateOrUpdateEnvSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateEnvSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateOrUpdateEnvSecret(ctx, "\n", "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateOrUpdateEnvSecret(ctx, "o", "r", "e", "secret", input) + }) +} + +func TestActionsService_DeleteEnvSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/secrets/secret", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteEnvSecret(ctx, "o", "r", "e", "secret") + if err != nil { + t.Errorf("Actions.DeleteEnvSecret returned error: %v", err) + } + + const methodName = "DeleteEnvSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteEnvSecret(ctx, "\n", "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteEnvSecret(ctx, "o", "r", "e", "secret") + }) +} diff --git a/github/actions_variables.go b/github/actions_variables.go new file mode 100644 index 00000000000..97ecf82f356 --- /dev/null +++ b/github/actions_variables.go @@ -0,0 +1,467 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" +) + +// ActionsCreateOrgVariableRequest represents a request to create an +// organization variable. +type ActionsCreateOrgVariableRequest struct { + Name string `json:"name"` + Value string `json:"value"` + Visibility string `json:"visibility"` + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitzero"` +} + +// ActionsUpdateOrgVariableRequest represents a request to update an +// organization variable. +type ActionsUpdateOrgVariableRequest struct { + Name *string `json:"name,omitempty"` + Value *string `json:"value,omitempty"` + Visibility *string `json:"visibility,omitempty"` + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitzero"` +} + +// ActionsCreateVariableRequest represents a request to create a variable +// for a repository or repository environment variable. +type ActionsCreateVariableRequest struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// ActionsUpdateVariableRequest represents a request to update a variable +// for a repository or repository environment variable. +type ActionsUpdateVariableRequest struct { + Name *string `json:"name,omitempty"` + Value *string `json:"value,omitempty"` +} + +// ActionsVariable represents a repository action variable. +type ActionsVariable struct { + Name string `json:"name"` + Value string `json:"value"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + + // Used by ListOrgVariables and GetOrgVariables + Visibility *string `json:"visibility,omitempty"` + SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` +} + +// ActionsVariables represents one item from the ListVariables response. +type ActionsVariables struct { + TotalCount int `json:"total_count"` + Variables []*ActionsVariable `json:"variables"` +} + +// ListRepoVariables lists all variables available in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#list-repository-variables +// +//meta:operation GET /repos/{owner}/{repo}/actions/variables +func (s *ActionsService) ListRepoVariables(ctx context.Context, owner, repo string, opts *ListOptions) (*ActionsVariables, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/variables", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variables *ActionsVariables + resp, err := s.client.Do(req, &variables) + if err != nil { + return nil, resp, err + } + + return variables, resp, nil +} + +// ListRepoOrgVariables lists all organization variables available in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#list-repository-organization-variables +// +//meta:operation GET /repos/{owner}/{repo}/actions/organization-variables +func (s *ActionsService) ListRepoOrgVariables(ctx context.Context, owner, repo string, opts *ListOptions) (*ActionsVariables, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/organization-variables", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variables *ActionsVariables + resp, err := s.client.Do(req, &variables) + if err != nil { + return nil, resp, err + } + + return variables, resp, nil +} + +// ListOrgVariables lists all variables available in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#list-organization-variables +// +//meta:operation GET /orgs/{org}/actions/variables +func (s *ActionsService) ListOrgVariables(ctx context.Context, org string, opts *ListOptions) (*ActionsVariables, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variables *ActionsVariables + resp, err := s.client.Do(req, &variables) + if err != nil { + return nil, resp, err + } + + return variables, resp, nil +} + +// ListEnvVariables lists all variables available in an environment. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#list-environment-variables +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/variables +func (s *ActionsService) ListEnvVariables(ctx context.Context, owner, repo, env string, opts *ListOptions) (*ActionsVariables, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/variables", owner, repo, env) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variables *ActionsVariables + resp, err := s.client.Do(req, &variables) + if err != nil { + return nil, resp, err + } + + return variables, resp, nil +} + +// GetRepoVariable gets a single repository variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#get-a-repository-variable +// +//meta:operation GET /repos/{owner}/{repo}/actions/variables/{name} +func (s *ActionsService) GetRepoVariable(ctx context.Context, owner, repo, name string) (*ActionsVariable, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/variables/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variable *ActionsVariable + resp, err := s.client.Do(req, &variable) + if err != nil { + return nil, resp, err + } + + return variable, resp, nil +} + +// GetOrgVariable gets a single organization variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#get-an-organization-variable +// +//meta:operation GET /orgs/{org}/actions/variables/{name} +func (s *ActionsService) GetOrgVariable(ctx context.Context, org, name string) (*ActionsVariable, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables/%v", org, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variable *ActionsVariable + resp, err := s.client.Do(req, &variable) + if err != nil { + return nil, resp, err + } + + return variable, resp, nil +} + +// GetEnvVariable gets a single environment variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#get-an-environment-variable +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/variables/{name} +func (s *ActionsService) GetEnvVariable(ctx context.Context, owner, repo, env, variableName string) (*ActionsVariable, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/variables/%v", owner, repo, env, variableName) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var variable *ActionsVariable + resp, err := s.client.Do(req, &variable) + if err != nil { + return nil, resp, err + } + + return variable, resp, nil +} + +// CreateRepoVariable creates a repository variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#create-a-repository-variable +// +//meta:operation POST /repos/{owner}/{repo}/actions/variables +func (s *ActionsService) CreateRepoVariable(ctx context.Context, owner, repo string, body ActionsCreateVariableRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/variables", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateOrgVariable creates an organization variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#create-an-organization-variable +// +//meta:operation POST /orgs/{org}/actions/variables +func (s *ActionsService) CreateOrgVariable(ctx context.Context, org string, body ActionsCreateOrgVariableRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateEnvVariable creates an environment variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#create-an-environment-variable +// +//meta:operation POST /repos/{owner}/{repo}/environments/{environment_name}/variables +func (s *ActionsService) CreateEnvVariable(ctx context.Context, owner, repo, env string, body ActionsCreateVariableRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/variables", owner, repo, env) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, err + } + return s.client.Do(req, nil) +} + +// UpdateRepoVariable updates a repository variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#update-a-repository-variable +// +//meta:operation PATCH /repos/{owner}/{repo}/actions/variables/{name} +func (s *ActionsService) UpdateRepoVariable(ctx context.Context, owner, repo, name string, body ActionsUpdateVariableRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/variables/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// UpdateOrgVariable updates an organization variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#update-an-organization-variable +// +//meta:operation PATCH /orgs/{org}/actions/variables/{name} +func (s *ActionsService) UpdateOrgVariable(ctx context.Context, org, name string, body ActionsUpdateOrgVariableRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables/%v", org, name) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// UpdateEnvVariable updates an environment variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#update-an-environment-variable +// +//meta:operation PATCH /repos/{owner}/{repo}/environments/{environment_name}/variables/{name} +func (s *ActionsService) UpdateEnvVariable(ctx context.Context, owner, repo, env, name string, body ActionsUpdateVariableRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/variables/%v", owner, repo, env, name) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteRepoVariable deletes a variable in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#delete-a-repository-variable +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/variables/{name} +func (s *ActionsService) DeleteRepoVariable(ctx context.Context, owner, repo, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/variables/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteOrgVariable deletes a variable in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#delete-an-organization-variable +// +//meta:operation DELETE /orgs/{org}/actions/variables/{name} +func (s *ActionsService) DeleteOrgVariable(ctx context.Context, org, name string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables/%v", org, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteEnvVariable deletes a variable in an environment. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#delete-an-environment-variable +// +//meta:operation DELETE /repos/{owner}/{repo}/environments/{environment_name}/variables/{name} +func (s *ActionsService) DeleteEnvVariable(ctx context.Context, owner, repo, env, variableName string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/variables/%v", owner, repo, env, variableName) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListSelectedReposForOrgVariable lists all repositories that have access to a variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#list-selected-repositories-for-an-organization-variable +// +//meta:operation GET /orgs/{org}/actions/variables/{name}/repositories +func (s *ActionsService) ListSelectedReposForOrgVariable(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories", org, name) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *SelectedReposList + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// SetSelectedReposForOrgVariable sets the repositories that have access to a variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#set-selected-repositories-for-an-organization-variable +// +//meta:operation PUT /orgs/{org}/actions/variables/{name}/repositories +func (s *ActionsService) SetSelectedReposForOrgVariable(ctx context.Context, org, name string, ids []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories", org, name) + + type repoIDs struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + } + + req, err := s.client.NewRequest(ctx, "PUT", u, repoIDs{SelectedIDs: ids}) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddSelectedRepoToOrgVariable adds a repository to an organization variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#add-selected-repository-to-an-organization-variable +// +//meta:operation PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id} +func (s *ActionsService) AddSelectedRepoToOrgVariable(ctx context.Context, org, name string, repo *Repository) (*Response, error) { + if repo == nil { + return nil, errors.New("repository must be provided") + } + if repo.ID == nil { + return nil, errors.New("id must be provided") + } + + u := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories/%v", org, name, *repo.ID) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveSelectedRepoFromOrgVariable removes a repository from an organization variable. +// +// GitHub API docs: https://docs.github.com/rest/actions/variables?apiVersion=2022-11-28#remove-selected-repository-from-an-organization-variable +// +//meta:operation DELETE /orgs/{org}/actions/variables/{name}/repositories/{repository_id} +func (s *ActionsService) RemoveSelectedRepoFromOrgVariable(ctx context.Context, org, name string, repo *Repository) (*Response, error) { + if repo == nil { + return nil, errors.New("repository must be provided") + } + if repo.ID == nil { + return nil, errors.New("id must be provided") + } + + u := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories/%v", org, name, *repo.ID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_variables_test.go b/github/actions_variables_test.go new file mode 100644 index 00000000000..d251677e4e1 --- /dev/null +++ b/github/actions_variables_test.go @@ -0,0 +1,731 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListRepoVariables(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"variables":[{"name":"A","value":"AA","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","value":"BB","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + variables, _, err := client.Actions.ListRepoVariables(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRepoVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 4, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {Name: "B", Value: "BB", CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListRepoVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListRepoVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepoVariables(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepoVariables(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListRepoOrgVariables(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/organization-variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"variables":[{"name":"A","value":"AA","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","value":"BB","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + variables, _, err := client.Actions.ListRepoOrgVariables(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRepoOrgVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 4, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {Name: "B", Value: "BB", CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListRepoOrgVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListRepoOrgVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepoOrgVariables(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepoOrgVariables(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRepoVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","value":"VALUE","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + variable, _, err := client.Actions.GetRepoVariable(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Actions.GetRepoVariable returned error: %v", err) + } + + want := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + if !cmp.Equal(variable, want) { + t.Errorf("Actions.GetRepoVariable returned %+v, want %+v", variable, want) + } + + const methodName = "GetRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoVariable(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoVariable(ctx, "o", "r", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateRepoVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ActionsCreateVariableRequest{ + Name: "NAME", + Value: "VALUE", + } + + mux.HandleFunc("/repos/o/r/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.CreateRepoVariable(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.CreateRepoVariable returned error: %v", err) + } + + const methodName = "CreateRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateRepoVariable(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateRepoVariable(ctx, "o", "r", input) + }) +} + +func TestActionsService_UpdateRepoVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + name := "NAME" + input := ActionsUpdateVariableRequest{ + Name: &name, + Value: Ptr("VALUE"), + } + + mux.HandleFunc("/repos/o/r/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.UpdateRepoVariable(ctx, "o", "r", name, input) + if err != nil { + t.Errorf("Actions.UpdateRepoVariable returned error: %v", err) + } + + const methodName = "UpdateRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateRepoVariable(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateRepoVariable(ctx, "o", "r", name, input) + }) +} + +func TestActionsService_DeleteRepoVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/variables/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteRepoVariable(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Actions.DeleteRepoVariable returned error: %v", err) + } + + const methodName = "DeleteRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteRepoVariable(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteRepoVariable(ctx, "o", "r", "NAME") + }) +} + +func TestActionsService_ListOrgVariables(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"variables":[{"name":"A","value":"AA","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"private"},{"name":"B","value":"BB","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`,"visibility":"all"},{"name":"C","value":"CC","created_at":`+refTimeStr(1136178004)+`,"updated_at":`+refTimeStr(1136178005)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + variables, _, err := client.Actions.ListOrgVariables(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListOrgVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 3, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001), Visibility: Ptr("private")}, + {Name: "B", Value: "BB", CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003), Visibility: Ptr("all")}, + {Name: "C", Value: "CC", CreatedAt: refTimestamp(1136178004), UpdatedAt: refTimestamp(1136178005), Visibility: Ptr("selected"), SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories")}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListOrgVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListOrgVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrgVariables(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrgVariables(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","value":"VALUE","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories"}`) + }) + + ctx := t.Context() + variable, _, err := client.Actions.GetOrgVariable(ctx, "o", "NAME") + if err != nil { + t.Errorf("Actions.GetOrgVariable returned error: %v", err) + } + + want := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Visibility: Ptr("selected"), + SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories"), + } + if !cmp.Equal(variable, want) { + t.Errorf("Actions.GetOrgVariable returned %+v, want %+v", variable, want) + } + + const methodName = "GetOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrgVariable(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrgVariable(ctx, "o", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ActionsCreateOrgVariableRequest{ + Name: "NAME", + Value: "VALUE", + Visibility: "selected", + SelectedRepositoryIDs: []int64{1296269, 1269280}, + } + + mux.HandleFunc("/orgs/o/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.CreateOrgVariable(ctx, "o", input) + if err != nil { + t.Errorf("Actions.CreateOrgVariable returned error: %v", err) + } + + const methodName = "CreateOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateOrgVariable(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateOrgVariable(ctx, "o", input) + }) +} + +func TestActionsService_UpdateOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + name := "NAME" + input := ActionsUpdateOrgVariableRequest{ + Name: &name, + Value: Ptr("VALUE"), + Visibility: Ptr("selected"), + SelectedRepositoryIDs: []int64{1296269, 1269280}, + } + + mux.HandleFunc("/orgs/o/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.UpdateOrgVariable(ctx, "o", name, input) + if err != nil { + t.Errorf("Actions.UpdateOrgVariable returned error: %v", err) + } + + const methodName = "UpdateOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateOrgVariable(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateOrgVariable(ctx, "o", name, input) + }) +} + +func TestActionsService_ListSelectedReposForOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + repos, _, err := client.Actions.ListSelectedReposForOrgVariable(ctx, "o", "NAME", opts) + if err != nil { + t.Errorf("Actions.ListSelectedReposForOrgVariable returned error: %v", err) + } + + want := &SelectedReposList{ + TotalCount: Ptr(1), + Repositories: []*Repository{ + {ID: Ptr(int64(1))}, + }, + } + if !cmp.Equal(repos, want) { + t.Errorf("Actions.ListSelectedReposForOrgVariable returned %+v, want %+v", repos, want) + } + + const methodName = "ListSelectedReposForOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListSelectedReposForOrgVariable(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListSelectedReposForOrgVariable(ctx, "o", "NAME", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetSelectedReposForOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{64780797} + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + }{ + SelectedIDs: input, + }) + }) + + ctx := t.Context() + _, err := client.Actions.SetSelectedReposForOrgVariable(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Actions.SetSelectedReposForOrgVariable returned error: %v", err) + } + + const methodName = "SetSelectedReposForOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetSelectedReposForOrgVariable(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetSelectedReposForOrgVariable(ctx, "o", "NAME", input) + }) +} + +func TestActionsService_AddSelectedRepoToOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + repo := &Repository{ID: Ptr(int64(1234))} + ctx := t.Context() + _, err := client.Actions.AddSelectedRepoToOrgVariable(ctx, "o", "NAME", repo) + if err != nil { + t.Errorf("Actions.AddSelectedRepoToOrgVariable returned error: %v", err) + } + + const methodName = "AddSelectedRepoToOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddSelectedRepoToOrgVariable(ctx, "o", "NAME", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddSelectedRepoToOrgVariable(ctx, "o", "NAME", &Repository{ID: nil}) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddSelectedRepoToOrgVariable(ctx, "\n", "\n", repo) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddSelectedRepoToOrgVariable(ctx, "o", "NAME", repo) + }) +} + +func TestActionsService_RemoveSelectedRepoFromOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + repo := &Repository{ID: Ptr(int64(1234))} + ctx := t.Context() + _, err := client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "o", "NAME", repo) + if err != nil { + t.Errorf("Actions.RemoveSelectedRepoFromOrgVariable returned error: %v", err) + } + + const methodName = "RemoveSelectedRepoFromOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "o", "NAME", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "o", "NAME", &Repository{ID: nil}) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "\n", "\n", repo) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "o", "NAME", repo) + }) +} + +func TestActionsService_DeleteOrgVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/variables/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteOrgVariable(ctx, "o", "NAME") + if err != nil { + t.Errorf("Actions.DeleteOrgVariable returned error: %v", err) + } + + const methodName = "DeleteOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteOrgVariable(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteOrgVariable(ctx, "o", "NAME") + }) +} + +func TestActionsService_ListEnvVariables(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"variables":[{"name":"A","value":"AA","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","value":"BB","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + variables, _, err := client.Actions.ListEnvVariables(ctx, "o", "r", "e", opts) + if err != nil { + t.Errorf("Actions.ListEnvVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 4, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {Name: "B", Value: "BB", CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListEnvVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListEnvVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListEnvVariables(ctx, "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListEnvVariables(ctx, "o", "r", "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetEnvVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","value":"VAR","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + variable, _, err := client.Actions.GetEnvVariable(ctx, "o", "r", "e", "NAME") + if err != nil { + t.Errorf("Actions.GetEnvVariable returned error: %v", err) + } + + want := &ActionsVariable{ + Name: "NAME", + Value: "VAR", + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + if !cmp.Equal(variable, want) { + t.Errorf("Actions.GetEnvVariable returned %+v, want %+v", variable, want) + } + + const methodName = "GetEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetEnvVariable(ctx, "\n", "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetEnvVariable(ctx, "o", "r", "e", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateEnvVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ActionsCreateVariableRequest{ + Name: "NAME", + Value: "VAR", + } + + mux.HandleFunc("/repos/o/r/environments/e/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Actions.CreateEnvVariable(ctx, "o", "r", "e", input) + if err != nil { + t.Errorf("Actions.CreateEnvVariable returned error: %v", err) + } + + const methodName = "CreateEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateEnvVariable(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateEnvVariable(ctx, "o", "r", "e", input) + }) +} + +func TestActionsService_UpdateEnvVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + name := "NAME" + input := ActionsUpdateVariableRequest{ + Name: &name, + Value: Ptr("VAR"), + } + + mux.HandleFunc("/repos/o/r/environments/e/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Actions.UpdateEnvVariable(ctx, "o", "r", "e", name, input) + if err != nil { + t.Errorf("Actions.UpdateEnvVariable returned error: %v", err) + } + + const methodName = "UpdateEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateEnvVariable(ctx, "\n", "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateEnvVariable(ctx, "o", "r", "e", name, input) + }) +} + +func TestActionsService_DeleteEnvVariable(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/variables/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Actions.DeleteEnvVariable(ctx, "o", "r", "e", "NAME") + if err != nil { + t.Errorf("Actions.DeleteEnvVariable returned error: %v", err) + } + + const methodName = "DeleteEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteEnvVariable(ctx, "\n", "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteEnvVariable(ctx, "o", "r", "e", "NAME") + }) +} diff --git a/github/actions_workflow_jobs.go b/github/actions_workflow_jobs.go new file mode 100644 index 00000000000..9419cf89711 --- /dev/null +++ b/github/actions_workflow_jobs.go @@ -0,0 +1,193 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "net/url" +) + +// TaskStep represents a single task step from a sequence of tasks of a job. +type TaskStep struct { + Name *string `json:"name,omitempty"` + Status *string `json:"status,omitempty"` + Conclusion *string `json:"conclusion,omitempty"` + Number *int64 `json:"number,omitempty"` + StartedAt *Timestamp `json:"started_at,omitempty"` + CompletedAt *Timestamp `json:"completed_at,omitempty"` +} + +// WorkflowJob represents a repository action workflow job. +type WorkflowJob struct { + ID *int64 `json:"id,omitempty"` + RunID *int64 `json:"run_id,omitempty"` + RunURL *string `json:"run_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` + HeadBranch *string `json:"head_branch,omitempty"` + HeadSHA *string `json:"head_sha,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Status *string `json:"status,omitempty"` + Conclusion *string `json:"conclusion,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + StartedAt *Timestamp `json:"started_at,omitempty"` + CompletedAt *Timestamp `json:"completed_at,omitempty"` + Name *string `json:"name,omitempty"` + Steps []*TaskStep `json:"steps,omitempty"` + CheckRunURL *string `json:"check_run_url,omitempty"` + // Labels represents runner labels from the `runs-on:` key from a GitHub Actions workflow. + Labels []string `json:"labels,omitempty"` + RunnerID *int64 `json:"runner_id,omitempty"` + RunnerName *string `json:"runner_name,omitempty"` + RunnerGroupID *int64 `json:"runner_group_id,omitempty"` + RunnerGroupName *string `json:"runner_group_name,omitempty"` + RunAttempt *int64 `json:"run_attempt,omitempty"` + WorkflowName *string `json:"workflow_name,omitempty"` +} + +// Jobs represents a slice of repository action workflow job. +type Jobs struct { + TotalCount *int `json:"total_count,omitempty"` + Jobs []*WorkflowJob `json:"jobs,omitempty"` +} + +// ListWorkflowJobsOptions specifies optional parameters to ListWorkflowJobs. +type ListWorkflowJobsOptions struct { + // Filter specifies how jobs should be filtered by their completed_at timestamp. + // Possible values are: + // latest - Returns jobs from the most recent execution of the workflow run + // all - Returns all jobs for a workflow run, including from old executions of the workflow run + // + // Default value is "latest". + Filter string `url:"filter,omitempty"` + ListOptions +} + +// ListWorkflowJobs lists all jobs for a workflow run. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-jobs?apiVersion=2022-11-28#list-jobs-for-a-workflow-run +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs +func (s *ActionsService) ListWorkflowJobs(ctx context.Context, owner, repo string, runID int64, opts *ListWorkflowJobsOptions) (*Jobs, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/jobs", owner, repo, runID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var jobs *Jobs + resp, err := s.client.Do(req, &jobs) + if err != nil { + return nil, resp, err + } + + return jobs, resp, nil +} + +// ListWorkflowJobsAttempt lists jobs for a workflow run Attempt. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-jobs?apiVersion=2022-11-28#list-jobs-for-a-workflow-run-attempt +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs +func (s *ActionsService) ListWorkflowJobsAttempt(ctx context.Context, owner, repo string, runID, attemptNumber int64, opts *ListOptions) (*Jobs, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/attempts/%v/jobs", owner, repo, runID, attemptNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var jobs *Jobs + resp, err := s.client.Do(req, &jobs) + if err != nil { + return nil, resp, err + } + + return jobs, resp, nil +} + +// GetWorkflowJobByID gets a specific job in a workflow run by ID. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-jobs?apiVersion=2022-11-28#get-a-job-for-a-workflow-run +// +//meta:operation GET /repos/{owner}/{repo}/actions/jobs/{job_id} +func (s *ActionsService) GetWorkflowJobByID(ctx context.Context, owner, repo string, jobID int64) (*WorkflowJob, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v", owner, repo, jobID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var job *WorkflowJob + resp, err := s.client.Do(req, &job) + if err != nil { + return nil, resp, err + } + + return job, resp, nil +} + +// GetWorkflowJobLogs gets a redirect URL to download a plain text file of logs for a workflow job. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-jobs?apiVersion=2022-11-28#download-job-logs-for-a-workflow-run +// +//meta:operation GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs +func (s *ActionsService) GetWorkflowJobLogs(ctx context.Context, owner, repo string, jobID int64, maxRedirects int) (*url.URL, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v/logs", owner, repo, jobID) + + if s.client.rateLimitRedirectionalEndpoints { + return s.getWorkflowJobLogsWithRateLimit(ctx, u, maxRedirects) + } + + return s.getWorkflowJobLogsWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) getWorkflowJobLogsWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusFound { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) + } + + parsedURL, err := url.Parse(resp.Header.Get("Location")) + return parsedURL, newResponse(resp), err +} + +func (s *ActionsService) getWorkflowJobLogsWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} diff --git a/github/actions_workflow_jobs_test.go b/github/actions_workflow_jobs_test.go new file mode 100644 index 00000000000..519fbd4ff55 --- /dev/null +++ b/github/actions_workflow_jobs_test.go @@ -0,0 +1,383 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListWorkflowJobs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":`+refTimeStr(1136178000)+`,"completed_at":`+refTimeStr(1136178001)+`},{"id":399444497,"run_id":29679449,"started_at":`+refTimeStr(1136178002)+`,"completed_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListWorkflowJobsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + jobs, _, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts) + if err != nil { + t.Errorf("Actions.ListWorkflowJobs returned error: %v", err) + } + + want := &Jobs{ + TotalCount: Ptr(4), + Jobs: []*WorkflowJob{ + {ID: Ptr(int64(399444496)), RunID: Ptr(int64(29679449)), StartedAt: refTimestamp(1136178000), CompletedAt: refTimestamp(1136178001)}, + {ID: Ptr(int64(399444497)), RunID: Ptr(int64(29679449)), StartedAt: refTimestamp(1136178002), CompletedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(jobs, want) { + t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want) + } + + const methodName = "ListWorkflowJobs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListWorkflowJobs(ctx, "\n", "\n", 29679449, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListWorkflowJobs_Filter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"filter": "all", "per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":`+refTimeStr(1136178000)+`,"completed_at":`+refTimeStr(1136178001)+`},{"id":399444497,"run_id":29679449,"started_at":`+refTimeStr(1136178002)+`,"completed_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListWorkflowJobsOptions{Filter: "all", ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + jobs, _, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts) + if err != nil { + t.Errorf("Actions.ListWorkflowJobs returned error: %v", err) + } + + want := &Jobs{ + TotalCount: Ptr(4), + Jobs: []*WorkflowJob{ + {ID: Ptr(int64(399444496)), RunID: Ptr(int64(29679449)), StartedAt: refTimestamp(1136178000), CompletedAt: refTimestamp(1136178001)}, + {ID: Ptr(int64(399444497)), RunID: Ptr(int64(29679449)), StartedAt: refTimestamp(1136178002), CompletedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(jobs, want) { + t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want) + } +} + +func TestActionsService_ListWorkflowJobsAttempt(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/29679449/attempts/1/jobs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":`+refTimeStr(1136178000)+`,"completed_at":`+refTimeStr(1136178001)+`,"run_attempt":2},{"id":399444497,"run_id":29679449,"started_at":`+refTimeStr(1136178002)+`,"completed_at":`+refTimeStr(1136178003)+`,"run_attempt":2}]}`) + }) + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + jobs, _, err := client.Actions.ListWorkflowJobsAttempt(ctx, "o", "r", 29679449, 1, opts) + if err != nil { + t.Errorf("Actions.ListWorkflowJobsAttempt returned error: %v", err) + } + + want := &Jobs{ + TotalCount: Ptr(4), + Jobs: []*WorkflowJob{ + { + ID: Ptr(int64(399444496)), + RunID: Ptr(int64(29679449)), + StartedAt: refTimestamp(1136178000), + CompletedAt: refTimestamp(1136178001), + RunAttempt: Ptr(int64(2)), + }, + { + ID: Ptr(int64(399444497)), + RunID: Ptr(int64(29679449)), + StartedAt: refTimestamp(1136178002), + CompletedAt: refTimestamp(1136178003), + RunAttempt: Ptr(int64(2)), + }, + }, + } + if !cmp.Equal(jobs, want) { + t.Errorf("Actions.ListWorkflowJobsAttempt returned %+v, want %+v", jobs, want) + } + + const methodName = "ListWorkflowJobsAttempt" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListWorkflowJobsAttempt(ctx, "\n", "\n", 29679449, 1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListWorkflowJobsAttempt(ctx, "o", "r", 29679449, 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowJobByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/jobs/399444496", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":399444496,"started_at":`+refTimeStr(1136178000)+`,"completed_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + job, _, err := client.Actions.GetWorkflowJobByID(ctx, "o", "r", 399444496) + if err != nil { + t.Errorf("Actions.GetWorkflowJobByID returned error: %v", err) + } + + want := &WorkflowJob{ + ID: Ptr(int64(399444496)), + StartedAt: refTimestamp(1136178000), + CompletedAt: refTimestamp(1136178001), + } + if !cmp.Equal(job, want) { + t.Errorf("Actions.GetWorkflowJobByID returned %+v, want %+v", job, want) + } + + const methodName = "GetWorkflowJobByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowJobByID(ctx, "\n", "\n", 399444496) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowJobByID(ctx, "o", "r", 399444496) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowJobLogs(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) + if err != nil { + t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.GetWorkflowJobLogs returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url, want) + } + + const methodName = "GetWorkflowJobLogs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowJobLogs(ctx, "\n", "\n", 399444496, 1) + return err + }) + + // Create "bad" client with custom round tripper + badClient, err := client.Clone(WithTransport(roundTripperFunc(func(*http.Request) (*http.Response, error) { + return nil, errors.New("failed to get workflow logs") + }))) + if err != nil { + t.Fatalf("failed to clone client: %v", err) + } + testBadOptions(t, methodName, func() (err error) { + _, _, err = badClient.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) + return err + }) + }) + } +} + +func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusMovedPermanently) + }) + + ctx := t.Context() + _, resp, _ := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 0) + if resp.StatusCode != http.StatusMovedPermanently { + t.Errorf("Actions.GetWorkflowJobLogs returned status: %v, want %v", resp.StatusCode, http.StatusMovedPermanently) + } + }) + } +} + +func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_followRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) + if err != nil { + t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err) + } + + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.GetWorkflowJobLogs returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url, want) + } + }) + } +} + +func TestActionsService_GetWorkflowJobLogs_unexpectedCode(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) + if err == nil { + t.Fatal("Actions.GetWorkflowJobLogs should return error on unexpected code") + } + if !strings.Contains(err.Error(), "unexpected status code") { + t.Error("Actions.GetWorkflowJobLogs should return unexpected status code") + } + if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want { + t.Errorf("Actions.GetWorkflowJobLogs return status %v, want %v", got, want) + } + if url != nil { + t.Errorf("Actions.GetWorkflowJobLogs return %+v, want nil", url) + } + }) + } +} diff --git a/github/actions_workflow_runs.go b/github/actions_workflow_runs.go new file mode 100644 index 00000000000..c56fa4df4f8 --- /dev/null +++ b/github/actions_workflow_runs.go @@ -0,0 +1,547 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "net/url" +) + +// WorkflowRun represents a repository action workflow run. +type WorkflowRun struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + NodeID *string `json:"node_id,omitempty"` + HeadBranch *string `json:"head_branch,omitempty"` + HeadSHA *string `json:"head_sha,omitempty"` + Path *string `json:"path,omitempty"` + RunNumber *int `json:"run_number,omitempty"` + RunAttempt *int `json:"run_attempt,omitempty"` + Event *string `json:"event,omitempty"` + DisplayTitle *string `json:"display_title,omitempty"` + Status *string `json:"status,omitempty"` + Conclusion *string `json:"conclusion,omitempty"` + WorkflowID *int64 `json:"workflow_id,omitempty"` + CheckSuiteID *int64 `json:"check_suite_id,omitempty"` + CheckSuiteNodeID *string `json:"check_suite_node_id,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + PullRequests []*PullRequest `json:"pull_requests,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + RunStartedAt *Timestamp `json:"run_started_at,omitempty"` + JobsURL *string `json:"jobs_url,omitempty"` + LogsURL *string `json:"logs_url,omitempty"` + CheckSuiteURL *string `json:"check_suite_url,omitempty"` + ArtifactsURL *string `json:"artifacts_url,omitempty"` + CancelURL *string `json:"cancel_url,omitempty"` + RerunURL *string `json:"rerun_url,omitempty"` + PreviousAttemptURL *string `json:"previous_attempt_url,omitempty"` + HeadCommit *HeadCommit `json:"head_commit,omitempty"` + WorkflowURL *string `json:"workflow_url,omitempty"` + Repository *Repository `json:"repository,omitempty"` + HeadRepository *Repository `json:"head_repository,omitempty"` + Actor *User `json:"actor,omitempty"` + TriggeringActor *User `json:"triggering_actor,omitempty"` + ReferencedWorkflows []*ReferencedWorkflow `json:"referenced_workflows,omitempty"` +} + +// WorkflowRuns represents a slice of repository action workflow run. +type WorkflowRuns struct { + TotalCount *int `json:"total_count,omitempty"` + WorkflowRuns []*WorkflowRun `json:"workflow_runs,omitempty"` +} + +// ListWorkflowRunsOptions specifies optional parameters to ListWorkflowRuns. +type ListWorkflowRunsOptions struct { + Actor string `url:"actor,omitempty"` + Branch string `url:"branch,omitempty"` + Event string `url:"event,omitempty"` + Status string `url:"status,omitempty"` + Created string `url:"created,omitempty"` + HeadSHA string `url:"head_sha,omitempty"` + ExcludePullRequests bool `url:"exclude_pull_requests,omitempty"` + CheckSuiteID int64 `url:"check_suite_id,omitempty"` + ListOptions +} + +// WorkflowRunUsage represents a usage of a specific workflow run. +type WorkflowRunUsage struct { + Billable *WorkflowRunBillMap `json:"billable,omitempty"` + RunDurationMS *int64 `json:"run_duration_ms,omitempty"` +} + +// WorkflowRunBillMap represents different runner environments available for a workflow run. +// Its key is the name of its environment, e.g. "UBUNTU", "MACOS", "WINDOWS", etc. +type WorkflowRunBillMap map[string]*WorkflowRunBill + +// WorkflowRunBill specifies billable time for a specific environment in a workflow run. +type WorkflowRunBill struct { + TotalMS *int64 `json:"total_ms,omitempty"` + Jobs *int `json:"jobs,omitempty"` + JobRuns []*WorkflowRunJobRun `json:"job_runs,omitempty"` +} + +// WorkflowRunJobRun represents a usage of individual jobs of a specific workflow run. +type WorkflowRunJobRun struct { + JobID *int `json:"job_id,omitempty"` + DurationMS *int64 `json:"duration_ms,omitempty"` +} + +// WorkflowRunAttemptOptions specifies optional parameters to GetWorkflowRunAttempt. +type WorkflowRunAttemptOptions struct { + ExcludePullRequests *bool `url:"exclude_pull_requests,omitempty"` +} + +// PendingDeploymentsRequest specifies body parameters to PendingDeployments. +type PendingDeploymentsRequest struct { + EnvironmentIDs []int64 `json:"environment_ids"` + // State can be one of: "approved", "rejected". + State string `json:"state"` + Comment string `json:"comment"` +} + +// ReferencedWorkflow represents a referenced workflow in a workflow run. +type ReferencedWorkflow struct { + Path *string `json:"path,omitempty"` + SHA *string `json:"sha,omitempty"` + Ref *string `json:"ref,omitempty"` +} + +// PendingDeployment represents the pending_deployments response. +type PendingDeployment struct { + Environment *PendingDeploymentEnvironment `json:"environment,omitempty"` + WaitTimer *int64 `json:"wait_timer,omitempty"` + WaitTimerStartedAt *Timestamp `json:"wait_timer_started_at,omitempty"` + CurrentUserCanApprove *bool `json:"current_user_can_approve,omitempty"` + Reviewers []*RequiredReviewer `json:"reviewers,omitempty"` +} + +// PendingDeploymentEnvironment represents pending deployment environment properties. +type PendingDeploymentEnvironment struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// ReviewCustomDeploymentProtectionRuleRequest specifies the parameters to ReviewCustomDeploymentProtectionRule. +type ReviewCustomDeploymentProtectionRuleRequest struct { + EnvironmentName string `json:"environment_name"` + State string `json:"state"` + Comment string `json:"comment"` +} + +func (s *ActionsService) listWorkflowRuns(ctx context.Context, endpoint string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) { + u, err := addOptions(endpoint, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runs *WorkflowRuns + resp, err := s.client.Do(req, &runs) + if err != nil { + return nil, resp, err + } + + return runs, resp, nil +} + +// ListWorkflowRunsByID lists all workflow runs by workflow ID. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-workflow +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs +func (s *ActionsService) ListWorkflowRunsByID(ctx context.Context, owner, repo string, workflowID int64, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/runs", owner, repo, workflowID) + return s.listWorkflowRuns(ctx, u, opts) +} + +// ListWorkflowRunsByFileName lists all workflow runs by workflow file name. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-workflow +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs +func (s *ActionsService) ListWorkflowRunsByFileName(ctx context.Context, owner, repo, workflowFileName string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/runs", owner, repo, workflowFileName) + return s.listWorkflowRuns(ctx, u, opts) +} + +// ListRepositoryWorkflowRuns lists all workflow runs for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs +func (s *ActionsService) ListRepositoryWorkflowRuns(ctx context.Context, owner, repo string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runs *WorkflowRuns + resp, err := s.client.Do(req, &runs) + if err != nil { + return nil, resp, err + } + + return runs, resp, nil +} + +// GetWorkflowRunByID gets a specific workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id} +func (s *ActionsService) GetWorkflowRunByID(ctx context.Context, owner, repo string, runID int64) (*WorkflowRun, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var run *WorkflowRun + resp, err := s.client.Do(req, &run) + if err != nil { + return nil, resp, err + } + + return run, resp, nil +} + +// GetWorkflowRunAttempt gets a specific workflow run attempt. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#get-a-workflow-run-attempt +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number} +func (s *ActionsService) GetWorkflowRunAttempt(ctx context.Context, owner, repo string, runID int64, attemptNumber int, opts *WorkflowRunAttemptOptions) (*WorkflowRun, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/attempts/%v", owner, repo, runID, attemptNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var run *WorkflowRun + resp, err := s.client.Do(req, &run) + if err != nil { + return nil, resp, err + } + + return run, resp, nil +} + +// GetWorkflowRunAttemptLogs gets a redirect URL to download a plain text file of logs for a workflow run for attempt number. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve a workflow run ID from the DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#download-workflow-run-attempt-logs +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/logs +func (s *ActionsService) GetWorkflowRunAttemptLogs(ctx context.Context, owner, repo string, runID int64, attemptNumber, maxRedirects int) (*url.URL, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/attempts/%v/logs", owner, repo, runID, attemptNumber) + + if s.client.rateLimitRedirectionalEndpoints { + return s.getWorkflowRunAttemptLogsWithRateLimit(ctx, u, maxRedirects) + } + + return s.getWorkflowRunAttemptLogsWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) getWorkflowRunAttemptLogsWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusFound { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) + } + + parsedURL, err := url.Parse(resp.Header.Get("Location")) + return parsedURL, newResponse(resp), err +} + +func (s *ActionsService) getWorkflowRunAttemptLogsWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} + +// RerunWorkflowByID re-runs a workflow by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID of a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#re-run-a-workflow +// +//meta:operation POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun +func (s *ActionsService) RerunWorkflowByID(ctx context.Context, owner, repo string, runID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/rerun", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RerunFailedJobsByID re-runs all of the failed jobs and their dependent jobs in a workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#re-run-failed-jobs-from-a-workflow-run +// +//meta:operation POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs +func (s *ActionsService) RerunFailedJobsByID(ctx context.Context, owner, repo string, runID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/rerun-failed-jobs", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RerunJobByID re-runs a job and its dependent jobs in a workflow run by ID. +// +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#re-run-a-job-from-a-workflow-run +// +//meta:operation POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun +func (s *ActionsService) RerunJobByID(ctx context.Context, owner, repo string, jobID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v/rerun", owner, repo, jobID) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CancelWorkflowRunByID cancels a workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#cancel-a-workflow-run +// +//meta:operation POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel +func (s *ActionsService) CancelWorkflowRunByID(ctx context.Context, owner, repo string, runID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/cancel", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetWorkflowRunLogs gets a redirect URL to download a plain text file of logs for a workflow run. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#download-workflow-run-logs +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs +func (s *ActionsService) GetWorkflowRunLogs(ctx context.Context, owner, repo string, runID int64, maxRedirects int) (*url.URL, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/logs", owner, repo, runID) + + if s.client.rateLimitRedirectionalEndpoints { + return s.getWorkflowRunLogsWithRateLimit(ctx, u, maxRedirects) + } + + return s.getWorkflowRunLogsWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) getWorkflowRunLogsWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusFound { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) + } + + parsedURL, err := url.Parse(resp.Header.Get("Location")) + return parsedURL, newResponse(resp), err +} + +func (s *ActionsService) getWorkflowRunLogsWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} + +// DeleteWorkflowRun deletes a workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#delete-a-workflow-run +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/runs/{run_id} +func (s *ActionsService) DeleteWorkflowRun(ctx context.Context, owner, repo string, runID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteWorkflowRunLogs deletes all logs for a workflow run. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#delete-workflow-run-logs +// +//meta:operation DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs +func (s *ActionsService) DeleteWorkflowRunLogs(ctx context.Context, owner, repo string, runID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/logs", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetWorkflowRunUsageByID gets a specific workflow usage run by run ID in the unit of billable milliseconds. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#get-workflow-run-usage +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/timing +func (s *ActionsService) GetWorkflowRunUsageByID(ctx context.Context, owner, repo string, runID int64) (*WorkflowRunUsage, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/timing", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var workflowRunUsage *WorkflowRunUsage + resp, err := s.client.Do(req, &workflowRunUsage) + if err != nil { + return nil, resp, err + } + + return workflowRunUsage, resp, nil +} + +// GetPendingDeployments get all deployment environments for a workflow run that are waiting for protection rules to pass. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#get-pending-deployments-for-a-workflow-run +// +//meta:operation GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments +func (s *ActionsService) GetPendingDeployments(ctx context.Context, owner, repo string, runID int64) ([]*PendingDeployment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/pending_deployments", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var deployments []*PendingDeployment + resp, err := s.client.Do(req, &deployments) + if err != nil { + return nil, resp, err + } + + return deployments, resp, nil +} + +// PendingDeployments approve or reject pending deployments that are waiting on approval by a required reviewer. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#review-pending-deployments-for-a-workflow-run +// +//meta:operation POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments +func (s *ActionsService) PendingDeployments(ctx context.Context, owner, repo string, runID int64, body *PendingDeploymentsRequest) ([]*Deployment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/pending_deployments", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var deployments []*Deployment + resp, err := s.client.Do(req, &deployments) + if err != nil { + return nil, resp, err + } + + return deployments, resp, nil +} + +// ReviewCustomDeploymentProtectionRule approves or rejects custom deployment protection rules provided by a GitHub App for a workflow run. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflow-runs?apiVersion=2022-11-28#review-custom-deployment-protection-rules-for-a-workflow-run +// +//meta:operation POST /repos/{owner}/{repo}/actions/runs/{run_id}/deployment_protection_rule +func (s *ActionsService) ReviewCustomDeploymentProtectionRule(ctx context.Context, owner, repo string, runID int64, body *ReviewCustomDeploymentProtectionRuleRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/deployment_protection_rule", owner, repo, runID) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + return resp, err +} diff --git a/github/actions_workflow_runs_test.go b/github/actions_workflow_runs_test.go new file mode 100644 index 00000000000..1731bd21b6c --- /dev/null +++ b/github/actions_workflow_runs_test.go @@ -0,0 +1,1051 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListWorkflowRunsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/29679449/runs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"workflow_runs":[{"id":399444496,"run_number":296,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"id":399444497,"run_number":296,"created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListWorkflowRunsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + runs, _, err := client.Actions.ListWorkflowRunsByID(ctx, "o", "r", 29679449, opts) + if err != nil { + t.Errorf("Actions.ListWorkflowRunsByID returned error: %v", err) + } + + want := &WorkflowRuns{ + TotalCount: Ptr(4), + WorkflowRuns: []*WorkflowRun{ + {ID: Ptr(int64(399444496)), RunNumber: Ptr(296), CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {ID: Ptr(int64(399444497)), RunNumber: Ptr(296), CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(runs, want) { + t.Errorf("Actions.ListWorkflowRunsByID returned %+v, want %+v", runs, want) + } + + const methodName = "ListWorkflowRunsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListWorkflowRunsByID(ctx, "\n", "\n", 29679449, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListWorkflowRunsByID(ctx, "o", "r", 29679449, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_ListWorkflowRunsFileName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/29679449/runs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"workflow_runs":[{"id":399444496,"run_number":296,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"id":399444497,"run_number":296,"created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListWorkflowRunsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + runs, _, err := client.Actions.ListWorkflowRunsByFileName(ctx, "o", "r", "29679449", opts) + if err != nil { + t.Errorf("Actions.ListWorkflowRunsByFileName returned error: %v", err) + } + + want := &WorkflowRuns{ + TotalCount: Ptr(4), + WorkflowRuns: []*WorkflowRun{ + {ID: Ptr(int64(399444496)), RunNumber: Ptr(296), CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {ID: Ptr(int64(399444497)), RunNumber: Ptr(296), CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(runs, want) { + t.Errorf("Actions.ListWorkflowRunsByFileName returned %+v, want %+v", runs, want) + } + + const methodName = "ListWorkflowRunsByFileName" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListWorkflowRunsByFileName(ctx, "\n", "\n", "29679449", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListWorkflowRunsByFileName(ctx, "o", "r", "29679449", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowRunByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/29679449", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":399444496,"run_number":296,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + runs, _, err := client.Actions.GetWorkflowRunByID(ctx, "o", "r", 29679449) + if err != nil { + t.Errorf("Actions.GetWorkflowRunByID returned error: %v", err) + } + + want := &WorkflowRun{ + ID: Ptr(int64(399444496)), + RunNumber: Ptr(296), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + + if !cmp.Equal(runs, want) { + t.Errorf("Actions.GetWorkflowRunByID returned %+v, want %+v", runs, want) + } + + const methodName = "GetWorkflowRunByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunByID(ctx, "\n", "\n", 29679449) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowRunByID(ctx, "o", "r", 29679449) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowRunAttempt(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/29679449/attempts/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"exclude_pull_requests": "true"}) + fmt.Fprint(w, `{"id":399444496,"run_number":296,"run_attempt":3,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + opts := &WorkflowRunAttemptOptions{ExcludePullRequests: Ptr(true)} + ctx := t.Context() + runs, _, err := client.Actions.GetWorkflowRunAttempt(ctx, "o", "r", 29679449, 3, opts) + if err != nil { + t.Errorf("Actions.GetWorkflowRunAttempt returned error: %v", err) + } + + want := &WorkflowRun{ + ID: Ptr(int64(399444496)), + RunNumber: Ptr(296), + RunAttempt: Ptr(3), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + + if !cmp.Equal(runs, want) { + t.Errorf("Actions.GetWorkflowRunAttempt returned %+v, want %+v", runs, want) + } + + const methodName = "GetWorkflowRunAttempt" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunAttempt(ctx, "\n", "\n", 29679449, 3, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowRunAttempt(ctx, "o", "r", 29679449, 3, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowRunAttemptLogs(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, 1) + if err != nil { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned %+v, want %+v", url, want) + } + + const methodName = "GetWorkflowRunAttemptLogs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunAttemptLogs(ctx, "\n", "\n", 399444496, 2, 1) + return err + }) + }) + } +} + +func TestActionsService_GetWorkflowRunAttemptLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusMovedPermanently) + }) + + ctx := t.Context() + _, resp, _ := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, 0) + if resp.StatusCode != http.StatusMovedPermanently { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned status: %v, want %v", resp.StatusCode, http.StatusMovedPermanently) + } + }) + } +} + +func TestActionsService_GetWorkflowRunAttemptLogs_StatusMovedPermanently_followRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, 1) + if err != nil { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned error: %v", err) + } + + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Actions.GetWorkflowRunAttemptLogs returned %+v, want %+v", url, want) + } + + const methodName = "GetWorkflowRunAttemptLogs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunAttemptLogs(ctx, "\n", "\n", 399444496, 2, 1) + return err + }) + }) + } +} + +func TestActionsService_GetWorkflowRunAttemptLogs_unexpectedCode(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/actions/runs/399444496/attempts/2/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowRunAttemptLogs(ctx, "o", "r", 399444496, 2, 1) + if err == nil { + t.Fatal("Actions.GetWorkflowRunAttemptLogs should return error on unexpected code") + } + if !strings.Contains(err.Error(), "unexpected status code") { + t.Error("Actions.GetWorkflowRunAttemptLogs should return unexpected status code") + } + if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want { + t.Errorf("Actions.GetWorkflowRunAttemptLogs return status %v, want %v", got, want) + } + if url != nil { + t.Errorf("Actions.GetWorkflowRunAttemptLogs return %+v, want nil", url) + } + }) + } +} + +func TestActionsService_RerunWorkflowByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/3434/rerun", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + resp, err := client.Actions.RerunWorkflowByID(ctx, "o", "r", 3434) + if err != nil { + t.Errorf("Actions.RerunWorkflowByID returned error: %v", err) + } + if resp.StatusCode != http.StatusCreated { + t.Errorf("Actions.RerunWorkflowByID returned status: %v, want %v", resp.StatusCode, http.StatusCreated) + } + + const methodName = "RerunWorkflowByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RerunWorkflowByID(ctx, "\n", "\n", 3434) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RerunWorkflowByID(ctx, "o", "r", 3434) + }) +} + +func TestActionsService_RerunFailedJobsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/3434/rerun-failed-jobs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + resp, err := client.Actions.RerunFailedJobsByID(ctx, "o", "r", 3434) + if err != nil { + t.Errorf("Actions.RerunFailedJobsByID returned error: %v", err) + } + if resp.StatusCode != http.StatusCreated { + t.Errorf("Actions.RerunFailedJobsByID returned status: %v, want %v", resp.StatusCode, http.StatusCreated) + } + + const methodName = "RerunFailedJobsByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RerunFailedJobsByID(ctx, "\n", "\n", 3434) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RerunFailedJobsByID(ctx, "o", "r", 3434) + }) +} + +func TestActionsService_RerunJobByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/jobs/3434/rerun", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + resp, err := client.Actions.RerunJobByID(ctx, "o", "r", 3434) + if err != nil { + t.Errorf("Actions.RerunJobByID returned error: %v", err) + } + if resp.StatusCode != http.StatusCreated { + t.Errorf("Actions.RerunJobByID returned status: %v, want %v", resp.StatusCode, http.StatusCreated) + } + + const methodName = "RerunJobByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RerunJobByID(ctx, "\n", "\n", 3434) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RerunJobByID(ctx, "o", "r", 3434) + }) +} + +func TestActionsService_CancelWorkflowRunByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/3434/cancel", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusAccepted) + }) + + ctx := t.Context() + resp, err := client.Actions.CancelWorkflowRunByID(ctx, "o", "r", 3434) + if !errors.As(err, new(*AcceptedError)) { + t.Errorf("Actions.CancelWorkflowRunByID returned error: %v (want AcceptedError)", err) + } + if resp.StatusCode != http.StatusAccepted { + t.Errorf("Actions.CancelWorkflowRunByID returned status: %v, want %v", resp.StatusCode, http.StatusAccepted) + } + + const methodName = "CancelWorkflowRunByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CancelWorkflowRunByID(ctx, "\n", "\n", 3434) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CancelWorkflowRunByID(ctx, "o", "r", 3434) + }) +} + +func TestActionsService_GetWorkflowRunLogs(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowRunLogs(ctx, "o", "r", 399444496, 1) + if err != nil { + t.Errorf("Actions.GetWorkflowRunLogs returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.GetWorkflowRunLogs returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Actions.GetWorkflowRunLogs returned %+v, want %+v", url, want) + } + + const methodName = "GetWorkflowRunLogs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunLogs(ctx, "\n", "\n", 399444496, 1) + return err + }) + }) + } +} + +func TestActionsService_GetWorkflowRunLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusMovedPermanently) + }) + + ctx := t.Context() + _, resp, _ := client.Actions.GetWorkflowRunLogs(ctx, "o", "r", 399444496, 0) + if resp.StatusCode != http.StatusMovedPermanently { + t.Errorf("Actions.GetWorkflowRunLogs returned status: %v, want %v", resp.StatusCode, http.StatusMovedPermanently) + } + }) + } +} + +func TestActionsService_GetWorkflowRunLogs_StatusMovedPermanently_followRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/actions/runs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowRunLogs(ctx, "o", "r", 399444496, 1) + if err != nil { + t.Errorf("Actions.GetWorkflowRunLogs returned error: %v", err) + } + + if resp.StatusCode != http.StatusFound { + t.Errorf("Actions.GetWorkflowRunLogs returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Actions.GetWorkflowRunLogs returned %+v, want %+v", url, want) + } + + const methodName = "GetWorkflowRunLogs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunLogs(ctx, "\n", "\n", 399444496, 1) + return err + }) + }) + } +} + +func TestActionsService_GetWorkflowRunLogs_unexpectedCode(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/actions/runs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + url, resp, err := client.Actions.GetWorkflowRunLogs(ctx, "o", "r", 399444496, 1) + if err == nil { + t.Fatal("Actions.GetWorkflowRunLogs should return error on unexpected code") + } + if !strings.Contains(err.Error(), "unexpected status code") { + t.Error("Actions.GetWorkflowRunLogs should return unexpected status code") + } + if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want { + t.Errorf("Actions.GetWorkflowRunLogs return status %v, want %v", got, want) + } + if url != nil { + t.Errorf("Actions.GetWorkflowRunLogs return %+v, want nil", url) + } + }) + } +} + +func TestActionsService_ListRepositoryWorkflowRuns(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":2, + "workflow_runs":[ + {"id":298499444,"run_number":301,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}, + {"id":298499445,"run_number":302,"created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListWorkflowRunsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + runs, _, err := client.Actions.ListRepositoryWorkflowRuns(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRepositoryWorkflowRuns returned error: %v", err) + } + + expected := &WorkflowRuns{ + TotalCount: Ptr(2), + WorkflowRuns: []*WorkflowRun{ + {ID: Ptr(int64(298499444)), RunNumber: Ptr(301), CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {ID: Ptr(int64(298499445)), RunNumber: Ptr(302), CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + + if !cmp.Equal(runs, expected) { + t.Errorf("Actions.ListRepositoryWorkflowRuns returned %+v, want %+v", runs, expected) + } + + const methodName = "ListRepositoryWorkflowRuns" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepositoryWorkflowRuns(ctx, "\n", "\n", opts) + + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepositoryWorkflowRuns(ctx, "o", "r", opts) + + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_DeleteWorkflowRun(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/399444496", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Actions.DeleteWorkflowRun(ctx, "o", "r", 399444496); err != nil { + t.Errorf("DeleteWorkflowRun returned error: %v", err) + } + + const methodName = "DeleteWorkflowRun" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteWorkflowRun(ctx, "\n", "\n", 399444496) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteWorkflowRun(ctx, "o", "r", 399444496) + }) +} + +func TestActionsService_DeleteWorkflowRunLogs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Actions.DeleteWorkflowRunLogs(ctx, "o", "r", 399444496); err != nil { + t.Errorf("DeleteWorkflowRunLogs returned error: %v", err) + } + + const methodName = "DeleteWorkflowRunLogs" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteWorkflowRunLogs(ctx, "\n", "\n", 399444496) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteWorkflowRunLogs(ctx, "o", "r", 399444496) + }) +} + +func TestActionsService_ReviewCustomDeploymentProtectionRule(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/9444496/deployment_protection_rule", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + + w.WriteHeader(http.StatusNoContent) + }) + + request := ReviewCustomDeploymentProtectionRuleRequest{ + EnvironmentName: "production", + State: "approved", + Comment: "Approve deployment", + } + + ctx := t.Context() + if _, err := client.Actions.ReviewCustomDeploymentProtectionRule(ctx, "o", "r", 9444496, &request); err != nil { + t.Errorf("ReviewCustomDeploymentProtectionRule returned error: %v", err) + } + + const methodName = "ReviewCustomDeploymentProtectionRule" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.ReviewCustomDeploymentProtectionRule(ctx, "\n", "\n", 9444496, &request) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.ReviewCustomDeploymentProtectionRule(ctx, "o", "r", 9444496, &request) + }) +} + +func TestActionsService_GetWorkflowRunUsageByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/29679449/timing", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"billable":{"UBUNTU":{"total_ms":180000,"jobs":1,"job_runs":[{"job_id":1,"duration_ms":60000}]},"MACOS":{"total_ms":240000,"jobs":2,"job_runs":[{"job_id":2,"duration_ms":30000},{"job_id":3,"duration_ms":10000}]},"WINDOWS":{"total_ms":300000,"jobs":2}},"run_duration_ms":500000}`) + }) + + ctx := t.Context() + workflowRunUsage, _, err := client.Actions.GetWorkflowRunUsageByID(ctx, "o", "r", 29679449) + if err != nil { + t.Errorf("Actions.GetWorkflowRunUsageByID returned error: %v", err) + } + + want := &WorkflowRunUsage{ + Billable: &WorkflowRunBillMap{ + "UBUNTU": &WorkflowRunBill{ + TotalMS: Ptr(int64(180000)), + Jobs: Ptr(1), + JobRuns: []*WorkflowRunJobRun{ + { + JobID: Ptr(1), + DurationMS: Ptr(int64(60000)), + }, + }, + }, + "MACOS": &WorkflowRunBill{ + TotalMS: Ptr(int64(240000)), + Jobs: Ptr(2), + JobRuns: []*WorkflowRunJobRun{ + { + JobID: Ptr(2), + DurationMS: Ptr(int64(30000)), + }, + { + JobID: Ptr(3), + DurationMS: Ptr(int64(10000)), + }, + }, + }, + "WINDOWS": &WorkflowRunBill{ + TotalMS: Ptr(int64(300000)), + Jobs: Ptr(2), + }, + }, + RunDurationMS: Ptr(int64(500000)), + } + + if !cmp.Equal(workflowRunUsage, want) { + t.Errorf("Actions.GetWorkflowRunUsageByID returned %+v, want %+v", workflowRunUsage, want) + } + + const methodName = "GetWorkflowRunUsageByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowRunUsageByID(ctx, "\n", "\n", 29679449) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowRunUsageByID(ctx, "o", "r", 29679449) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_PendingDeployments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PendingDeploymentsRequest{EnvironmentIDs: []int64{3, 4}, State: "approved", Comment: ""} + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/pending_deployments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + ctx := t.Context() + deployments, _, err := client.Actions.PendingDeployments(ctx, "o", "r", 399444496, input) + if err != nil { + t.Errorf("Actions.PendingDeployments returned error: %v", err) + } + + want := []*Deployment{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(deployments, want) { + t.Errorf("Actions.PendingDeployments returned %+v, want %+v", deployments, want) + } + + const methodName = "PendingDeployments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.PendingDeployments(ctx, "\n", "\n", 399444496, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.PendingDeployments(ctx, "o", "r", 399444496, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetPendingDeployments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/runs/399444496/pending_deployments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "environment": { + "id": 1, + "node_id": "nid", + "name": "n", + "url": "u", + "html_url": "hu" + }, + "wait_timer": 0, + "wait_timer_started_at": `+refTimeStr(1136178000)+`, + "current_user_can_approve": false, + "reviewers": [] + }, + { + "environment": { + "id": 2, + "node_id": "nid", + "name": "n", + "url": "u", + "html_url": "hu" + }, + "wait_timer": 13, + "wait_timer_started_at": `+refTimeStr(1136178001)+`, + "current_user_can_approve": true, + "reviewers": [ + { + "type": "User", + "reviewer": { + "login": "l" + } + }, + { + "type": "Team", + "reviewer": { + "name": "t", + "slug": "s" + } + } + ] + } + ]`) + }) + + ctx := t.Context() + deployments, _, err := client.Actions.GetPendingDeployments(ctx, "o", "r", 399444496) + if err != nil { + t.Errorf("Actions.GetPendingDeployments returned error: %v", err) + } + + want := []*PendingDeployment{ + { + Environment: &PendingDeploymentEnvironment{ + ID: Ptr(int64(1)), + NodeID: Ptr("nid"), + Name: Ptr("n"), + URL: Ptr("u"), + HTMLURL: Ptr("hu"), + }, + WaitTimer: Ptr(int64(0)), + WaitTimerStartedAt: refTimestamp(1136178000), + CurrentUserCanApprove: Ptr(false), + Reviewers: []*RequiredReviewer{}, + }, + { + Environment: &PendingDeploymentEnvironment{ + ID: Ptr(int64(2)), + NodeID: Ptr("nid"), + Name: Ptr("n"), + URL: Ptr("u"), + HTMLURL: Ptr("hu"), + }, + WaitTimer: Ptr(int64(13)), + WaitTimerStartedAt: refTimestamp(1136178001), + CurrentUserCanApprove: Ptr(true), + Reviewers: []*RequiredReviewer{ + { + Type: Ptr("User"), + Reviewer: &User{ + Login: Ptr("l"), + }, + }, + { + Type: Ptr("Team"), + Reviewer: &Team{ + Name: Ptr("t"), + Slug: Ptr("s"), + }, + }, + }, + }, + } + + if !cmp.Equal(deployments, want) { + t.Errorf("Actions.GetPendingDeployments returned %+v, want %+v", deployments, want) + } + + const methodName = "GetPendingDeployments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetPendingDeployments(ctx, "\n", "\n", 399444496) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetPendingDeployments(ctx, "o", "r", 399444496) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/actions_workflows.go b/github/actions_workflows.go new file mode 100644 index 00000000000..8c2610a4b02 --- /dev/null +++ b/github/actions_workflows.go @@ -0,0 +1,284 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Workflow represents a repository action workflow. +type Workflow struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + State *string `json:"state,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + BadgeURL *string `json:"badge_url,omitempty"` +} + +// Workflows represents a slice of repository action workflows. +type Workflows struct { + TotalCount *int `json:"total_count,omitempty"` + Workflows []*Workflow `json:"workflows,omitempty"` +} + +// WorkflowUsage represents a usage of a specific workflow. +type WorkflowUsage struct { + Billable *WorkflowBillMap `json:"billable,omitempty"` +} + +// WorkflowBillMap represents different runner environments available for a workflow. +// Its key is the name of its environment, e.g. "UBUNTU", "MACOS", "WINDOWS", etc. +type WorkflowBillMap map[string]*WorkflowBill + +// WorkflowBill specifies billable time for a specific environment in a workflow. +type WorkflowBill struct { + TotalMS *int64 `json:"total_ms,omitempty"` +} + +// CreateWorkflowDispatchEventRequest represents a request to create a workflow dispatch event. +type CreateWorkflowDispatchEventRequest struct { + // Ref represents the reference of the workflow run. + // The reference can be a branch or a tag. + // Ref is required when creating a workflow dispatch event. + Ref string `json:"ref"` + // Inputs represents input keys and values configured in the workflow file. + // The maximum number of properties is 25. + // Default: Any default properties configured in the workflow file will be used when `inputs` are omitted. + Inputs map[string]any `json:"inputs,omitempty"` + // ReturnRunDetails specifies whether the response should include + // the workflow run ID and URLs. + ReturnRunDetails *bool `json:"return_run_details,omitempty"` +} + +// WorkflowDispatchRunDetails represents the response from creating +// a workflow dispatch event when ReturnRunDetails is set to true. +type WorkflowDispatchRunDetails struct { + WorkflowRunID *int64 `json:"workflow_run_id,omitempty"` + RunURL *string `json:"run_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// WorkflowsPermissions represents the permissions for workflows in a repository. +type WorkflowsPermissions struct { + RunWorkflowsFromForkPullRequests *bool `json:"run_workflows_from_fork_pull_requests,omitempty"` + SendWriteTokensToWorkflows *bool `json:"send_write_tokens_to_workflows,omitempty"` + SendSecretsAndVariables *bool `json:"send_secrets_and_variables,omitempty"` + RequireApprovalForForkPRWorkflows *bool `json:"require_approval_for_fork_pr_workflows,omitempty"` +} + +func (w WorkflowsPermissions) String() string { + return Stringify(w) +} + +// WorkflowsPermissionsOpt specifies options for editing workflows permissions in a repository. +type WorkflowsPermissionsOpt struct { + RunWorkflowsFromForkPullRequests bool `json:"run_workflows_from_fork_pull_requests"` + SendWriteTokensToWorkflows *bool `json:"send_write_tokens_to_workflows,omitempty"` + SendSecretsAndVariables *bool `json:"send_secrets_and_variables,omitempty"` + RequireApprovalForForkPRWorkflows *bool `json:"require_approval_for_fork_pr_workflows,omitempty"` +} + +// ContributorApprovalPermissions represents the policy that controls +// when fork PR workflows require approval from a maintainer. +type ContributorApprovalPermissions struct { + ApprovalPolicy string `json:"approval_policy"` +} + +func (p ContributorApprovalPermissions) String() string { + return Stringify(p) +} + +// ListWorkflows lists all workflows in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#list-repository-workflows +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows +func (s *ActionsService) ListWorkflows(ctx context.Context, owner, repo string, opts *ListOptions) (*Workflows, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var workflows *Workflows + resp, err := s.client.Do(req, &workflows) + if err != nil { + return nil, resp, err + } + + return workflows, resp, nil +} + +// GetWorkflowByID gets a specific workflow by ID. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} +func (s *ActionsService) GetWorkflowByID(ctx context.Context, owner, repo string, workflowID int64) (*Workflow, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v", owner, repo, workflowID) + + return s.getWorkflow(ctx, u) +} + +// GetWorkflowByFileName gets a specific workflow by file name. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} +func (s *ActionsService) GetWorkflowByFileName(ctx context.Context, owner, repo, workflowFileName string) (*Workflow, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v", owner, repo, workflowFileName) + + return s.getWorkflow(ctx, u) +} + +func (s *ActionsService) getWorkflow(ctx context.Context, url string) (*Workflow, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var workflow *Workflow + resp, err := s.client.Do(req, &workflow) + if err != nil { + return nil, resp, err + } + + return workflow, resp, nil +} + +// GetWorkflowUsageByID gets a specific workflow usage by ID in the unit of billable milliseconds. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#get-workflow-usage +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing +func (s *ActionsService) GetWorkflowUsageByID(ctx context.Context, owner, repo string, workflowID int64) (*WorkflowUsage, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/timing", owner, repo, workflowID) + + return s.getWorkflowUsage(ctx, u) +} + +// GetWorkflowUsageByFileName gets a specific workflow usage by file name in the unit of billable milliseconds. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#get-workflow-usage +// +//meta:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing +func (s *ActionsService) GetWorkflowUsageByFileName(ctx context.Context, owner, repo, workflowFileName string) (*WorkflowUsage, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/timing", owner, repo, workflowFileName) + + return s.getWorkflowUsage(ctx, u) +} + +func (s *ActionsService) getWorkflowUsage(ctx context.Context, url string) (*WorkflowUsage, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var workflowUsage *WorkflowUsage + resp, err := s.client.Do(req, &workflowUsage) + if err != nil { + return nil, resp, err + } + + return workflowUsage, resp, nil +} + +// CreateWorkflowDispatchEventByID manually triggers a GitHub Actions workflow run. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event +// +//meta:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches +func (s *ActionsService) CreateWorkflowDispatchEventByID(ctx context.Context, owner, repo string, workflowID int64, body CreateWorkflowDispatchEventRequest) (*WorkflowDispatchRunDetails, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/dispatches", owner, repo, workflowID) + + return s.createWorkflowDispatchEvent(ctx, u, &body) +} + +// CreateWorkflowDispatchEventByFileName manually triggers a GitHub Actions workflow run. +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event +// +//meta:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches +func (s *ActionsService) CreateWorkflowDispatchEventByFileName(ctx context.Context, owner, repo, workflowFileName string, body CreateWorkflowDispatchEventRequest) (*WorkflowDispatchRunDetails, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/dispatches", owner, repo, workflowFileName) + + return s.createWorkflowDispatchEvent(ctx, u, &body) +} + +func (s *ActionsService) createWorkflowDispatchEvent(ctx context.Context, url string, body *CreateWorkflowDispatchEventRequest) (*WorkflowDispatchRunDetails, *Response, error) { + req, err := s.client.NewRequest(ctx, "POST", url, body) + if err != nil { + return nil, nil, err + } + + var dispatchRunDetails *WorkflowDispatchRunDetails + resp, err := s.client.Do(req, &dispatchRunDetails) + if err != nil { + return nil, resp, err + } + + return dispatchRunDetails, resp, nil +} + +// EnableWorkflowByID enables a workflow and sets the state of the workflow to "active". +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#enable-a-workflow +// +//meta:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable +func (s *ActionsService) EnableWorkflowByID(ctx context.Context, owner, repo string, workflowID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/enable", owner, repo, workflowID) + return s.doNewPutRequest(ctx, u) +} + +// EnableWorkflowByFileName enables a workflow and sets the state of the workflow to "active". +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#enable-a-workflow +// +//meta:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable +func (s *ActionsService) EnableWorkflowByFileName(ctx context.Context, owner, repo, workflowFileName string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/enable", owner, repo, workflowFileName) + return s.doNewPutRequest(ctx, u) +} + +// DisableWorkflowByID disables a workflow and sets the state of the workflow to "disabled_manually". +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#disable-a-workflow +// +//meta:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable +func (s *ActionsService) DisableWorkflowByID(ctx context.Context, owner, repo string, workflowID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/disable", owner, repo, workflowID) + return s.doNewPutRequest(ctx, u) +} + +// DisableWorkflowByFileName disables a workflow and sets the state of the workflow to "disabled_manually". +// +// GitHub API docs: https://docs.github.com/rest/actions/workflows?apiVersion=2022-11-28#disable-a-workflow +// +//meta:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable +func (s *ActionsService) DisableWorkflowByFileName(ctx context.Context, owner, repo, workflowFileName string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/disable", owner, repo, workflowFileName) + return s.doNewPutRequest(ctx, u) +} + +func (s *ActionsService) doNewPutRequest(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "PUT", url, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/actions_workflows_test.go b/github/actions_workflows_test.go new file mode 100644 index 00000000000..a1c9ca728b1 --- /dev/null +++ b/github/actions_workflows_test.go @@ -0,0 +1,533 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListWorkflows(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"workflows":[{"id":72844,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"id":72845,"created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + workflows, _, err := client.Actions.ListWorkflows(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListWorkflows returned error: %v", err) + } + + want := &Workflows{ + TotalCount: Ptr(4), + Workflows: []*Workflow{ + {ID: Ptr(int64(72844)), CreatedAt: refTimestamp(1136178000), UpdatedAt: refTimestamp(1136178001)}, + {ID: Ptr(int64(72845)), CreatedAt: refTimestamp(1136178002), UpdatedAt: refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(workflows, want) { + t.Errorf("Actions.ListWorkflows returned %+v, want %+v", workflows, want) + } + + const methodName = "ListWorkflows" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListWorkflows(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListWorkflows(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/72844", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":72844,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + workflow, _, err := client.Actions.GetWorkflowByID(ctx, "o", "r", 72844) + if err != nil { + t.Errorf("Actions.GetWorkflowByID returned error: %v", err) + } + + want := &Workflow{ + ID: Ptr(int64(72844)), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + if !cmp.Equal(workflow, want) { + t.Errorf("Actions.GetWorkflowByID returned %+v, want %+v", workflow, want) + } + + const methodName = "GetWorkflowByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowByID(ctx, "\n", "\n", -72844) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowByID(ctx, "o", "r", 72844) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowByFileName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/main.yml", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":72844,"created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + workflow, _, err := client.Actions.GetWorkflowByFileName(ctx, "o", "r", "main.yml") + if err != nil { + t.Errorf("Actions.GetWorkflowByFileName returned error: %v", err) + } + + want := &Workflow{ + ID: Ptr(int64(72844)), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + if !cmp.Equal(workflow, want) { + t.Errorf("Actions.GetWorkflowByFileName returned %+v, want %+v", workflow, want) + } + + const methodName = "GetWorkflowByFileName" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowByFileName(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowByFileName(ctx, "o", "r", "main.yml") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowUsageByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/72844/timing", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"billable":{"UBUNTU":{"total_ms":180000},"MACOS":{"total_ms":240000},"WINDOWS":{"total_ms":300000}}}`) + }) + + ctx := t.Context() + workflowUsage, _, err := client.Actions.GetWorkflowUsageByID(ctx, "o", "r", 72844) + if err != nil { + t.Errorf("Actions.GetWorkflowUsageByID returned error: %v", err) + } + + want := &WorkflowUsage{ + Billable: &WorkflowBillMap{ + "UBUNTU": &WorkflowBill{ + TotalMS: Ptr(int64(180000)), + }, + "MACOS": &WorkflowBill{ + TotalMS: Ptr(int64(240000)), + }, + "WINDOWS": &WorkflowBill{ + TotalMS: Ptr(int64(300000)), + }, + }, + } + if !cmp.Equal(workflowUsage, want) { + t.Errorf("Actions.GetWorkflowUsageByID returned %+v, want %+v", workflowUsage, want) + } + + const methodName = "GetWorkflowUsageByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowUsageByID(ctx, "\n", "\n", -72844) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowUsageByID(ctx, "o", "r", 72844) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetWorkflowUsageByFileName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/main.yml/timing", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"billable":{"UBUNTU":{"total_ms":180000},"MACOS":{"total_ms":240000},"WINDOWS":{"total_ms":300000}}}`) + }) + + ctx := t.Context() + workflowUsage, _, err := client.Actions.GetWorkflowUsageByFileName(ctx, "o", "r", "main.yml") + if err != nil { + t.Errorf("Actions.GetWorkflowUsageByFileName returned error: %v", err) + } + + want := &WorkflowUsage{ + Billable: &WorkflowBillMap{ + "UBUNTU": &WorkflowBill{ + TotalMS: Ptr(int64(180000)), + }, + "MACOS": &WorkflowBill{ + TotalMS: Ptr(int64(240000)), + }, + "WINDOWS": &WorkflowBill{ + TotalMS: Ptr(int64(300000)), + }, + }, + } + if !cmp.Equal(workflowUsage, want) { + t.Errorf("Actions.GetWorkflowUsageByFileName returned %+v, want %+v", workflowUsage, want) + } + + const methodName = "GetWorkflowUsageByFileName" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetWorkflowUsageByFileName(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetWorkflowUsageByFileName(ctx, "o", "r", "main.yml") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateWorkflowDispatchEventByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + event := CreateWorkflowDispatchEventRequest{ + Ref: "d4cfb6e7", + ReturnRunDetails: Ptr(true), + Inputs: map[string]any{ + "key": "value", + }, + } + mux.HandleFunc("/repos/o/r/actions/workflows/72844/dispatches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, event) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{"workflow_run_id":1,"run_url":"https://api.github.com/repos/o/r/actions/runs/1","html_url":"https://github.com/o/r/actions/runs/1"}`) + }) + + ctx := t.Context() + dispatchResponse, _, err := client.Actions.CreateWorkflowDispatchEventByID(ctx, "o", "r", 72844, event) + if err != nil { + t.Errorf("Actions.CreateWorkflowDispatchEventByID returned error: %v", err) + } + + want := &WorkflowDispatchRunDetails{ + WorkflowRunID: Ptr(int64(1)), + RunURL: Ptr("https://api.github.com/repos/o/r/actions/runs/1"), + HTMLURL: Ptr("https://github.com/o/r/actions/runs/1"), + } + if !cmp.Equal(dispatchResponse, want) { + t.Errorf("Actions.CreateWorkflowDispatchEventByID = %+v, want %+v", dispatchResponse, want) + } + + // Test s.client.NewRequest failure + client.baseURL.Path = "" + _, _, err = client.Actions.CreateWorkflowDispatchEventByID(ctx, "o", "r", 72844, event) + if err == nil { + t.Error("client.BaseURL.Path='' CreateWorkflowDispatchEventByID err = nil, want error") + } + + const methodName = "CreateWorkflowDispatchEventByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateWorkflowDispatchEventByID(ctx, "o", "r", 72844, event) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateWorkflowDispatchEventByID(ctx, "o", "r", 72844, event) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateWorkflowDispatchEventByFileName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + event := CreateWorkflowDispatchEventRequest{ + Ref: "d4cfb6e7", + ReturnRunDetails: Ptr(true), + Inputs: map[string]any{ + "key": "value", + }, + } + mux.HandleFunc("/repos/o/r/actions/workflows/main.yml/dispatches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, event) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{"workflow_run_id":1,"run_url":"https://api.github.com/repos/o/r/actions/runs/1","html_url":"https://github.com/o/r/actions/runs/1"}`) + }) + + ctx := t.Context() + dispatchResponse, _, err := client.Actions.CreateWorkflowDispatchEventByFileName(ctx, "o", "r", "main.yml", event) + if err != nil { + t.Errorf("Actions.CreateWorkflowDispatchEventByFileName returned error: %v", err) + } + + want := &WorkflowDispatchRunDetails{ + WorkflowRunID: Ptr(int64(1)), + RunURL: Ptr("https://api.github.com/repos/o/r/actions/runs/1"), + HTMLURL: Ptr("https://github.com/o/r/actions/runs/1"), + } + if !cmp.Equal(dispatchResponse, want) { + t.Errorf("Actions.CreateWorkflowDispatchEventByFileName = %+v, want %+v", dispatchResponse, want) + } + + // Test s.client.NewRequest failure + client.baseURL.Path = "" + _, _, err = client.Actions.CreateWorkflowDispatchEventByFileName(ctx, "o", "r", "main.yml", event) + if err == nil { + t.Error("client.BaseURL.Path='' CreateWorkflowDispatchEventByFileName err = nil, want error") + } + + const methodName = "CreateWorkflowDispatchEventByFileName" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.CreateWorkflowDispatchEventByFileName(ctx, "o", "r", "main.yml", event) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.CreateWorkflowDispatchEventByFileName(ctx, "o", "r", "main.yml", event) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateWorkflowDispatchEventByID_noRunDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + event := CreateWorkflowDispatchEventRequest{ + Ref: "d4cfb6e7", + Inputs: map[string]any{ + "key": "value", + }, + } + mux.HandleFunc("/repos/o/r/actions/workflows/72844/dispatches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, event) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + dispatchResponse, _, err := client.Actions.CreateWorkflowDispatchEventByID(ctx, "o", "r", 72844, event) + if err != nil { + t.Errorf("Actions.CreateWorkflowDispatchEventByID returned error: %v", err) + } + + if dispatchResponse != nil { + t.Errorf("Actions.CreateWorkflowDispatchEventByID = %+v, want nil", dispatchResponse) + } +} + +func TestActionsService_CreateWorkflowDispatchEventByFileName_noRunDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + event := CreateWorkflowDispatchEventRequest{ + Ref: "d4cfb6e7", + Inputs: map[string]any{ + "key": "value", + }, + } + mux.HandleFunc("/repos/o/r/actions/workflows/main.yml/dispatches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, event) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + dispatchResponse, _, err := client.Actions.CreateWorkflowDispatchEventByFileName(ctx, "o", "r", "main.yml", event) + if err != nil { + t.Errorf("Actions.CreateWorkflowDispatchEventByFileName returned error: %v", err) + } + + if dispatchResponse != nil { + t.Errorf("Actions.CreateWorkflowDispatchEventByFileName = %+v, want nil", dispatchResponse) + } +} + +func TestActionsService_EnableWorkflowByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/72844/enable", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + if r.Body != http.NoBody { + t.Errorf("Request body = %+v, want %+v", r.Body, http.NoBody) + } + }) + + ctx := t.Context() + _, err := client.Actions.EnableWorkflowByID(ctx, "o", "r", 72844) + if err != nil { + t.Errorf("Actions.EnableWorkflowByID returned error: %v", err) + } + + // Test s.client.NewRequest failure + client.baseURL.Path = "" + _, err = client.Actions.EnableWorkflowByID(ctx, "o", "r", 72844) + if err == nil { + t.Error("client.BaseURL.Path='' EnableWorkflowByID err = nil, want error") + } + + const methodName = "EnableWorkflowByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.EnableWorkflowByID(ctx, "o", "r", 72844) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.EnableWorkflowByID(ctx, "o", "r", 72844) + }) +} + +func TestActionsService_EnableWorkflowByFileName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/main.yml/enable", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + if r.Body != http.NoBody { + t.Errorf("Request body = %+v, want %+v", r.Body, http.NoBody) + } + }) + + ctx := t.Context() + _, err := client.Actions.EnableWorkflowByFileName(ctx, "o", "r", "main.yml") + if err != nil { + t.Errorf("Actions.EnableWorkflowByFileName returned error: %v", err) + } + + // Test s.client.NewRequest failure + client.baseURL.Path = "" + _, err = client.Actions.EnableWorkflowByFileName(ctx, "o", "r", "main.yml") + if err == nil { + t.Error("client.BaseURL.Path='' EnableWorkflowByFileName err = nil, want error") + } + + const methodName = "EnableWorkflowByFileName" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.EnableWorkflowByFileName(ctx, "o", "r", "main.yml") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.EnableWorkflowByFileName(ctx, "o", "r", "main.yml") + }) +} + +func TestActionsService_DisableWorkflowByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/72844/disable", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + if r.Body != http.NoBody { + t.Errorf("Request body = %+v, want %+v", r.Body, http.NoBody) + } + }) + + ctx := t.Context() + _, err := client.Actions.DisableWorkflowByID(ctx, "o", "r", 72844) + if err != nil { + t.Errorf("Actions.DisableWorkflowByID returned error: %v", err) + } + + // Test s.client.NewRequest failure + client.baseURL.Path = "" + _, err = client.Actions.DisableWorkflowByID(ctx, "o", "r", 72844) + if err == nil { + t.Error("client.BaseURL.Path='' DisableWorkflowByID err = nil, want error") + } + + const methodName = "DisableWorkflowByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DisableWorkflowByID(ctx, "o", "r", 72844) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DisableWorkflowByID(ctx, "o", "r", 72844) + }) +} + +func TestActionsService_DisableWorkflowByFileName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/workflows/main.yml/disable", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + if r.Body != http.NoBody { + t.Errorf("Request body = %+v, want %+v", r.Body, http.NoBody) + } + }) + + ctx := t.Context() + _, err := client.Actions.DisableWorkflowByFileName(ctx, "o", "r", "main.yml") + if err != nil { + t.Errorf("Actions.DisableWorkflowByFileName returned error: %v", err) + } + + // Test s.client.NewRequest failure + client.baseURL.Path = "" + _, err = client.Actions.DisableWorkflowByFileName(ctx, "o", "r", "main.yml") + if err == nil { + t.Error("client.BaseURL.Path='' DisableWorkflowByFileName err = nil, want error") + } + + const methodName = "DisableWorkflowByFileName" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DisableWorkflowByFileName(ctx, "o", "r", "main.yml") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DisableWorkflowByFileName(ctx, "o", "r", "main.yml") + }) +} diff --git a/github/activity.go b/github/activity.go index d6c992c7f50..f69bd38261d 100644 --- a/github/activity.go +++ b/github/activity.go @@ -10,7 +10,7 @@ import "context" // ActivityService handles communication with the activity related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/activity/ +// GitHub API docs: https://docs.github.com/rest/activity?apiVersion=2022-11-28 type ActivityService service // FeedLink represents a link to a related resource. @@ -21,46 +21,54 @@ type FeedLink struct { // Feeds represents timeline resources in Atom format. type Feeds struct { - TimelineURL *string `json:"timeline_url,omitempty"` - UserURL *string `json:"user_url,omitempty"` - CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"` - CurrentUserURL *string `json:"current_user_url,omitempty"` - CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"` - CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"` - CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"` - Links *struct { - Timeline *FeedLink `json:"timeline,omitempty"` - User *FeedLink `json:"user,omitempty"` - CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` - CurrentUser *FeedLink `json:"current_user,omitempty"` - CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` - CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` - CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` - } `json:"_links,omitempty"` + TimelineURL *string `json:"timeline_url,omitempty"` + UserURL *string `json:"user_url,omitempty"` + CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"` + CurrentUserURL *string `json:"current_user_url,omitempty"` + CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"` + CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"` + CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"` + Links *FeedLinks `json:"_links,omitempty"` +} + +// FeedLinks represents the links in a Feed. +type FeedLinks struct { + Timeline *FeedLink `json:"timeline,omitempty"` + User *FeedLink `json:"user,omitempty"` + CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` + CurrentUser *FeedLink `json:"current_user,omitempty"` + CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` + CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` + CurrentUserOrganizations []*FeedLink `json:"current_user_organizations,omitempty"` } // ListFeeds lists all the feeds available to the authenticated user. // // GitHub provides several timeline resources in Atom format: -// Timeline: The GitHub global public timeline -// User: The public timeline for any user, using URI template -// Current user public: The public timeline for the authenticated user -// Current user: The private timeline for the authenticated user -// Current user actor: The private timeline for activity created by the -// authenticated user -// Current user organizations: The private timeline for the organizations -// the authenticated user is a member of. +// +// Timeline: The GitHub global public timeline +// User: The public timeline for any user, using URI template +// Current user public: The public timeline for the authenticated user +// Current user: The private timeline for the authenticated user +// Current user actor: The private timeline for activity created by the +// authenticated user +// Current user organizations: The private timeline for the organizations +// the authenticated user is a member of. // // Note: Private feeds are only returned when authenticating via Basic Auth // since current feed URIs use the older, non revocable auth tokens. +// +// GitHub API docs: https://docs.github.com/rest/activity/feeds?apiVersion=2022-11-28#get-feeds +// +//meta:operation GET /feeds func (s *ActivityService) ListFeeds(ctx context.Context) (*Feeds, *Response, error) { - req, err := s.client.NewRequest("GET", "feeds", nil) + req, err := s.client.NewRequest(ctx, "GET", "feeds", nil) if err != nil { return nil, nil, err } - f := &Feeds{} - resp, err := s.client.Do(ctx, req, f) + var f *Feeds + resp, err := s.client.Do(req, &f) if err != nil { return nil, resp, err } diff --git a/github/activity_events.go b/github/activity_events.go index 1754b78e9da..83635fbb7e0 100644 --- a/github/activity_events.go +++ b/github/activity_events.go @@ -12,20 +12,22 @@ import ( // ListEvents drinks from the firehose of all public events across GitHub. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events -func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*Event, *Response, error) { - u, err := addOptions("events", opt) +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-public-events +// +//meta:operation GET /events +func (s *ActivityService) ListEvents(ctx context.Context, opts *ListOptions) ([]*Event, *Response, error) { + u, err := addOptions("events", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -35,21 +37,23 @@ func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]* // ListRepositoryEvents lists events for a repository. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-repository-events -func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-repository-events +// +//meta:operation GET /repos/{owner}/{repo}/events +func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Event, *Response, error) { u := fmt.Sprintf("repos/%v/%v/events", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -59,21 +63,23 @@ func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo // ListIssueEventsForRepository lists issue events for a repository. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository -func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/events?apiVersion=2022-11-28#list-issue-events-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/issues/events +func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opts *ListOptions) ([]*IssueEvent, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*IssueEvent - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -83,21 +89,23 @@ func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owne // ListEventsForRepoNetwork lists public events for a network of repositories. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories -func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-public-events-for-a-network-of-repositories +// +//meta:operation GET /networks/{owner}/{repo}/events +func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Event, *Response, error) { u := fmt.Sprintf("networks/%v/%v/events", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -107,21 +115,23 @@ func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, r // ListEventsForOrganization lists public events for an organization. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization -func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opt *ListOptions) ([]*Event, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-public-organization-events +// +//meta:operation GET /orgs/{org}/events +func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opts *ListOptions) ([]*Event, *Response, error) { u := fmt.Sprintf("orgs/%v/events", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -132,26 +142,31 @@ func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org str // ListEventsPerformedByUser lists the events performed by a user. If publicOnly is // true, only public events will be returned. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user -func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-events-for-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-public-events-for-a-user +// +//meta:operation GET /users/{username}/events +//meta:operation GET /users/{username}/events/public +func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opts *ListOptions) ([]*Event, *Response, error) { var u string if publicOnly { u = fmt.Sprintf("users/%v/events/public", user) } else { u = fmt.Sprintf("users/%v/events", user) } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -162,26 +177,31 @@ func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user st // ListEventsReceivedByUser lists the events received by a user. If publicOnly is // true, only public events will be returned. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received -func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-events-received-by-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-public-events-received-by-a-user +// +//meta:operation GET /users/{username}/received_events +//meta:operation GET /users/{username}/received_events/public +func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opts *ListOptions) ([]*Event, *Response, error) { var u string if publicOnly { u = fmt.Sprintf("users/%v/received_events/public", user) } else { u = fmt.Sprintf("users/%v/received_events", user) } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -192,21 +212,23 @@ func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user str // ListUserEventsForOrganization provides the user’s organization dashboard. You // must be authenticated as the user to view this. // -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-an-organization -func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opt *ListOptions) ([]*Event, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/events?apiVersion=2022-11-28#list-organization-events-for-the-authenticated-user +// +//meta:operation GET /users/{username}/events/orgs/{org} +func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opts *ListOptions) ([]*Event, *Response, error) { u := fmt.Sprintf("users/%v/events/orgs/%v", user, org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*Event - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } diff --git a/github/activity_events_test.go b/github/activity_events_test.go index 0b01c4a02f2..459c8ab4f46 100644 --- a/github/activity_events_test.go +++ b/github/activity_events_test.go @@ -6,17 +6,17 @@ package github import ( - "context" "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestActivityService_ListEvents(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,20 +27,30 @@ func TestActivityService_ListEvents(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListEvents(context.Background(), opt) + ctx := t.Context() + events, _, err := client.Activity.ListEvents(ctx, opt) if err != nil { t.Errorf("Activities.ListEvents returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { t.Errorf("Activities.ListEvents returned %+v, want %+v", events, want) } + + const methodName = "ListEvents" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListEvents(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListRepositoryEvents(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -51,28 +61,44 @@ func TestActivityService_ListRepositoryEvents(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListRepositoryEvents(context.Background(), "o", "r", opt) + ctx := t.Context() + events, _, err := client.Activity.ListRepositoryEvents(ctx, "o", "r", opt) if err != nil { t.Errorf("Activities.ListRepositoryEvents returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { t.Errorf("Activities.ListRepositoryEvents returned %+v, want %+v", events, want) } + + const methodName = "ListRepositoryEvents" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListRepositoryEvents(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListRepositoryEvents(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListRepositoryEvents_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListRepositoryEvents(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Activity.ListRepositoryEvents(ctx, "%", "%", nil) testURLParseError(t, err) } func TestActivityService_ListIssueEventsForRepository(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -83,28 +109,44 @@ func TestActivityService_ListIssueEventsForRepository(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "o", "r", opt) + ctx := t.Context() + events, _, err := client.Activity.ListIssueEventsForRepository(ctx, "o", "r", opt) if err != nil { t.Errorf("Activities.ListIssueEventsForRepository returned error: %v", err) } - want := []*IssueEvent{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(events, want) { + want := []*IssueEvent{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(events, want) { t.Errorf("Activities.ListIssueEventsForRepository returned %+v, want %+v", events, want) } + + const methodName = "ListIssueEventsForRepository" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListIssueEventsForRepository(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListIssueEventsForRepository(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListIssueEventsForRepository_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListIssueEventsForRepository(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Activity.ListIssueEventsForRepository(ctx, "%", "%", nil) testURLParseError(t, err) } func TestActivityService_ListEventsForRepoNetwork(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/networks/o/r/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -115,28 +157,44 @@ func TestActivityService_ListEventsForRepoNetwork(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "o", "r", opt) + ctx := t.Context() + events, _, err := client.Activity.ListEventsForRepoNetwork(ctx, "o", "r", opt) if err != nil { - t.Errorf("Activities.ListEventsForRepoNetwork returned error: %v", err) + t.Errorf("Activity.ListEventsForRepoNetwork returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Activities.ListEventsForRepoNetwork returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListEventsForRepoNetwork returned %+v, want %+v", events, want) } + + const methodName = "ListEventsForRepoNetwork" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListEventsForRepoNetwork(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListEventsForRepoNetwork(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListEventsForRepoNetwork_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListEventsForRepoNetwork(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Activity.ListEventsForRepoNetwork(ctx, "%", "%", nil) testURLParseError(t, err) } func TestActivityService_ListEventsForOrganization(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -147,28 +205,44 @@ func TestActivityService_ListEventsForOrganization(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListEventsForOrganization(context.Background(), "o", opt) + ctx := t.Context() + events, _, err := client.Activity.ListEventsForOrganization(ctx, "o", opt) if err != nil { - t.Errorf("Activities.ListEventsForOrganization returned error: %v", err) + t.Errorf("Activity.ListEventsForOrganization returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Activities.ListEventsForOrganization returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListEventsForOrganization returned %+v, want %+v", events, want) } + + const methodName = "ListEventsForOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListEventsForOrganization(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListEventsForOrganization(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListEventsForOrganization_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListEventsForOrganization(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Activity.ListEventsForOrganization(ctx, "%", nil) testURLParseError(t, err) } func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -179,48 +253,65 @@ func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "u", false, opt) + ctx := t.Context() + events, _, err := client.Activity.ListEventsPerformedByUser(ctx, "u", false, opt) if err != nil { - t.Errorf("Events.ListPerformedByUser returned error: %v", err) + t.Errorf("Activity.ListEventsPerformedByUser returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListEventsPerformedByUser returned %+v, want %+v", events, want) } + + const methodName = "ListEventsPerformedByUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListEventsPerformedByUser(ctx, "\n", false, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListEventsPerformedByUser(ctx, "u", false, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/events/public", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) }) - events, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "u", true, nil) + ctx := t.Context() + events, _, err := client.Activity.ListEventsPerformedByUser(ctx, "u", true, nil) if err != nil { - t.Errorf("Events.ListPerformedByUser returned error: %v", err) + t.Errorf("Activity.ListEventsPerformedByUser returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListEventsPerformedByUser returned %+v, want %+v", events, want) } } func TestActivityService_ListEventsPerformedByUser_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListEventsPerformedByUser(context.Background(), "%", false, nil) + ctx := t.Context() + _, _, err := client.Activity.ListEventsPerformedByUser(ctx, "%", false, nil) testURLParseError(t, err) } func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/received_events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -231,48 +322,65 @@ func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "u", false, opt) + ctx := t.Context() + events, _, err := client.Activity.ListEventsReceivedByUser(ctx, "u", false, opt) if err != nil { - t.Errorf("Events.ListReceivedByUser returned error: %v", err) + t.Errorf("Activity.ListEventsReceivedByUser returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Events.ListReceivedUser returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListEventsReceivedByUser returned %+v, want %+v", events, want) } + + const methodName = "ListEventsReceivedByUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListEventsReceivedByUser(ctx, "\n", false, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListEventsReceivedByUser(ctx, "u", false, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListEventsReceivedByUser_publicOnly(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/received_events/public", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) }) - events, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "u", true, nil) + ctx := t.Context() + events, _, err := client.Activity.ListEventsReceivedByUser(ctx, "u", true, nil) if err != nil { - t.Errorf("Events.ListReceivedByUser returned error: %v", err) + t.Errorf("Activity.ListEventsReceivedByUser returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Events.ListReceivedByUser returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListEventsReceivedByUser returned %+v, want %+v", events, want) } } func TestActivityService_ListEventsReceivedByUser_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListEventsReceivedByUser(context.Background(), "%", false, nil) + ctx := t.Context() + _, _, err := client.Activity.ListEventsReceivedByUser(ctx, "%", false, nil) testURLParseError(t, err) } func TestActivityService_ListUserEventsForOrganization(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/events/orgs/o", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -283,67 +391,85 @@ func TestActivityService_ListUserEventsForOrganization(t *testing.T) { }) opt := &ListOptions{Page: 2} - events, _, err := client.Activity.ListUserEventsForOrganization(context.Background(), "o", "u", opt) + ctx := t.Context() + events, _, err := client.Activity.ListUserEventsForOrganization(ctx, "o", "u", opt) if err != nil { - t.Errorf("Activities.ListUserEventsForOrganization returned error: %v", err) + t.Errorf("Activity.ListUserEventsForOrganization returned error: %v", err) } - want := []*Event{{ID: String("1")}, {ID: String("2")}} - if !reflect.DeepEqual(events, want) { - t.Errorf("Activities.ListUserEventsForOrganization returned %+v, want %+v", events, want) + want := []*Event{{ID: Ptr("1")}, {ID: Ptr("2")}} + if !cmp.Equal(events, want) { + t.Errorf("Activity.ListUserEventsForOrganization returned %+v, want %+v", events, want) } + + const methodName = "ListUserEventsForOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListUserEventsForOrganization(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListUserEventsForOrganization(ctx, "o", "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_EventParsePayload_typed(t *testing.T) { + t.Parallel() raw := []byte(`{"type": "PushEvent","payload":{"push_id": 1}}`) var event *Event if err := json.Unmarshal(raw, &event); err != nil { t.Fatalf("Unmarshal Event returned error: %v", err) } - want := &PushEvent{PushID: Int64(1)} + want := &PushEvent{PushID: Ptr(int64(1))} got, err := event.ParsePayload() if err != nil { t.Fatalf("ParsePayload returned unexpected error: %v", err) } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want) } } // TestEvent_Payload_untyped checks that unrecognized events are parsed to an -// interface{} value (instead of being discarded or throwing an error), for +// any value (instead of being discarded or throwing an error), for // forward compatibility with new event types. func TestActivityService_EventParsePayload_untyped(t *testing.T) { + t.Parallel() raw := []byte(`{"type": "UnrecognizedEvent","payload":{"field": "val"}}`) var event *Event if err := json.Unmarshal(raw, &event); err != nil { t.Fatalf("Unmarshal Event returned error: %v", err) } - want := map[string]interface{}{"field": "val"} + want := map[string]any{"field": "val"} got, err := event.ParsePayload() if err != nil { t.Fatalf("ParsePayload returned unexpected error: %v", err) } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want) } } func TestActivityService_EventParsePayload_installation(t *testing.T) { + t.Parallel() raw := []byte(`{"type": "PullRequestEvent","payload":{"installation":{"id":1}}}`) var event *Event if err := json.Unmarshal(raw, &event); err != nil { t.Fatalf("Unmarshal Event returned error: %v", err) } - want := &PullRequestEvent{Installation: &Installation{ID: Int64(1)}} + want := &PullRequestEvent{Installation: &Installation{ID: Ptr(int64(1))}} got, err := event.ParsePayload() if err != nil { t.Fatalf("ParsePayload returned unexpected error: %v", err) } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Event.ParsePayload returned %+v, want %+v", got, want) } } diff --git a/github/activity_notifications.go b/github/activity_notifications.go index 45c8b2aeced..edd074dd2f5 100644 --- a/github/activity_notifications.go +++ b/github/activity_notifications.go @@ -19,12 +19,12 @@ type Notification struct { // Reason identifies the event that triggered the notification. // - // GitHub API docs: https://developer.github.com/v3/activity/notifications/#notification-reasons + // GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#notification-reasons Reason *string `json:"reason,omitempty"` Unread *bool `json:"unread,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - LastReadAt *time.Time `json:"last_read_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + LastReadAt *Timestamp `json:"last_read_at,omitempty"` URL *string `json:"url,omitempty"` } @@ -49,21 +49,23 @@ type NotificationListOptions struct { // ListNotifications lists all notifications for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications -func (s *ActivityService) ListNotifications(ctx context.Context, opt *NotificationListOptions) ([]*Notification, *Response, error) { - u := fmt.Sprintf("notifications") - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#list-notifications-for-the-authenticated-user +// +//meta:operation GET /notifications +func (s *ActivityService) ListNotifications(ctx context.Context, opts *NotificationListOptions) ([]*Notification, *Response, error) { + u := "notifications" + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var notifications []*Notification - resp, err := s.client.Do(ctx, req, ¬ifications) + resp, err := s.client.Do(req, ¬ifications) if err != nil { return nil, resp, err } @@ -74,21 +76,23 @@ func (s *ActivityService) ListNotifications(ctx context.Context, opt *Notificati // ListRepositoryNotifications lists all notifications in a given repository // for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository -func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#list-repository-notifications-for-the-authenticated-user +// +//meta:operation GET /repos/{owner}/{repo}/notifications +func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opts *NotificationListOptions) ([]*Notification, *Response, error) { u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var notifications []*Notification - resp, err := s.client.Do(ctx, req, ¬ifications) + resp, err := s.client.Do(req, ¬ifications) if err != nil { return nil, resp, err } @@ -97,54 +101,62 @@ func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner } type markReadOptions struct { - LastReadAt time.Time `json:"last_read_at,omitempty"` + LastReadAt Timestamp `json:"last_read_at,omitzero"` } // MarkNotificationsRead marks all notifications up to lastRead as read. +// If lastRead is the zero value, all notifications in the repository are marked as read. +// +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#mark-notifications-as-read // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read -func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) { +//meta:operation PUT /notifications +func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead Timestamp) (*Response, error) { opts := &markReadOptions{ LastReadAt: lastRead, } - req, err := s.client.NewRequest("PUT", "notifications", opts) + req, err := s.client.NewRequest(ctx, "PUT", "notifications", opts) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // MarkRepositoryNotificationsRead marks all notifications up to lastRead in // the specified repository as read. +// If lastRead is the zero value, all notifications in the repository are marked as read. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository -func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#mark-repository-notifications-as-read +// +//meta:operation PUT /repos/{owner}/{repo}/notifications +func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead Timestamp) (*Response, error) { opts := &markReadOptions{ LastReadAt: lastRead, } u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) - req, err := s.client.NewRequest("PUT", u, opts) + req, err := s.client.NewRequest(ctx, "PUT", u, opts) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // GetThread gets the specified notification thread. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#get-a-thread +// +//meta:operation GET /notifications/threads/{thread_id} func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) { u := fmt.Sprintf("notifications/threads/%v", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - notification := new(Notification) - resp, err := s.client.Do(ctx, req, notification) + var notification *Notification + resp, err := s.client.Do(req, ¬ification) if err != nil { return nil, resp, err } @@ -154,32 +166,53 @@ func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notificati // MarkThreadRead marks the specified thread as read. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#mark-a-thread-as-read +// +//meta:operation PATCH /notifications/threads/{thread_id} func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Response, error) { u := fmt.Sprintf("notifications/threads/%v", id) - req, err := s.client.NewRequest("PATCH", u, nil) + req, err := s.client.NewRequest(ctx, "PATCH", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// MarkThreadDone marks the specified thread as done. +// Marking a thread as "done" is equivalent to marking a notification in your notification inbox on GitHub as done. +// +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#mark-a-thread-as-done +// +//meta:operation DELETE /notifications/threads/{thread_id} +func (s *ActivityService) MarkThreadDone(ctx context.Context, id string) (*Response, error) { + u := fmt.Sprintf("notifications/threads/%v", id) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) } // GetThreadSubscription checks to see if the authenticated user is subscribed // to a thread. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#get-a-thread-subscription-for-the-authenticated-user +// +//meta:operation GET /notifications/threads/{thread_id}/subscription func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) { u := fmt.Sprintf("notifications/threads/%v/subscription", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) + var sub *Subscription + resp, err := s.client.Do(req, &sub) if err != nil { return nil, resp, err } @@ -190,17 +223,19 @@ func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) // SetThreadSubscription sets the subscription for the specified thread for the // authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription -func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, subscription *Subscription) (*Subscription, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#set-a-thread-subscription +// +//meta:operation PUT /notifications/threads/{thread_id}/subscription +func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, body *Subscription) (*Subscription, *Response, error) { u := fmt.Sprintf("notifications/threads/%v/subscription", id) - req, err := s.client.NewRequest("PUT", u, subscription) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) + var sub *Subscription + resp, err := s.client.Do(req, &sub) if err != nil { return nil, resp, err } @@ -211,13 +246,15 @@ func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, // DeleteThreadSubscription deletes the subscription for the specified thread // for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription +// GitHub API docs: https://docs.github.com/rest/activity/notifications?apiVersion=2022-11-28#delete-a-thread-subscription +// +//meta:operation DELETE /notifications/threads/{thread_id}/subscription func (s *ActivityService) DeleteThreadSubscription(ctx context.Context, id string) (*Response, error) { u := fmt.Sprintf("notifications/threads/%v/subscription", id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/activity_notifications_test.go b/github/activity_notifications_test.go index a36b253da3e..ef91f0b4303 100644 --- a/github/activity_notifications_test.go +++ b/github/activity_notifications_test.go @@ -6,25 +6,24 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" "time" + + "github.com/google/go-cmp/cmp" ) func TestActivityService_ListNotification(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{ "all": "true", "participating": "true", - "since": "2006-01-02T15:04:05Z", + "since": referenceTimeRaw, "before": "2007-03-04T15:04:05Z", }) @@ -34,171 +33,349 @@ func TestActivityService_ListNotification(t *testing.T) { opt := &NotificationListOptions{ All: true, Participating: true, - Since: time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC), - Before: time.Date(2007, time.March, 04, 15, 04, 05, 0, time.UTC), + Since: referenceTime, + Before: time.Date(2007, time.March, 4, 15, 4, 5, 0, time.UTC), } - notifications, _, err := client.Activity.ListNotifications(context.Background(), opt) + ctx := t.Context() + notifications, _, err := client.Activity.ListNotifications(ctx, opt) if err != nil { t.Errorf("Activity.ListNotifications returned error: %v", err) } - want := []*Notification{{ID: String("1"), Subject: &NotificationSubject{Title: String("t")}}} - if !reflect.DeepEqual(notifications, want) { + want := []*Notification{{ID: Ptr("1"), Subject: &NotificationSubject{Title: Ptr("t")}}} + if !cmp.Equal(notifications, want) { t.Errorf("Activity.ListNotifications returned %+v, want %+v", notifications, want) } + + const methodName = "ListNotifications" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListNotifications(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestActivityService_ListRepositoryNotification(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestActivityService_ListRepositoryNotifications(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":"1"}]`) }) - notifications, _, err := client.Activity.ListRepositoryNotifications(context.Background(), "o", "r", nil) + ctx := t.Context() + notifications, _, err := client.Activity.ListRepositoryNotifications(ctx, "o", "r", nil) if err != nil { t.Errorf("Activity.ListRepositoryNotifications returned error: %v", err) } - want := []*Notification{{ID: String("1")}} - if !reflect.DeepEqual(notifications, want) { + want := []*Notification{{ID: Ptr("1")}} + if !cmp.Equal(notifications, want) { t.Errorf("Activity.ListRepositoryNotifications returned %+v, want %+v", notifications, want) } + + const methodName = "ListRepositoryNotifications" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListRepositoryNotifications(ctx, "\n", "\n", &NotificationListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListRepositoryNotifications(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_MarkNotificationsRead(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := markReadOptions{ + LastReadAt: referenceTimestamp, + } + testJSONBody(t, r, want) + + w.WriteHeader(http.StatusResetContent) + }) + + ctx := t.Context() + _, err := client.Activity.MarkNotificationsRead(ctx, referenceTimestamp) + if err != nil { + t.Errorf("Activity.MarkNotificationsRead returned error: %v", err) + } + + const methodName = "MarkNotificationsRead" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.MarkNotificationsRead(ctx, referenceTimestamp) + }) +} + +func TestActivityService_MarkNotificationsRead_EmptyLastReadAt(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") testHeader(t, r, "Content-Type", "application/json") - testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n") + want := markReadOptions{ + LastReadAt: referenceTimestamp, + } + testJSONBody(t, r, want) w.WriteHeader(http.StatusResetContent) }) - _, err := client.Activity.MarkNotificationsRead(context.Background(), time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + ctx := t.Context() + _, err := client.Activity.MarkNotificationsRead(ctx, referenceTimestamp) if err != nil { t.Errorf("Activity.MarkNotificationsRead returned error: %v", err) } } func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := markReadOptions{ + LastReadAt: referenceTimestamp, + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusResetContent) + }) + + ctx := t.Context() + _, err := client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", referenceTimestamp) + if err != nil { + t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err) + } + + const methodName = "MarkRepositoryNotificationsRead" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.MarkRepositoryNotificationsRead(ctx, "\n", "\n", referenceTimestamp) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", referenceTimestamp) + }) +} + +func TestActivityService_MarkRepositoryNotificationsRead_EmptyLastReadAt(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") testHeader(t, r, "Content-Type", "application/json") - testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n") + want := markReadOptions{ + LastReadAt: referenceTimestamp, + } + testJSONBody(t, r, want) w.WriteHeader(http.StatusResetContent) }) - _, err := client.Activity.MarkRepositoryNotificationsRead(context.Background(), "o", "r", time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)) + ctx := t.Context() + _, err := client.Activity.MarkRepositoryNotificationsRead(ctx, "o", "r", referenceTimestamp) if err != nil { t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err) } } func TestActivityService_GetThread(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":"1"}`) }) - notification, _, err := client.Activity.GetThread(context.Background(), "1") + ctx := t.Context() + notification, _, err := client.Activity.GetThread(ctx, "1") if err != nil { t.Errorf("Activity.GetThread returned error: %v", err) } - want := &Notification{ID: String("1")} - if !reflect.DeepEqual(notification, want) { + want := &Notification{ID: Ptr("1")} + if !cmp.Equal(notification, want) { t.Errorf("Activity.GetThread returned %+v, want %+v", notification, want) } + + const methodName = "GetThread" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.GetThread(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.GetThread(ctx, "1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_MarkThreadRead(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") w.WriteHeader(http.StatusResetContent) }) - _, err := client.Activity.MarkThreadRead(context.Background(), "1") + ctx := t.Context() + _, err := client.Activity.MarkThreadRead(ctx, "1") if err != nil { t.Errorf("Activity.MarkThreadRead returned error: %v", err) } + + const methodName = "MarkThreadRead" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.MarkThreadRead(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.MarkThreadRead(ctx, "1") + }) +} + +func TestActivityService_MarkThreadDone(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusResetContent) + }) + + ctx := t.Context() + _, err := client.Activity.MarkThreadDone(ctx, "1") + if err != nil { + t.Errorf("Activity.MarkThreadDone returned error: %v", err) + } + + const methodName = "MarkThreadDone" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.MarkThreadDone(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.MarkThreadDone(ctx, "1") + }) } func TestActivityService_GetThreadSubscription(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"subscribed":true}`) }) - sub, _, err := client.Activity.GetThreadSubscription(context.Background(), "1") + ctx := t.Context() + sub, _, err := client.Activity.GetThreadSubscription(ctx, "1") if err != nil { t.Errorf("Activity.GetThreadSubscription returned error: %v", err) } - want := &Subscription{Subscribed: Bool(true)} - if !reflect.DeepEqual(sub, want) { + want := &Subscription{Subscribed: Ptr(true)} + if !cmp.Equal(sub, want) { t.Errorf("Activity.GetThreadSubscription returned %+v, want %+v", sub, want) } + + const methodName = "GetThreadSubscription" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.GetThreadSubscription(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.GetThreadSubscription(ctx, "1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_SetThreadSubscription(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Subscription{Subscribed: Bool(true)} + input := &Subscription{Subscribed: Ptr(true)} mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { - v := new(Subscription) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"ignored":true}`) }) - sub, _, err := client.Activity.SetThreadSubscription(context.Background(), "1", input) + ctx := t.Context() + sub, _, err := client.Activity.SetThreadSubscription(ctx, "1", input) if err != nil { t.Errorf("Activity.SetThreadSubscription returned error: %v", err) } - want := &Subscription{Ignored: Bool(true)} - if !reflect.DeepEqual(sub, want) { + want := &Subscription{Ignored: Ptr(true)} + if !cmp.Equal(sub, want) { t.Errorf("Activity.SetThreadSubscription returned %+v, want %+v", sub, want) } + + const methodName = "SetThreadSubscription" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.SetThreadSubscription(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.SetThreadSubscription(ctx, "1", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_DeleteThreadSubscription(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Activity.DeleteThreadSubscription(context.Background(), "1") + ctx := t.Context() + _, err := client.Activity.DeleteThreadSubscription(ctx, "1") if err != nil { t.Errorf("Activity.DeleteThreadSubscription returned error: %v", err) } + + const methodName = "DeleteThreadSubscription" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.DeleteThreadSubscription(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.DeleteThreadSubscription(ctx, "1") + }) } diff --git a/github/activity_star.go b/github/activity_star.go index 5ae5c101651..d405d85a240 100644 --- a/github/activity_star.go +++ b/github/activity_star.go @@ -25,24 +25,25 @@ type Stargazer struct { // ListStargazers lists people who have starred the specified repo. // -// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-stargazers -func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/activity/starring?apiVersion=2022-11-28#list-stargazers +// +//meta:operation GET /repos/{owner}/{repo}/stargazers +func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Stargazer, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/stargazers", owner, repo) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeStarringPreview) + req.Header.Set("Accept", mediaTypeStarring) var stargazers []*Stargazer - resp, err := s.client.Do(ctx, req, &stargazers) + resp, err := s.client.Do(req, &stargazers) if err != nil { return nil, resp, err } @@ -58,7 +59,7 @@ type ActivityListStarredOptions struct { Sort string `url:"sort,omitempty"` // Direction in which to sort repositories. Possible values are: asc, desc. - // Default is "asc" when sort is "full_name", otherwise default is "desc". + // Default is "asc" when sort is "full_name"; otherwise, default is "desc". Direction string `url:"direction,omitempty"` ListOptions @@ -67,30 +68,34 @@ type ActivityListStarredOptions struct { // ListStarred lists all the repos starred by a user. Passing the empty string // will list the starred repositories for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred -func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/starring?apiVersion=2022-11-28#list-repositories-starred-by-a-user +// +// GitHub API docs: https://docs.github.com/rest/activity/starring?apiVersion=2022-11-28#list-repositories-starred-by-the-authenticated-user +// +//meta:operation GET /user/starred +//meta:operation GET /users/{username}/starred +func (s *ActivityService) ListStarred(ctx context.Context, user string, opts *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/starred", user) } else { u = "user/starred" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when APIs fully launch - acceptHeaders := []string{mediaTypeStarringPreview, mediaTypeTopicsPreview} + acceptHeaders := []string{mediaTypeStarring, mediaTypeTopicsPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var repos []*StarredRepository - resp, err := s.client.Do(ctx, req, &repos) + resp, err := s.client.Do(req, &repos) if err != nil { return nil, resp, err } @@ -100,38 +105,47 @@ func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *Act // IsStarred checks if a repository is starred by authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository +// GitHub API docs: https://docs.github.com/rest/activity/starring?apiVersion=2022-11-28#check-if-a-repository-is-starred-by-the-authenticated-user +// +//meta:operation GET /user/starred/{owner}/{repo} func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bool, *Response, error) { u := fmt.Sprintf("user/starred/%v/%v", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + + resp, err := s.client.Do(req, nil) starred, err := parseBoolResponse(err) return starred, resp, err } // Star a repository as the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository +// GitHub API docs: https://docs.github.com/rest/activity/starring?apiVersion=2022-11-28#star-a-repository-for-the-authenticated-user +// +//meta:operation PUT /user/starred/{owner}/{repo} func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Response, error) { u := fmt.Sprintf("user/starred/%v/%v", owner, repo) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } // Unstar a repository as the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository +// GitHub API docs: https://docs.github.com/rest/activity/starring?apiVersion=2022-11-28#unstar-a-repository-for-the-authenticated-user +// +//meta:operation DELETE /user/starred/{owner}/{repo} func (s *ActivityService) Unstar(ctx context.Context, owner, repo string) (*Response, error) { u := fmt.Sprintf("user/starred/%v/%v", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } diff --git a/github/activity_star_test.go b/github/activity_star_test.go index 8a122478e1c..6286a166772 100644 --- a/github/activity_star_test.go +++ b/github/activity_star_test.go @@ -6,180 +6,280 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "strings" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestActivityService_ListStargazers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeStarringPreview) + testHeader(t, r, "Accept", mediaTypeStarring) testFormValues(t, r, values{ "page": "2", }) - fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","user":{"id":1}}]`) + fmt.Fprint(w, `[{"starred_at":`+referenceTimeStr+`,"user":{"id":1}}]`) }) - stargazers, _, err := client.Activity.ListStargazers(context.Background(), "o", "r", &ListOptions{Page: 2}) + ctx := t.Context() + stargazers, _, err := client.Activity.ListStargazers(ctx, "o", "r", &ListOptions{Page: 2}) if err != nil { t.Errorf("Activity.ListStargazers returned error: %v", err) } - want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int64(1)}}} - if !reflect.DeepEqual(stargazers, want) { + want := []*Stargazer{{StarredAt: &referenceTimestamp, User: &User{ID: Ptr(int64(1))}}} + if !cmp.Equal(stargazers, want) { t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want) } + + const methodName = "ListStargazers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListStargazers(ctx, "\n", "\n", &ListOptions{Page: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListStargazers(ctx, "o", "r", &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListStarred_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", ")) - fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`) + testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarring, mediaTypeTopicsPreview}, ", ")) + fmt.Fprint(w, `[{"starred_at":`+referenceTimeStr+`,"repo":{"id":1}}]`) }) - repos, _, err := client.Activity.ListStarred(context.Background(), "", nil) + ctx := t.Context() + repos, _, err := client.Activity.ListStarred(ctx, "", nil) if err != nil { t.Errorf("Activity.ListStarred returned error: %v", err) } - want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(1)}}} - if !reflect.DeepEqual(repos, want) { + want := []*StarredRepository{{StarredAt: &referenceTimestamp, Repository: &Repository{ID: Ptr(int64(1))}}} + if !cmp.Equal(repos, want) { t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) } + + const methodName = "ListStarred" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListStarred(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListStarred(ctx, "", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListStarred_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", ")) + testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarring, mediaTypeTopicsPreview}, ", ")) testFormValues(t, r, values{ "sort": "created", "direction": "asc", "page": "2", }) - fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":2}}]`) + fmt.Fprint(w, `[{"starred_at":`+referenceTimeStr+`,"repo":{"id":2}}]`) }) opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}} - repos, _, err := client.Activity.ListStarred(context.Background(), "u", opt) + ctx := t.Context() + repos, _, err := client.Activity.ListStarred(ctx, "u", opt) if err != nil { t.Errorf("Activity.ListStarred returned error: %v", err) } - want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(2)}}} - if !reflect.DeepEqual(repos, want) { + want := []*StarredRepository{{StarredAt: &referenceTimestamp, Repository: &Repository{ID: Ptr(int64(2))}}} + if !cmp.Equal(repos, want) { t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) } + + const methodName = "ListStarred" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListStarred(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListStarred(ctx, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListStarred_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.ListStarred(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Activity.ListStarred(ctx, "%", nil) testURLParseError(t, err) } func TestActivityService_IsStarred_hasStar(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - star, _, err := client.Activity.IsStarred(context.Background(), "o", "r") + ctx := t.Context() + star, _, err := client.Activity.IsStarred(ctx, "o", "r") if err != nil { t.Errorf("Activity.IsStarred returned error: %v", err) } if want := true; star != want { t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) } + + const methodName = "IsStarred" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.IsStarred(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.IsStarred(ctx, "o", "r") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestActivityService_IsStarred_noStar(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - star, _, err := client.Activity.IsStarred(context.Background(), "o", "r") + ctx := t.Context() + star, _, err := client.Activity.IsStarred(ctx, "o", "r") if err != nil { t.Errorf("Activity.IsStarred returned error: %v", err) } if want := false; star != want { t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) } + + const methodName = "IsStarred" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.IsStarred(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.IsStarred(ctx, "o", "r") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestActivityService_IsStarred_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Activity.IsStarred(context.Background(), "%", "%") + ctx := t.Context() + _, _, err := client.Activity.IsStarred(ctx, "%", "%") testURLParseError(t, err) } func TestActivityService_Star(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/user/starred/o/r", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") }) - _, err := client.Activity.Star(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Activity.Star(ctx, "o", "r") if err != nil { t.Errorf("Activity.Star returned error: %v", err) } + + const methodName = "Star" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.Star(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.Star(ctx, "o", "r") + }) } func TestActivityService_Star_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Activity.Star(context.Background(), "%", "%") + ctx := t.Context() + _, err := client.Activity.Star(ctx, "%", "%") testURLParseError(t, err) } func TestActivityService_Unstar(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/user/starred/o/r", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Activity.Unstar(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Activity.Unstar(ctx, "o", "r") if err != nil { t.Errorf("Activity.Unstar returned error: %v", err) } + + const methodName = "Unstar" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.Unstar(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.Unstar(ctx, "o", "r") + }) } func TestActivityService_Unstar_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Activity.Unstar(context.Background(), "%", "%") + ctx := t.Context() + _, err := client.Activity.Unstar(ctx, "%", "%") testURLParseError(t, err) } diff --git a/github/activity_test.go b/github/activity_test.go index b577118aad3..dc0090591df 100644 --- a/github/activity_test.go +++ b/github/activity_test.go @@ -6,59 +6,40 @@ package github import ( - "context" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestActivityService_List(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/feeds", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusOK) - w.Write(feedsJSON) + assertWrite(t, w, feedsJSON) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Activity.ListFeeds(ctx) if err != nil { t.Errorf("Activity.ListFeeds returned error: %v", err) } - if want := wantFeeds; !reflect.DeepEqual(got, want) { + if want := wantFeeds; !cmp.Equal(got, want) { t.Errorf("Activity.ListFeeds = %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Activity.ListFeeds(ctx) - if got != nil { - t.Errorf("client.BaseURL.Path='' ListFeeds = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListFeeds resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListFeeds err = nil, want error") - } - - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Activity.ListFeeds(ctx) - if got != nil { - t.Errorf("rate.Reset.Time > now ListFeeds = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListFeeds resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListFeeds err = nil, want error") - } + const methodName = "ListFeeds" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListFeeds(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } var feedsJSON = []byte(`{ @@ -106,52 +87,44 @@ var feedsJSON = []byte(`{ }`) var wantFeeds = &Feeds{ - TimelineURL: String("https://github.com/timeline"), - UserURL: String("https://github.com/{user}"), - CurrentUserPublicURL: String("https://github.com/defunkt"), - CurrentUserURL: String("https://github.com/defunkt.private?token=abc123"), - CurrentUserActorURL: String("https://github.com/defunkt.private.actor?token=abc123"), - CurrentUserOrganizationURL: String(""), + TimelineURL: Ptr("https://github.com/timeline"), + UserURL: Ptr("https://github.com/{user}"), + CurrentUserPublicURL: Ptr("https://github.com/defunkt"), + CurrentUserURL: Ptr("https://github.com/defunkt.private?token=abc123"), + CurrentUserActorURL: Ptr("https://github.com/defunkt.private.actor?token=abc123"), + CurrentUserOrganizationURL: Ptr(""), CurrentUserOrganizationURLs: []string{ "https://github.com/organizations/github/defunkt.private.atom?token=abc123", }, - Links: &struct { - Timeline *FeedLink `json:"timeline,omitempty"` - User *FeedLink `json:"user,omitempty"` - CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` - CurrentUser *FeedLink `json:"current_user,omitempty"` - CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` - CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` - CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` - }{ + Links: &FeedLinks{ Timeline: &FeedLink{ - HRef: String("https://github.com/timeline"), - Type: String("application/atom+xml"), + HRef: Ptr("https://github.com/timeline"), + Type: Ptr("application/atom+xml"), }, User: &FeedLink{ - HRef: String("https://github.com/{user}"), - Type: String("application/atom+xml"), + HRef: Ptr("https://github.com/{user}"), + Type: Ptr("application/atom+xml"), }, CurrentUserPublic: &FeedLink{ - HRef: String("https://github.com/defunkt"), - Type: String("application/atom+xml"), + HRef: Ptr("https://github.com/defunkt"), + Type: Ptr("application/atom+xml"), }, CurrentUser: &FeedLink{ - HRef: String("https://github.com/defunkt.private?token=abc123"), - Type: String("application/atom+xml"), + HRef: Ptr("https://github.com/defunkt.private?token=abc123"), + Type: Ptr("application/atom+xml"), }, CurrentUserActor: &FeedLink{ - HRef: String("https://github.com/defunkt.private.actor?token=abc123"), - Type: String("application/atom+xml"), + HRef: Ptr("https://github.com/defunkt.private.actor?token=abc123"), + Type: Ptr("application/atom+xml"), }, CurrentUserOrganization: &FeedLink{ - HRef: String(""), - Type: String(""), + HRef: Ptr(""), + Type: Ptr(""), }, - CurrentUserOrganizations: []FeedLink{ + CurrentUserOrganizations: []*FeedLink{ { - HRef: String("https://github.com/organizations/github/defunkt.private.atom?token=abc123"), - Type: String("application/atom+xml"), + HRef: Ptr("https://github.com/organizations/github/defunkt.private.atom?token=abc123"), + Type: Ptr("application/atom+xml"), }, }, }, diff --git a/github/activity_watching.go b/github/activity_watching.go index c749ca86e7c..86cb4aa151e 100644 --- a/github/activity_watching.go +++ b/github/activity_watching.go @@ -27,21 +27,23 @@ type Subscription struct { // ListWatchers lists watchers of a particular repo. // -// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-watchers -func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/activity/watching?apiVersion=2022-11-28#list-watchers +// +//meta:operation GET /repos/{owner}/{repo}/subscribers +func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opts *ListOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/subscribers", owner, repo) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var watchers []*User - resp, err := s.client.Do(ctx, req, &watchers) + resp, err := s.client.Do(req, &watchers) if err != nil { return nil, resp, err } @@ -52,26 +54,31 @@ func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, // ListWatched lists the repositories the specified user is watching. Passing // the empty string will fetch watched repos for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched -func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *ListOptions) ([]*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/activity/watching?apiVersion=2022-11-28#list-repositories-watched-by-a-user +// +// GitHub API docs: https://docs.github.com/rest/activity/watching?apiVersion=2022-11-28#list-repositories-watched-by-the-authenticated-user +// +//meta:operation GET /user/subscriptions +//meta:operation GET /users/{username}/subscriptions +func (s *ActivityService) ListWatched(ctx context.Context, user string, opts *ListOptions) ([]*Repository, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/subscriptions", user) } else { u = "user/subscriptions" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var watched []*Repository - resp, err := s.client.Do(ctx, req, &watched) + resp, err := s.client.Do(req, &watched) if err != nil { return nil, resp, err } @@ -83,17 +90,19 @@ func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *Lis // repository for the authenticated user. If the authenticated user is not // watching the repository, a nil Subscription is returned. // -// GitHub API docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription +// GitHub API docs: https://docs.github.com/rest/activity/watching?apiVersion=2022-11-28#get-a-repository-subscription +// +//meta:operation GET /repos/{owner}/{repo}/subscription func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, repo string) (*Subscription, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) + u := fmt.Sprintf("repos/%v/%v/subscription", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) + var sub *Subscription + resp, err := s.client.Do(req, &sub) if err != nil { // if it's just a 404, don't return that as an error _, err = parseBoolResponse(err) @@ -110,17 +119,19 @@ func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, // To ignore notifications made within a repository, set subscription.Ignored to true. // To stop watching a repository, use DeleteRepositorySubscription. // -// GitHub API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription -func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, subscription *Subscription) (*Subscription, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) +// GitHub API docs: https://docs.github.com/rest/activity/watching?apiVersion=2022-11-28#set-a-repository-subscription +// +//meta:operation PUT /repos/{owner}/{repo}/subscription +func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, body *Subscription) (*Subscription, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/subscription", owner, repo) - req, err := s.client.NewRequest("PUT", u, subscription) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) + var sub *Subscription + resp, err := s.client.Do(req, &sub) if err != nil { return nil, resp, err } @@ -134,13 +145,15 @@ func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, // This is used to stop watching a repository. To control whether or not to // receive notifications from a repository, use SetRepositorySubscription. // -// GitHub API docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription +// GitHub API docs: https://docs.github.com/rest/activity/watching?apiVersion=2022-11-28#delete-a-repository-subscription +// +//meta:operation DELETE /repos/{owner}/{repo}/subscription func (s *ActivityService) DeleteRepositorySubscription(ctx context.Context, owner, repo string) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/subscription", owner, repo) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/activity_watching_test.go b/github/activity_watching_test.go index b017d8d59e5..a5f0c5979e2 100644 --- a/github/activity_watching_test.go +++ b/github/activity_watching_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestActivityService_ListWatchers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/subscribers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,20 +26,35 @@ func TestActivityService_ListWatchers(t *testing.T) { fmt.Fprint(w, `[{"id":1}]`) }) - watchers, _, err := client.Activity.ListWatchers(context.Background(), "o", "r", &ListOptions{Page: 2}) + ctx := t.Context() + watchers, _, err := client.Activity.ListWatchers(ctx, "o", "r", &ListOptions{Page: 2}) if err != nil { t.Errorf("Activity.ListWatchers returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(watchers, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(watchers, want) { t.Errorf("Activity.ListWatchers returned %+v, want %+v", watchers, want) } + + const methodName = "ListWatchers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListWatchers(ctx, "\n", "\n", &ListOptions{Page: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListWatchers(ctx, "o", "r", &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListWatched_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/subscriptions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -50,20 +64,35 @@ func TestActivityService_ListWatched_authenticatedUser(t *testing.T) { fmt.Fprint(w, `[{"id":1}]`) }) - watched, _, err := client.Activity.ListWatched(context.Background(), "", &ListOptions{Page: 2}) + ctx := t.Context() + watched, _, err := client.Activity.ListWatched(ctx, "", &ListOptions{Page: 2}) if err != nil { t.Errorf("Activity.ListWatched returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(watched, want) { + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(watched, want) { t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) } + + const methodName = "ListWatched" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.ListWatched(ctx, "\n", &ListOptions{Page: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.ListWatched(ctx, "", &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_ListWatched_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/subscriptions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -73,112 +102,150 @@ func TestActivityService_ListWatched_specifiedUser(t *testing.T) { fmt.Fprint(w, `[{"id":1}]`) }) - watched, _, err := client.Activity.ListWatched(context.Background(), "u", &ListOptions{Page: 2}) + ctx := t.Context() + watched, _, err := client.Activity.ListWatched(ctx, "u", &ListOptions{Page: 2}) if err != nil { t.Errorf("Activity.ListWatched returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(watched, want) { + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(watched, want) { t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) } } func TestActivityService_GetRepositorySubscription_true(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"subscribed":true}`) }) - sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r") + ctx := t.Context() + sub, _, err := client.Activity.GetRepositorySubscription(ctx, "o", "r") if err != nil { t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) } - want := &Subscription{Subscribed: Bool(true)} - if !reflect.DeepEqual(sub, want) { + want := &Subscription{Subscribed: Ptr(true)} + if !cmp.Equal(sub, want) { t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) } + + const methodName = "GetRepositorySubscription" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.GetRepositorySubscription(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.GetRepositorySubscription(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_GetRepositorySubscription_false(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r") + ctx := t.Context() + sub, _, err := client.Activity.GetRepositorySubscription(ctx, "o", "r") if err != nil { t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) } var want *Subscription - if !reflect.DeepEqual(sub, want) { + if !cmp.Equal(sub, want) { t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) } } func TestActivityService_GetRepositorySubscription_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusBadRequest) }) - _, _, err := client.Activity.GetRepositorySubscription(context.Background(), "o", "r") + ctx := t.Context() + _, _, err := client.Activity.GetRepositorySubscription(ctx, "o", "r") if err == nil { - t.Errorf("Expected HTTP 400 response") + t.Error("Expected HTTP 400 response") } } func TestActivityService_SetRepositorySubscription(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Subscription{Subscribed: Bool(true)} + input := &Subscription{Subscribed: Ptr(true)} mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { - v := new(Subscription) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"ignored":true}`) }) - sub, _, err := client.Activity.SetRepositorySubscription(context.Background(), "o", "r", input) + ctx := t.Context() + sub, _, err := client.Activity.SetRepositorySubscription(ctx, "o", "r", input) if err != nil { t.Errorf("Activity.SetRepositorySubscription returned error: %v", err) } - want := &Subscription{Ignored: Bool(true)} - if !reflect.DeepEqual(sub, want) { + want := &Subscription{Ignored: Ptr(true)} + if !cmp.Equal(sub, want) { t.Errorf("Activity.SetRepositorySubscription returned %+v, want %+v", sub, want) } + + const methodName = "SetRepositorySubscription" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Activity.SetRepositorySubscription(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Activity.SetRepositorySubscription(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestActivityService_DeleteRepositorySubscription(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Activity.DeleteRepositorySubscription(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Activity.DeleteRepositorySubscription(ctx, "o", "r") if err != nil { t.Errorf("Activity.DeleteRepositorySubscription returned error: %v", err) } + + const methodName = "DeleteRepositorySubscription" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Activity.DeleteRepositorySubscription(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Activity.DeleteRepositorySubscription(ctx, "o", "r") + }) } diff --git a/github/admin.go b/github/admin.go index 2d96733a1c7..df89abcfc3d 100644 --- a/github/admin.go +++ b/github/admin.go @@ -14,7 +14,7 @@ import ( // GitHub API. These API routes are normally only accessible for GitHub // Enterprise installations. // -// GitHub API docs: https://developer.github.com/v3/enterprise/ +// GitHub API docs: https://docs.github.com/rest/enterprise-admin?apiVersion=2022-11-28 type AdminService service // TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group. @@ -36,6 +36,12 @@ func (m TeamLDAPMapping) String() string { return Stringify(m) } +// UpdateTeamLDAPMappingRequest represents a request to update the mapping +// between a GitHub team and an LDAP group. +type UpdateTeamLDAPMappingRequest struct { + LDAPDN string `json:"ldap_dn"` +} + // UserLDAPMapping represents the mapping between a GitHub user and an LDAP user. type UserLDAPMapping struct { ID *int64 `json:"id,omitempty"` @@ -62,18 +68,44 @@ func (m UserLDAPMapping) String() string { return Stringify(m) } +// UpdateUserLDAPMappingRequest represents a request to update the mapping +// between a GitHub user and an LDAP user. +type UpdateUserLDAPMappingRequest struct { + LDAPDN string `json:"ldap_dn"` +} + +// Enterprise represents the GitHub enterprise profile. +type Enterprise struct { + ID *int `json:"id,omitempty"` + Slug *string `json:"slug,omitempty"` + Name *string `json:"name,omitempty"` + NodeID *string `json:"node_id,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + Description *string `json:"description,omitempty"` + WebsiteURL *string `json:"website_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +func (m Enterprise) String() string { + return Stringify(m) +} + // UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user. // -// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user -func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, mapping *UserLDAPMapping) (*UserLDAPMapping, *Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/ldap#update-ldap-mapping-for-a-user +// +//meta:operation PATCH /admin/ldap/users/{username}/mapping +func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, body UpdateUserLDAPMappingRequest) (*UserLDAPMapping, *Response, error) { u := fmt.Sprintf("admin/ldap/users/%v/mapping", user) - req, err := s.client.NewRequest("PATCH", u, mapping) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - m := new(UserLDAPMapping) - resp, err := s.client.Do(ctx, req, m) + var m *UserLDAPMapping + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -83,16 +115,18 @@ func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, m // UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group. // -// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team -func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int64, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) { - u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team) - req, err := s.client.NewRequest("PATCH", u, mapping) +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/ldap#update-ldap-mapping-for-a-team +// +//meta:operation PATCH /admin/ldap/teams/{team_id}/mapping +func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, teamID int64, body UpdateTeamLDAPMappingRequest) (*TeamLDAPMapping, *Response, error) { + u := fmt.Sprintf("admin/ldap/teams/%v/mapping", teamID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - m := new(TeamLDAPMapping) - resp, err := s.client.Do(ctx, req, m) + var m *TeamLDAPMapping + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } diff --git a/github/admin_orgs.go b/github/admin_orgs.go index f063831085b..b9b19f89317 100644 --- a/github/admin_orgs.go +++ b/github/admin_orgs.go @@ -5,7 +5,11 @@ package github -import "context" +import ( + "context" + "errors" + "fmt" +) // createOrgRequest is a subset of Organization and is used internally // by CreateOrg to pass only the known fields for the endpoint. @@ -19,7 +23,9 @@ type createOrgRequest struct { // Note that only a subset of the org fields are used and org must // not be nil. // -// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#create-an-organization +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/orgs#create-an-organization +// +//meta:operation POST /admin/organizations func (s *AdminService) CreateOrg(ctx context.Context, org *Organization, admin string) (*Organization, *Response, error) { u := "admin/organizations" @@ -28,13 +34,67 @@ func (s *AdminService) CreateOrg(ctx context.Context, org *Organization, admin s Admin: &admin, } - req, err := s.client.NewRequest("POST", u, orgReq) + req, err := s.client.NewRequest(ctx, "POST", u, orgReq) + if err != nil { + return nil, nil, err + } + + var o *Organization + resp, err := s.client.Do(req, &o) + if err != nil { + return nil, resp, err + } + + return o, resp, nil +} + +// renameOrgRequest is a subset of Organization and is used internally +// by RenameOrg and RenameOrgByName to pass only the known fields for the endpoint. +type renameOrgRequest struct { + Login *string `json:"login,omitempty"` +} + +// RenameOrgResponse is the response given when renaming an Organization. +type RenameOrgResponse struct { + Message *string `json:"message,omitempty"` + URL *string `json:"url,omitempty"` +} + +// RenameOrg renames an organization in GitHub Enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/orgs#update-an-organization-name +// +//meta:operation PATCH /admin/organizations/{org} +func (s *AdminService) RenameOrg(ctx context.Context, org *Organization, newName string) (*RenameOrgResponse, *Response, error) { + if org == nil { + return nil, nil, errors.New("organization must be provided") + } + if org.Login == nil { + return nil, nil, errors.New("login must be provided") + } + + return s.RenameOrgByName(ctx, *org.Login, newName) +} + +// RenameOrgByName renames an organization in GitHub Enterprise using its current name. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/orgs#update-an-organization-name +// +//meta:operation PATCH /admin/organizations/{org} +func (s *AdminService) RenameOrgByName(ctx context.Context, org, newName string) (*RenameOrgResponse, *Response, error) { + u := fmt.Sprintf("admin/organizations/%v", org) + + orgReq := &renameOrgRequest{ + Login: &newName, + } + + req, err := s.client.NewRequest(ctx, "PATCH", u, orgReq) if err != nil { return nil, nil, err } - o := new(Organization) - resp, err := s.client.Do(ctx, req, o) + var o *RenameOrgResponse + resp, err := s.client.Do(req, &o) if err != nil { return nil, resp, err } diff --git a/github/admin_orgs_test.go b/github/admin_orgs_test.go index 6966a179c05..83ef0ba963f 100644 --- a/github/admin_orgs_test.go +++ b/github/admin_orgs_test.go @@ -6,42 +6,126 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) -func TestAdminOrgs_Create(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAdminService_CreateOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) input := &Organization{ - Login: String("github"), + Login: Ptr("github"), } mux.HandleFunc("/admin/organizations", func(w http.ResponseWriter, r *http.Request) { - v := new(createOrgRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - want := &createOrgRequest{Login: String("github"), Admin: String("ghAdmin")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"login":"github","id":1}`) }) - org, _, err := client.Admin.CreateOrg(context.Background(), input, "ghAdmin") + ctx := t.Context() + org, _, err := client.Admin.CreateOrg(ctx, input, "ghAdmin") if err != nil { t.Errorf("Admin.CreateOrg returned error: %v", err) } - want := &Organization{ID: Int64(1), Login: String("github")} - if !reflect.DeepEqual(org, want) { + want := &Organization{ID: Ptr(int64(1)), Login: Ptr("github")} + if !cmp.Equal(org, want) { t.Errorf("Admin.CreateOrg returned %+v, want %+v", org, want) } + + const methodName = "CreateOrg" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.CreateOrg(ctx, input, "ghAdmin") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAdminService_RenameOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &Organization{ + Login: Ptr("o"), + } + + mux.HandleFunc("/admin/organizations/o", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + want := &renameOrgRequest{Login: Ptr("the-new-octocats")} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"message":"Job queued to rename organization. It may take a few minutes to complete.","url":"https:///api/v3/organizations/1"}`) + }) + + ctx := t.Context() + resp, _, err := client.Admin.RenameOrg(ctx, input, "the-new-octocats") + if err != nil { + t.Errorf("Admin.RenameOrgByName returned error: %v", err) + } + + want := &RenameOrgResponse{Message: Ptr("Job queued to rename organization. It may take a few minutes to complete."), URL: Ptr("https:///api/v3/organizations/1")} + if !cmp.Equal(resp, want) { + t.Errorf("Admin.RenameOrgByName returned %+v, want %+v", resp, want) + } + + const methodName = "RenameOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Admin.RenameOrg(ctx, nil, "the-new-octocats") + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Admin.RenameOrg(ctx, &Organization{Login: nil}, "the-new-octocats") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.RenameOrg(ctx, input, "the-new-octocats") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAdminService_RenameOrgByName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/admin/organizations/o", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + want := &renameOrgRequest{Login: Ptr("the-new-octocats")} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"message":"Job queued to rename organization. It may take a few minutes to complete.","url":"https:///api/v3/organizations/1"}`) + }) + + ctx := t.Context() + resp, _, err := client.Admin.RenameOrgByName(ctx, "o", "the-new-octocats") + if err != nil { + t.Errorf("Admin.RenameOrgByName returned error: %v", err) + } + + want := &RenameOrgResponse{Message: Ptr("Job queued to rename organization. It may take a few minutes to complete."), URL: Ptr("https:///api/v3/organizations/1")} + if !cmp.Equal(resp, want) { + t.Errorf("Admin.RenameOrgByName returned %+v, want %+v", resp, want) + } + + const methodName = "RenameOrgByName" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Admin.RenameOrgByName(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.RenameOrgByName(ctx, "o", "the-new-octocats") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/admin_stats.go b/github/admin_stats.go index dabefde3e7b..17816c500b0 100644 --- a/github/admin_stats.go +++ b/github/admin_stats.go @@ -7,7 +7,6 @@ package github import ( "context" - "fmt" ) // AdminStats represents a variety of stats of a GitHub Enterprise @@ -119,13 +118,13 @@ func (s GistStats) String() string { return Stringify(s) } -// PullStats represents the number of total, merged, mergable and unmergeable +// PullStats represents the number of total, merged, mergeable and unmergeable // pull-requests. type PullStats struct { - TotalPulls *int `json:"total_pulls,omitempty"` - MergedPulls *int `json:"merged_pulls,omitempty"` - MergablePulls *int `json:"mergeable_pulls,omitempty"` - UnmergablePulls *int `json:"unmergeable_pulls,omitempty"` + TotalPulls *int `json:"total_pulls,omitempty"` + MergedPulls *int `json:"merged_pulls,omitempty"` + MergeablePulls *int `json:"mergeable_pulls,omitempty"` + UnmergeablePulls *int `json:"unmergeable_pulls,omitempty"` } func (s PullStats) String() string { @@ -153,16 +152,18 @@ func (s RepoStats) String() string { // Please note that this is only available to site administrators, // otherwise it will error with a 404 not found (instead of 401 or 403). // -// GitHub API docs: https://developer.github.com/v3/enterprise-admin/admin_stats/ +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-all-statistics +// +//meta:operation GET /enterprise/stats/all func (s *AdminService) GetAdminStats(ctx context.Context) (*AdminStats, *Response, error) { - u := fmt.Sprintf("enterprise/stats/all") - req, err := s.client.NewRequest("GET", u, nil) + u := "enterprise/stats/all" + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - m := new(AdminStats) - resp, err := s.client.Do(ctx, req, m) + var m *AdminStats + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } diff --git a/github/admin_stats_test.go b/github/admin_stats_test.go index 745cd606439..96c8e744cf2 100644 --- a/github/admin_stats_test.go +++ b/github/admin_stats_test.go @@ -1,17 +1,21 @@ +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package github import ( - "context" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestAdminService_GetAdminStats(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/enterprise/stats/all", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -76,46 +80,29 @@ func TestAdminService_GetAdminStats(t *testing.T) { `) }) - ctx := context.Background() + ctx := t.Context() stats, _, err := client.Admin.GetAdminStats(ctx) if err != nil { t.Errorf("AdminService.GetAdminStats returned error: %v", err) } - if want := testAdminStats; !reflect.DeepEqual(stats, want) { + if want := testAdminStats; !cmp.Equal(stats, want) { t.Errorf("AdminService.GetAdminStats returned %+v, want %+v", stats, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Admin.GetAdminStats(ctx) - if got != nil { - t.Errorf("client.BaseURL.Path='' GetAdminStats = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' GetAdminStats resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' GetAdminStats err = nil, want error") - } - - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Admin.GetAdminStats(ctx) - if got != nil { - t.Errorf("rate.Reset.Time > now GetAdminStats = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now GetAdminStats resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now GetAdminStats err = nil, want error") - } + const methodName = "GetAdminStats" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.GetAdminStats(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAdminService_Stringify(t *testing.T) { - want := "github.AdminStats{Issues:github.IssueStats{TotalIssues:179, OpenIssues:83, ClosedIssues:96}, Hooks:github.HookStats{TotalHooks:27, ActiveHooks:23, InactiveHooks:4}, Milestones:github.MilestoneStats{TotalMilestones:7, OpenMilestones:6, ClosedMilestones:1}, Orgs:github.OrgStats{TotalOrgs:33, DisabledOrgs:0, TotalTeams:60, TotalTeamMembers:314}, Comments:github.CommentStats{TotalCommitComments:6, TotalGistComments:28, TotalIssueComments:366, TotalPullRequestComments:30}, Pages:github.PageStats{TotalPages:36}, Users:github.UserStats{TotalUsers:254, AdminUsers:45, SuspendedUsers:21}, Gists:github.GistStats{TotalGists:178, PrivateGists:151, PublicGists:25}, Pulls:github.PullStats{TotalPulls:86, MergedPulls:60, MergablePulls:21, UnmergablePulls:3}, Repos:github.RepoStats{TotalRepos:212, RootRepos:194, ForkRepos:18, OrgRepos:51, TotalPushes:3082, TotalWikis:15}}" + t.Parallel() + want := "github.AdminStats{Issues:github.IssueStats{TotalIssues:179, OpenIssues:83, ClosedIssues:96}, Hooks:github.HookStats{TotalHooks:27, ActiveHooks:23, InactiveHooks:4}, Milestones:github.MilestoneStats{TotalMilestones:7, OpenMilestones:6, ClosedMilestones:1}, Orgs:github.OrgStats{TotalOrgs:33, DisabledOrgs:0, TotalTeams:60, TotalTeamMembers:314}, Comments:github.CommentStats{TotalCommitComments:6, TotalGistComments:28, TotalIssueComments:366, TotalPullRequestComments:30}, Pages:github.PageStats{TotalPages:36}, Users:github.UserStats{TotalUsers:254, AdminUsers:45, SuspendedUsers:21}, Gists:github.GistStats{TotalGists:178, PrivateGists:151, PublicGists:25}, Pulls:github.PullStats{TotalPulls:86, MergedPulls:60, MergeablePulls:21, UnmergeablePulls:3}, Repos:github.RepoStats{TotalRepos:212, RootRepos:194, ForkRepos:18, OrgRepos:51, TotalPushes:3082, TotalWikis:15}}" if got := testAdminStats.String(); got != want { t.Errorf("testAdminStats.String = %q, want %q", got, want) } @@ -160,7 +147,7 @@ func TestAdminService_Stringify(t *testing.T) { t.Errorf("testAdminStats.Gists.String = %q, want %q", got, want) } - want = "github.PullStats{TotalPulls:86, MergedPulls:60, MergablePulls:21, UnmergablePulls:3}" + want = "github.PullStats{TotalPulls:86, MergedPulls:60, MergeablePulls:21, UnmergeablePulls:3}" if got := testAdminStats.Pulls.String(); got != want { t.Errorf("testAdminStats.Pulls.String = %q, want %q", got, want) } @@ -173,57 +160,57 @@ func TestAdminService_Stringify(t *testing.T) { var testAdminStats = &AdminStats{ Repos: &RepoStats{ - TotalRepos: Int(212), - RootRepos: Int(194), - ForkRepos: Int(18), - OrgRepos: Int(51), - TotalPushes: Int(3082), - TotalWikis: Int(15), + TotalRepos: Ptr(212), + RootRepos: Ptr(194), + ForkRepos: Ptr(18), + OrgRepos: Ptr(51), + TotalPushes: Ptr(3082), + TotalWikis: Ptr(15), }, Hooks: &HookStats{ - TotalHooks: Int(27), - ActiveHooks: Int(23), - InactiveHooks: Int(4), + TotalHooks: Ptr(27), + ActiveHooks: Ptr(23), + InactiveHooks: Ptr(4), }, Pages: &PageStats{ - TotalPages: Int(36), + TotalPages: Ptr(36), }, Orgs: &OrgStats{ - TotalOrgs: Int(33), - DisabledOrgs: Int(0), - TotalTeams: Int(60), - TotalTeamMembers: Int(314), + TotalOrgs: Ptr(33), + DisabledOrgs: Ptr(0), + TotalTeams: Ptr(60), + TotalTeamMembers: Ptr(314), }, Users: &UserStats{ - TotalUsers: Int(254), - AdminUsers: Int(45), - SuspendedUsers: Int(21), + TotalUsers: Ptr(254), + AdminUsers: Ptr(45), + SuspendedUsers: Ptr(21), }, Pulls: &PullStats{ - TotalPulls: Int(86), - MergedPulls: Int(60), - MergablePulls: Int(21), - UnmergablePulls: Int(3), + TotalPulls: Ptr(86), + MergedPulls: Ptr(60), + MergeablePulls: Ptr(21), + UnmergeablePulls: Ptr(3), }, Issues: &IssueStats{ - TotalIssues: Int(179), - OpenIssues: Int(83), - ClosedIssues: Int(96), + TotalIssues: Ptr(179), + OpenIssues: Ptr(83), + ClosedIssues: Ptr(96), }, Milestones: &MilestoneStats{ - TotalMilestones: Int(7), - OpenMilestones: Int(6), - ClosedMilestones: Int(1), + TotalMilestones: Ptr(7), + OpenMilestones: Ptr(6), + ClosedMilestones: Ptr(1), }, Gists: &GistStats{ - TotalGists: Int(178), - PrivateGists: Int(151), - PublicGists: Int(25), + TotalGists: Ptr(178), + PrivateGists: Ptr(151), + PublicGists: Ptr(25), }, Comments: &CommentStats{ - TotalCommitComments: Int(6), - TotalGistComments: Int(28), - TotalIssueComments: Int(366), - TotalPullRequestComments: Int(30), + TotalCommitComments: Ptr(6), + TotalGistComments: Ptr(28), + TotalIssueComments: Ptr(366), + TotalPullRequestComments: Ptr(30), }, } diff --git a/github/admin_test.go b/github/admin_test.go index 9d2943332c0..ca4fd3e4d56 100644 --- a/github/admin_test.go +++ b/github/admin_test.go @@ -6,149 +6,112 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestAdminService_UpdateUserLDAPMapping(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &UserLDAPMapping{ - LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"), + input := UpdateUserLDAPMappingRequest{ + LDAPDN: "uid=asdf,ou=users,dc=github,dc=com", } mux.HandleFunc("/admin/ldap/users/u/mapping", func(w http.ResponseWriter, r *http.Request) { - v := new(UserLDAPMapping) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1,"ldap_dn":"uid=asdf,ou=users,dc=github,dc=com"}`) }) - ctx := context.Background() + ctx := t.Context() mapping, _, err := client.Admin.UpdateUserLDAPMapping(ctx, "u", input) if err != nil { t.Errorf("Admin.UpdateUserLDAPMapping returned error: %v", err) } want := &UserLDAPMapping{ - ID: Int64(1), - LDAPDN: String("uid=asdf,ou=users,dc=github,dc=com"), + ID: Ptr(int64(1)), + LDAPDN: Ptr("uid=asdf,ou=users,dc=github,dc=com"), } - if !reflect.DeepEqual(mapping, want) { + if !cmp.Equal(mapping, want) { t.Errorf("Admin.UpdateUserLDAPMapping returned %+v, want %+v", mapping, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Admin.UpdateUserLDAPMapping(ctx, "u", input) - if got != nil { - t.Errorf("client.BaseURL.Path='' UpdateUserLDAPMapping = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' UpdateUserLDAPMapping resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' UpdateUserLDAPMapping err = nil, want error") - } + const methodName = "UpdateUserLDAPMapping" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Admin.UpdateUserLDAPMapping(ctx, "\n", input) + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Admin.UpdateUserLDAPMapping(ctx, "u", input) - if got != nil { - t.Errorf("rate.Reset.Time > now UpdateUserLDAPMapping = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now UpdateUserLDAPMapping resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now UpdateUserLDAPMapping err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.UpdateUserLDAPMapping(ctx, "u", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAdminService_UpdateTeamLDAPMapping(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &TeamLDAPMapping{ - LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"), + input := UpdateTeamLDAPMappingRequest{ + LDAPDN: "cn=Enterprise Ops,ou=teams,dc=github,dc=com", } mux.HandleFunc("/admin/ldap/teams/1/mapping", func(w http.ResponseWriter, r *http.Request) { - v := new(TeamLDAPMapping) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1,"ldap_dn":"cn=Enterprise Ops,ou=teams,dc=github,dc=com"}`) }) - ctx := context.Background() + ctx := t.Context() mapping, _, err := client.Admin.UpdateTeamLDAPMapping(ctx, 1, input) if err != nil { t.Errorf("Admin.UpdateTeamLDAPMapping returned error: %v", err) } want := &TeamLDAPMapping{ - ID: Int64(1), - LDAPDN: String("cn=Enterprise Ops,ou=teams,dc=github,dc=com"), + ID: Ptr(int64(1)), + LDAPDN: Ptr("cn=Enterprise Ops,ou=teams,dc=github,dc=com"), } - if !reflect.DeepEqual(mapping, want) { + if !cmp.Equal(mapping, want) { t.Errorf("Admin.UpdateTeamLDAPMapping returned %+v, want %+v", mapping, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Admin.UpdateTeamLDAPMapping(ctx, 1, input) - if got != nil { - t.Errorf("client.BaseURL.Path='' UpdateTeamLDAPMapping = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' UpdateTeamLDAPMapping resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' UpdateTeamLDAPMapping err = nil, want error") - } + const methodName = "UpdateTeamLDAPMapping" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Admin.UpdateTeamLDAPMapping(ctx, -1, input) + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Admin.UpdateTeamLDAPMapping(ctx, 1, input) - if got != nil { - t.Errorf("rate.Reset.Time > now UpdateTeamLDAPMapping = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now UpdateTeamLDAPMapping resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now UpdateTeamLDAPMapping err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.UpdateTeamLDAPMapping(ctx, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAdminService_TeamLDAPMapping_String(t *testing.T) { + t.Parallel() v := &TeamLDAPMapping{ - ID: Int64(1), - LDAPDN: String("a"), - URL: String("b"), - Name: String("c"), - Slug: String("d"), - Description: String("e"), - Privacy: String("f"), - Permission: String("g"), - MembersURL: String("h"), - RepositoriesURL: String("i"), + ID: Ptr(int64(1)), + LDAPDN: Ptr("a"), + URL: Ptr("b"), + Name: Ptr("c"), + Slug: Ptr("d"), + Description: Ptr("e"), + Privacy: Ptr("f"), + Permission: Ptr("g"), + MembersURL: Ptr("h"), + RepositoriesURL: Ptr("i"), } want := `github.TeamLDAPMapping{ID:1, LDAPDN:"a", URL:"b", Name:"c", Slug:"d", Description:"e", Privacy:"f", Permission:"g", MembersURL:"h", RepositoriesURL:"i"}` @@ -158,24 +121,25 @@ func TestAdminService_TeamLDAPMapping_String(t *testing.T) { } func TestAdminService_UserLDAPMapping_String(t *testing.T) { + t.Parallel() v := &UserLDAPMapping{ - ID: Int64(1), - LDAPDN: String("a"), - Login: String("b"), - AvatarURL: String("c"), - GravatarID: String("d"), - Type: String("e"), - SiteAdmin: Bool(true), - URL: String("f"), - EventsURL: String("g"), - FollowingURL: String("h"), - FollowersURL: String("i"), - GistsURL: String("j"), - OrganizationsURL: String("k"), - ReceivedEventsURL: String("l"), - ReposURL: String("m"), - StarredURL: String("n"), - SubscriptionsURL: String("o"), + ID: Ptr(int64(1)), + LDAPDN: Ptr("a"), + Login: Ptr("b"), + AvatarURL: Ptr("c"), + GravatarID: Ptr("d"), + Type: Ptr("e"), + SiteAdmin: Ptr(true), + URL: Ptr("f"), + EventsURL: Ptr("g"), + FollowingURL: Ptr("h"), + FollowersURL: Ptr("i"), + GistsURL: Ptr("j"), + OrganizationsURL: Ptr("k"), + ReceivedEventsURL: Ptr("l"), + ReposURL: Ptr("m"), + StarredURL: Ptr("n"), + SubscriptionsURL: Ptr("o"), } want := `github.UserLDAPMapping{ID:1, LDAPDN:"a", Login:"b", AvatarURL:"c", GravatarID:"d", Type:"e", SiteAdmin:true, URL:"f", EventsURL:"g", FollowingURL:"h", FollowersURL:"i", GistsURL:"j", OrganizationsURL:"k", ReceivedEventsURL:"l", ReposURL:"m", StarredURL:"n", SubscriptionsURL:"o"}` diff --git a/github/admin_users.go b/github/admin_users.go index ea7a47d31ce..9468f077f15 100644 --- a/github/admin_users.go +++ b/github/admin_users.go @@ -7,52 +7,128 @@ package github import ( "context" + "fmt" ) -// createUserRequest is a subset of User and is used internally -// by CreateUser to pass only the known fields for the endpoint. -type createUserRequest struct { - Login *string `json:"login,omitempty"` - Email *string `json:"email,omitempty"` +// CreateUserRequest represents the fields sent to the `CreateUser` endpoint. +// Note that `Login` is a required field. +type CreateUserRequest struct { + Login string `json:"login"` + Email *string `json:"email,omitempty"` + Suspended *bool `json:"suspended,omitempty"` } // CreateUser creates a new user in GitHub Enterprise. // -// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-a-new-user -func (s *AdminService) CreateUser(ctx context.Context, login, email string) (*User, *Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#create-a-user +// +//meta:operation POST /admin/users +func (s *AdminService) CreateUser(ctx context.Context, body CreateUserRequest) (*User, *Response, error) { u := "admin/users" - userReq := &createUserRequest{ - Login: &login, - Email: &email, - } - - req, err := s.client.NewRequest("POST", u, userReq) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - var user User - resp, err := s.client.Do(ctx, req, &user) + var user *User + resp, err := s.client.Do(req, &user) if err != nil { return nil, resp, err } - return &user, resp, nil + return user, resp, nil } // DeleteUser deletes a user in GitHub Enterprise. // -// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-a-user +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-a-user +// +//meta:operation DELETE /admin/users/{username} func (s *AdminService) DeleteUser(ctx context.Context, username string) (*Response, error) { u := "admin/users/" + username - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ImpersonateUserOptions represents the scoping for the OAuth token. +type ImpersonateUserOptions struct { + Scopes []string `json:"scopes,omitempty"` +} + +// OAuthAPP represents the GitHub Site Administrator OAuth app. +type OAuthAPP struct { + URL *string `json:"url,omitempty"` + Name *string `json:"name,omitempty"` + ClientID *string `json:"client_id,omitempty"` +} + +func (s OAuthAPP) String() string { + return Stringify(s) +} + +// UserAuthorization represents the impersonation response. +type UserAuthorization struct { + ID *int64 `json:"id,omitempty"` + URL *string `json:"url,omitempty"` + Scopes []string `json:"scopes,omitempty"` + Token *string `json:"token,omitempty"` + TokenLastEight *string `json:"token_last_eight,omitempty"` + HashedToken *string `json:"hashed_token,omitempty"` + App *OAuthAPP `json:"app,omitempty"` + Note *string `json:"note,omitempty"` + NoteURL *string `json:"note_url,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +// CreateUserImpersonation creates an impersonation OAuth token. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#create-an-impersonation-oauth-token +// +//meta:operation POST /admin/users/{username}/authorizations +func (s *AdminService) CreateUserImpersonation(ctx context.Context, username string, body *ImpersonateUserOptions) (*UserAuthorization, *Response, error) { + u := fmt.Sprintf("admin/users/%v/authorizations", username) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var a *UserAuthorization + resp, err := s.client.Do(req, &a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// DeleteUserImpersonation deletes an impersonation OAuth token. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-an-impersonation-oauth-token +// +//meta:operation DELETE /admin/users/{username}/authorizations +func (s *AdminService) DeleteUserImpersonation(ctx context.Context, username string) (*Response, error) { + u := fmt.Sprintf("admin/users/%v/authorizations", username) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) if err != nil { return resp, err } diff --git a/github/admin_users_test.go b/github/admin_users_test.go index ad5379e2f2b..bf462a8b2de 100644 --- a/github/admin_users_test.go +++ b/github/admin_users_test.go @@ -6,52 +6,164 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestAdminUsers_Create(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/admin/users", func(w http.ResponseWriter, r *http.Request) { - v := new(createUserRequest) - json.NewDecoder(r.Body).Decode(v) + input := CreateUserRequest{Login: "github", Email: Ptr("email@example.com"), Suspended: Ptr(false)} + mux.HandleFunc("/admin/users", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - want := &createUserRequest{Login: String("github"), Email: String("email@domain.com")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"login":"github","id":1}`) }) - org, _, err := client.Admin.CreateUser(context.Background(), "github", "email@domain.com") + ctx := t.Context() + org, _, err := client.Admin.CreateUser(ctx, input) if err != nil { t.Errorf("Admin.CreateUser returned error: %v", err) } - want := &User{ID: Int64(1), Login: String("github")} - if !reflect.DeepEqual(org, want) { + want := &User{ID: Ptr(int64(1)), Login: Ptr("github")} + if !cmp.Equal(org, want) { t.Errorf("Admin.CreateUser returned %+v, want %+v", org, want) } + + const methodName = "CreateUser" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.CreateUser(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAdminUsers_Delete(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/admin/users/github", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/admin/users/github", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Admin.DeleteUser(context.Background(), "github") + ctx := t.Context() + _, err := client.Admin.DeleteUser(ctx, "github") if err != nil { t.Errorf("Admin.DeleteUser returned error: %v", err) } + + const methodName = "DeleteUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Admin.DeleteUser(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Admin.DeleteUser(ctx, "github") + }) +} + +func TestUserImpersonation_Create(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &ImpersonateUserOptions{Scopes: []string{"repo"}} + + mux.HandleFunc("/admin/users/github/authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, opt) + fmt.Fprint(w, `{"id": 1234, + "url": "https://example.com/authorizations", + "app": { + "name": "GitHub Site Administrator", + "url": "https://docs.github.com/en/rest/enterprise/users/", + "client_id": "1234" + }, + "token": "1234", + "hashed_token": "1234", + "token_last_eight": "1234", + "note": null, + "note_url": null, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "scopes": [ + "repo" + ], + "fingerprint": null}`) + }) + + ctx := t.Context() + auth, _, err := client.Admin.CreateUserImpersonation(ctx, "github", opt) + if err != nil { + t.Errorf("Admin.CreateUserImpersonation returned error: %v", err) + } + + want := &UserAuthorization{ + ID: Ptr(int64(1234)), + URL: Ptr("https://example.com/authorizations"), + App: &OAuthAPP{ + Name: Ptr("GitHub Site Administrator"), + URL: Ptr("https://docs.github.com/en/rest/enterprise/users/"), + ClientID: Ptr("1234"), + }, + Token: Ptr("1234"), + HashedToken: Ptr("1234"), + TokenLastEight: Ptr("1234"), + Note: nil, + NoteURL: nil, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Scopes: []string{"repo"}, + Fingerprint: nil, + } + if !cmp.Equal(auth, want) { + t.Errorf("Admin.CreateUserImpersonation returned %+v, want %+v", auth, want) + } + + const methodName = "CreateUserImpersonation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Admin.CreateUserImpersonation(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Admin.CreateUserImpersonation(ctx, "github", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUserImpersonation_Delete(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/admin/users/github/authorizations", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Admin.DeleteUserImpersonation(ctx, "github") + if err != nil { + t.Errorf("Admin.DeleteUserImpersonation returned error: %v", err) + } + + const methodName = "DeleteUserImpersonation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Admin.DeleteUserImpersonation(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Admin.DeleteUserImpersonation(ctx, "github") + }) } diff --git a/github/apps.go b/github/apps.go index 5041e801280..66a1afbbe95 100644 --- a/github/apps.go +++ b/github/apps.go @@ -8,32 +8,36 @@ package github import ( "context" "fmt" - "time" ) // AppsService provides access to the installation related functions // in the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/apps/ +// GitHub API docs: https://docs.github.com/rest/apps?apiVersion=2022-11-28 type AppsService service // App represents a GitHub App. type App struct { - ID *int64 `json:"id,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Owner *User `json:"owner,omitempty"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - ExternalURL *string `json:"external_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ID *int64 `json:"id,omitempty"` + Slug *string `json:"slug,omitempty"` + ClientID *string `json:"client_id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Owner *User `json:"owner,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + ExternalURL *string `json:"external_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Permissions *InstallationPermissions `json:"permissions,omitempty"` + Events []string `json:"events,omitempty"` + InstallationsCount *int `json:"installations_count,omitempty"` } // InstallationToken represents an installation token. type InstallationToken struct { Token *string `json:"token,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` Permissions *InstallationPermissions `json:"permissions,omitempty"` Repositories []*Repository `json:"repositories,omitempty"` } @@ -44,6 +48,26 @@ type InstallationTokenOptions struct { // Providing repository IDs restricts the access of an installation token to specific repositories. RepositoryIDs []int64 `json:"repository_ids,omitempty"` + // The names of the repositories that the installation token can access. + // Providing repository names restricts the access of an installation token to specific repositories. + Repositories []string `json:"repositories,omitempty"` + + // The permissions granted to the access token. + // The permissions object includes the permission names and their access type. + Permissions *InstallationPermissions `json:"permissions,omitempty"` +} + +// InstallationTokenListRepoOptions allow restricting a token's access to a list of all repositories in an installation. +// It differs from InstallationTokenOptions as a parameter which does not omit RepositoryIDs if that field is nil or an empty array. +type InstallationTokenListRepoOptions struct { + // The IDs of the repositories that the installation token can access. + // Providing repository IDs restricts the access of an installation token to specific repositories. + RepositoryIDs []int64 `json:"repository_ids"` + + // The names of the repositories that the installation token can access. + // Providing repository names restricts the access of an installation token to specific repositories. + Repositories []string `json:"repositories,omitempty"` + // The permissions granted to the access token. // The permissions object includes the permission names and their access type. Permissions *InstallationPermissions `json:"permissions,omitempty"` @@ -52,51 +76,132 @@ type InstallationTokenOptions struct { // InstallationPermissions lists the repository and organization permissions for an installation. // // Permission names taken from: -// https://developer.github.com/v3/apps/permissions/ -// https://developer.github.com/enterprise/v3/apps/permissions/ +// +// https://docs.github.com/enterprise-server@3.0/rest/apps#create-an-installation-access-token-for-an-app +// https://docs.github.com/rest/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app type InstallationPermissions struct { - Administration *string `json:"administration,omitempty"` - Checks *string `json:"checks,omitempty"` - Contents *string `json:"contents,omitempty"` - ContentReferences *string `json:"content_references,omitempty"` - Deployments *string `json:"deployments,omitempty"` - Issues *string `json:"issues,omitempty"` - Metadata *string `json:"metadata,omitempty"` - Members *string `json:"members,omitempty"` - OrganizationAdministration *string `json:"organization_administration,omitempty"` - OrganizationHooks *string `json:"organization_hooks,omitempty"` - OrganizationPlan *string `json:"organization_plan,omitempty"` - OrganizationPreReceiveHooks *string `json:"organization_pre_receive_hooks,omitempty"` - OrganizationProjects *string `json:"organization_projects,omitempty"` - OrganizationUserBlocking *string `json:"organization_user_blocking,omitempty"` - Packages *string `json:"packages,omitempty"` - Pages *string `json:"pages,omitempty"` - PullRequests *string `json:"pull_requests,omitempty"` - RepositoryHooks *string `json:"repository_hooks,omitempty"` - RepositoryProjects *string `json:"repository_projects,omitempty"` - RepositoryPreReceiveHooks *string `json:"repository_pre_receive_hooks,omitempty"` - SingleFile *string `json:"single_file,omitempty"` - Statuses *string `json:"statuses,omitempty"` - TeamDiscussions *string `json:"team_discussions,omitempty"` - VulnerabilityAlerts *string `json:"vulnerability_alerts,omitempty"` + Actions *string `json:"actions,omitempty"` + ActionsVariables *string `json:"actions_variables,omitempty"` + Administration *string `json:"administration,omitempty"` + Attestations *string `json:"attestations,omitempty"` + Blocking *string `json:"blocking,omitempty"` + Checks *string `json:"checks,omitempty"` + Codespaces *string `json:"codespaces,omitempty"` + CodespacesLifecycleAdmin *string `json:"codespaces_lifecycle_admin,omitempty"` + CodespacesMetadata *string `json:"codespaces_metadata,omitempty"` + CodespacesSecrets *string `json:"codespaces_secrets,omitempty"` + CodespacesUserSecrets *string `json:"codespaces_user_secrets,omitempty"` + Contents *string `json:"contents,omitempty"` + ContentReferences *string `json:"content_references,omitempty"` + CopilotMessages *string `json:"copilot_messages,omitempty"` + DependabotSecrets *string `json:"dependabot_secrets,omitempty"` + Deployments *string `json:"deployments,omitempty"` + Discussions *string `json:"discussions,omitempty"` + Emails *string `json:"emails,omitempty"` + EnterpriseAIControls *string `json:"enterprise_ai_controls,omitempty"` + EnterpriseCopilotMetrics *string `json:"enterprise_copilot_metrics,omitempty"` + EnterpriseCredentials *string `json:"enterprise_credentials,omitempty"` + EnterpriseCustomEnterpriseRoles *string `json:"enterprise_custom_enterprise_roles,omitempty"` + EnterpriseCustomOrgRoles *string `json:"enterprise_custom_org_roles,omitempty"` + EnterpriseCustomProperties *string `json:"enterprise_custom_properties,omitempty"` + EnterpriseCustomPropertiesForOrgs *string `json:"enterprise_custom_properties_for_organizations,omitempty"` + EnterpriseOrganizations *string `json:"enterprise_organizations,omitempty"` + EnterpriseOrganizationInstallations *string `json:"enterprise_organization_installations,omitempty"` + EnterpriseOrgInstallationRepos *string `json:"enterprise_organization_installation_repositories,omitempty"` + EnterprisePeople *string `json:"enterprise_people,omitempty"` + EnterpriseSSO *string `json:"enterprise_sso,omitempty"` + EnterpriseTeams *string `json:"enterprise_teams,omitempty"` + Environments *string `json:"environments,omitempty"` + Followers *string `json:"followers,omitempty"` + Gists *string `json:"gists,omitempty"` + GitSigningSSHPublicKeys *string `json:"git_signing_ssh_public_keys,omitempty"` + GPGKeys *string `json:"gpg_keys,omitempty"` + InteractionLimits *string `json:"interaction_limits,omitempty"` + Issues *string `json:"issues,omitempty"` + Keys *string `json:"keys,omitempty"` + Metadata *string `json:"metadata,omitempty"` + Members *string `json:"members,omitempty"` + MergeQueues *string `json:"merge_queues,omitempty"` + OrganizationActionsVariables *string `json:"organization_actions_variables,omitempty"` + OrganizationAdministration *string `json:"organization_administration,omitempty"` + OrganizationAnnouncementBanners *string `json:"organization_announcement_banners,omitempty"` + OrganizationAPIInsights *string `json:"organization_api_insights,omitempty"` + OrganizationCodespaces *string `json:"organization_codespaces,omitempty"` + OrganizationCodespacesSecrets *string `json:"organization_codespaces_secrets,omitempty"` + OrganizationCodespacesSettings *string `json:"organization_codespaces_settings,omitempty"` + OrganizationCopilotMetrics *string `json:"organization_copilot_metrics,omitempty"` + OrganizationCopilotSeatManagement *string `json:"organization_copilot_seat_management,omitempty"` + OrganizationCustomProperties *string `json:"organization_custom_properties,omitempty"` + OrganizationCustomRoles *string `json:"organization_custom_roles,omitempty"` + OrganizationCustomOrgRoles *string `json:"organization_custom_org_roles,omitempty"` + OrganizationDependabotSecrets *string `json:"organization_dependabot_secrets,omitempty"` + OrganizationEvents *string `json:"organization_events,omitempty"` + OrganizationHooks *string `json:"organization_hooks,omitempty"` + OrganizationKnowledgeBases *string `json:"organization_knowledge_bases,omitempty"` + OrganizationPackages *string `json:"organization_packages,omitempty"` + OrganizationPersonalAccessTokens *string `json:"organization_personal_access_tokens,omitempty"` + OrganizationPersonalAccessTokenRequests *string `json:"organization_personal_access_token_requests,omitempty"` + OrganizationPlan *string `json:"organization_plan,omitempty"` + OrganizationPreReceiveHooks *string `json:"organization_pre_receive_hooks,omitempty"` + OrganizationProjects *string `json:"organization_projects,omitempty"` + OrganizationSecrets *string `json:"organization_secrets,omitempty"` + OrganizationSelfHostedRunners *string `json:"organization_self_hosted_runners,omitempty"` + OrganizationUserBlocking *string `json:"organization_user_blocking,omitempty"` + Packages *string `json:"packages,omitempty"` + Pages *string `json:"pages,omitempty"` + Plan *string `json:"plan,omitempty"` + Profile *string `json:"profile,omitempty"` + PullRequests *string `json:"pull_requests,omitempty"` + RepositoryAdvisories *string `json:"repository_advisories,omitempty"` + RepositoryCustomProperties *string `json:"repository_custom_properties,omitempty"` + RepositoryHooks *string `json:"repository_hooks,omitempty"` + RepositoryProjects *string `json:"repository_projects,omitempty"` + RepositoryPreReceiveHooks *string `json:"repository_pre_receive_hooks,omitempty"` + Secrets *string `json:"secrets,omitempty"` + SecretScanningAlerts *string `json:"secret_scanning_alerts,omitempty"` + SecurityEvents *string `json:"security_events,omitempty"` + SingleFile *string `json:"single_file,omitempty"` + Starring *string `json:"starring,omitempty"` + Statuses *string `json:"statuses,omitempty"` + TeamDiscussions *string `json:"team_discussions,omitempty"` + UserEvents *string `json:"user_events,omitempty"` + VulnerabilityAlerts *string `json:"vulnerability_alerts,omitempty"` + Watching *string `json:"watching,omitempty"` + Workflows *string `json:"workflows,omitempty"` +} + +// InstallationRequest represents a pending GitHub App installation request. +type InstallationRequest struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Account *User `json:"account,omitempty"` + Requester *User `json:"requester,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` } // Installation represents a GitHub Apps installation. type Installation struct { - ID *int64 `json:"id,omitempty"` - AppID *int64 `json:"app_id,omitempty"` - TargetID *int64 `json:"target_id,omitempty"` - Account *User `json:"account,omitempty"` - AccessTokensURL *string `json:"access_tokens_url,omitempty"` - RepositoriesURL *string `json:"repositories_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - TargetType *string `json:"target_type,omitempty"` - SingleFileName *string `json:"single_file_name,omitempty"` - RepositorySelection *string `json:"repository_selection,omitempty"` - Events []string `json:"events,omitempty"` - Permissions *InstallationPermissions `json:"permissions,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + ClientID *string `json:"client_id,omitempty"` + AppID *int64 `json:"app_id,omitempty"` + AppSlug *string `json:"app_slug,omitempty"` + TargetID *int64 `json:"target_id,omitempty"` + Account *User `json:"account,omitempty"` + AccessTokensURL *string `json:"access_tokens_url,omitempty"` + RepositoriesURL *string `json:"repositories_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + TargetType *string `json:"target_type,omitempty"` + SingleFileName *string `json:"single_file_name,omitempty"` + RepositorySelection *string `json:"repository_selection,omitempty"` + Events []string `json:"events,omitempty"` + SingleFilePaths []string `json:"single_file_paths,omitempty"` + Permissions *InstallationPermissions `json:"permissions,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + HasMultipleSingleFiles *bool `json:"has_multiple_single_files,omitempty"` + SuspendedBy *User `json:"suspended_by,omitempty"` + SuspendedAt *Timestamp `json:"suspended_at,omitempty"` } // Attachment represents a GitHub Apps attachment. @@ -106,6 +211,13 @@ type Attachment struct { Body *string `json:"body,omitempty"` } +// ContentReference represents a reference to a URL in an issue or pull request. +type ContentReference struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Reference *string `json:"reference,omitempty"` +} + func (i Installation) String() string { return Stringify(i) } @@ -117,7 +229,12 @@ func (i Installation) String() string { // You can find this on the settings page for your GitHub App // (e.g., https://github.com/settings/apps/:app_slug). // -// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-an-app +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-the-authenticated-app +// +//meta:operation GET /app +//meta:operation GET /apps/{app_slug} func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) { var u string if appSlug != "" { @@ -126,16 +243,13 @@ func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, u = "app" } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - app := new(App) - resp, err := s.client.Do(ctx, req, app) + var app *App + resp, err := s.client.Do(req, &app) if err != nil { return nil, resp, err } @@ -143,25 +257,49 @@ func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, return app, resp, nil } -// ListInstallations lists the installations that the current GitHub App has. +// ListInstallationRequests lists the pending installation requests that the current GitHub App has. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#list-installation-requests-for-the-authenticated-app // -// GitHub API docs: https://developer.github.com/v3/apps/#find-installations -func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { - u, err := addOptions("app/installations", opt) +//meta:operation GET /app/installation-requests +func (s *AppsService) ListInstallationRequests(ctx context.Context, opts *ListOptions) ([]*InstallationRequest, *Response, error) { + u, err := addOptions("app/installation-requests", opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + var i []*InstallationRequest + resp, err := s.client.Do(req, &i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} + +// ListInstallations lists the installations that the current GitHub App has. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#list-installations-for-the-authenticated-app +// +//meta:operation GET /app/installations +func (s *AppsService) ListInstallations(ctx context.Context, opts *ListOptions) ([]*Installation, *Response, error) { + u, err := addOptions("app/installations", opts) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } var i []*Installation - resp, err := s.client.Do(ctx, req, &i) + resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } @@ -171,32 +309,33 @@ func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ( // GetInstallation returns the specified installation. // -// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-an-installation-for-the-authenticated-app +// +//meta:operation GET /app/installations/{installation_id} func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) { return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id)) } // ListUserInstallations lists installations that are accessible to the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user -func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { - u, err := addOptions("user/installations", opt) +// GitHub API docs: https://docs.github.com/rest/apps/installations?apiVersion=2022-11-28#list-app-installations-accessible-to-the-user-access-token +// +//meta:operation GET /user/installations +func (s *AppsService) ListUserInstallations(ctx context.Context, opts *ListOptions) ([]*Installation, *Response, error) { + u, err := addOptions("user/installations", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - var i struct { Installations []*Installation `json:"installations"` } - resp, err := s.client.Do(ctx, req, &i) + resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } @@ -204,22 +343,93 @@ func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOption return i.Installations, resp, nil } +// SuspendInstallation suspends the specified installation. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#suspend-an-app-installation +// +//meta:operation PUT /app/installations/{installation_id}/suspended +func (s *AppsService) SuspendInstallation(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("app/installations/%v/suspended", id) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// UnsuspendInstallation unsuspends the specified installation. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#unsuspend-an-app-installation +// +//meta:operation DELETE /app/installations/{installation_id}/suspended +func (s *AppsService) UnsuspendInstallation(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("app/installations/%v/suspended", id) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteInstallation deletes the specified installation. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#delete-an-installation-for-the-authenticated-app +// +//meta:operation DELETE /app/installations/{installation_id} +func (s *AppsService) DeleteInstallation(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("app/installations/%v", id) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + // CreateInstallationToken creates a new installation token. // -// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token -func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64, opt *InstallationTokenOptions) (*InstallationToken, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app +// +//meta:operation POST /app/installations/{installation_id}/access_tokens +func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64, body *InstallationTokenOptions) (*InstallationToken, *Response, error) { u := fmt.Sprintf("app/installations/%v/access_tokens", id) - req, err := s.client.NewRequest("POST", u, opt) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) + var t *InstallationToken + resp, err := s.client.Do(req, &t) + if err != nil { + return nil, resp, err + } - t := new(InstallationToken) - resp, err := s.client.Do(ctx, req, t) + return t, resp, nil +} + +// CreateInstallationTokenListRepos creates a new installation token with a list of all repositories in an installation which is not possible with CreateInstallationToken. +// +// It differs from CreateInstallationToken by taking InstallationTokenListRepoOptions as a parameter which does not omit RepositoryIDs if that field is nil or an empty array. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-an-installation-access-token-for-an-app +// +//meta:operation POST /app/installations/{installation_id}/access_tokens +func (s *AppsService) CreateInstallationTokenListRepos(ctx context.Context, id int64, body *InstallationTokenListRepoOptions) (*InstallationToken, *Response, error) { + u := fmt.Sprintf("app/installations/%v/access_tokens", id) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var t *InstallationToken + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -227,22 +437,25 @@ func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64, opt return t, resp, nil } -// Create a new attachment on user comment containing a url. +// CreateAttachment creates a new attachment on user comment containing a url. // -// GitHub API docs: https://developer.github.com/v3/apps/#create-a-content-attachment +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.3/rest/reference/apps#create-a-content-attachment +// +//meta:operation POST /repos/{owner}/{repo}/content_references/{content_reference_id}/attachments func (s *AppsService) CreateAttachment(ctx context.Context, contentReferenceID int64, title, body string) (*Attachment, *Response, error) { u := fmt.Sprintf("content_references/%v/attachments", contentReferenceID) - payload := &Attachment{Title: String(title), Body: String(body)} - req, err := s.client.NewRequest("POST", u, payload) + payload := &Attachment{Title: &title, Body: &body} + req, err := s.client.NewRequest(ctx, "POST", u, payload) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeReactionsPreview) + req.Header.Set("Accept", mediaTypeContentAttachmentsPreview) - m := &Attachment{} - resp, err := s.client.Do(ctx, req, m) + var m *Attachment + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -250,45 +463,59 @@ func (s *AppsService) CreateAttachment(ctx context.Context, contentReferenceID i return m, resp, nil } -// FindOrganizationInstallation finds the organization's installation information. +// GetOrganizationInstallation finds the organization's installation information. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-an-organization-installation-for-the-authenticated-app // -// GitHub API docs: https://developer.github.com/v3/apps/#find-organization-installation -func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) { +//meta:operation GET /orgs/{org}/installation +func (s *AppsService) GetOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) { return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org)) } -// FindRepositoryInstallation finds the repository's installation information. +// GetEnterpriseInstallation finds the enterprise's installation information. // -// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation -func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/apps/apps?apiVersion=2022-11-28#get-an-enterprise-installation-for-the-authenticated-app +// +//meta:operation GET /enterprises/{enterprise}/installation +func (s *AppsService) GetEnterpriseInstallation(ctx context.Context, enterprise string) (*Installation, *Response, error) { + return s.getInstallation(ctx, fmt.Sprintf("enterprises/%v/installation", enterprise)) +} + +// GetRepositoryInstallation finds the repository's installation information. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-repository-installation-for-the-authenticated-app +// +//meta:operation GET /repos/{owner}/{repo}/installation +func (s *AppsService) GetRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) { return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo)) } -// FindRepositoryInstallationByID finds the repository's installation information. +// GetRepositoryInstallationByID finds the repository's installation information. +// +// Note: GetRepositoryInstallationByID uses the undocumented GitHub API endpoint "GET /repositories/{repository_id}/installation". // -// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation. -func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) { - return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id)) +//meta:operation GET /repositories/{repository_id}/installation +func (s *AppsService) GetRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) { + return s.getInstallation(ctx, fmt.Sprintf("repositories/%v/installation", id)) } -// FindUserInstallation finds the user's installation information. +// GetUserInstallation finds the user's installation information. // -// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation -func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#get-a-user-installation-for-the-authenticated-app +// +//meta:operation GET /users/{username}/installation +func (s *AppsService) GetUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) { return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user)) } func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) { - req, err := s.client.NewRequest("GET", url, nil) + req, err := s.client.NewRequest(ctx, "GET", url, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - i := new(Installation) - resp, err := s.client.Do(ctx, req, i) + var i *Installation + resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } diff --git a/github/apps_hooks.go b/github/apps_hooks.go new file mode 100644 index 00000000000..6f0b35fb83d --- /dev/null +++ b/github/apps_hooks.go @@ -0,0 +1,52 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// GetHookConfig returns the webhook configuration for a GitHub App. +// The underlying transport must be authenticated as an app. +// +// GitHub API docs: https://docs.github.com/rest/apps/webhooks?apiVersion=2022-11-28#get-a-webhook-configuration-for-an-app +// +//meta:operation GET /app/hook/config +func (s *AppsService) GetHookConfig(ctx context.Context) (*HookConfig, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "app/hook/config", nil) + if err != nil { + return nil, nil, err + } + + var config *HookConfig + resp, err := s.client.Do(req, &config) + if err != nil { + return nil, resp, err + } + + return config, resp, nil +} + +// UpdateHookConfig updates the webhook configuration for a GitHub App. +// The underlying transport must be authenticated as an app. +// +// GitHub API docs: https://docs.github.com/rest/apps/webhooks?apiVersion=2022-11-28#update-a-webhook-configuration-for-an-app +// +//meta:operation PATCH /app/hook/config +func (s *AppsService) UpdateHookConfig(ctx context.Context, body HookConfig) (*HookConfig, *Response, error) { + req, err := s.client.NewRequest(ctx, "PATCH", "app/hook/config", body) + if err != nil { + return nil, nil, err + } + + var c *HookConfig + resp, err := s.client.Do(req, &c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} diff --git a/github/apps_hooks_deliveries.go b/github/apps_hooks_deliveries.go new file mode 100644 index 00000000000..e737bbae094 --- /dev/null +++ b/github/apps_hooks_deliveries.go @@ -0,0 +1,78 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListHookDeliveries lists deliveries of an App webhook. +// +// GitHub API docs: https://docs.github.com/rest/apps/webhooks?apiVersion=2022-11-28#list-deliveries-for-an-app-webhook +// +//meta:operation GET /app/hook/deliveries +func (s *AppsService) ListHookDeliveries(ctx context.Context, opts *ListCursorOptions) ([]*HookDelivery, *Response, error) { + u, err := addOptions("app/hook/deliveries", opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + deliveries := []*HookDelivery{} + resp, err := s.client.Do(req, &deliveries) + if err != nil { + return nil, resp, err + } + + return deliveries, resp, nil +} + +// GetHookDelivery returns the App webhook delivery with the specified ID. +// +// GitHub API docs: https://docs.github.com/rest/apps/webhooks?apiVersion=2022-11-28#get-a-delivery-for-an-app-webhook +// +//meta:operation GET /app/hook/deliveries/{delivery_id} +func (s *AppsService) GetHookDelivery(ctx context.Context, deliveryID int64) (*HookDelivery, *Response, error) { + u := fmt.Sprintf("app/hook/deliveries/%v", deliveryID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var h *HookDelivery + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} + +// RedeliverHookDelivery redelivers a delivery for an App webhook. +// +// GitHub API docs: https://docs.github.com/rest/apps/webhooks?apiVersion=2022-11-28#redeliver-a-delivery-for-an-app-webhook +// +//meta:operation POST /app/hook/deliveries/{delivery_id}/attempts +func (s *AppsService) RedeliverHookDelivery(ctx context.Context, deliveryID int64) (*HookDelivery, *Response, error) { + u := fmt.Sprintf("app/hook/deliveries/%v/attempts", deliveryID) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var h *HookDelivery + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} diff --git a/github/apps_hooks_deliveries_test.go b/github/apps_hooks_deliveries_test.go new file mode 100644 index 00000000000..483611544ec --- /dev/null +++ b/github/apps_hooks_deliveries_test.go @@ -0,0 +1,118 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestAppsService_ListHookDeliveries(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/hook/deliveries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"cursor": "v1_12077215967"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opts := &ListCursorOptions{Cursor: "v1_12077215967"} + + ctx := t.Context() + + deliveries, _, err := client.Apps.ListHookDeliveries(ctx, opts) + if err != nil { + t.Errorf("Apps.ListHookDeliveries returned error: %v", err) + } + + want := []*HookDelivery{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if d := cmp.Diff(deliveries, want); d != "" { + t.Errorf("Apps.ListHooks want (-), got (+):\n%v", d) + } + + const methodName = "ListHookDeliveries" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.ListHookDeliveries(ctx, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAppsService_GetHookDelivery(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/hook/deliveries/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + hook, _, err := client.Apps.GetHookDelivery(ctx, 1) + if err != nil { + t.Errorf("Apps.GetHookDelivery returned error: %v", err) + } + + want := &HookDelivery{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { + t.Errorf("Apps.GetHookDelivery returned %+v, want %+v", hook, want) + } + + const methodName = "GetHookDelivery" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetHookDelivery(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetHookDelivery(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAppsService_RedeliverHookDelivery(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/hook/deliveries/1/attempts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + hook, _, err := client.Apps.RedeliverHookDelivery(ctx, 1) + if err != nil { + t.Errorf("Apps.RedeliverHookDelivery returned error: %v", err) + } + + want := &HookDelivery{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { + t.Errorf("Apps.RedeliverHookDelivery returned %+v, want %+v", hook, want) + } + + const methodName = "RedeliverHookDelivery" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.RedeliverHookDelivery(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.RedeliverHookDelivery(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/apps_hooks_test.go b/github/apps_hooks_test.go new file mode 100644 index 00000000000..4ca7ded24d4 --- /dev/null +++ b/github/apps_hooks_test.go @@ -0,0 +1,102 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestAppsService_GetHookConfig(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/hook/config", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "content_type": "json", + "insecure_ssl": "0", + "secret": "********", + "url": "https://example.com/webhook" + }`) + }) + + ctx := t.Context() + config, _, err := client.Apps.GetHookConfig(ctx) + if err != nil { + t.Errorf("Apps.GetHookConfig returned error: %v", err) + } + + want := &HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("0"), + Secret: Ptr("********"), + URL: Ptr("https://example.com/webhook"), + } + if !cmp.Equal(config, want) { + t.Errorf("Apps.GetHookConfig returned %+v, want %+v", config, want) + } + + const methodName = "GetHookConfig" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetHookConfig(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAppsService_UpdateHookConfig(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("1"), + Secret: Ptr("s"), + URL: Ptr("u"), + } + + mux.HandleFunc("/app/hook/config", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "content_type": "json", + "insecure_ssl": "1", + "secret": "********", + "url": "u" + }`) + }) + + ctx := t.Context() + config, _, err := client.Apps.UpdateHookConfig(ctx, input) + if err != nil { + t.Errorf("Apps.UpdateHookConfig returned error: %v", err) + } + + want := &HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("1"), + Secret: Ptr("********"), + URL: Ptr("u"), + } + if !cmp.Equal(config, want) { + t.Errorf("Apps.UpdateHookConfig returned %+v, want %+v", config, want) + } + + const methodName = "UpdateHookConfig" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.UpdateHookConfig(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/apps_installation.go b/github/apps_installation.go index 09d4043fe64..7fbcc260d3b 100644 --- a/github/apps_installation.go +++ b/github/apps_installation.go @@ -10,77 +10,78 @@ import ( "fmt" ) +// ListRepositories represents the response from the list repos endpoints. +type ListRepositories struct { + TotalCount *int `json:"total_count,omitempty"` + Repositories []*Repository `json:"repositories"` +} + // ListRepos lists the repositories that are accessible to the authenticated installation. // -// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories -func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repository, *Response, error) { - u, err := addOptions("installation/repositories", opt) +// GitHub API docs: https://docs.github.com/rest/apps/installations?apiVersion=2022-11-28#list-repositories-accessible-to-the-app-installation +// +//meta:operation GET /installation/repositories +func (s *AppsService) ListRepos(ctx context.Context, opts *ListOptions) (*ListRepositories, *Response, error) { + u, err := addOptions("installation/repositories", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - var r struct { - Repositories []*Repository `json:"repositories"` - } - resp, err := s.client.Do(ctx, req, &r) + var r *ListRepositories + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return r.Repositories, resp, nil + return r, resp, nil } // ListUserRepos lists repositories that are accessible // to the authenticated user for an installation. // -// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation -func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOptions) ([]*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/apps/installations?apiVersion=2022-11-28#list-repositories-accessible-to-the-user-access-token +// +//meta:operation GET /user/installations/{installation_id}/repositories +func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opts *ListOptions) (*ListRepositories, *Response, error) { u := fmt.Sprintf("user/installations/%v/repositories", id) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - var r struct { - Repositories []*Repository `json:"repositories"` - } - resp, err := s.client.Do(ctx, req, &r) + var r *ListRepositories + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return r.Repositories, resp, nil + return r, resp, nil } // AddRepository adds a single repository to an installation. // -// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-repository-to-installation +// GitHub API docs: https://docs.github.com/rest/apps/installations?apiVersion=2022-11-28#add-a-repository-to-an-app-installation +// +//meta:operation PUT /user/installations/{installation_id}/repositories/{repository_id} func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (*Repository, *Response, error) { u := fmt.Sprintf("user/installations/%v/repositories/%v", instID, repoID) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeIntegrationPreview) - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) + var r *Repository + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -90,14 +91,30 @@ func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) ( // RemoveRepository removes a single repository from an installation. // -// GitHub docs: https://developer.github.com/v3/apps/installations/#remove-repository-from-installation +// GitHub API docs: https://docs.github.com/rest/apps/installations?apiVersion=2022-11-28#remove-a-repository-from-an-app-installation +// +//meta:operation DELETE /user/installations/{installation_id}/repositories/{repository_id} func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64) (*Response, error) { u := fmt.Sprintf("user/installations/%v/repositories/%v", instID, repoID) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RevokeInstallationToken revokes an installation token. +// +// GitHub API docs: https://docs.github.com/rest/apps/installations?apiVersion=2022-11-28#revoke-an-installation-access-token +// +//meta:operation DELETE /installation/token +func (s *AppsService) RevokeInstallationToken(ctx context.Context) (*Response, error) { + u := "installation/token" + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - req.Header.Set("Accept", mediaTypeIntegrationPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/apps_installation_test.go b/github/apps_installation_test.go index 745e1e5dc4d..f8e833b1f23 100644 --- a/github/apps_installation_test.go +++ b/github/apps_installation_test.go @@ -6,98 +6,156 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestAppsService_ListRepos(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/installation/repositories", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) testFormValues(t, r, values{ "page": "1", "per_page": "2", }) - fmt.Fprint(w, `{"repositories": [{"id":1}]}`) + fmt.Fprint(w, `{"total_count": 1,"repositories": [{"id": 1}]}`) }) opt := &ListOptions{Page: 1, PerPage: 2} - repositories, _, err := client.Apps.ListRepos(context.Background(), opt) + ctx := t.Context() + repositories, _, err := client.Apps.ListRepos(ctx, opt) if err != nil { t.Errorf("Apps.ListRepos returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(repositories, want) { + want := &ListRepositories{TotalCount: Ptr(1), Repositories: []*Repository{{ID: Ptr(int64(1))}}} + if !cmp.Equal(repositories, want) { t.Errorf("Apps.ListRepos returned %+v, want %+v", repositories, want) } + + const methodName = "ListRepos" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.ListRepos(ctx, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_ListUserRepos(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/installations/1/repositories", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) testFormValues(t, r, values{ "page": "1", "per_page": "2", }) - fmt.Fprint(w, `{"repositories": [{"id":1}]}`) + fmt.Fprint(w, `{"total_count":1,"repositories": [{"id":1}]}`) }) opt := &ListOptions{Page: 1, PerPage: 2} - repositories, _, err := client.Apps.ListUserRepos(context.Background(), 1, opt) + ctx := t.Context() + repositories, _, err := client.Apps.ListUserRepos(ctx, 1, opt) if err != nil { t.Errorf("Apps.ListUserRepos returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(repositories, want) { + want := &ListRepositories{TotalCount: Ptr(1), Repositories: []*Repository{{ID: Ptr(int64(1))}}} + if !cmp.Equal(repositories, want) { t.Errorf("Apps.ListUserRepos returned %+v, want %+v", repositories, want) } + + const methodName = "ListUserRepos" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.ListUserRepos(ctx, -1, &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.ListUserRepos(ctx, 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_AddRepository(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/installations/1/repositories/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) }) - repo, _, err := client.Apps.AddRepository(context.Background(), 1, 1) + ctx := t.Context() + repo, _, err := client.Apps.AddRepository(ctx, 1, 1) if err != nil { t.Errorf("Apps.AddRepository returned error: %v", err) } - want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} - if !reflect.DeepEqual(repo, want) { + want := &Repository{ID: Ptr(int64(1)), Name: Ptr("n"), Description: Ptr("d"), Owner: &User{Login: Ptr("l")}, License: &License{Key: Ptr("mit")}} + if !cmp.Equal(repo, want) { t.Errorf("AddRepository returned %+v, want %+v", repo, want) } + + const methodName = "AddRepository" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.AddRepository(ctx, 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_RemoveRepository(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/installations/1/repositories/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) w.WriteHeader(http.StatusNoContent) }) - _, err := client.Apps.RemoveRepository(context.Background(), 1, 1) + ctx := t.Context() + _, err := client.Apps.RemoveRepository(ctx, 1, 1) if err != nil { t.Errorf("Apps.RemoveRepository returned error: %v", err) } + + const methodName = "RemoveRepository" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Apps.RemoveRepository(ctx, 1, 1) + }) +} + +func TestAppsService_RevokeInstallationToken(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/installation/token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Apps.RevokeInstallationToken(ctx) + if err != nil { + t.Errorf("Apps.RevokeInstallationToken returned error: %v", err) + } + + const methodName = "RevokeInstallationToken" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Apps.RevokeInstallationToken(ctx) + }) } diff --git a/github/apps_manifest.go b/github/apps_manifest.go new file mode 100644 index 00000000000..af6c9a5a58b --- /dev/null +++ b/github/apps_manifest.go @@ -0,0 +1,51 @@ +// Copyright 2019 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// AppConfig describes the configuration of a GitHub App. +type AppConfig struct { + ID *int64 `json:"id,omitempty"` + Slug *string `json:"slug,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Owner *User `json:"owner,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + ExternalURL *string `json:"external_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClientID *string `json:"client_id,omitempty"` + ClientSecret *string `json:"client_secret,omitempty"` + WebhookSecret *string `json:"webhook_secret,omitempty"` + PEM *string `json:"pem,omitempty"` +} + +// CompleteAppManifest completes the App manifest handshake flow for the given +// code. +// +// GitHub API docs: https://docs.github.com/rest/apps/apps?apiVersion=2022-11-28#create-a-github-app-from-a-manifest +// +//meta:operation POST /app-manifests/{code}/conversions +func (s *AppsService) CompleteAppManifest(ctx context.Context, code string) (*AppConfig, *Response, error) { + u := fmt.Sprintf("app-manifests/%v/conversions", code) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var cfg *AppConfig + resp, err := s.client.Do(req, &cfg) + if err != nil { + return nil, resp, err + } + + return cfg, resp, nil +} diff --git a/github/apps_manifest_test.go b/github/apps_manifest_test.go new file mode 100644 index 00000000000..799bb2eb494 --- /dev/null +++ b/github/apps_manifest_test.go @@ -0,0 +1,67 @@ +// Copyright 2019 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +const ( + manifestJSON = `{ + "id": 1, + "client_id": "a" , + "client_secret": "b", + "webhook_secret": "c", + "pem": "key" +} +` +) + +func TestAppsService_CompleteAppManifest(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app-manifests/code/conversions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, manifestJSON) + }) + + ctx := t.Context() + cfg, _, err := client.Apps.CompleteAppManifest(ctx, "code") + if err != nil { + t.Errorf("Apps.CompleteAppManifest returned error: %v", err) + } + + want := &AppConfig{ + ID: Ptr(int64(1)), + ClientID: Ptr("a"), + ClientSecret: Ptr("b"), + WebhookSecret: Ptr("c"), + PEM: Ptr("key"), + } + + if !cmp.Equal(cfg, want) { + t.Errorf("Apps.CompleteAppManifest returned %+v, want %+v", cfg, want) + } + + const methodName = "CompleteAppManifest" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.CompleteAppManifest(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.CompleteAppManifest(ctx, "code") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/apps_marketplace.go b/github/apps_marketplace.go index 863ba934cbc..ed545df093e 100644 --- a/github/apps_marketplace.go +++ b/github/apps_marketplace.go @@ -13,7 +13,7 @@ import ( // MarketplaceService handles communication with the marketplace related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/ +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28 type MarketplaceService struct { client *Client // Stubbed controls whether endpoints that return stubbed data are used @@ -21,7 +21,7 @@ type MarketplaceService struct { // for testing your GitHub Apps. Stubbed data is hard-coded and will not // change based on actual subscriptions. // - // GitHub API docs: https://developer.github.com/v3/apps/marketplace/ + // GitHub API docs: https://docs.github.com/rest/apps?apiVersion=2022-11-28#testing-with-stubbed-endpoints Stubbed bool } @@ -30,6 +30,7 @@ type MarketplacePlan struct { URL *string `json:"url,omitempty"` AccountsURL *string `json:"accounts_url,omitempty"` ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` MonthlyPriceInCents *int `json:"monthly_price_in_cents,omitempty"` @@ -45,14 +46,15 @@ type MarketplacePlan struct { // MarketplacePurchase represents a GitHub Apps Marketplace Purchase. type MarketplacePurchase struct { + Account *MarketplacePurchaseAccount `json:"account,omitempty"` // BillingCycle can be one of the values "yearly", "monthly" or nil. - BillingCycle *string `json:"billing_cycle,omitempty"` - NextBillingDate *Timestamp `json:"next_billing_date,omitempty"` - UnitCount *int `json:"unit_count,omitempty"` - Plan *MarketplacePlan `json:"plan,omitempty"` - Account *MarketplacePlanAccount `json:"account,omitempty"` - OnFreeTrial *bool `json:"on_free_trial,omitempty"` - FreeTrialEndsOn *Timestamp `json:"free_trial_ends_on,omitempty"` + BillingCycle *string `json:"billing_cycle,omitempty"` + NextBillingDate *Timestamp `json:"next_billing_date,omitempty"` + UnitCount *int `json:"unit_count,omitempty"` + Plan *MarketplacePlan `json:"plan,omitempty"` + OnFreeTrial *bool `json:"on_free_trial,omitempty"` + FreeTrialEndsOn *Timestamp `json:"free_trial_ends_on,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` } // MarketplacePendingChange represents a pending change to a GitHub Apps Marketplace Plan. @@ -68,31 +70,45 @@ type MarketplacePlanAccount struct { URL *string `json:"url,omitempty"` Type *string `json:"type,omitempty"` ID *int64 `json:"id,omitempty"` - NodeID *string `json:"node_id,omitempty"` Login *string `json:"login,omitempty"` - Email *string `json:"email,omitempty"` OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"` MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` MarketplacePendingChange *MarketplacePendingChange `json:"marketplace_pending_change,omitempty"` } +// MarketplacePurchaseAccount represents a GitHub Account (user or organization) for a Purchase. +type MarketplacePurchaseAccount struct { + URL *string `json:"url,omitempty"` + Type *string `json:"type,omitempty"` + ID *int64 `json:"id,omitempty"` + Login *string `json:"login,omitempty"` + OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"` + Email *string `json:"email,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + // ListPlans lists all plans for your Marketplace listing. // -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing -func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#list-plans +// +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#list-plans-stubbed +// +//meta:operation GET /marketplace_listing/plans +//meta:operation GET /marketplace_listing/stubbed/plans +func (s *MarketplaceService) ListPlans(ctx context.Context, opts *ListOptions) ([]*MarketplacePlan, *Response, error) { uri := s.marketplaceURI("plans") - u, err := addOptions(uri, opt) + u, err := addOptions(uri, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var plans []*MarketplacePlan - resp, err := s.client.Do(ctx, req, &plans) + resp, err := s.client.Do(req, &plans) if err != nil { return nil, resp, err } @@ -102,21 +118,26 @@ func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([ // ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan. // -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan -func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#list-accounts-for-a-plan +// +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#list-accounts-for-a-plan-stubbed +// +//meta:operation GET /marketplace_listing/plans/{plan_id}/accounts +//meta:operation GET /marketplace_listing/stubbed/plans/{plan_id}/accounts +func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opts *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID)) - u, err := addOptions(uri, opt) + u, err := addOptions(uri, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var accounts []*MarketplacePlanAccount - resp, err := s.client.Do(ctx, req, &accounts) + resp, err := s.client.Do(req, &accounts) if err != nil { return nil, resp, err } @@ -124,51 +145,57 @@ func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID return accounts, resp, nil } -// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account. +// GetPlanAccountForAccount get GitHub account (user or organization) associated with an account. +// +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#get-a-subscription-plan-for-an-account +// +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#get-a-subscription-plan-for-an-account-stubbed // -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing -func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { +//meta:operation GET /marketplace_listing/accounts/{account_id} +//meta:operation GET /marketplace_listing/stubbed/accounts/{account_id} +func (s *MarketplaceService) GetPlanAccountForAccount(ctx context.Context, accountID int64) (*MarketplacePlanAccount, *Response, error) { uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID)) - u, err := addOptions(uri, opt) - if err != nil { - return nil, nil, err - } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", uri, nil) if err != nil { return nil, nil, err } - var accounts []*MarketplacePlanAccount - resp, err := s.client.Do(ctx, req, &accounts) + var account *MarketplacePlanAccount + resp, err := s.client.Do(req, &account) if err != nil { return nil, resp, err } - return accounts, resp, nil + return account, resp, nil } // ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user. // -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases -func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#list-subscriptions-for-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/apps/marketplace?apiVersion=2022-11-28#list-subscriptions-for-the-authenticated-user-stubbed +// +//meta:operation GET /user/marketplace_purchases +//meta:operation GET /user/marketplace_purchases/stubbed +func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opts *ListOptions) ([]*MarketplacePurchase, *Response, error) { uri := "user/marketplace_purchases" if s.Stubbed { uri = "user/marketplace_purchases/stubbed" } - u, err := addOptions(uri, opt) + u, err := addOptions(uri, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var purchases []*MarketplacePurchase - resp, err := s.client.Do(ctx, req, &purchases) + resp, err := s.client.Do(req, &purchases) if err != nil { return nil, resp, err } diff --git a/github/apps_marketplace_test.go b/github/apps_marketplace_test.go index e293c83a16d..8363f725dbb 100644 --- a/github/apps_marketplace_test.go +++ b/github/apps_marketplace_test.go @@ -6,16 +6,16 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestMarketplaceService_ListPlans(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/marketplace_listing/plans", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -28,20 +28,30 @@ func TestMarketplaceService_ListPlans(t *testing.T) { opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = false - plans, _, err := client.Marketplace.ListPlans(context.Background(), opt) + ctx := t.Context() + plans, _, err := client.Marketplace.ListPlans(ctx, opt) if err != nil { t.Errorf("Marketplace.ListPlans returned error: %v", err) } - want := []*MarketplacePlan{{ID: Int64(1)}} - if !reflect.DeepEqual(plans, want) { + want := []*MarketplacePlan{{ID: Ptr(int64(1))}} + if !cmp.Equal(plans, want) { t.Errorf("Marketplace.ListPlans returned %+v, want %+v", plans, want) } + + const methodName = "ListPlans" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Marketplace.ListPlans(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMarketplaceService_Stubbed_ListPlans(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/marketplace_listing/stubbed/plans", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -50,20 +60,21 @@ func TestMarketplaceService_Stubbed_ListPlans(t *testing.T) { opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = true - plans, _, err := client.Marketplace.ListPlans(context.Background(), opt) + ctx := t.Context() + plans, _, err := client.Marketplace.ListPlans(ctx, opt) if err != nil { t.Errorf("Marketplace.ListPlans (Stubbed) returned error: %v", err) } - want := []*MarketplacePlan{{ID: Int64(1)}} - if !reflect.DeepEqual(plans, want) { + want := []*MarketplacePlan{{ID: Ptr(int64(1))}} + if !cmp.Equal(plans, want) { t.Errorf("Marketplace.ListPlans (Stubbed) returned %+v, want %+v", plans, want) } } func TestMarketplaceService_ListPlanAccountsForPlan(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/marketplace_listing/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -72,20 +83,30 @@ func TestMarketplaceService_ListPlanAccountsForPlan(t *testing.T) { opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = false - accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt) + ctx := t.Context() + accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(ctx, 1, opt) if err != nil { t.Errorf("Marketplace.ListPlanAccountsForPlan returned error: %v", err) } - want := []*MarketplacePlanAccount{{ID: Int64(1)}} - if !reflect.DeepEqual(accounts, want) { + want := []*MarketplacePlanAccount{{ID: Ptr(int64(1))}} + if !cmp.Equal(accounts, want) { t.Errorf("Marketplace.ListPlanAccountsForPlan returned %+v, want %+v", accounts, want) } + + const methodName = "ListPlanAccountsForPlan" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Marketplace.ListPlanAccountsForPlan(ctx, 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMarketplaceService_Stubbed_ListPlanAccountsForPlan(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/marketplace_listing/stubbed/plans/1/accounts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -94,64 +115,74 @@ func TestMarketplaceService_Stubbed_ListPlanAccountsForPlan(t *testing.T) { opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = true - accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(context.Background(), 1, opt) + ctx := t.Context() + accounts, _, err := client.Marketplace.ListPlanAccountsForPlan(ctx, 1, opt) if err != nil { t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned error: %v", err) } - want := []*MarketplacePlanAccount{{ID: Int64(1)}} - if !reflect.DeepEqual(accounts, want) { + want := []*MarketplacePlanAccount{{ID: Ptr(int64(1))}} + if !cmp.Equal(accounts, want) { t.Errorf("Marketplace.ListPlanAccountsForPlan (Stubbed) returned %+v, want %+v", accounts, want) } } -func TestMarketplaceService_ListPlanAccountsForAccount(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestMarketplaceService_GetPlanAccountForAccount(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/marketplace_listing/accounts/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `[{"id":1, "marketplace_pending_change": {"id": 77}}]`) + fmt.Fprint(w, `{"id":1, "marketplace_pending_change": {"id": 77}}`) }) - opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = false - accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt) + ctx := t.Context() + account, _, err := client.Marketplace.GetPlanAccountForAccount(ctx, 1) if err != nil { - t.Errorf("Marketplace.ListPlanAccountsForAccount returned error: %v", err) + t.Errorf("Marketplace.GetPlanAccountForAccount returned error: %v", err) } - want := []*MarketplacePlanAccount{{ID: Int64(1), MarketplacePendingChange: &MarketplacePendingChange{ID: Int64(77)}}} - if !reflect.DeepEqual(accounts, want) { - t.Errorf("Marketplace.ListPlanAccountsForAccount returned %+v, want %+v", accounts, want) + want := &MarketplacePlanAccount{ID: Ptr(int64(1)), MarketplacePendingChange: &MarketplacePendingChange{ID: Ptr(int64(77))}} + if !cmp.Equal(account, want) { + t.Errorf("Marketplace.GetPlanAccountForAccount returned %+v, want %+v", account, want) } + + const methodName = "GetPlanAccountForAccount" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Marketplace.GetPlanAccountForAccount(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestMarketplaceService_Stubbed_ListPlanAccountsForAccount(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestMarketplaceService_Stubbed_GetPlanAccountForAccount(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/marketplace_listing/stubbed/accounts/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `[{"id":1}]`) + fmt.Fprint(w, `{"id":1}`) }) - opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = true - accounts, _, err := client.Marketplace.ListPlanAccountsForAccount(context.Background(), 1, opt) + ctx := t.Context() + account, _, err := client.Marketplace.GetPlanAccountForAccount(ctx, 1) if err != nil { - t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned error: %v", err) + t.Errorf("Marketplace.GetPlanAccountForAccount (Stubbed) returned error: %v", err) } - want := []*MarketplacePlanAccount{{ID: Int64(1)}} - if !reflect.DeepEqual(accounts, want) { - t.Errorf("Marketplace.ListPlanAccountsForAccount (Stubbed) returned %+v, want %+v", accounts, want) + want := &MarketplacePlanAccount{ID: Ptr(int64(1))} + if !cmp.Equal(account, want) { + t.Errorf("Marketplace.GetPlanAccountForAccount (Stubbed) returned %+v, want %+v", account, want) } } func TestMarketplaceService_ListMarketplacePurchasesForUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/marketplace_purchases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -160,20 +191,30 @@ func TestMarketplaceService_ListMarketplacePurchasesForUser(t *testing.T) { opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = false - purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt) + ctx := t.Context() + purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(ctx, opt) if err != nil { t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err) } - want := []*MarketplacePurchase{{BillingCycle: String("monthly")}} - if !reflect.DeepEqual(purchases, want) { + want := []*MarketplacePurchase{{BillingCycle: Ptr("monthly")}} + if !cmp.Equal(purchases, want) { t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want) } + + const methodName = "ListMarketplacePurchasesForUser" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Marketplace.ListMarketplacePurchasesForUser(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMarketplaceService_Stubbed_ListMarketplacePurchasesForUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/marketplace_purchases/stubbed", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -182,13 +223,14 @@ func TestMarketplaceService_Stubbed_ListMarketplacePurchasesForUser(t *testing.T opt := &ListOptions{Page: 1, PerPage: 2} client.Marketplace.Stubbed = true - purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(context.Background(), opt) + ctx := t.Context() + purchases, _, err := client.Marketplace.ListMarketplacePurchasesForUser(ctx, opt) if err != nil { t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned error: %v", err) } - want := []*MarketplacePurchase{{BillingCycle: String("monthly")}} - if !reflect.DeepEqual(purchases, want) { + want := []*MarketplacePurchase{{BillingCycle: Ptr("monthly")}} + if !cmp.Equal(purchases, want) { t.Errorf("Marketplace.ListMarketplacePurchasesForUser returned %+v, want %+v", purchases, want) } } diff --git a/github/apps_test.go b/github/apps_test.go index 55ef2a095ae..d936a4f3aac 100644 --- a/github/apps_test.go +++ b/github/apps_test.go @@ -6,67 +6,121 @@ package github import ( - "bytes" - "context" - "encoding/json" "fmt" - "io" - "io/ioutil" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestAppsService_Get_authenticatedApp(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/app", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"id":1}`) }) - app, _, err := client.Apps.Get(context.Background(), "") + ctx := t.Context() + app, _, err := client.Apps.Get(ctx, "") if err != nil { t.Errorf("Apps.Get returned error: %v", err) } - want := &App{ID: Int64(1)} - if !reflect.DeepEqual(app, want) { + want := &App{ID: Ptr(int64(1))} + if !cmp.Equal(app, want) { t.Errorf("Apps.Get returned %+v, want %+v", app, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.Get(ctx, "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_Get_specifiedApp(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/apps/a", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"html_url":"https://github.com/apps/a"}`) }) - app, _, err := client.Apps.Get(context.Background(), "a") + ctx := t.Context() + app, _, err := client.Apps.Get(ctx, "a") if err != nil { t.Errorf("Apps.Get returned error: %v", err) } - want := &App{HTMLURL: String("https://github.com/apps/a")} - if !reflect.DeepEqual(app, want) { + want := &App{HTMLURL: Ptr("https://github.com/apps/a")} + if !cmp.Equal(app, want) { t.Errorf("Apps.Get returned %+v, want %+v", *app.HTMLURL, *want.HTMLURL) } } +func TestAppsService_ListInstallationRequests(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/installation-requests", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `[{ + "id": 1, + "account": { "id": 2 }, + "requester": { "id": 3 }, + "created_at": `+referenceTimeStr+` + }]`, + ) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + ctx := t.Context() + installationRequests, _, err := client.Apps.ListInstallationRequests(ctx, opt) + if err != nil { + t.Errorf("Apps.ListInstallationRequests returned error: %v", err) + } + + want := []*InstallationRequest{{ + ID: Ptr(int64(1)), + Account: &User{ID: Ptr(int64(2))}, + Requester: &User{ID: Ptr(int64(3))}, + CreatedAt: &referenceTimestamp, + }} + if !cmp.Equal(installationRequests, want) { + t.Errorf("Apps.ListInstallationRequests returned %+v, want %+v", installationRequests, want) + } + + const methodName = "ListInstallationRequests" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.ListInstallationRequests(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestAppsService_ListInstallations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/app/installations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) testFormValues(t, r, values{ "page": "1", "per_page": "2", @@ -77,19 +131,27 @@ func TestAppsService_ListInstallations(t *testing.T) { "target_id":1, "target_type": "Organization", "permissions": { + "actions": "read", "administration": "read", "checks": "read", "contents": "read", "content_references": "read", "deployments": "read", + "environments": "read", "issues": "write", "metadata": "read", "members": "read", "organization_administration": "write", + "organization_custom_roles": "write", "organization_hooks": "write", + "organization_packages": "write", + "organization_personal_access_tokens": "read", + "organization_personal_access_token_requests": "read", "organization_plan": "read", "organization_pre_receive_hooks": "write", "organization_projects": "read", + "organization_secrets": "read", + "organization_self_hosted_runners": "read", "organization_user_blocking": "write", "packages": "read", "pages": "read", @@ -97,10 +159,14 @@ func TestAppsService_ListInstallations(t *testing.T) { "repository_hooks": "write", "repository_projects": "read", "repository_pre_receive_hooks": "read", + "secrets": "read", + "secret_scanning_alerts": "read", + "security_events": "read", "single_file": "write", "statuses": "write", "team_discussions": "read", - "vulnerability_alerts": "read" + "vulnerability_alerts": "read", + "workflows": "write" }, "events": [ "push", @@ -108,87 +174,122 @@ func TestAppsService_ListInstallations(t *testing.T) { ], "single_file_name": "config.yml", "repository_selection": "selected", - "created_at": "2018-01-01T00:00:00Z", - "updated_at": "2018-01-01T00:00:00Z"}]`, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`}]`, ) }) opt := &ListOptions{Page: 1, PerPage: 2} - installations, _, err := client.Apps.ListInstallations(context.Background(), opt) + ctx := t.Context() + installations, _, err := client.Apps.ListInstallations(ctx, opt) if err != nil { t.Errorf("Apps.ListInstallations returned error: %v", err) } - date := Timestamp{Time: time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)} want := []*Installation{{ - ID: Int64(1), - AppID: Int64(1), - TargetID: Int64(1), - TargetType: String("Organization"), - SingleFileName: String("config.yml"), - RepositorySelection: String("selected"), + ID: Ptr(int64(1)), + AppID: Ptr(int64(1)), + TargetID: Ptr(int64(1)), + TargetType: Ptr("Organization"), + SingleFileName: Ptr("config.yml"), + RepositorySelection: Ptr("selected"), Permissions: &InstallationPermissions{ - Administration: String("read"), - Checks: String("read"), - Contents: String("read"), - ContentReferences: String("read"), - Deployments: String("read"), - Issues: String("write"), - Metadata: String("read"), - Members: String("read"), - OrganizationAdministration: String("write"), - OrganizationHooks: String("write"), - OrganizationPlan: String("read"), - OrganizationPreReceiveHooks: String("write"), - OrganizationProjects: String("read"), - OrganizationUserBlocking: String("write"), - Packages: String("read"), - Pages: String("read"), - PullRequests: String("write"), - RepositoryHooks: String("write"), - RepositoryProjects: String("read"), - RepositoryPreReceiveHooks: String("read"), - SingleFile: String("write"), - Statuses: String("write"), - TeamDiscussions: String("read"), - VulnerabilityAlerts: String("read")}, + Actions: Ptr("read"), + Administration: Ptr("read"), + Checks: Ptr("read"), + Contents: Ptr("read"), + ContentReferences: Ptr("read"), + Deployments: Ptr("read"), + Environments: Ptr("read"), + Issues: Ptr("write"), + Metadata: Ptr("read"), + Members: Ptr("read"), + OrganizationAdministration: Ptr("write"), + OrganizationCustomRoles: Ptr("write"), + OrganizationHooks: Ptr("write"), + OrganizationPackages: Ptr("write"), + OrganizationPersonalAccessTokens: Ptr("read"), + OrganizationPersonalAccessTokenRequests: Ptr("read"), + OrganizationPlan: Ptr("read"), + OrganizationPreReceiveHooks: Ptr("write"), + OrganizationProjects: Ptr("read"), + OrganizationSecrets: Ptr("read"), + OrganizationSelfHostedRunners: Ptr("read"), + OrganizationUserBlocking: Ptr("write"), + Packages: Ptr("read"), + Pages: Ptr("read"), + PullRequests: Ptr("write"), + RepositoryHooks: Ptr("write"), + RepositoryProjects: Ptr("read"), + RepositoryPreReceiveHooks: Ptr("read"), + Secrets: Ptr("read"), + SecretScanningAlerts: Ptr("read"), + SecurityEvents: Ptr("read"), + SingleFile: Ptr("write"), + Statuses: Ptr("write"), + TeamDiscussions: Ptr("read"), + VulnerabilityAlerts: Ptr("read"), + Workflows: Ptr("write"), + }, Events: []string{"push", "pull_request"}, - CreatedAt: &date, - UpdatedAt: &date, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), }} - if !reflect.DeepEqual(installations, want) { + if !cmp.Equal(installations, want) { t.Errorf("Apps.ListInstallations returned %+v, want %+v", installations, want) } + + const methodName = "ListInstallations" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.ListInstallations(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_GetInstallation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/app/installations/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`) }) - installation, _, err := client.Apps.GetInstallation(context.Background(), 1) + ctx := t.Context() + installation, _, err := client.Apps.GetInstallation(ctx, 1) if err != nil { t.Errorf("Apps.GetInstallation returned error: %v", err) } - want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")} - if !reflect.DeepEqual(installation, want) { + want := &Installation{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("Organization")} + if !cmp.Equal(installation, want) { t.Errorf("Apps.GetInstallation returned %+v, want %+v", installation, want) } + + const methodName = "GetInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetInstallation(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetInstallation(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_ListUserInstallations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/installations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) testFormValues(t, r, values{ "page": "1", "per_page": "2", @@ -197,216 +298,445 @@ func TestAppsService_ListUserInstallations(t *testing.T) { }) opt := &ListOptions{Page: 1, PerPage: 2} - installations, _, err := client.Apps.ListUserInstallations(context.Background(), opt) + ctx := t.Context() + installations, _, err := client.Apps.ListUserInstallations(ctx, opt) if err != nil { t.Errorf("Apps.ListUserInstallations returned error: %v", err) } - want := []*Installation{{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")}} - if !reflect.DeepEqual(installations, want) { + want := []*Installation{{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("Organization")}} + if !cmp.Equal(installations, want) { t.Errorf("Apps.ListUserInstallations returned %+v, want %+v", installations, want) } + + const methodName = "ListUserInstallations" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.ListUserInstallations(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAppsService_SuspendInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/installations/1/suspended", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Apps.SuspendInstallation(ctx, 1); err != nil { + t.Errorf("Apps.SuspendInstallation returned error: %v", err) + } + + const methodName = "SuspendInstallation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Apps.SuspendInstallation(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Apps.SuspendInstallation(ctx, 1) + }) +} + +func TestAppsService_UnsuspendInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/installations/1/suspended", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Apps.UnsuspendInstallation(ctx, 1); err != nil { + t.Errorf("Apps.UnsuspendInstallation returned error: %v", err) + } + + const methodName = "UnsuspendInstallation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Apps.UnsuspendInstallation(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Apps.UnsuspendInstallation(ctx, 1) + }) +} + +func TestAppsService_DeleteInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/app/installations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Apps.DeleteInstallation(ctx, 1) + if err != nil { + t.Errorf("Apps.DeleteInstallation returned error: %v", err) + } + + const methodName = "DeleteInstallation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Apps.DeleteInstallation(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Apps.DeleteInstallation(ctx, 1) + }) } func TestAppsService_CreateInstallationToken(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/app/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"token":"t"}`) }) - token, _, err := client.Apps.CreateInstallationToken(context.Background(), 1, nil) + ctx := t.Context() + token, _, err := client.Apps.CreateInstallationToken(ctx, 1, nil) if err != nil { t.Errorf("Apps.CreateInstallationToken returned error: %v", err) } - want := &InstallationToken{Token: String("t")} - if !reflect.DeepEqual(token, want) { + want := &InstallationToken{Token: Ptr("t")} + if !cmp.Equal(token, want) { t.Errorf("Apps.CreateInstallationToken returned %+v, want %+v", token, want) } + + const methodName = "CreateInstallationToken" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.CreateInstallationToken(ctx, -1, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.CreateInstallationToken(ctx, 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAppsService_CreateInstallationTokenWithOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) installationTokenOptions := &InstallationTokenOptions{ RepositoryIDs: []int64{1234}, + Repositories: []string{"foo"}, Permissions: &InstallationPermissions{ - Contents: String("write"), - Issues: String("read"), + Contents: Ptr("write"), + Issues: Ptr("read"), }, } - // Convert InstallationTokenOptions into an io.ReadCloser object for comparison. - wantBody, err := GetReadCloser(installationTokenOptions) + mux.HandleFunc("/app/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, installationTokenOptions) + fmt.Fprint(w, `{"token":"t"}`) + }) + + ctx := t.Context() + token, _, err := client.Apps.CreateInstallationToken(ctx, 1, installationTokenOptions) if err != nil { - t.Errorf("GetReadCloser returned error: %v", err) + t.Errorf("Apps.CreateInstallationToken returned error: %v", err) + } + + want := &InstallationToken{Token: Ptr("t")} + if !cmp.Equal(token, want) { + t.Errorf("Apps.CreateInstallationToken returned %+v, want %+v", token, want) + } +} + +func TestAppsService_CreateInstallationTokenListReposWithOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + installationTokenListRepoOptions := &InstallationTokenListRepoOptions{ + Repositories: []string{"foo"}, + Permissions: &InstallationPermissions{ + Contents: Ptr("write"), + Issues: Ptr("read"), + }, } mux.HandleFunc("/app/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { - // Read request body contents. - var gotBodyBytes []byte - gotBodyBytes, err = ioutil.ReadAll(r.Body) - if err != nil { - t.Errorf("ReadAll returned error: %v", err) - } - r.Body = ioutil.NopCloser(bytes.NewBuffer(gotBodyBytes)) + testMethod(t, r, "POST") + testJSONBody(t, r, installationTokenListRepoOptions) + fmt.Fprint(w, `{"token":"t"}`) + }) - if !reflect.DeepEqual(r.Body, wantBody) { - t.Errorf("request sent %+v, want %+v", r.Body, wantBody) - } + ctx := t.Context() + token, _, err := client.Apps.CreateInstallationTokenListRepos(ctx, 1, installationTokenListRepoOptions) + if err != nil { + t.Errorf("Apps.CreateInstallationTokenListRepos returned error: %v", err) + } + + want := &InstallationToken{Token: Ptr("t")} + if !cmp.Equal(token, want) { + t.Errorf("Apps.CreateInstallationTokenListRepos returned %+v, want %+v", token, want) + } +} + +func TestAppsService_CreateInstallationTokenListReposWithNoOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/app/installations/1/access_tokens", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"token":"t"}`) }) - token, _, err := client.Apps.CreateInstallationToken(context.Background(), 1, installationTokenOptions) + ctx := t.Context() + token, _, err := client.Apps.CreateInstallationTokenListRepos(ctx, 1, nil) if err != nil { - t.Errorf("Apps.CreateInstallationToken returned error: %v", err) + t.Errorf("Apps.CreateInstallationTokenListRepos returned error: %v", err) } - want := &InstallationToken{Token: String("t")} - if !reflect.DeepEqual(token, want) { - t.Errorf("Apps.CreateInstallationToken returned %+v, want %+v", token, want) + want := &InstallationToken{Token: Ptr("t")} + if !cmp.Equal(token, want) { + t.Errorf("Apps.CreateInstallationTokenListRepos returned %+v, want %+v", token, want) } + + const methodName = "CreateInstallationTokenListRepos" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.CreateInstallationTokenListRepos(ctx, -1, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.CreateInstallationTokenListRepos(ctx, 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestAppsService_CreateAttachement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAppsService_CreateAttachment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/content_references/11/attachments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testHeader(t, r, "Accept", mediaTypeContentAttachmentsPreview) w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"id":1,"title":"title1","body":"body1"}`)) + assertWrite(t, w, []byte(`{"id":1,"title":"title1","body":"body1"}`)) }) - got, _, err := client.Apps.CreateAttachment(context.Background(), 11, "title1", "body1") + ctx := t.Context() + got, _, err := client.Apps.CreateAttachment(ctx, 11, "title1", "body1") if err != nil { t.Errorf("CreateAttachment returned error: %v", err) } - want := &Attachment{ID: Int64(1), Title: String("title1"), Body: String("body1")} - if !reflect.DeepEqual(got, want) { + want := &Attachment{ID: Ptr(int64(1)), Title: Ptr("title1"), Body: Ptr("body1")} + if !cmp.Equal(got, want) { t.Errorf("CreateAttachment = %+v, want %+v", got, want) } + + const methodName = "CreateAttachment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.CreateAttachment(ctx, -11, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.CreateAttachment(ctx, 11, "title1", "body1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestAppsService_FindOrganizationInstallation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAppsService_GetOrganizationInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/installation", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`) }) - installation, _, err := client.Apps.FindOrganizationInstallation(context.Background(), "o") + ctx := t.Context() + installation, _, err := client.Apps.GetOrganizationInstallation(ctx, "o") if err != nil { - t.Errorf("Apps.FindOrganizationInstallation returned error: %v", err) + t.Errorf("Apps.GetOrganizationInstallation returned error: %v", err) } - want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")} - if !reflect.DeepEqual(installation, want) { - t.Errorf("Apps.FindOrganizationInstallation returned %+v, want %+v", installation, want) + want := &Installation{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("Organization")} + if !cmp.Equal(installation, want) { + t.Errorf("Apps.GetOrganizationInstallation returned %+v, want %+v", installation, want) } + + const methodName = "GetOrganizationInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetOrganizationInstallation(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetOrganizationInstallation(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestAppsService_FindRepositoryInstallation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAppsService_GetEnterpriseInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/installation", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/enterprises/e/installation", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) - fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`) + fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Enterprise"}`) }) - installation, _, err := client.Apps.FindRepositoryInstallation(context.Background(), "o", "r") + ctx := t.Context() + installation, _, err := client.Apps.GetEnterpriseInstallation(ctx, "e") if err != nil { - t.Errorf("Apps.FindRepositoryInstallation returned error: %v", err) + t.Errorf("Apps.GetEnterpriseInstallation returned error: %v", err) } - want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")} - if !reflect.DeepEqual(installation, want) { - t.Errorf("Apps.FindRepositoryInstallation returned %+v, want %+v", installation, want) + want := &Installation{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("Enterprise")} + if !cmp.Equal(installation, want) { + t.Errorf("Apps.GetEnterpriseInstallation returned %+v, want %+v", installation, want) } + + const methodName = "GetEnterpriseInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetEnterpriseInstallation(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetEnterpriseInstallation(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestAppsService_FindRepositoryInstallationByID(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAppsService_GetRepositoryInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repositories/1/installation", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/installation", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`) }) - installation, _, err := client.Apps.FindRepositoryInstallationByID(context.Background(), 1) + ctx := t.Context() + installation, _, err := client.Apps.GetRepositoryInstallation(ctx, "o", "r") if err != nil { - t.Errorf("Apps.FindRepositoryInstallationByID returned error: %v", err) + t.Errorf("Apps.GetRepositoryInstallation returned error: %v", err) } - want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("Organization")} - if !reflect.DeepEqual(installation, want) { - t.Errorf("Apps.FindRepositoryInstallationByID returned %+v, want %+v", installation, want) + want := &Installation{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("Organization")} + if !cmp.Equal(installation, want) { + t.Errorf("Apps.GetRepositoryInstallation returned %+v, want %+v", installation, want) } + + const methodName = "GetRepositoryInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetRepositoryInstallation(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetRepositoryInstallation(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestAppsService_FindUserInstallation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAppsService_GetRepositoryInstallationByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/users/u/installation", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repositories/1/installation", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeIntegrationPreview) - fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "User"}`) + fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "Organization"}`) }) - installation, _, err := client.Apps.FindUserInstallation(context.Background(), "u") + ctx := t.Context() + installation, _, err := client.Apps.GetRepositoryInstallationByID(ctx, 1) if err != nil { - t.Errorf("Apps.FindUserInstallation returned error: %v", err) + t.Errorf("Apps.GetRepositoryInstallationByID returned error: %v", err) } - want := &Installation{ID: Int64(1), AppID: Int64(1), TargetID: Int64(1), TargetType: String("User")} - if !reflect.DeepEqual(installation, want) { - t.Errorf("Apps.FindUserInstallation returned %+v, want %+v", installation, want) + want := &Installation{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("Organization")} + if !cmp.Equal(installation, want) { + t.Errorf("Apps.GetRepositoryInstallationByID returned %+v, want %+v", installation, want) } -} -// GetReadWriter converts a body interface into an io.ReadWriter object. -func GetReadWriter(body interface{}) (io.ReadWriter, error) { - var buf io.ReadWriter - if body != nil { - buf = new(bytes.Buffer) - enc := json.NewEncoder(buf) - err := enc.Encode(body) - if err != nil { - return nil, err + const methodName = "GetRepositoryInstallationByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetRepositoryInstallationByID(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetRepositoryInstallationByID(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } - } - return buf, nil + return resp, err + }) } -// GetReadCloser converts a body interface into an io.ReadCloser object. -func GetReadCloser(body interface{}) (io.ReadCloser, error) { - buf, err := GetReadWriter(body) +func TestAppsService_GetUserInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/installation", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1, "app_id":1, "target_id":1, "target_type": "User"}`) + }) + + ctx := t.Context() + installation, _, err := client.Apps.GetUserInstallation(ctx, "u") if err != nil { - return nil, err + t.Errorf("Apps.GetUserInstallation returned error: %v", err) } - all, err := ioutil.ReadAll(buf) - if err != nil { - return nil, err + want := &Installation{ID: Ptr(int64(1)), AppID: Ptr(int64(1)), TargetID: Ptr(int64(1)), TargetType: Ptr("User")} + if !cmp.Equal(installation, want) { + t.Errorf("Apps.GetUserInstallation returned %+v, want %+v", installation, want) } - return ioutil.NopCloser(bytes.NewBuffer(all)), nil + + const methodName = "GetUserInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Apps.GetUserInstallation(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Apps.GetUserInstallation(ctx, "u") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/attestations.go b/github/attestations.go new file mode 100644 index 00000000000..618d5d73f6c --- /dev/null +++ b/github/attestations.go @@ -0,0 +1,27 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" +) + +// Attestation represents an artifact attestation associated with a repository. +// The provided bundle can be used to verify the provenance of artifacts. +// +// https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds +type Attestation struct { + // The attestation's Sigstore Bundle. + // Refer to the sigstore bundle specification for more info: + // https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto + Bundle json.RawMessage `json:"bundle"` + RepositoryID int64 `json:"repository_id"` +} + +// AttestationsResponse represents a collection of artifact attestations. +type AttestationsResponse struct { + Attestations []*Attestation `json:"attestations"` +} diff --git a/github/authorizations.go b/github/authorizations.go index 8ad14328e94..af59f0b3dab 100644 --- a/github/authorizations.go +++ b/github/authorizations.go @@ -12,10 +12,10 @@ import ( // Scope models a GitHub authorization scope. // -// GitHub API docs: https://developer.github.com/v3/oauth/#scopes +// GitHub API docs: https://docs.github.com/rest/oauth/#scopes type Scope string -// This is the set of scopes for GitHub API V3 +// This is the set of scopes for GitHub API V3. const ( ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact? ScopeUser Scope = "user" @@ -41,6 +41,7 @@ const ( ScopeReadGPGKey Scope = "read:gpg_key" ScopeWriteGPGKey Scope = "write:gpg_key" ScopeAdminGPGKey Scope = "admin:gpg_key" + ScopeSecurityEvents Scope = "security_events" ) // AuthorizationsService handles communication with the authorization related @@ -49,7 +50,7 @@ const ( // This service requires HTTP Basic Authentication; it cannot be accessed using // an OAuth token. // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/ +// GitHub API docs: https://docs.github.com/rest/oauth-authorizations?apiVersion=2022-11-28 type AuthorizationsService service // Authorization represents an individual GitHub authorization. @@ -120,7 +121,7 @@ func (a AuthorizationRequest) String() string { // fields. That is, you may provide only one of "Scopes", or "AddScopes", or // "RemoveScopes". // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization +// GitHub API docs: https://docs.github.com/rest/oauth-authorizations?apiVersion=2022-11-28#update-an-existing-authorization type AuthorizationUpdateRequest struct { Scopes []string `json:"scopes,omitempty"` AddScopes []string `json:"add_scopes,omitempty"` @@ -134,137 +135,6 @@ func (a AuthorizationUpdateRequest) String() string { return Stringify(a) } -// List the authorizations for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations -func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*Authorization, *Response, error) { - u := "authorizations" - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var auths []*Authorization - resp, err := s.client.Do(ctx, req, &auths) - if err != nil { - return nil, resp, err - } - return auths, resp, nil -} - -// Get a single authorization. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization -func (s *AuthorizationsService) Get(ctx context.Context, id int64) (*Authorization, *Response, error) { - u := fmt.Sprintf("authorizations/%d", id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - return a, resp, nil -} - -// Create a new authorization for the specified OAuth application. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization -func (s *AuthorizationsService) Create(ctx context.Context, auth *AuthorizationRequest) (*Authorization, *Response, error) { - u := "authorizations" - - req, err := s.client.NewRequest("POST", u, auth) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - return a, resp, nil -} - -// GetOrCreateForApp creates a new authorization for the specified OAuth -// application, only if an authorization for that application doesn’t already -// exist for the user. -// -// If a new token is created, the HTTP status code will be "201 Created", and -// the returned Authorization.Token field will be populated. If an existing -// token is returned, the status code will be "200 OK" and the -// Authorization.Token field will be empty. -// -// clientID is the OAuth Client ID with which to create the token. -// -// GitHub API docs: -// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app -// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint -func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) { - var u string - if auth.Fingerprint == nil || *auth.Fingerprint == "" { - u = fmt.Sprintf("authorizations/clients/%v", clientID) - } else { - u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint) - } - - req, err := s.client.NewRequest("PUT", u, auth) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - - return a, resp, nil -} - -// Edit a single authorization. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization -func (s *AuthorizationsService) Edit(ctx context.Context, id int64, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) { - u := fmt.Sprintf("authorizations/%d", id) - - req, err := s.client.NewRequest("PATCH", u, auth) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - - return a, resp, nil -} - -// Delete a single authorization. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization -func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("authorizations/%d", id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - // Check if an OAuth token is valid for a specific app. // // Note that this operation requires the use of BasicAuth, but where the @@ -273,17 +143,24 @@ func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response // // The returned Authorization.User field will be populated. // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization -func (s *AuthorizationsService) Check(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { - u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) +// GitHub API docs: https://docs.github.com/rest/apps/oauth-applications?apiVersion=2022-11-28#check-a-token +// +//meta:operation POST /applications/{client_id}/token +func (s *AuthorizationsService) Check(ctx context.Context, clientID, accessToken string) (*Authorization, *Response, error) { + u := fmt.Sprintf("applications/%v/token", clientID) - req, err := s.client.NewRequest("GET", u, nil) + reqBody := &struct { + AccessToken string `json:"access_token"` + }{AccessToken: accessToken} + + req, err := s.client.NewRequest(ctx, "POST", u, reqBody) if err != nil { return nil, nil, err } + req.Header.Set("Accept", mediaTypeOAuthAppPreview) - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) + var a *Authorization + resp, err := s.client.Do(req, &a) if err != nil { return nil, resp, err } @@ -301,17 +178,24 @@ func (s *AuthorizationsService) Check(ctx context.Context, clientID string, toke // // The returned Authorization.User field will be populated. // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization -func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { - u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) +// GitHub API docs: https://docs.github.com/rest/apps/oauth-applications?apiVersion=2022-11-28#reset-a-token +// +//meta:operation PATCH /applications/{client_id}/token +func (s *AuthorizationsService) Reset(ctx context.Context, clientID, accessToken string) (*Authorization, *Response, error) { + u := fmt.Sprintf("applications/%v/token", clientID) - req, err := s.client.NewRequest("POST", u, nil) + reqBody := &struct { + AccessToken string `json:"access_token"` + }{AccessToken: accessToken} + + req, err := s.client.NewRequest(ctx, "PATCH", u, reqBody) if err != nil { return nil, nil, err } + req.Header.Set("Accept", mediaTypeOAuthAppPreview) - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) + var a *Authorization + resp, err := s.client.Do(req, &a) if err != nil { return nil, resp, err } @@ -325,76 +209,46 @@ func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, toke // username is the OAuth application clientID, and the password is its // clientSecret. Invalid tokens will return a 404 Not Found. // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application -func (s *AuthorizationsService) Revoke(ctx context.Context, clientID string, token string) (*Response, error) { - u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ListGrants lists the set of OAuth applications that have been granted -// access to a user's account. This will return one entry for each application -// that has been granted access to the account, regardless of the number of -// tokens an application has generated for the user. +// GitHub API docs: https://docs.github.com/rest/apps/oauth-applications?apiVersion=2022-11-28#delete-an-app-token // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants -func (s *AuthorizationsService) ListGrants(ctx context.Context, opt *ListOptions) ([]*Grant, *Response, error) { - u, err := addOptions("applications/grants", opt) - if err != nil { - return nil, nil, err - } +//meta:operation DELETE /applications/{client_id}/token +func (s *AuthorizationsService) Revoke(ctx context.Context, clientID, accessToken string) (*Response, error) { + u := fmt.Sprintf("applications/%v/token", clientID) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } + reqBody := &struct { + AccessToken string `json:"access_token"` + }{AccessToken: accessToken} - grants := []*Grant{} - resp, err := s.client.Do(ctx, req, &grants) + req, err := s.client.NewRequest(ctx, "DELETE", u, reqBody) if err != nil { - return nil, resp, err - } - - return grants, resp, nil -} - -// GetGrant gets a single OAuth application grant. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant -func (s *AuthorizationsService) GetGrant(ctx context.Context, id int64) (*Grant, *Response, error) { - u := fmt.Sprintf("applications/grants/%d", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - grant := new(Grant) - resp, err := s.client.Do(ctx, req, grant) - if err != nil { - return nil, resp, err + return nil, err } + req.Header.Set("Accept", mediaTypeOAuthAppPreview) - return grant, resp, nil + return s.client.Do(req, nil) } // DeleteGrant deletes an OAuth application grant. Deleting an application's // grant will also delete all OAuth tokens associated with the application for // the user. // -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant -func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("applications/grants/%d", id) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/apps/oauth-applications?apiVersion=2022-11-28#delete-an-app-authorization +// +//meta:operation DELETE /applications/{client_id}/grant +func (s *AuthorizationsService) DeleteGrant(ctx context.Context, clientID, accessToken string) (*Response, error) { + u := fmt.Sprintf("applications/%v/grant", clientID) + + reqBody := &struct { + AccessToken string `json:"access_token"` + }{AccessToken: accessToken} + + req, err := s.client.NewRequest(ctx, "DELETE", u, reqBody) if err != nil { return nil, err } + req.Header.Set("Accept", mediaTypeOAuthAppPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // CreateImpersonation creates an impersonation OAuth token. @@ -403,16 +257,18 @@ func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Res // you can e.g. create or delete a user's public SSH key. NOTE: creating a // new token automatically revokes an existing one. // -// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-an-impersonation-oauth-token -func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#create-an-impersonation-oauth-token +// +//meta:operation POST /admin/users/{username}/authorizations +func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, body *AuthorizationRequest) (*Authorization, *Response, error) { u := fmt.Sprintf("admin/users/%v/authorizations", username) - req, err := s.client.NewRequest("POST", u, authReq) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) + var a *Authorization + resp, err := s.client.Do(req, &a) if err != nil { return nil, resp, err } @@ -423,13 +279,15 @@ func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, usernam // // NOTE: there can be only one at a time. // -// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-an-impersonation-oauth-token +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-an-impersonation-oauth-token +// +//meta:operation DELETE /admin/users/{username}/authorizations func (s *AuthorizationsService) DeleteImpersonation(ctx context.Context, username string) (*Response, error) { u := fmt.Sprintf("admin/users/%v/authorizations", username) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/authorizations_test.go b/github/authorizations_test.go index faf25749cdc..d1867728655 100644 --- a/github/authorizations_test.go +++ b/github/authorizations_test.go @@ -6,354 +6,217 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" -) - -func TestAuthorizationsService_List(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{"page": "1", "per_page": "2"}) - fmt.Fprint(w, `[{"id":1}]`) - }) - - opt := &ListOptions{Page: 1, PerPage: 2} - got, _, err := client.Authorizations.List(context.Background(), opt) - if err != nil { - t.Errorf("Authorizations.List returned error: %v", err) - } - - want := []*Authorization{{ID: Int64(1)}} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID) - } -} + "github.com/google/go-cmp/cmp" +) -func TestAuthorizationsService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestAuthorizationsService_Check(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") + mux.HandleFunc("/applications/id/token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, struct { + AccessToken string `json:"access_token"` + }{ + AccessToken: "a", + }) + testHeader(t, r, "Accept", mediaTypeOAuthAppPreview) fmt.Fprint(w, `{"id":1}`) }) - got, _, err := client.Authorizations.Get(context.Background(), 1) + ctx := t.Context() + got, _, err := client.Authorizations.Check(ctx, "id", "a") if err != nil { - t.Errorf("Authorizations.Get returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want) + t.Errorf("Authorizations.Check returned error: %v", err) } -} - -func TestAuthorizationsService_Create(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - input := &AuthorizationRequest{ - Note: String("test"), + want := &Authorization{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { + t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want) } - mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { - v := new(AuthorizationRequest) - json.NewDecoder(r.Body).Decode(v) - - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - - fmt.Fprint(w, `{"ID":1}`) + const methodName = "Check" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Authorizations.Check(ctx, "\n", "\n") + return err }) - got, _, err := client.Authorizations.Create(context.Background(), input) - if err != nil { - t.Errorf("Authorizations.Create returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorization.Create returned %+v, want %+v", got, want) - } -} - -func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &AuthorizationRequest{ - Note: String("test"), - } - - mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) { - v := new(AuthorizationRequest) - json.NewDecoder(r.Body).Decode(v) - - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Authorizations.Check(ctx, "id", "a") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } - - fmt.Fprint(w, `{"ID":1}`) + return resp, err }) - - got, _, err := client.Authorizations.GetOrCreateForApp(context.Background(), "id", input) - if err != nil { - t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) - } -} - -func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &AuthorizationRequest{ - Note: String("test"), - Fingerprint: String("fp"), - } - - mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) { - v := new(AuthorizationRequest) - json.NewDecoder(r.Body).Decode(v) - - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - - fmt.Fprint(w, `{"ID":1}`) - }) - - got, _, err := client.Authorizations.GetOrCreateForApp(context.Background(), "id", input) - if err != nil { - t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) - } } -func TestAuthorizationsService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &AuthorizationUpdateRequest{ - Note: String("test"), - } - - mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { - v := new(AuthorizationUpdateRequest) - json.NewDecoder(r.Body).Decode(v) +func TestAuthorizationsService_Reset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/applications/id/token", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, struct { + AccessToken string `json:"access_token"` + }{ + AccessToken: "a", + }) + testHeader(t, r, "Accept", mediaTypeOAuthAppPreview) fmt.Fprint(w, `{"ID":1}`) }) - got, _, err := client.Authorizations.Edit(context.Background(), 1, input) + ctx := t.Context() + got, _, err := client.Authorizations.Reset(ctx, "id", "a") if err != nil { - t.Errorf("Authorizations.Edit returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorization.Update returned %+v, want %+v", got, want) + t.Errorf("Authorizations.Reset returned error: %v", err) } -} -func TestAuthorizationsService_Delete(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - w.WriteHeader(http.StatusNoContent) - }) - - _, err := client.Authorizations.Delete(context.Background(), 1) - if err != nil { - t.Errorf("Authorizations.Delete returned error: %v", err) + want := &Authorization{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { + t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want) } -} - -func TestAuthorizationsService_Check(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"id":1}`) + const methodName = "Reset" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Authorizations.Reset(ctx, "\n", "\n") + return err }) - got, _, err := client.Authorizations.Check(context.Background(), "id", "t") - if err != nil { - t.Errorf("Authorizations.Check returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want) - } -} - -func TestAuthorizationsService_Reset(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - fmt.Fprint(w, `{"ID":1}`) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Authorizations.Reset(ctx, "id", "a") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err }) - - got, _, err := client.Authorizations.Reset(context.Background(), "id", "t") - if err != nil { - t.Errorf("Authorizations.Reset returned error: %v", err) - } - - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want) - } } func TestAuthorizationsService_Revoke(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/applications/id/token", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") + testJSONBody(t, r, struct { + AccessToken string `json:"access_token"` + }{ + AccessToken: "a", + }) + testHeader(t, r, "Accept", mediaTypeOAuthAppPreview) w.WriteHeader(http.StatusNoContent) }) - _, err := client.Authorizations.Revoke(context.Background(), "id", "t") + ctx := t.Context() + _, err := client.Authorizations.Revoke(ctx, "id", "a") if err != nil { t.Errorf("Authorizations.Revoke returned error: %v", err) } -} - -func TestListGrants(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `[{"id": 1}]`) + const methodName = "Revoke" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Authorizations.Revoke(ctx, "\n", "\n") + return err }) - got, _, err := client.Authorizations.ListGrants(context.Background(), nil) - if err != nil { - t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err) - } - - want := []*Grant{{ID: Int64(1)}} - if !reflect.DeepEqual(got, want) { - t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want) - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Authorizations.Revoke(ctx, "id", "a") + }) } -func TestListGrants_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestDeleteGrant(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{ - "page": "2", + mux.HandleFunc("/applications/id/grant", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, struct { + AccessToken string `json:"access_token"` + }{ + AccessToken: "a", }) - fmt.Fprint(w, `[{"id": 1}]`) + testHeader(t, r, "Accept", mediaTypeOAuthAppPreview) }) - _, _, err := client.Authorizations.ListGrants(context.Background(), &ListOptions{Page: 2}) + ctx := t.Context() + _, err := client.Authorizations.DeleteGrant(ctx, "id", "a") if err != nil { - t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err) + t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err) } -} -func TestGetGrant(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"id": 1}`) + const methodName = "DeleteGrant" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Authorizations.DeleteGrant(ctx, "\n", "\n") + return err }) - got, _, err := client.Authorizations.GetGrant(context.Background(), 1) - if err != nil { - t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err) - } - - want := &Grant{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { - t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want) - } -} - -func TestDeleteGrant(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Authorizations.DeleteGrant(ctx, "id", "a") }) - - _, err := client.Authorizations.DeleteGrant(context.Background(), 1) - if err != nil { - t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err) - } } func TestAuthorizationsService_CreateImpersonation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + req := &AuthorizationRequest{Scopes: []Scope{ScopePublicRepo}} mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") + testJSONBody(t, r, req) fmt.Fprint(w, `{"id":1}`) }) - req := &AuthorizationRequest{Scopes: []Scope{ScopePublicRepo}} - got, _, err := client.Authorizations.CreateImpersonation(context.Background(), "u", req) + ctx := t.Context() + got, _, err := client.Authorizations.CreateImpersonation(ctx, "u", req) if err != nil { t.Errorf("Authorizations.CreateImpersonation returned error: %+v", err) } - want := &Authorization{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { + want := &Authorization{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { t.Errorf("Authorizations.CreateImpersonation returned %+v, want %+v", *got.ID, *want.ID) } + + const methodName = "CreateImpersonation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Authorizations.CreateImpersonation(ctx, "\n", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Authorizations.CreateImpersonation(ctx, "u", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestAuthorizationsService_DeleteImpersonation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/admin/users/u/authorizations", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Authorizations.DeleteImpersonation(context.Background(), "u") + ctx := t.Context() + _, err := client.Authorizations.DeleteImpersonation(ctx, "u") if err != nil { t.Errorf("Authorizations.DeleteImpersonation returned error: %+v", err) } + + const methodName = "DeleteImpersonation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Authorizations.DeleteImpersonation(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Authorizations.DeleteImpersonation(ctx, "u") + }) } diff --git a/github/billing.go b/github/billing.go new file mode 100644 index 00000000000..89d44ec5138 --- /dev/null +++ b/github/billing.go @@ -0,0 +1,458 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// BillingService provides access to the billing related functions +// in the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/billing?apiVersion=2022-11-28 +type BillingService service + +// MinutesUsedBreakdown counts the actions minutes used by machine type (e.g. UBUNTU, WINDOWS, MACOS). +type MinutesUsedBreakdown = map[string]int + +// PackagesBilling represents billing of GitHub Packages . +type PackagesBilling struct { + TotalGigabytesBandwidthUsed int `json:"total_gigabytes_bandwidth_used"` + TotalPaidGigabytesBandwidthUsed int `json:"total_paid_gigabytes_bandwidth_used"` + IncludedGigabytesBandwidth int `json:"included_gigabytes_bandwidth"` +} + +// StorageBilling represents a GitHub Storage billing. +type StorageBilling struct { + DaysLeftInBillingCycle int `json:"days_left_in_billing_cycle"` + EstimatedPaidStorageForMonth int `json:"estimated_paid_storage_for_month"` + EstimatedStorageForMonth int `json:"estimated_storage_for_month"` +} + +// ActiveCommittersListOptions specifies optional parameters to the +// BillingService.GetAdvancedSecurityActiveCommittersOrg method. +type ActiveCommittersListOptions struct { + // The security product to get GitHub Advanced Security active committers for. For standalone + // Code Scanning or Secret Protection products, this parameter is required to specify which + // product you want committer information for. For other plans this parameter cannot be used. + // + // Can be one of: "code_security", "secret_protection". + AdvancedSecurityProduct *string `url:"advanced_security_product,omitempty"` + + ListOptions +} + +// ActiveCommitters represents the total active committers across all repositories in an Organization. +type ActiveCommitters struct { + TotalAdvancedSecurityCommitters *int `json:"total_advanced_security_committers,omitempty"` + TotalCount *int `json:"total_count,omitempty"` + MaximumAdvancedSecurityCommitters *int `json:"maximum_advanced_security_committers,omitempty"` + PurchasedAdvancedSecurityCommitters *int `json:"purchased_advanced_security_committers,omitempty"` + Repositories []*RepositoryActiveCommitters `json:"repositories"` +} + +// RepositoryActiveCommitters represents active committers on each repository. +type RepositoryActiveCommitters struct { + Name string `json:"name"` + AdvancedSecurityCommitters int `json:"advanced_security_committers"` + AdvancedSecurityCommittersBreakdown []*AdvancedSecurityCommittersBreakdown `json:"advanced_security_committers_breakdown"` +} + +// AdvancedSecurityCommittersBreakdown represents the user activity breakdown for ActiveCommitters. +type AdvancedSecurityCommittersBreakdown struct { + UserLogin string `json:"user_login"` + LastPushedDate string `json:"last_pushed_date"` + LastPushedEmail string `json:"last_pushed_email"` +} + +// UsageReportOptions specifies optional parameters for the enhanced billing platform usage report. +type UsageReportOptions struct { + // If specified, only return results for a single year. The value of year is an integer with four digits representing a year. For example, 2025. + // Default value is the current year. + Year *int `url:"year,omitempty"` + + // If specified, only return results for a single month. The value of month is an integer between 1 and 12. + // If no year is specified the default year is used. + Month *int `url:"month,omitempty"` + + // If specified, only return results for a single day. The value of day is an integer between 1 and 31. + // If no year or month is specified, the default year and month are used. + Day *int `url:"day,omitempty"` + + // If specified, only return results for a single hour. The value of hour is an integer between 0 and 23. + // If no year, month, or day is specified, the default year, month, and day are used. + Hour *int `url:"hour,omitempty"` +} + +// PremiumRequestUsageReportOptions specifies optional parameters +// for the enhanced billing platform premium request usage report. +type PremiumRequestUsageReportOptions struct { + // If specified, only return results for a single year. + // The value of year is an integer with four digits representing a year. For example, 2025. + // Default value is the current year. + Year *int `url:"year,omitempty"` + + // If specified, only return results for a single month. + // The value of month is an integer between 1 and 12. Default value is the current month. + // If no year is specified the default year is used. + Month *int `url:"month,omitempty"` + + // If specified, only return results for a single day. + // The value of day is an integer between 1 and 31. + // If no year or month is specified, the default year and month are used. + Day *int `url:"day,omitempty"` + + // The user name to query usage for. The name is not case-sensitive. + User *string `url:"user,omitempty"` + + // The model name to query usage for. The name is not case-sensitive. + Model *string `url:"model,omitempty"` + + // The product name to query usage for. The name is not case-sensitive. + Product *string `url:"product,omitempty"` +} + +// UsageItem represents a single usage item in the enhanced billing platform report. +type UsageItem struct { + Date string `json:"date"` + Product string `json:"product"` + SKU string `json:"sku"` + Quantity float64 `json:"quantity"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossAmount float64 `json:"grossAmount"` + DiscountAmount float64 `json:"discountAmount"` + NetAmount float64 `json:"netAmount"` + RepositoryName *string `json:"repositoryName,omitempty"` + // Organization name is only used for organization-level reports. + OrganizationName *string `json:"organizationName,omitempty"` +} + +// UsageReport represents the enhanced billing platform usage report response. +type UsageReport struct { + UsageItems []*UsageItem `json:"usageItems,omitempty"` +} + +// PremiumRequestUsageItem represents a single usage line item in premium request usage reports. +type PremiumRequestUsageItem struct { + Product string `json:"product"` + SKU string `json:"sku"` + Model string `json:"model"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossQuantity float64 `json:"grossQuantity"` + GrossAmount float64 `json:"grossAmount"` + DiscountQuantity float64 `json:"discountQuantity"` + DiscountAmount float64 `json:"discountAmount"` + NetQuantity float64 `json:"netQuantity"` + NetAmount float64 `json:"netAmount"` +} + +// PremiumRequestUsageTimePeriod represents a time period for premium request usage reports. +type PremiumRequestUsageTimePeriod struct { + Year int `json:"year"` + Month *int `json:"month,omitempty"` + Day *int `json:"day,omitempty"` +} + +// PremiumRequestUsageReport represents the premium request usage report response. +type PremiumRequestUsageReport struct { + TimePeriod PremiumRequestUsageTimePeriod `json:"timePeriod"` + // Organization is only set for organization-level reports. + Organization *string `json:"organization,omitempty"` + // User is only set for user-level reports. + User *string `json:"user,omitempty"` + Product *string `json:"product,omitempty"` + Model *string `json:"model,omitempty"` + UsageItems []*PremiumRequestUsageItem `json:"usageItems"` +} + +// GetOrganizationPackagesBilling returns the free and paid storage used for GitHub Packages in gigabytes for an Org. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-github-packages-billing-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/billing/packages +func (s *BillingService) GetOrganizationPackagesBilling(ctx context.Context, org string) (*PackagesBilling, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/billing/packages", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *PackagesBilling + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// GetOrganizationStorageBilling returns the estimated paid and estimated total storage used for GitHub Actions +// and GitHub Packages in gigabytes for an Org. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-shared-storage-billing-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/billing/shared-storage +func (s *BillingService) GetOrganizationStorageBilling(ctx context.Context, org string) (*StorageBilling, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/billing/shared-storage", org) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *StorageBilling + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// GetOrganizationAdvancedSecurityActiveCommitters returns the GitHub Advanced Security active committers for an organization per repository. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/billing?apiVersion=2022-11-28#get-github-advanced-security-active-committers-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/billing/advanced-security +func (s *BillingService) GetOrganizationAdvancedSecurityActiveCommitters(ctx context.Context, org string, opts *ActiveCommittersListOptions) (*ActiveCommitters, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/billing/advanced-security", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *ActiveCommitters + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// GetPackagesBilling returns the free and paid storage used for GitHub Packages in gigabytes for a user. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-github-packages-billing-for-a-user +// +//meta:operation GET /users/{username}/settings/billing/packages +func (s *BillingService) GetPackagesBilling(ctx context.Context, user string) (*PackagesBilling, *Response, error) { + u := fmt.Sprintf("users/%v/settings/billing/packages", user) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var packagesUserBilling *PackagesBilling + resp, err := s.client.Do(req, &packagesUserBilling) + if err != nil { + return nil, resp, err + } + + return packagesUserBilling, resp, nil +} + +// GetStorageBilling returns the estimated paid and estimated total storage used for GitHub Actions +// and GitHub Packages in gigabytes for a user. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-shared-storage-billing-for-a-user +// +//meta:operation GET /users/{username}/settings/billing/shared-storage +func (s *BillingService) GetStorageBilling(ctx context.Context, user string) (*StorageBilling, *Response, error) { + u := fmt.Sprintf("users/%v/settings/billing/shared-storage", user) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var storageUserBilling *StorageBilling + resp, err := s.client.Do(req, &storageUserBilling) + if err != nil { + return nil, resp, err + } + + return storageUserBilling, resp, nil +} + +// GetOrganizationUsageReport returns a report of the total usage for an organization using the enhanced billing platform. +// +// Note: This endpoint is only available to organizations with access to the enhanced billing platform. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-usage-report-for-an-organization +// +//meta:operation GET /organizations/{org}/settings/billing/usage +func (s *BillingService) GetOrganizationUsageReport(ctx context.Context, org string, opts *UsageReportOptions) (*UsageReport, *Response, error) { + u := fmt.Sprintf("organizations/%v/settings/billing/usage", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var usageReport *UsageReport + resp, err := s.client.Do(req, &usageReport) + if err != nil { + return nil, resp, err + } + + return usageReport, resp, nil +} + +// GetUsageReport returns a report of the total usage for a user using the enhanced billing platform. +// +// Note: This endpoint is only available to users with access to the enhanced billing platform. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-usage-report-for-a-user +// +//meta:operation GET /users/{username}/settings/billing/usage +func (s *BillingService) GetUsageReport(ctx context.Context, user string, opts *UsageReportOptions) (*UsageReport, *Response, error) { + u := fmt.Sprintf("users/%v/settings/billing/usage", user) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var usageReport *UsageReport + resp, err := s.client.Do(req, &usageReport) + if err != nil { + return nil, resp, err + } + + return usageReport, resp, nil +} + +// GetOrganizationPremiumRequestUsageReport returns a report of the premium request +// usage for an organization using the enhanced billing platform. +// +// Note: This endpoint is only available to organizations with access to the enhanced billing platform. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-premium-request-usage-report-for-an-organization +// +//meta:operation GET /organizations/{org}/settings/billing/premium_request/usage +func (s *BillingService) GetOrganizationPremiumRequestUsageReport(ctx context.Context, org string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) { + u := fmt.Sprintf("organizations/%v/settings/billing/premium_request/usage", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var premiumRequestUsageReport *PremiumRequestUsageReport + resp, err := s.client.Do(req, &premiumRequestUsageReport) + if err != nil { + return nil, resp, err + } + + return premiumRequestUsageReport, resp, nil +} + +// GetPremiumRequestUsageReport returns a report of the premium request +// usage for a user using the enhanced billing platform. +// +// Note: This endpoint is only available to users with access to the enhanced billing platform. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-premium-request-usage-report-for-a-user +// +//meta:operation GET /users/{username}/settings/billing/premium_request/usage +func (s *BillingService) GetPremiumRequestUsageReport(ctx context.Context, user string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) { + u := fmt.Sprintf("users/%v/settings/billing/premium_request/usage", user) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var premiumRequestUsageReport *PremiumRequestUsageReport + resp, err := s.client.Do(req, &premiumRequestUsageReport) + if err != nil { + return nil, resp, err + } + + return premiumRequestUsageReport, resp, nil +} + +// GetOrgAICreditUsage returns a report of the AI credit usage for an organization. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-ai-credit-usage-report-for-an-organization +// +//meta:operation GET /organizations/{org}/settings/billing/ai_credit/usage +func (s *BillingService) GetOrgAICreditUsage(ctx context.Context, org string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) { + u := fmt.Sprintf("organizations/%v/settings/billing/ai_credit/usage", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var aiCreditUsage *PremiumRequestUsageReport + resp, err := s.client.Do(req, &aiCreditUsage) + if err != nil { + return nil, resp, err + } + + return aiCreditUsage, resp, nil +} + +// GetUserAICreditUsage returns a report of the AI credit usage for a user. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-ai-credit-usage-report-for-a-user +// +//meta:operation GET /users/{username}/settings/billing/ai_credit/usage +func (s *BillingService) GetUserAICreditUsage(ctx context.Context, username string, opts *PremiumRequestUsageReportOptions) (*PremiumRequestUsageReport, *Response, error) { + u := fmt.Sprintf("users/%v/settings/billing/ai_credit/usage", username) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var aiCreditUsage *PremiumRequestUsageReport + resp, err := s.client.Do(req, &aiCreditUsage) + if err != nil { + return nil, resp, err + } + + return aiCreditUsage, resp, nil +} diff --git a/github/billing_test.go b/github/billing_test.go new file mode 100644 index 00000000000..fc6c48fbd1c --- /dev/null +++ b/github/billing_test.go @@ -0,0 +1,1054 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestBillingService_GetOrganizationPackagesBilling(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/billing/packages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_gigabytes_bandwidth_used": 50, + "total_paid_gigabytes_bandwidth_used": 40, + "included_gigabytes_bandwidth": 10 + }`) + }) + + ctx := t.Context() + hook, _, err := client.Billing.GetOrganizationPackagesBilling(ctx, "o") + if err != nil { + t.Errorf("Billing.GetOrganizationPackagesBilling returned error: %v", err) + } + + want := &PackagesBilling{ + TotalGigabytesBandwidthUsed: 50, + TotalPaidGigabytesBandwidthUsed: 40, + IncludedGigabytesBandwidth: 10, + } + if !cmp.Equal(hook, want) { + t.Errorf("Billing.GetOrganizationPackagesBilling returned %+v, want %+v", hook, want) + } + + const methodName = "GetOrganizationPackagesBilling" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrganizationPackagesBilling(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrganizationPackagesBilling(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrganizationPackagesBilling_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrganizationPackagesBilling(ctx, "%") + testURLParseError(t, err) +} + +func TestBillingService_GetOrganizationStorageBilling(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/billing/shared-storage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "days_left_in_billing_cycle": 20, + "estimated_paid_storage_for_month": 15, + "estimated_storage_for_month": 40 + }`) + }) + + ctx := t.Context() + hook, _, err := client.Billing.GetOrganizationStorageBilling(ctx, "o") + if err != nil { + t.Errorf("Billing.GetOrganizationStorageBilling returned error: %v", err) + } + + want := &StorageBilling{ + DaysLeftInBillingCycle: 20, + EstimatedPaidStorageForMonth: 15, + EstimatedStorageForMonth: 40, + } + if !cmp.Equal(hook, want) { + t.Errorf("Billing.GetOrganizationStorageBilling returned %+v, want %+v", hook, want) + } + + const methodName = "GetOrganizationStorageBilling" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrganizationStorageBilling(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrganizationStorageBilling(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrganizationStorageBilling_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrganizationStorageBilling(ctx, "%") + testURLParseError(t, err) +} + +func TestBillingService_GetPackagesBilling(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/settings/billing/packages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_gigabytes_bandwidth_used": 50, + "total_paid_gigabytes_bandwidth_used": 40, + "included_gigabytes_bandwidth": 10 + }`) + }) + + ctx := t.Context() + hook, _, err := client.Billing.GetPackagesBilling(ctx, "u") + if err != nil { + t.Errorf("Billing.GetPackagesBilling returned error: %v", err) + } + + want := &PackagesBilling{ + TotalGigabytesBandwidthUsed: 50, + TotalPaidGigabytesBandwidthUsed: 40, + IncludedGigabytesBandwidth: 10, + } + if !cmp.Equal(hook, want) { + t.Errorf("Billing.GetPackagesBilling returned %+v, want %+v", hook, want) + } + + const methodName = "GetPackagesBilling" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetPackagesBilling(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetPackagesBilling(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetPackagesBilling_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetPackagesBilling(ctx, "%") + testURLParseError(t, err) +} + +func TestBillingService_GetStorageBilling(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/settings/billing/shared-storage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "days_left_in_billing_cycle": 20, + "estimated_paid_storage_for_month": 15, + "estimated_storage_for_month": 40 + }`) + }) + + ctx := t.Context() + hook, _, err := client.Billing.GetStorageBilling(ctx, "u") + if err != nil { + t.Errorf("Billing.GetStorageBilling returned error: %v", err) + } + + want := &StorageBilling{ + DaysLeftInBillingCycle: 20, + EstimatedPaidStorageForMonth: 15, + EstimatedStorageForMonth: 40, + } + if !cmp.Equal(hook, want) { + t.Errorf("Billing.GetStorageBilling returned %+v, want %+v", hook, want) + } + + const methodName = "GetStorageBilling" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetStorageBilling(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetStorageBilling(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetStorageBilling_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetStorageBilling(ctx, "%") + testURLParseError(t, err) +} + +func TestBillingService_GetOrganizationAdvancedSecurityActiveCommitters(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/billing/advanced-security", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_advanced_security_committers": 2, + "total_count": 2, + "maximum_advanced_security_committers": 3, + "purchased_advanced_security_committers": 4, + "repositories": [ + { + "name": "octocat-org/Hello-World", + "advanced_security_committers": 2, + "advanced_security_committers_breakdown": [ + { + "user_login": "octokitten", + "last_pushed_date": "2021-10-25", + "last_pushed_email": "octokitten@example.com" + } + ] + } + ] +}`) + }) + + ctx := t.Context() + opts := &ActiveCommittersListOptions{ + nil, + ListOptions{Page: 2, PerPage: 50}, + } + hook, _, err := client.Billing.GetOrganizationAdvancedSecurityActiveCommitters(ctx, "o", opts) + if err != nil { + t.Errorf("Billing.GetOrganizationAdvancedSecurityActiveCommitters returned error: %v", err) + } + + want := &ActiveCommitters{ + TotalAdvancedSecurityCommitters: Ptr(2), + TotalCount: Ptr(2), + MaximumAdvancedSecurityCommitters: Ptr(3), + PurchasedAdvancedSecurityCommitters: Ptr(4), + Repositories: []*RepositoryActiveCommitters{ + { + Name: "octocat-org/Hello-World", + AdvancedSecurityCommitters: 2, + AdvancedSecurityCommittersBreakdown: []*AdvancedSecurityCommittersBreakdown{ + { + UserLogin: "octokitten", + LastPushedDate: "2021-10-25", + LastPushedEmail: "octokitten@example.com", + }, + }, + }, + }, + } + if !cmp.Equal(hook, want) { + t.Errorf("Billing.GetOrganizationAdvancedSecurityActiveCommitters returned %+v, want %+v", hook, want) + } + + const methodName = "GetOrganizationAdvancedSecurityActiveCommitters" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrganizationAdvancedSecurityActiveCommitters(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrganizationAdvancedSecurityActiveCommitters(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrganizationAdvancedSecurityActiveCommitters_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrganizationAdvancedSecurityActiveCommitters(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetOrganizationUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/o/settings/billing/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2023", + "month": "8", + }) + fmt.Fprint(w, `{ + "usageItems": [ + { + "date": "2023-08-01", + "product": "Actions", + "sku": "Actions Linux", + "quantity": 100, + "unitType": "minutes", + "pricePerUnit": 0.008, + "grossAmount": 0.8, + "discountAmount": 0, + "netAmount": 0.8, + "organizationName": "GitHub", + "repositoryName": "github/example" + } + ] + }`) + }) + + ctx := t.Context() + opts := &UsageReportOptions{ + Year: Ptr(2023), + Month: Ptr(8), + } + report, _, err := client.Billing.GetOrganizationUsageReport(ctx, "o", opts) + if err != nil { + t.Errorf("Billing.GetOrganizationUsageReport returned error: %v", err) + } + + want := &UsageReport{ + UsageItems: []*UsageItem{ + { + Date: "2023-08-01", + Product: "Actions", + SKU: "Actions Linux", + Quantity: 100.0, + UnitType: "minutes", + PricePerUnit: 0.008, + GrossAmount: 0.8, + DiscountAmount: 0.0, + NetAmount: 0.8, + OrganizationName: Ptr("GitHub"), + RepositoryName: Ptr("github/example"), + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetOrganizationUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetOrganizationUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrganizationUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrganizationUsageReport(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrganizationUsageReport_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrganizationUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/settings/billing/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "day": "15", + }) + fmt.Fprint(w, `{ + "usageItems": [ + { + "date": "2023-08-15", + "product": "Codespaces", + "sku": "Codespaces Linux", + "quantity": 50, + "unitType": "hours", + "pricePerUnit": 0.18, + "grossAmount": 9.0, + "discountAmount": 1.0, + "netAmount": 8.0, + "repositoryName": "user/example" + } + ] + }`) + }) + + ctx := t.Context() + opts := &UsageReportOptions{ + Day: Ptr(15), + } + report, _, err := client.Billing.GetUsageReport(ctx, "u", opts) + if err != nil { + t.Errorf("Billing.GetUsageReport returned error: %v", err) + } + + want := &UsageReport{ + UsageItems: []*UsageItem{ + { + Date: "2023-08-15", + Product: "Codespaces", + SKU: "Codespaces Linux", + Quantity: 50.0, + UnitType: "hours", + PricePerUnit: 0.18, + GrossAmount: 9.0, + DiscountAmount: 1.0, + NetAmount: 8.0, + RepositoryName: Ptr("user/example"), + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetUsageReport(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetUsageReport_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetOrganizationPremiumRequestUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/organizations/o/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "month": "10", + "user": "testuser", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 10 + }, + "organization": "GitHub", + "user": "testuser", + "product": "Copilot", + "model": "GPT-5", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot Premium Request", + "model": "GPT-5", + "unitType": "requests", + "pricePerUnit": 0.04, + "grossQuantity": 100, + "grossAmount": 4.0, + "discountQuantity": 0, + "discountAmount": 0.0, + "netQuantity": 100, + "netAmount": 4.0 + } + ] + }`) + }) + ctx := t.Context() + opts := &PremiumRequestUsageReportOptions{ + Year: Ptr(2025), + Month: Ptr(10), + User: Ptr("testuser"), + } + report, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", opts) + if err != nil { + t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned error: %v", err) + } + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2025, + Month: Ptr(10), + }, + Organization: Ptr("GitHub"), + User: Ptr("testuser"), + Product: Ptr("Copilot"), + Model: Ptr("GPT-5"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot Premium Request", + Model: "GPT-5", + UnitType: "requests", + PricePerUnit: 0.04, + GrossQuantity: 100.0, + GrossAmount: 4.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 100.0, + NetAmount: 4.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetOrganizationPremiumRequestUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrganizationPremiumRequestUsageReport_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetPremiumRequestUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "day": "15", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "day": 15 + }, + "user": "User", + "product": "Copilot", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot Premium Request", + "model": "GPT-4", + "unitType": "requests", + "pricePerUnit": 0.02, + "grossQuantity": 50, + "grossAmount": 1.0, + "discountQuantity": 5, + "discountAmount": 0.1, + "netQuantity": 45, + "netAmount": 0.9 + } + ] + }`) + }) + ctx := t.Context() + opts := &PremiumRequestUsageReportOptions{ + Year: Ptr(2025), + Day: Ptr(15), + } + report, _, err := client.Billing.GetPremiumRequestUsageReport(ctx, "u", opts) + if err != nil { + t.Errorf("Billing.GetPremiumRequestUsageReport returned error: %v", err) + } + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2025, + Day: Ptr(15), + }, + User: Ptr("User"), + Product: Ptr("Copilot"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot Premium Request", + Model: "GPT-4", + UnitType: "requests", + PricePerUnit: 0.02, + GrossQuantity: 50.0, + GrossAmount: 1.0, + DiscountQuantity: 5.0, + DiscountAmount: 0.1, + NetQuantity: 45.0, + NetAmount: 0.9, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetPremiumRequestUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetPremiumRequestUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetPremiumRequestUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetPremiumRequestUsageReport(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetPremiumRequestUsageReport_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetPremiumRequestUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_PremiumRequestUsageItem_FloatQuantities(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/organizations/o/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2026, + "month": 2 + }, + "organization": "testorg", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot Premium Request", + "model": "GPT-5.2", + "unitType": "requests", + "pricePerUnit": 0.04, + "grossQuantity": 5054.0, + "grossAmount": 202.16, + "discountQuantity": 4974.0, + "discountAmount": 198.96, + "netQuantity": 80.0, + "netAmount": 3.2 + } + ] + }`) + }) + ctx := t.Context() + report, _, err := client.Billing.GetOrganizationPremiumRequestUsageReport(ctx, "o", nil) + if err != nil { + t.Fatalf("Billing.GetOrganizationPremiumRequestUsageReport returned error: %v", err) + } + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2026, + Month: Ptr(2), + }, + Organization: Ptr("testorg"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot Premium Request", + Model: "GPT-5.2", + UnitType: "requests", + PricePerUnit: 0.04, + GrossQuantity: 5054.0, + GrossAmount: 202.16, + DiscountQuantity: 4974.0, + DiscountAmount: 198.96, + NetQuantity: 80.0, + NetAmount: 3.2, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetOrganizationPremiumRequestUsageReport returned %+v, want %+v", report, want) + } +} + +func TestBillingService_GetOrgAICreditUsage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/o/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "month": "6", + "user": "testuser", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 6 + }, + "organization": "GitHub", + "user": "testuser", + "product": "Copilot", + "model": "GPT-5", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "GPT-5", + "unitType": "credits", + "pricePerUnit": 0.01, + "grossQuantity": 100, + "grossAmount": 1.0, + "discountQuantity": 0, + "discountAmount": 0.0, + "netQuantity": 100, + "netAmount": 1.0 + } + ] + }`) + }) + + ctx := t.Context() + opts := &PremiumRequestUsageReportOptions{ + Year: Ptr(2025), + Month: Ptr(6), + User: Ptr("testuser"), + } + report, _, err := client.Billing.GetOrgAICreditUsage(ctx, "o", opts) + if err != nil { + t.Errorf("Billing.GetOrgAICreditUsage returned error: %v", err) + } + + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2025, + Month: Ptr(6), + }, + Organization: Ptr("GitHub"), + User: Ptr("testuser"), + Product: Ptr("Copilot"), + Model: Ptr("GPT-5"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot AI Credits", + Model: "GPT-5", + UnitType: "credits", + PricePerUnit: 0.01, + GrossQuantity: 100.0, + GrossAmount: 1.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 100.0, + NetAmount: 1.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetOrgAICreditUsage returned %+v, want %+v", report, want) + } + + const methodName = "GetOrgAICreditUsage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrgAICreditUsage(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrgAICreditUsage(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrgAICreditUsage_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrgAICreditUsage(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetOrgAICreditUsage_FloatQuantities(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/o/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 6, + "day": 15 + }, + "organization": "testorg", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "GPT-5", + "unitType": "credits", + "pricePerUnit": 0.01, + "grossQuantity": 1500.5, + "grossAmount": 15.005, + "discountQuantity": 100.5, + "discountAmount": 1.005, + "netQuantity": 1400.0, + "netAmount": 14.0 + } + ] + }`) + }) + + ctx := t.Context() + report, _, err := client.Billing.GetOrgAICreditUsage(ctx, "o", nil) + if err != nil { + t.Fatalf("Billing.GetOrgAICreditUsage returned error: %v", err) + } + + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2025, + Month: Ptr(6), + Day: Ptr(15), + }, + Organization: Ptr("testorg"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot AI Credits", + Model: "GPT-5", + UnitType: "credits", + PricePerUnit: 0.01, + GrossQuantity: 1500.5, + GrossAmount: 15.005, + DiscountQuantity: 100.5, + DiscountAmount: 1.005, + NetQuantity: 1400.0, + NetAmount: 14.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetOrgAICreditUsage returned %+v, want %+v", report, want) + } +} + +func TestBillingService_GetUserAICreditUsage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "month": "6", + "user": "testuser", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 6 + }, + "user": "testuser", + "product": "Copilot", + "model": "GPT-5", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "GPT-5", + "unitType": "credits", + "pricePerUnit": 0.01, + "grossQuantity": 100, + "grossAmount": 1.0, + "discountQuantity": 0, + "discountAmount": 0.0, + "netQuantity": 100, + "netAmount": 1.0 + } + ] + }`) + }) + + ctx := t.Context() + opts := &PremiumRequestUsageReportOptions{ + Year: Ptr(2025), + Month: Ptr(6), + User: Ptr("testuser"), + } + report, _, err := client.Billing.GetUserAICreditUsage(ctx, "u", opts) + if err != nil { + t.Errorf("Billing.GetUserAICreditUsage returned error: %v", err) + } + + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2025, + Month: Ptr(6), + }, + User: Ptr("testuser"), + Product: Ptr("Copilot"), + Model: Ptr("GPT-5"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot AI Credits", + Model: "GPT-5", + UnitType: "credits", + PricePerUnit: 0.01, + GrossQuantity: 100.0, + GrossAmount: 1.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 100.0, + NetAmount: 1.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetUserAICreditUsage returned %+v, want %+v", report, want) + } + + const methodName = "GetUserAICreditUsage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetUserAICreditUsage(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetUserAICreditUsage(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetUserAICreditUsage_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetUserAICreditUsage(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetUserAICreditUsage_FloatQuantities(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 6, + "day": 15 + }, + "user": "testuser", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "GPT-5", + "unitType": "credits", + "pricePerUnit": 0.01, + "grossQuantity": 1500.5, + "grossAmount": 15.005, + "discountQuantity": 100.5, + "discountAmount": 1.005, + "netQuantity": 1400.0, + "netAmount": 14.0 + } + ] + }`) + }) + + ctx := t.Context() + report, _, err := client.Billing.GetUserAICreditUsage(ctx, "u", nil) + if err != nil { + t.Fatalf("Billing.GetUserAICreditUsage returned error: %v", err) + } + + want := &PremiumRequestUsageReport{ + TimePeriod: PremiumRequestUsageTimePeriod{ + Year: 2025, + Month: Ptr(6), + Day: Ptr(15), + }, + User: Ptr("testuser"), + UsageItems: []*PremiumRequestUsageItem{ + { + Product: "Copilot", + SKU: "Copilot AI Credits", + Model: "GPT-5", + UnitType: "credits", + PricePerUnit: 0.01, + GrossQuantity: 1500.5, + GrossAmount: 15.005, + DiscountQuantity: 100.5, + DiscountAmount: 1.005, + NetQuantity: 1400.0, + NetAmount: 14.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetUserAICreditUsage returned %+v, want %+v", report, want) + } +} diff --git a/github/checks.go b/github/checks.go index 0c88cbb58aa..83a22236987 100644 --- a/github/checks.go +++ b/github/checks.go @@ -8,13 +8,12 @@ package github import ( "context" "fmt" - "net/url" ) // ChecksService provides access to the Checks API in the // GitHub API. // -// GitHub API docs: https://developer.github.com/v3/checks/ +// GitHub API docs: https://docs.github.com/rest/checks?apiVersion=2022-11-28 type ChecksService service // CheckRun represents a GitHub check run on a repository associated with a GitHub app. @@ -51,7 +50,6 @@ type CheckRunOutput struct { // CheckRunAnnotation represents an annotation object for a CheckRun output. type CheckRunAnnotation struct { Path *string `json:"path,omitempty"` - BlobHRef *string `json:"blob_href,omitempty"` StartLine *int `json:"start_line,omitempty"` EndLine *int `json:"end_line,omitempty"` StartColumn *int `json:"start_column,omitempty"` @@ -80,12 +78,17 @@ type CheckSuite struct { AfterSHA *string `json:"after,omitempty"` Status *string `json:"status,omitempty"` Conclusion *string `json:"conclusion,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` App *App `json:"app,omitempty"` Repository *Repository `json:"repository,omitempty"` PullRequests []*PullRequest `json:"pull_requests,omitempty"` // The following fields are only populated by Webhook events. - HeadCommit *Commit `json:"head_commit,omitempty"` + HeadCommit *Commit `json:"head_commit,omitempty"` + LatestCheckRunsCount *int64 `json:"latest_check_runs_count,omitempty"` + Rerequestable *bool `json:"rerequestable,omitempty"` + RunsRerequestable *bool `json:"runs_rerequestable,omitempty"` } func (c CheckRun) String() string { @@ -98,18 +101,20 @@ func (c CheckSuite) String() string { // GetCheckRun gets a check-run for a repository. // -// GitHub API docs: https://developer.github.com/v3/checks/runs/#get-a-single-check-run +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#get-a-check-run +// +//meta:operation GET /repos/{owner}/{repo}/check-runs/{check_run_id} func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, checkRunID int64) (*CheckRun, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeCheckRunsPreview) - checkRun := new(CheckRun) - resp, err := s.client.Do(ctx, req, checkRun) + var checkRun *CheckRun + resp, err := s.client.Do(req, &checkRun) if err != nil { return nil, resp, err } @@ -119,18 +124,20 @@ func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, che // GetCheckSuite gets a single check suite. // -// GitHub API docs: https://developer.github.com/v3/checks/suites/#get-a-single-check-suite +// GitHub API docs: https://docs.github.com/rest/checks/suites?apiVersion=2022-11-28#get-a-check-suite +// +//meta:operation GET /repos/{owner}/{repo}/check-suites/{check_suite_id} func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*CheckSuite, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-suites/%v", owner, repo, checkSuiteID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeCheckRunsPreview) - checkSuite := new(CheckSuite) - resp, err := s.client.Do(ctx, req, checkSuite) + var checkSuite *CheckSuite + resp, err := s.client.Do(req, &checkSuite) if err != nil { return nil, resp, err } @@ -141,12 +148,11 @@ func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, c // CreateCheckRunOptions sets up parameters needed to create a CheckRun. type CreateCheckRunOptions struct { Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.) - HeadBranch string `json:"head_branch"` // The name of the branch to perform a check against. (Required.) HeadSHA string `json:"head_sha"` // The SHA of the commit. (Required.) DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.) ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.) Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.) - Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".) + Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "skipped", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".) StartedAt *Timestamp `json:"started_at,omitempty"` // The time that the check run began. (Optional.) CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.) Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional) @@ -162,18 +168,20 @@ type CheckRunAction struct { // CreateCheckRun creates a check run for repository. // -// GitHub API docs: https://developer.github.com/v3/checks/runs/#create-a-check-run -func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, opt CreateCheckRunOptions) (*CheckRun, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#create-a-check-run +// +//meta:operation POST /repos/{owner}/{repo}/check-runs +func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, body CreateCheckRunOptions) (*CheckRun, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-runs", owner, repo) - req, err := s.client.NewRequest("POST", u, opt) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeCheckRunsPreview) - checkRun := new(CheckRun) - resp, err := s.client.Do(ctx, req, checkRun) + var checkRun *CheckRun + resp, err := s.client.Do(req, &checkRun) if err != nil { return nil, resp, err } @@ -184,12 +192,10 @@ func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, // UpdateCheckRunOptions sets up parameters needed to update a CheckRun. type UpdateCheckRunOptions struct { Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.) - HeadBranch *string `json:"head_branch,omitempty"` // The name of the branch to perform a check against. (Optional.) - HeadSHA *string `json:"head_sha,omitempty"` // The SHA of the commit. (Optional.) DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.) ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.) Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.) - Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".) + Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "skipped", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".) CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.) Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional) Actions []*CheckRunAction `json:"actions,omitempty"` // Possible further actions the integrator can perform, which a user may trigger. (Optional.) @@ -197,18 +203,20 @@ type UpdateCheckRunOptions struct { // UpdateCheckRun updates a check run for a specific commit in a repository. // -// GitHub API docs: https://developer.github.com/v3/checks/runs/#update-a-check-run -func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, opt UpdateCheckRunOptions) (*CheckRun, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#update-a-check-run +// +//meta:operation PATCH /repos/{owner}/{repo}/check-runs/{check_run_id} +func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, body UpdateCheckRunOptions) (*CheckRun, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID) - req, err := s.client.NewRequest("PATCH", u, opt) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeCheckRunsPreview) - checkRun := new(CheckRun) - resp, err := s.client.Do(ctx, req, checkRun) + var checkRun *CheckRun + resp, err := s.client.Do(req, &checkRun) if err != nil { return nil, resp, err } @@ -218,15 +226,17 @@ func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, // ListCheckRunAnnotations lists the annotations for a check run. // -// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-annotations-for-a-check-run -func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opt *ListOptions) ([]*CheckRunAnnotation, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#list-check-run-annotations +// +//meta:operation GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations +func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opts *ListOptions) ([]*CheckRunAnnotation, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-runs/%v/annotations", owner, repo, checkRunID) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } @@ -234,7 +244,7 @@ func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo req.Header.Set("Accept", mediaTypeCheckRunsPreview) var checkRunAnnotations []*CheckRunAnnotation - resp, err := s.client.Do(ctx, req, &checkRunAnnotations) + resp, err := s.client.Do(req, &checkRunAnnotations) if err != nil { return nil, resp, err } @@ -247,6 +257,7 @@ type ListCheckRunsOptions struct { CheckName *string `url:"check_name,omitempty"` // Returns check runs with the specified name. Status *string `url:"status,omitempty"` // Returns check runs with the specified status. Can be one of "queued", "in_progress", or "completed". Filter *string `url:"filter,omitempty"` // Filters check runs by their completed_at timestamp. Can be one of "latest" (returning the most recent check runs) or "all". Default: "latest" + AppID *int64 `url:"app_id,omitempty"` // Filters check runs by GitHub App ID. ListOptions } @@ -258,16 +269,20 @@ type ListCheckRunsResults struct { } // ListCheckRunsForRef lists check runs for a specific ref. +// The ref can be a commit SHA, branch name `heads/`, or tag name `tags/`. +// For more information, see "Git References" in the Git documentation https://git-scm.com/book/en/v2/Git-Internals-Git-References. +// +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-for-a-git-reference // -// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref -func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, url.QueryEscape(ref)) - u, err := addOptions(u, opt) +//meta:operation GET /repos/{owner}/{repo}/commits/{ref}/check-runs +func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opts *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, refURLEscape(ref)) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } @@ -275,7 +290,7 @@ func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, re req.Header.Set("Accept", mediaTypeCheckRunsPreview) var checkRunResults *ListCheckRunsResults - resp, err := s.client.Do(ctx, req, &checkRunResults) + resp, err := s.client.Do(req, &checkRunResults) if err != nil { return nil, resp, err } @@ -285,15 +300,17 @@ func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, re // ListCheckRunsCheckSuite lists check runs for a check suite. // -// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-in-a-check-suite -func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#list-check-runs-in-a-check-suite +// +//meta:operation GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs +func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opts *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-suites/%v/check-runs", owner, repo, checkSuiteID) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } @@ -301,7 +318,7 @@ func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo req.Header.Set("Accept", mediaTypeCheckRunsPreview) var checkRunResults *ListCheckRunsResults - resp, err := s.client.Do(ctx, req, &checkRunResults) + resp, err := s.client.Do(req, &checkRunResults) if err != nil { return nil, resp, err } @@ -309,10 +326,28 @@ func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo return checkRunResults, resp, nil } +// ReRequestCheckRun triggers GitHub to rerequest an existing check run. +// +// GitHub API docs: https://docs.github.com/rest/checks/runs?apiVersion=2022-11-28#rerequest-a-check-run +// +//meta:operation POST /repos/{owner}/{repo}/check-runs/{check_run_id}/rerequest +func (s *ChecksService) ReRequestCheckRun(ctx context.Context, owner, repo string, checkRunID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/check-runs/%v/rerequest", owner, repo, checkRunID) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", mediaTypeCheckRunsPreview) + + return s.client.Do(req, nil) +} + // ListCheckSuiteOptions represents parameters to list check suites. type ListCheckSuiteOptions struct { CheckName *string `url:"check_name,omitempty"` // Filters checks suites by the name of the check run. - AppID *int `url:"app_id,omitempty"` // Filters check suites by GitHub App id. + AppID *int64 `url:"app_id,omitempty"` // Filters check suites by GitHub App id. ListOptions } @@ -324,16 +359,20 @@ type ListCheckSuiteResults struct { } // ListCheckSuitesForRef lists check suite for a specific ref. +// The ref can be a commit SHA, branch name `heads/`, or tag name `tags/`. +// For more information, see "Git References" in the Git documentation https://git-scm.com/book/en/v2/Git-Internals-Git-References. +// +// GitHub API docs: https://docs.github.com/rest/checks/suites?apiVersion=2022-11-28#list-check-suites-for-a-git-reference // -// GitHub API docs: https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-specific-ref -func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, url.QueryEscape(ref)) - u, err := addOptions(u, opt) +//meta:operation GET /repos/{owner}/{repo}/commits/{ref}/check-suites +func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opts *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, refURLEscape(ref)) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } @@ -341,7 +380,7 @@ func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, req.Header.Set("Accept", mediaTypeCheckRunsPreview) var checkSuiteResults *ListCheckSuiteResults - resp, err := s.client.Do(ctx, req, &checkSuiteResults) + resp, err := s.client.Do(req, &checkSuiteResults) if err != nil { return nil, resp, err } @@ -366,17 +405,19 @@ type CheckSuitePreferenceResults struct { Repository *Repository `json:"repository,omitempty"` } -// PreferenceList represents a list of auto trigger checks for repository +// PreferenceList represents a list of auto trigger checks for repository. type PreferenceList struct { AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository. } // SetCheckSuitePreferences changes the default automatic flow when creating check suites. // -// GitHub API docs: https://developer.github.com/v3/checks/suites/#set-preferences-for-check-suites-on-a-repository -func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, opt CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/checks/suites?apiVersion=2022-11-28#update-repository-preferences-for-check-suites +// +//meta:operation PATCH /repos/{owner}/{repo}/check-suites/preferences +func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, body CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-suites/preferences", owner, repo) - req, err := s.client.NewRequest("PATCH", u, opt) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } @@ -384,7 +425,7 @@ func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, rep req.Header.Set("Accept", mediaTypeCheckRunsPreview) var checkSuitePrefResults *CheckSuitePreferenceResults - resp, err := s.client.Do(ctx, req, &checkSuitePrefResults) + resp, err := s.client.Do(req, &checkSuitePrefResults) if err != nil { return nil, resp, err } @@ -392,7 +433,7 @@ func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, rep return checkSuitePrefResults, resp, nil } -// CreateCheckSuiteOptions sets up parameters to manually create a check suites +// CreateCheckSuiteOptions sets up parameters to manually create a check suite. type CreateCheckSuiteOptions struct { HeadSHA string `json:"head_sha"` // The sha of the head commit. (Required.) HeadBranch *string `json:"head_branch,omitempty"` // The name of the head branch where the code changes are implemented. @@ -400,18 +441,20 @@ type CreateCheckSuiteOptions struct { // CreateCheckSuite manually creates a check suite for a repository. // -// GitHub API docs: https://developer.github.com/v3/checks/suites/#create-a-check-suite -func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, opt CreateCheckSuiteOptions) (*CheckSuite, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/checks/suites?apiVersion=2022-11-28#create-a-check-suite +// +//meta:operation POST /repos/{owner}/{repo}/check-suites +func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, body CreateCheckSuiteOptions) (*CheckSuite, *Response, error) { u := fmt.Sprintf("repos/%v/%v/check-suites", owner, repo) - req, err := s.client.NewRequest("POST", u, opt) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeCheckRunsPreview) - checkSuite := new(CheckSuite) - resp, err := s.client.Do(ctx, req, checkSuite) + var checkSuite *CheckSuite + resp, err := s.client.Do(req, &checkSuite) if err != nil { return nil, resp, err } @@ -421,17 +464,19 @@ func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string // ReRequestCheckSuite triggers GitHub to rerequest an existing check suite, without pushing new code to a repository. // -// GitHub API docs: https://developer.github.com/v3/checks/suites/#rerequest-check-suite +// GitHub API docs: https://docs.github.com/rest/checks/suites?apiVersion=2022-11-28#rerequest-a-check-suite +// +//meta:operation POST /repos/{owner}/{repo}/check-suites/{check_suite_id}/rerequest func (s *ChecksService) ReRequestCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/check-suites/%v/rerequest", owner, repo, checkSuiteID) - req, err := s.client.NewRequest("POST", u, nil) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, err } req.Header.Set("Accept", mediaTypeCheckRunsPreview) - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) return resp, err } diff --git a/github/checks_test.go b/github/checks_test.go index 13f5a2ac9c7..c66483c6b33 100644 --- a/github/checks_test.go +++ b/github/checks_test.go @@ -6,17 +6,17 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" "time" + + "github.com/google/go-cmp/cmp" ) func TestChecksService_GetCheckRun(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-runs/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -29,7 +29,8 @@ func TestChecksService_GetCheckRun(t *testing.T) { "started_at": "2018-05-04T01:14:52Z", "completed_at": "2018-05-04T01:14:52Z"}`) }) - checkRun, _, err := client.Checks.GetCheckRun(context.Background(), "o", "r", 1) + ctx := t.Context() + checkRun, _, err := client.Checks.GetCheckRun(ctx, "o", "r", 1) if err != nil { t.Errorf("Checks.GetCheckRun return error: %v", err) } @@ -37,21 +38,35 @@ func TestChecksService_GetCheckRun(t *testing.T) { completeAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z") want := &CheckRun{ - ID: Int64(1), - Status: String("completed"), - Conclusion: String("neutral"), + ID: Ptr(int64(1)), + Status: Ptr("completed"), + Conclusion: Ptr("neutral"), StartedAt: &Timestamp{startedAt}, CompletedAt: &Timestamp{completeAt}, - Name: String("testCheckRun"), + Name: Ptr("testCheckRun"), } - if !reflect.DeepEqual(checkRun, want) { + if !cmp.Equal(checkRun, want) { t.Errorf("Checks.GetCheckRun return %+v, want %+v", checkRun, want) } + + const methodName = "GetCheckRun" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.GetCheckRun(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.GetCheckRun(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_GetCheckSuite(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-suites/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -65,27 +80,42 @@ func TestChecksService_GetCheckSuite(t *testing.T) { "after": "deadbeefa", "status": "completed"}`) }) - checkSuite, _, err := client.Checks.GetCheckSuite(context.Background(), "o", "r", 1) + ctx := t.Context() + checkSuite, _, err := client.Checks.GetCheckSuite(ctx, "o", "r", 1) if err != nil { t.Errorf("Checks.GetCheckSuite return error: %v", err) } want := &CheckSuite{ - ID: Int64(1), - HeadBranch: String("master"), - HeadSHA: String("deadbeef"), - AfterSHA: String("deadbeefa"), - BeforeSHA: String("deadbeefb"), - Status: String("completed"), - Conclusion: String("neutral"), - } - if !reflect.DeepEqual(checkSuite, want) { + ID: Ptr(int64(1)), + HeadBranch: Ptr("master"), + HeadSHA: Ptr("deadbeef"), + AfterSHA: Ptr("deadbeefa"), + BeforeSHA: Ptr("deadbeefb"), + Status: Ptr("completed"), + Conclusion: Ptr("neutral"), + } + if !cmp.Equal(checkSuite, want) { t.Errorf("Checks.GetCheckSuite return %+v, want %+v", checkSuite, want) } + + const methodName = "GetCheckSuite" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.GetCheckSuite(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.GetCheckSuite(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_CreateCheckRun(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-runs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") @@ -102,43 +132,57 @@ func TestChecksService_CreateCheckRun(t *testing.T) { }) startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z") checkRunOpt := CreateCheckRunOptions{ - HeadBranch: "master", - Name: "testCreateCheckRun", - HeadSHA: "deadbeef", - Status: String("in_progress"), - StartedAt: &Timestamp{startedAt}, + Name: "testCreateCheckRun", + HeadSHA: "deadbeef", + Status: Ptr("in_progress"), + StartedAt: &Timestamp{startedAt}, Output: &CheckRunOutput{ - Title: String("Mighty test report"), - Summary: String(""), - Text: String(""), + Title: Ptr("Mighty test report"), + Summary: Ptr(""), + Text: Ptr(""), }, } - checkRun, _, err := client.Checks.CreateCheckRun(context.Background(), "o", "r", checkRunOpt) + ctx := t.Context() + checkRun, _, err := client.Checks.CreateCheckRun(ctx, "o", "r", checkRunOpt) if err != nil { t.Errorf("Checks.CreateCheckRun return error: %v", err) } want := &CheckRun{ - ID: Int64(1), - Status: String("in_progress"), + ID: Ptr(int64(1)), + Status: Ptr("in_progress"), StartedAt: &Timestamp{startedAt}, - HeadSHA: String("deadbeef"), - Name: String("testCreateCheckRun"), + HeadSHA: Ptr("deadbeef"), + Name: Ptr("testCreateCheckRun"), Output: &CheckRunOutput{ - Title: String("Mighty test report"), - Summary: String(""), - Text: String(""), + Title: Ptr("Mighty test report"), + Summary: Ptr(""), + Text: Ptr(""), }, } - if !reflect.DeepEqual(checkRun, want) { + if !cmp.Equal(checkRun, want) { t.Errorf("Checks.CreateCheckRun return %+v, want %+v", checkRun, want) } + + const methodName = "CreateCheckRun" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.CreateCheckRun(ctx, "\n", "\n", CreateCheckRunOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.CreateCheckRun(ctx, "o", "r", checkRunOpt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_ListCheckRunAnnotations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-runs/1/annotations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -148,11 +192,10 @@ func TestChecksService_ListCheckRunAnnotations(t *testing.T) { }) fmt.Fprint(w, `[{ "path": "README.md", - "blob_href": "https://github.com/octocat/Hello-World/blob/837db83be4137ca555d9a5598d0a1ea2987ecfee/README.md", "start_line": 2, "end_line": 2, "start_column": 1, - "end_column": 5, + "end_column": 5, "annotation_level": "warning", "message": "Check your spelling for 'banaas'.", "title": "Spell check", @@ -160,32 +203,46 @@ func TestChecksService_ListCheckRunAnnotations(t *testing.T) { ) }) - checkRunAnnotations, _, err := client.Checks.ListCheckRunAnnotations(context.Background(), "o", "r", 1, &ListOptions{Page: 1}) + ctx := t.Context() + checkRunAnnotations, _, err := client.Checks.ListCheckRunAnnotations(ctx, "o", "r", 1, &ListOptions{Page: 1}) if err != nil { t.Errorf("Checks.ListCheckRunAnnotations return error: %v", err) } want := []*CheckRunAnnotation{{ - Path: String("README.md"), - BlobHRef: String("https://github.com/octocat/Hello-World/blob/837db83be4137ca555d9a5598d0a1ea2987ecfee/README.md"), - StartLine: Int(2), - EndLine: Int(2), - StartColumn: Int(1), - EndColumn: Int(5), - AnnotationLevel: String("warning"), - Message: String("Check your spelling for 'banaas'."), - Title: String("Spell check"), - RawDetails: String("Do you mean 'bananas' or 'banana'?"), + Path: Ptr("README.md"), + StartLine: Ptr(2), + EndLine: Ptr(2), + StartColumn: Ptr(1), + EndColumn: Ptr(5), + AnnotationLevel: Ptr("warning"), + Message: Ptr("Check your spelling for 'banaas'."), + Title: Ptr("Spell check"), + RawDetails: Ptr("Do you mean 'bananas' or 'banana'?"), }} - if !reflect.DeepEqual(checkRunAnnotations, want) { + if !cmp.Equal(checkRunAnnotations, want) { t.Errorf("Checks.ListCheckRunAnnotations returned %+v, want %+v", checkRunAnnotations, want) } + + const methodName = "ListCheckRunAnnotations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.ListCheckRunAnnotations(ctx, "\n", "\n", -1, &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.ListCheckRunAnnotations(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_UpdateCheckRun(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-runs/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") @@ -193,7 +250,6 @@ func TestChecksService_UpdateCheckRun(t *testing.T) { fmt.Fprint(w, `{ "id": 1, "name":"testUpdateCheckRun", - "head_sha":"deadbeef", "status": "completed", "conclusion": "neutral", "started_at": "2018-05-04T01:14:52Z", @@ -202,45 +258,57 @@ func TestChecksService_UpdateCheckRun(t *testing.T) { }) startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z") updateCheckRunOpt := UpdateCheckRunOptions{ - HeadBranch: String("master"), Name: "testUpdateCheckRun", - HeadSHA: String("deadbeef"), - Status: String("completed"), + Status: Ptr("completed"), CompletedAt: &Timestamp{startedAt}, Output: &CheckRunOutput{ - Title: String("Mighty test report"), - Summary: String("There are 0 failures, 2 warnings and 1 notice"), - Text: String("You may have misspelled some words."), + Title: Ptr("Mighty test report"), + Summary: Ptr("There are 0 failures, 2 warnings and 1 notice"), + Text: Ptr("You may have misspelled some words."), }, } - checkRun, _, err := client.Checks.UpdateCheckRun(context.Background(), "o", "r", 1, updateCheckRunOpt) + ctx := t.Context() + checkRun, _, err := client.Checks.UpdateCheckRun(ctx, "o", "r", 1, updateCheckRunOpt) if err != nil { t.Errorf("Checks.UpdateCheckRun return error: %v", err) } want := &CheckRun{ - ID: Int64(1), - Status: String("completed"), + ID: Ptr(int64(1)), + Status: Ptr("completed"), StartedAt: &Timestamp{startedAt}, CompletedAt: &Timestamp{startedAt}, - Conclusion: String("neutral"), - HeadSHA: String("deadbeef"), - Name: String("testUpdateCheckRun"), + Conclusion: Ptr("neutral"), + Name: Ptr("testUpdateCheckRun"), Output: &CheckRunOutput{ - Title: String("Mighty test report"), - Summary: String("There are 0 failures, 2 warnings and 1 notice"), - Text: String("You may have misspelled some words."), + Title: Ptr("Mighty test report"), + Summary: Ptr("There are 0 failures, 2 warnings and 1 notice"), + Text: Ptr("You may have misspelled some words."), }, } - if !reflect.DeepEqual(checkRun, want) { + if !cmp.Equal(checkRun, want) { t.Errorf("Checks.UpdateCheckRun return %+v, want %+v", checkRun, want) } + + const methodName = "UpdateCheckRun" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.UpdateCheckRun(ctx, "\n", "\n", -1, UpdateCheckRunOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.UpdateCheckRun(ctx, "o", "r", 1, updateCheckRunOpt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_ListCheckRunsForRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/master/check-runs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -250,6 +318,7 @@ func TestChecksService_ListCheckRunsForRef(t *testing.T) { "page": "1", "status": "completed", "filter": "all", + "app_id": "1", }) fmt.Fprint(w, `{"total_count":1, "check_runs": [{ @@ -258,41 +327,60 @@ func TestChecksService_ListCheckRunsForRef(t *testing.T) { "status": "completed", "conclusion": "neutral", "started_at": "2018-05-04T01:14:52Z", - "completed_at": "2018-05-04T01:14:52Z"}]}`, + "completed_at": "2018-05-04T01:14:52Z", + "app": { + "id": 1}}]}`, ) }) opt := &ListCheckRunsOptions{ - CheckName: String("testing"), - Status: String("completed"), - Filter: String("all"), + CheckName: Ptr("testing"), + Status: Ptr("completed"), + Filter: Ptr("all"), + AppID: Ptr(int64(1)), ListOptions: ListOptions{Page: 1}, } - checkRuns, _, err := client.Checks.ListCheckRunsForRef(context.Background(), "o", "r", "master", opt) + ctx := t.Context() + checkRuns, _, err := client.Checks.ListCheckRunsForRef(ctx, "o", "r", "master", opt) if err != nil { t.Errorf("Checks.ListCheckRunsForRef return error: %v", err) } startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z") want := &ListCheckRunsResults{ - Total: Int(1), + Total: Ptr(1), CheckRuns: []*CheckRun{{ - ID: Int64(1), - Status: String("completed"), + ID: Ptr(int64(1)), + Status: Ptr("completed"), StartedAt: &Timestamp{startedAt}, CompletedAt: &Timestamp{startedAt}, - Conclusion: String("neutral"), - HeadSHA: String("deadbeef"), + Conclusion: Ptr("neutral"), + HeadSHA: Ptr("deadbeef"), + App: &App{ID: Ptr(int64(1))}, }}, } - if !reflect.DeepEqual(checkRuns, want) { + if !cmp.Equal(checkRuns, want) { t.Errorf("Checks.ListCheckRunsForRef returned %+v, want %+v", checkRuns, want) } + + const methodName = "ListCheckRunsForRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.ListCheckRunsForRef(ctx, "\n", "\n", "\n", &ListCheckRunsOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.ListCheckRunsForRef(ctx, "o", "r", "master", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_ListCheckRunsCheckSuite(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-suites/1/check-runs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -315,36 +403,51 @@ func TestChecksService_ListCheckRunsCheckSuite(t *testing.T) { }) opt := &ListCheckRunsOptions{ - CheckName: String("testing"), - Status: String("completed"), - Filter: String("all"), + CheckName: Ptr("testing"), + Status: Ptr("completed"), + Filter: Ptr("all"), ListOptions: ListOptions{Page: 1}, } - checkRuns, _, err := client.Checks.ListCheckRunsCheckSuite(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + checkRuns, _, err := client.Checks.ListCheckRunsCheckSuite(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Checks.ListCheckRunsCheckSuite return error: %v", err) } startedAt, _ := time.Parse(time.RFC3339, "2018-05-04T01:14:52Z") want := &ListCheckRunsResults{ - Total: Int(1), + Total: Ptr(1), CheckRuns: []*CheckRun{{ - ID: Int64(1), - Status: String("completed"), + ID: Ptr(int64(1)), + Status: Ptr("completed"), StartedAt: &Timestamp{startedAt}, CompletedAt: &Timestamp{startedAt}, - Conclusion: String("neutral"), - HeadSHA: String("deadbeef"), + Conclusion: Ptr("neutral"), + HeadSHA: Ptr("deadbeef"), }}, } - if !reflect.DeepEqual(checkRuns, want) { + if !cmp.Equal(checkRuns, want) { t.Errorf("Checks.ListCheckRunsCheckSuite returned %+v, want %+v", checkRuns, want) } + + const methodName = "ListCheckRunsCheckSuite" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.ListCheckRunsCheckSuite(ctx, "\n", "\n", -1, &ListCheckRunsOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.ListCheckRunsCheckSuite(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_ListCheckSuiteForRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/master/check-suites", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -367,48 +470,66 @@ func TestChecksService_ListCheckSuiteForRef(t *testing.T) { }) opt := &ListCheckSuiteOptions{ - CheckName: String("testing"), - AppID: Int(2), + CheckName: Ptr("testing"), + AppID: Ptr(int64(2)), ListOptions: ListOptions{Page: 1}, } - checkSuites, _, err := client.Checks.ListCheckSuitesForRef(context.Background(), "o", "r", "master", opt) + ctx := t.Context() + checkSuites, _, err := client.Checks.ListCheckSuitesForRef(ctx, "o", "r", "master", opt) if err != nil { t.Errorf("Checks.ListCheckSuitesForRef return error: %v", err) } want := &ListCheckSuiteResults{ - Total: Int(1), + Total: Ptr(1), CheckSuites: []*CheckSuite{{ - ID: Int64(1), - Status: String("completed"), - Conclusion: String("neutral"), - HeadSHA: String("deadbeef"), - HeadBranch: String("master"), - BeforeSHA: String("deadbeefb"), - AfterSHA: String("deadbeefa"), + ID: Ptr(int64(1)), + Status: Ptr("completed"), + Conclusion: Ptr("neutral"), + HeadSHA: Ptr("deadbeef"), + HeadBranch: Ptr("master"), + BeforeSHA: Ptr("deadbeefb"), + AfterSHA: Ptr("deadbeefa"), }}, } - if !reflect.DeepEqual(checkSuites, want) { + if !cmp.Equal(checkSuites, want) { t.Errorf("Checks.ListCheckSuitesForRef returned %+v, want %+v", checkSuites, want) } + + const methodName = "ListCheckSuitesForRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.ListCheckSuitesForRef(ctx, "\n", "\n", "\n", &ListCheckSuiteOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.ListCheckSuitesForRef(ctx, "o", "r", "master", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_SetCheckSuitePreferences(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + a := []*AutoTriggerCheck{{ + AppID: Ptr(int64(2)), + Setting: Ptr(false), + }} + opt := CheckSuitePreferenceOptions{AutoTriggerChecks: a} mux.HandleFunc("/repos/o/r/check-suites/preferences", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") testHeader(t, r, "Accept", mediaTypeCheckRunsPreview) - testBody(t, r, `{"auto_trigger_checks":[{"app_id":2,"setting":false}]}`+"\n") + testJSONBody(t, r, opt) fmt.Fprint(w, `{"preferences":{"auto_trigger_checks":[{"app_id": 2,"setting": false}]}}`) }) - a := []*AutoTriggerCheck{{ - AppID: Int64(2), - Setting: Bool(false), - }} - opt := CheckSuitePreferenceOptions{AutoTriggerChecks: a} - prefResults, _, err := client.Checks.SetCheckSuitePreferences(context.Background(), "o", "r", opt) + + ctx := t.Context() + prefResults, _, err := client.Checks.SetCheckSuitePreferences(ctx, "o", "r", opt) if err != nil { t.Errorf("Checks.SetCheckSuitePreferences return error: %v", err) } @@ -420,14 +541,28 @@ func TestChecksService_SetCheckSuitePreferences(t *testing.T) { Preferences: p, } - if !reflect.DeepEqual(prefResults, want) { + if !cmp.Equal(prefResults, want) { t.Errorf("Checks.SetCheckSuitePreferences return %+v, want %+v", prefResults, want) } + + const methodName = "SetCheckSuitePreferences" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.SetCheckSuitePreferences(ctx, "\n", "\n", CheckSuitePreferenceOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.SetCheckSuitePreferences(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_CreateCheckSuite(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-suites", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") @@ -444,359 +579,89 @@ func TestChecksService_CreateCheckSuite(t *testing.T) { checkSuiteOpt := CreateCheckSuiteOptions{ HeadSHA: "deadbeef", - HeadBranch: String("master"), + HeadBranch: Ptr("master"), } - checkSuite, _, err := client.Checks.CreateCheckSuite(context.Background(), "o", "r", checkSuiteOpt) + ctx := t.Context() + checkSuite, _, err := client.Checks.CreateCheckSuite(ctx, "o", "r", checkSuiteOpt) if err != nil { t.Errorf("Checks.CreateCheckSuite return error: %v", err) } want := &CheckSuite{ - ID: Int64(2), - Status: String("completed"), - HeadSHA: String("deadbeef"), - HeadBranch: String("master"), - Conclusion: String("neutral"), - BeforeSHA: String("deadbeefb"), - AfterSHA: String("deadbeefa"), - } - if !reflect.DeepEqual(checkSuite, want) { + ID: Ptr(int64(2)), + Status: Ptr("completed"), + HeadSHA: Ptr("deadbeef"), + HeadBranch: Ptr("master"), + Conclusion: Ptr("neutral"), + BeforeSHA: Ptr("deadbeefb"), + AfterSHA: Ptr("deadbeefa"), + } + if !cmp.Equal(checkSuite, want) { t.Errorf("Checks.CreateCheckSuite return %+v, want %+v", checkSuite, want) } + + const methodName = "CreateCheckSuite" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Checks.CreateCheckSuite(ctx, "\n", "\n", CreateCheckSuiteOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Checks.CreateCheckSuite(ctx, "o", "r", checkSuiteOpt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestChecksService_ReRequestCheckSuite(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/check-suites/1/rerequest", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeCheckRunsPreview) w.WriteHeader(http.StatusCreated) }) - resp, err := client.Checks.ReRequestCheckSuite(context.Background(), "o", "r", 1) + ctx := t.Context() + resp, err := client.Checks.ReRequestCheckSuite(ctx, "o", "r", 1) if err != nil { t.Errorf("Checks.ReRequestCheckSuite return error: %v", err) } if got, want := resp.StatusCode, http.StatusCreated; got != want { t.Errorf("Checks.ReRequestCheckSuite = %v, want %v", got, want) } -} -func Test_CheckRunMarshal(t *testing.T) { - testJSONMarshal(t, &CheckRun{}, "{}") - - now := time.Now() - ts := now.Format(time.RFC3339Nano) - - c := CheckRun{ - ID: Int64(1), - NodeID: String("n"), - HeadSHA: String("h"), - ExternalID: String("1"), - URL: String("u"), - HTMLURL: String("u"), - DetailsURL: String("u"), - Status: String("s"), - Conclusion: String("c"), - StartedAt: &Timestamp{Time: now}, - CompletedAt: &Timestamp{Time: now}, - Output: &CheckRunOutput{ - Annotations: []*CheckRunAnnotation{ - { - AnnotationLevel: String("a"), - BlobHRef: String("b"), - EndLine: Int(1), - Message: String("m"), - Path: String("p"), - RawDetails: String("r"), - StartLine: Int(1), - Title: String("t"), - }, - }, - AnnotationsCount: Int(1), - AnnotationsURL: String("a"), - Images: []*CheckRunImage{ - { - Alt: String("a"), - ImageURL: String("i"), - Caption: String("c"), - }, - }, - Title: String("t"), - Summary: String("s"), - Text: String("t"), - }, - Name: String("n"), - CheckSuite: &CheckSuite{ - ID: Int64(1), - }, - App: &App{ - ID: Int64(1), - NodeID: String("n"), - Owner: &User{ - Login: String("l"), - ID: Int64(1), - NodeID: String("n"), - URL: String("u"), - ReposURL: String("r"), - EventsURL: String("e"), - AvatarURL: String("a"), - }, - Name: String("n"), - Description: String("d"), - HTMLURL: String("h"), - ExternalURL: String("u"), - CreatedAt: &Timestamp{now}, - UpdatedAt: &Timestamp{now}, - }, - PullRequests: []*PullRequest{ - { - URL: String("u"), - ID: Int64(1), - Number: Int(1), - Head: &PullRequestBranch{ - Ref: String("r"), - SHA: String("s"), - Repo: &Repository{ - ID: Int64(1), - URL: String("s"), - Name: String("n"), - }, - }, - Base: &PullRequestBranch{ - Ref: String("r"), - SHA: String("s"), - Repo: &Repository{ - ID: Int64(1), - URL: String("u"), - Name: String("n"), - }, - }, - }, - }, - } - w := fmt.Sprintf(`{ - "id": 1, - "node_id": "n", - "head_sha": "h", - "external_id": "1", - "url": "u", - "html_url": "u", - "details_url": "u", - "status": "s", - "conclusion": "c", - "started_at": "%s", - "completed_at": "%s", - "output": { - "title": "t", - "summary": "s", - "text": "t", - "annotations_count": 1, - "annotations_url": "a", - "annotations": [ - { - "path": "p", - "blob_href": "b", - "start_line": 1, - "end_line": 1, - "annotation_level": "a", - "message": "m", - "title": "t", - "raw_details": "r" - } - ], - "images": [ - { - "alt": "a", - "image_url": "i", - "caption": "c" - } - ] - }, - "name": "n", - "check_suite": { - "id": 1 - }, - "app": { - "id": 1, - "node_id": "n", - "owner": { - "login": "l", - "id": 1, - "node_id": "n", - "avatar_url": "a", - "url": "u", - "events_url": "e", - "repos_url": "r" - }, - "name": "n", - "description": "d", - "external_url": "u", - "html_url": "h", - "created_at": "%s", - "updated_at": "%s" - }, - "pull_requests": [ - { - "id": 1, - "number": 1, - "url": "u", - "head": { - "ref": "r", - "sha": "s", - "repo": { - "id": 1, - "name": "n", - "url": "s" - } - }, - "base": { - "ref": "r", - "sha": "s", - "repo": { - "id": 1, - "name": "n", - "url": "u" - } - } - } - ] - }`, ts, ts, ts, ts) - - testJSONMarshal(t, &c, w) + const methodName = "ReRequestCheckSuite" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Checks.ReRequestCheckSuite(ctx, "\n", "\n", 1) + return err + }) } -func Test_CheckSuiteMarshal(t *testing.T) { - testJSONMarshal(t, &CheckSuite{}, "{}") - - now := time.Now() - ts := now.Format(time.RFC3339Nano) - - c := CheckSuite{ - ID: Int64(1), - NodeID: String("n"), - HeadBranch: String("h"), - HeadSHA: String("h"), - URL: String("u"), - BeforeSHA: String("b"), - AfterSHA: String("a"), - Status: String("s"), - Conclusion: String("c"), - App: &App{ - ID: Int64(1), - NodeID: String("n"), - Owner: &User{ - Login: String("l"), - ID: Int64(1), - NodeID: String("n"), - URL: String("u"), - ReposURL: String("r"), - EventsURL: String("e"), - AvatarURL: String("a"), - }, - Name: String("n"), - Description: String("d"), - HTMLURL: String("h"), - ExternalURL: String("u"), - CreatedAt: &Timestamp{now}, - UpdatedAt: &Timestamp{now}, - }, - Repository: &Repository{ - ID: Int64(1), - }, - PullRequests: []*PullRequest{ - { - URL: String("u"), - ID: Int64(1), - Number: Int(1), - Head: &PullRequestBranch{ - Ref: String("r"), - SHA: String("s"), - Repo: &Repository{ - ID: Int64(1), - URL: String("s"), - Name: String("n"), - }, - }, - Base: &PullRequestBranch{ - Ref: String("r"), - SHA: String("s"), - Repo: &Repository{ - ID: Int64(1), - URL: String("u"), - Name: String("n"), - }, - }, - }, - }, - HeadCommit: &Commit{ - SHA: String("s"), - }, - } +func TestChecksService_ReRequestCheckRun(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - w := fmt.Sprintf(`{ - "id": 1, - "node_id": "n", - "head_branch": "h", - "head_sha": "h", - "url": "u", - "before": "b", - "after": "a", - "status": "s", - "conclusion": "c", - "app": { - "id": 1, - "node_id": "n", - "owner": { - "login": "l", - "id": 1, - "node_id": "n", - "avatar_url": "a", - "url": "u", - "events_url": "e", - "repos_url": "r" - }, - "name": "n", - "description": "d", - "external_url": "u", - "html_url": "h", - "created_at": "%s", - "updated_at": "%s" - }, - "repository": { - "id": 1 - }, - "pull_requests": [ - { - "id": 1, - "number": 1, - "url": "u", - "head": { - "ref": "r", - "sha": "s", - "repo": { - "id": 1, - "name": "n", - "url": "s" - } - }, - "base": { - "ref": "r", - "sha": "s", - "repo": { - "id": 1, - "name": "n", - "url": "u" - } - } - } - ], - "head_commit": { - "sha": "s" - } - }`, ts, ts) + mux.HandleFunc("/repos/o/r/check-runs/1/rerequest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeCheckRunsPreview) + w.WriteHeader(http.StatusCreated) + }) + ctx := t.Context() + resp, err := client.Checks.ReRequestCheckRun(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Checks.ReRequestCheckRun return error: %v", err) + } + if got, want := resp.StatusCode, http.StatusCreated; got != want { + t.Errorf("Checks.ReRequestCheckRun = %v, want %v", got, want) + } - testJSONMarshal(t, &c, w) + const methodName = "ReRequestCheckRun" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Checks.ReRequestCheckRun(ctx, "\n", "\n", 1) + return err + }) } diff --git a/github/classroom.go b/github/classroom.go new file mode 100644 index 00000000000..7a045206c72 --- /dev/null +++ b/github/classroom.go @@ -0,0 +1,268 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ClassroomService handles communication with the GitHub Classroom related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28 +type ClassroomService service + +// ClassroomUser represents a GitHub user simplified for Classroom. +type ClassroomUser struct { + ID *int64 `json:"id,omitempty"` + Login *string `json:"login,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +func (u ClassroomUser) String() string { + return Stringify(u) +} + +// Classroom represents a GitHub Classroom. +type Classroom struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Archived *bool `json:"archived,omitempty"` + Organization *Organization `json:"organization,omitempty"` + URL *string `json:"url,omitempty"` +} + +func (c Classroom) String() string { + return Stringify(c) +} + +// ClassroomAssignment represents a GitHub Classroom assignment. +type ClassroomAssignment struct { + ID *int64 `json:"id,omitempty"` + PublicRepo *bool `json:"public_repo,omitempty"` + Title *string `json:"title,omitempty"` + Type *string `json:"type,omitempty"` + InviteLink *string `json:"invite_link,omitempty"` + InvitationsEnabled *bool `json:"invitations_enabled,omitempty"` + Slug *string `json:"slug,omitempty"` + StudentsAreRepoAdmins *bool `json:"students_are_repo_admins,omitempty"` + FeedbackPullRequestsEnabled *bool `json:"feedback_pull_requests_enabled,omitempty"` + MaxTeams *int `json:"max_teams,omitempty"` + MaxMembers *int `json:"max_members,omitempty"` + Editor *string `json:"editor,omitempty"` + Accepted *int `json:"accepted,omitempty"` + Submitted *int `json:"submitted,omitempty"` + Passing *int `json:"passing,omitempty"` + Language *string `json:"language,omitempty"` + Deadline *Timestamp `json:"deadline,omitempty"` + StarterCodeRepository *Repository `json:"starter_code_repository,omitempty"` + Classroom *Classroom `json:"classroom,omitempty"` +} + +func (a ClassroomAssignment) String() string { + return Stringify(a) +} + +// AcceptedAssignment represents a GitHub Classroom accepted assignment. +type AcceptedAssignment struct { + ID *int64 `json:"id,omitempty"` + Submitted *bool `json:"submitted,omitempty"` + Passing *bool `json:"passing,omitempty"` + CommitCount *int `json:"commit_count,omitempty"` + Grade *string `json:"grade,omitempty"` + Students []*ClassroomUser `json:"students,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Assignment *ClassroomAssignment `json:"assignment,omitempty"` +} + +func (a AcceptedAssignment) String() string { + return Stringify(a) +} + +// AssignmentGrade represents a GitHub Classroom assignment grade. +type AssignmentGrade struct { + AssignmentName *string `json:"assignment_name,omitempty"` + AssignmentURL *string `json:"assignment_url,omitempty"` + StarterCodeURL *string `json:"starter_code_url,omitempty"` + GithubUsername *string `json:"github_username,omitempty"` + RosterIdentifier *string `json:"roster_identifier,omitempty"` + StudentRepositoryName *string `json:"student_repository_name,omitempty"` + StudentRepositoryURL *string `json:"student_repository_url,omitempty"` + SubmissionTimestamp *Timestamp `json:"submission_timestamp,omitempty"` + PointsAwarded *int `json:"points_awarded,omitempty"` + PointsAvailable *int `json:"points_available,omitempty"` + GroupName *string `json:"group_name,omitempty"` +} + +func (g AssignmentGrade) String() string { + return Stringify(g) +} + +// GetAssignment gets a GitHub Classroom assignment. Assignment will only be +// returned if the current user is an administrator of the GitHub Classroom +// for the assignment. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28#closing-down---get-an-assignment +// +//meta:operation GET /assignments/{assignment_id} +func (s *ClassroomService) GetAssignment(ctx context.Context, assignmentID int64) (*ClassroomAssignment, *Response, error) { + u := fmt.Sprintf("assignments/%v", assignmentID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var assignment *ClassroomAssignment + resp, err := s.client.Do(req, &assignment) + if err != nil { + return nil, resp, err + } + + return assignment, resp, nil +} + +// GetClassroom gets a GitHub Classroom for the current user. Classroom will only be +// returned if the current user is an administrator of the GitHub Classroom. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28#closing-down---get-a-classroom +// +//meta:operation GET /classrooms/{classroom_id} +func (s *ClassroomService) GetClassroom(ctx context.Context, classroomID int64) (*Classroom, *Response, error) { + u := fmt.Sprintf("classrooms/%v", classroomID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var classroom *Classroom + resp, err := s.client.Do(req, &classroom) + if err != nil { + return nil, resp, err + } + + return classroom, resp, nil +} + +// ListClassrooms lists GitHub Classrooms for the current user. Classrooms will only be +// returned if the current user is an administrator of one or more GitHub Classrooms. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28#closing-down---list-classrooms +// +//meta:operation GET /classrooms +func (s *ClassroomService) ListClassrooms(ctx context.Context, opts *ListOptions) ([]*Classroom, *Response, error) { + u, err := addOptions("classrooms", opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var classrooms []*Classroom + resp, err := s.client.Do(req, &classrooms) + if err != nil { + return nil, resp, err + } + + return classrooms, resp, nil +} + +// ListClassroomAssignments lists GitHub Classroom assignments for a classroom. Assignments will only be +// returned if the current user is an administrator of the GitHub Classroom. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28#closing-down---list-assignments-for-a-classroom +// +//meta:operation GET /classrooms/{classroom_id}/assignments +func (s *ClassroomService) ListClassroomAssignments(ctx context.Context, classroomID int64, opts *ListOptions) ([]*ClassroomAssignment, *Response, error) { + u := fmt.Sprintf("classrooms/%v/assignments", classroomID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var assignments []*ClassroomAssignment + resp, err := s.client.Do(req, &assignments) + if err != nil { + return nil, resp, err + } + + return assignments, resp, nil +} + +// ListAcceptedAssignments lists accepted assignments for a GitHub Classroom assignment. +// Accepted assignments will only be returned if the current user is an administrator +// of the GitHub Classroom for the assignment. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28#closing-down---list-accepted-assignments-for-an-assignment +// +//meta:operation GET /assignments/{assignment_id}/accepted_assignments +func (s *ClassroomService) ListAcceptedAssignments(ctx context.Context, assignmentID int64, opts *ListOptions) ([]*AcceptedAssignment, *Response, error) { + u := fmt.Sprintf("assignments/%v/accepted_assignments", assignmentID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var acceptedAssignments []*AcceptedAssignment + resp, err := s.client.Do(req, &acceptedAssignments) + if err != nil { + return nil, resp, err + } + + return acceptedAssignments, resp, nil +} + +// GetAssignmentGrades gets assignment grades for a GitHub Classroom assignment. +// Grades will only be returned if the current user is an administrator +// of the GitHub Classroom for the assignment. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/classroom/classroom?apiVersion=2022-11-28#closing-down---get-assignment-grades +// +//meta:operation GET /assignments/{assignment_id}/grades +func (s *ClassroomService) GetAssignmentGrades(ctx context.Context, assignmentID int64) ([]*AssignmentGrade, *Response, error) { + u := fmt.Sprintf("assignments/%v/grades", assignmentID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var grades []*AssignmentGrade + resp, err := s.client.Do(req, &grades) + if err != nil { + return nil, resp, err + } + + return grades, resp, nil +} diff --git a/github/classroom_test.go b/github/classroom_test.go new file mode 100644 index 00000000000..9cc64b98946 --- /dev/null +++ b/github/classroom_test.go @@ -0,0 +1,697 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestClassroomService_GetAssignment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/assignments/12", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 12, + "public_repo": false, + "title": "Intro to Binaries", + "type": "individual", + "invite_link": "https://example.com/a/Lx7jiUgx", + "invitations_enabled": true, + "slug": "intro-to-binaries", + "students_are_repo_admins": false, + "feedback_pull_requests_enabled": true, + "max_teams": 0, + "max_members": 0, + "editor": "codespaces", + "accepted": 100, + "submitted": 40, + "passing": 10, + "language": "ruby", + "deadline": `+referenceTimeStr+`, + "starter_code_repository": { + "id": 1296269, + "full_name": "octocat/Hello-World", + "html_url": "https://example.com/octocat/Hello-World", + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "private": false, + "default_branch": "main" + }, + "classroom": { + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "url": "https://example.com/classrooms/programming" + } + }`) + }) + + ctx := t.Context() + assignment, _, err := client.Classroom.GetAssignment(ctx, 12) + if err != nil { + t.Errorf("Classroom.GetAssignment returned error: %v", err) + } + + want := &ClassroomAssignment{ + ID: Ptr(int64(12)), + PublicRepo: Ptr(false), + Title: Ptr("Intro to Binaries"), + Type: Ptr("individual"), + InviteLink: Ptr("https://example.com/a/Lx7jiUgx"), + InvitationsEnabled: Ptr(true), + Slug: Ptr("intro-to-binaries"), + StudentsAreRepoAdmins: Ptr(false), + FeedbackPullRequestsEnabled: Ptr(true), + MaxTeams: Ptr(0), + MaxMembers: Ptr(0), + Editor: Ptr("codespaces"), + Accepted: Ptr(100), + Submitted: Ptr(40), + Passing: Ptr(10), + Language: Ptr("ruby"), + Deadline: &referenceTimestamp, + StarterCodeRepository: &Repository{ + ID: Ptr(int64(1296269)), + FullName: Ptr("octocat/Hello-World"), + HTMLURL: Ptr("https://example.com/octocat/Hello-World"), + NodeID: Ptr("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"), + Private: Ptr(false), + DefaultBranch: Ptr("main"), + }, + Classroom: &Classroom{ + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + URL: Ptr("https://example.com/classrooms/programming"), + }, + } + + if !cmp.Equal(assignment, want) { + t.Errorf("Classroom.GetAssignment returned %+v, want %+v", assignment, want) + } + + const methodName = "GetAssignment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Classroom.GetAssignment(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Classroom.GetAssignment(ctx, 12) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestClassroomService_GetClassroom(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/classrooms/1296269", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "organization": { + "id": 1, + "login": "programming-elixir", + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=", + "html_url": "https://example.com/programming-elixir", + "name": "Learn how to build fault tolerant applications", + "avatar_url": "https://example.com/avatars/u/9919?v=4" + }, + "url": "https://example.com/classrooms/programming" + }`) + }) + + ctx := t.Context() + classroom, _, err := client.Classroom.GetClassroom(ctx, 1296269) + if err != nil { + t.Errorf("Classroom.GetClassroom returned error: %v", err) + } + + want := &Classroom{ + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + Organization: &Organization{ + ID: Ptr(int64(1)), + Login: Ptr("programming-elixir"), + NodeID: Ptr("MDEyOk9yZ2FuaXphdGlvbjE="), + HTMLURL: Ptr("https://example.com/programming-elixir"), + Name: Ptr("Learn how to build fault tolerant applications"), + AvatarURL: Ptr("https://example.com/avatars/u/9919?v=4"), + }, + URL: Ptr("https://example.com/classrooms/programming"), + } + + if !cmp.Equal(classroom, want) { + t.Errorf("Classroom.GetClassroom returned %+v, want %+v", classroom, want) + } + + const methodName = "GetClassroom" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Classroom.GetClassroom(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Classroom.GetClassroom(ctx, 1296269) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestClassroomService_ListClassrooms(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/classrooms", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "2"}) + fmt.Fprint(w, `[ + { + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "url": "https://example.com/classrooms/programming" + }, + { + "id": 1296270, + "name": "Advanced Programming", + "archived": true, + "url": "https://example.com/classrooms/2-advanced-programming" + } + ]`) + }) + + opt := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + classrooms, _, err := client.Classroom.ListClassrooms(ctx, opt) + if err != nil { + t.Errorf("Classroom.ListClassrooms returned error: %v", err) + } + + want := []*Classroom{ + { + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + URL: Ptr("https://example.com/classrooms/programming"), + }, + { + ID: Ptr(int64(1296270)), + Name: Ptr("Advanced Programming"), + Archived: Ptr(true), + URL: Ptr("https://example.com/classrooms/2-advanced-programming"), + }, + } + + if !cmp.Equal(classrooms, want) { + t.Errorf("Classroom.ListClassrooms returned %+v, want %+v", classrooms, want) + } + + const methodName = "ListClassrooms" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Classroom.ListClassrooms(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestClassroomService_ListClassroomAssignments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/classrooms/1296269/assignments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "2"}) + fmt.Fprint(w, `[ + { + "id": 12, + "public_repo": false, + "title": "Intro to Binaries", + "type": "individual", + "invite_link": "https://example.com/a/Lx7jiUgx", + "invitations_enabled": true, + "slug": "intro-to-binaries", + "students_are_repo_admins": false, + "feedback_pull_requests_enabled": true, + "max_teams": 0, + "max_members": 0, + "editor": "codespaces", + "accepted": 100, + "submitted": 40, + "passing": 10, + "language": "ruby", + "deadline": `+referenceTimeStr+`, + "classroom": { + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "url": "https://example.com/classrooms/programming" + } + }, + { + "id": 13, + "public_repo": true, + "title": "Advanced Programming", + "type": "group", + "invite_link": "https://example.com/a/AdvancedProg", + "invitations_enabled": true, + "slug": "advanced-programming", + "students_are_repo_admins": true, + "feedback_pull_requests_enabled": false, + "max_teams": 5, + "max_members": 3, + "editor": "vscode", + "accepted": 50, + "submitted": 25, + "passing": 20, + "language": "python", + "deadline": `+referenceTimeStr+`, + "classroom": { + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "url": "https://example.com/classrooms/programming" + } + } + ]`) + }) + + opt := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + assignments, _, err := client.Classroom.ListClassroomAssignments(ctx, 1296269, opt) + if err != nil { + t.Errorf("Classroom.ListClassroomAssignments returned error: %v", err) + } + + want := []*ClassroomAssignment{ + { + ID: Ptr(int64(12)), + PublicRepo: Ptr(false), + Title: Ptr("Intro to Binaries"), + Type: Ptr("individual"), + InviteLink: Ptr("https://example.com/a/Lx7jiUgx"), + InvitationsEnabled: Ptr(true), + Slug: Ptr("intro-to-binaries"), + StudentsAreRepoAdmins: Ptr(false), + FeedbackPullRequestsEnabled: Ptr(true), + MaxTeams: Ptr(0), + MaxMembers: Ptr(0), + Editor: Ptr("codespaces"), + Accepted: Ptr(100), + Submitted: Ptr(40), + Passing: Ptr(10), + Language: Ptr("ruby"), + Deadline: &referenceTimestamp, + Classroom: &Classroom{ + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + URL: Ptr("https://example.com/classrooms/programming"), + }, + }, + { + ID: Ptr(int64(13)), + PublicRepo: Ptr(true), + Title: Ptr("Advanced Programming"), + Type: Ptr("group"), + InviteLink: Ptr("https://example.com/a/AdvancedProg"), + InvitationsEnabled: Ptr(true), + Slug: Ptr("advanced-programming"), + StudentsAreRepoAdmins: Ptr(true), + FeedbackPullRequestsEnabled: Ptr(false), + MaxTeams: Ptr(5), + MaxMembers: Ptr(3), + Editor: Ptr("vscode"), + Accepted: Ptr(50), + Submitted: Ptr(25), + Passing: Ptr(20), + Language: Ptr("python"), + Deadline: &referenceTimestamp, + Classroom: &Classroom{ + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + URL: Ptr("https://example.com/classrooms/programming"), + }, + }, + } + + if !cmp.Equal(assignments, want) { + t.Errorf("Classroom.ListClassroomAssignments returned %+v, want %+v", assignments, want) + } + + const methodName = "ListClassroomAssignments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Classroom.ListClassroomAssignments(ctx, -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Classroom.ListClassroomAssignments(ctx, 1296269, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestClassroomService_ListAcceptedAssignments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/assignments/12/accepted_assignments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "2"}) + fmt.Fprint(w, `[ + { + "id": 42, + "submitted": true, + "passing": true, + "commit_count": 5, + "grade": "10/10", + "students": [ + { + "id": 1, + "login": "octocat", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "html_url": "https://github.com/octocat" + } + ], + "repository": { + "id": 1296269, + "full_name": "octocat/Hello-World", + "html_url": "https://github.com/octocat/Hello-World", + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "private": false, + "default_branch": "main" + }, + "assignment": { + "id": 12, + "public_repo": false, + "title": "Intro to Binaries", + "type": "individual", + "invite_link": "https://example.com/a/Lx7jiUgx", + "invitations_enabled": true, + "slug": "intro-to-binaries", + "students_are_repo_admins": false, + "feedback_pull_requests_enabled": true, + "max_teams": 0, + "max_members": 0, + "editor": "codespaces", + "accepted": 100, + "submitted": 40, + "passing": 10, + "language": "ruby", + "deadline": `+referenceTimeStr+`, + "classroom": { + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "url": "https://example.com/classrooms/programming" + } + } + }, + { + "id": 43, + "submitted": false, + "passing": false, + "commit_count": 2, + "grade": "5/10", + "students": [ + { + "id": 2, + "login": "monalisa", + "avatar_url": "https://github.com/images/error/monalisa_happy.gif", + "html_url": "https://github.com/monalisa" + } + ], + "repository": { + "id": 1296270, + "full_name": "monalisa/Hello-World", + "html_url": "https://github.com/monalisa/Hello-World", + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2Mjcw", + "private": true, + "default_branch": "main" + }, + "assignment": { + "id": 12, + "public_repo": false, + "title": "Intro to Binaries", + "type": "individual", + "invite_link": "https://example.com/a/Lx7jiUgx", + "invitations_enabled": true, + "slug": "intro-to-binaries", + "students_are_repo_admins": false, + "feedback_pull_requests_enabled": true, + "max_teams": 0, + "max_members": 0, + "editor": "codespaces", + "accepted": 100, + "submitted": 40, + "passing": 10, + "language": "ruby", + "deadline": `+referenceTimeStr+`, + "classroom": { + "id": 1296269, + "name": "Programming Elixir", + "archived": false, + "url": "https://example.com/classrooms/programming" + } + } + } + ]`) + }) + + ctx := t.Context() + opt := &ListOptions{Page: 2, PerPage: 2} + acceptedAssignments, _, err := client.Classroom.ListAcceptedAssignments(ctx, 12, opt) + if err != nil { + t.Errorf("Classroom.ListAcceptedAssignments returned error: %v", err) + } + + want := []*AcceptedAssignment{ + { + ID: Ptr(int64(42)), + Submitted: Ptr(true), + Passing: Ptr(true), + CommitCount: Ptr(5), + Grade: Ptr("10/10"), + Students: []*ClassroomUser{ + { + ID: Ptr(int64(1)), + Login: Ptr("octocat"), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + HTMLURL: Ptr("https://github.com/octocat"), + }, + }, + Repository: &Repository{ + ID: Ptr(int64(1296269)), + FullName: Ptr("octocat/Hello-World"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World"), + NodeID: Ptr("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"), + Private: Ptr(false), + DefaultBranch: Ptr("main"), + }, + Assignment: &ClassroomAssignment{ + ID: Ptr(int64(12)), + PublicRepo: Ptr(false), + Title: Ptr("Intro to Binaries"), + Type: Ptr("individual"), + InviteLink: Ptr("https://example.com/a/Lx7jiUgx"), + InvitationsEnabled: Ptr(true), + Slug: Ptr("intro-to-binaries"), + StudentsAreRepoAdmins: Ptr(false), + FeedbackPullRequestsEnabled: Ptr(true), + MaxTeams: Ptr(0), + MaxMembers: Ptr(0), + Editor: Ptr("codespaces"), + Accepted: Ptr(100), + Submitted: Ptr(40), + Passing: Ptr(10), + Language: Ptr("ruby"), + Deadline: &referenceTimestamp, + Classroom: &Classroom{ + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + URL: Ptr("https://example.com/classrooms/programming"), + }, + }, + }, + { + ID: Ptr(int64(43)), + Submitted: Ptr(false), + Passing: Ptr(false), + CommitCount: Ptr(2), + Grade: Ptr("5/10"), + Students: []*ClassroomUser{ + { + ID: Ptr(int64(2)), + Login: Ptr("monalisa"), + AvatarURL: Ptr("https://github.com/images/error/monalisa_happy.gif"), + HTMLURL: Ptr("https://github.com/monalisa"), + }, + }, + Repository: &Repository{ + ID: Ptr(int64(1296270)), + FullName: Ptr("monalisa/Hello-World"), + HTMLURL: Ptr("https://github.com/monalisa/Hello-World"), + NodeID: Ptr("MDEwOlJlcG9zaXRvcnkxMjk2Mjcw"), + Private: Ptr(true), + DefaultBranch: Ptr("main"), + }, + Assignment: &ClassroomAssignment{ + ID: Ptr(int64(12)), + PublicRepo: Ptr(false), + Title: Ptr("Intro to Binaries"), + Type: Ptr("individual"), + InviteLink: Ptr("https://example.com/a/Lx7jiUgx"), + InvitationsEnabled: Ptr(true), + Slug: Ptr("intro-to-binaries"), + StudentsAreRepoAdmins: Ptr(false), + FeedbackPullRequestsEnabled: Ptr(true), + MaxTeams: Ptr(0), + MaxMembers: Ptr(0), + Editor: Ptr("codespaces"), + Accepted: Ptr(100), + Submitted: Ptr(40), + Passing: Ptr(10), + Language: Ptr("ruby"), + Deadline: &referenceTimestamp, + Classroom: &Classroom{ + ID: Ptr(int64(1296269)), + Name: Ptr("Programming Elixir"), + Archived: Ptr(false), + URL: Ptr("https://example.com/classrooms/programming"), + }, + }, + }, + } + + if !cmp.Equal(acceptedAssignments, want) { + t.Errorf("Classroom.ListAcceptedAssignments returned %+v, want %+v", acceptedAssignments, want) + } + + const methodName = "ListAcceptedAssignments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Classroom.ListAcceptedAssignments(ctx, -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Classroom.ListAcceptedAssignments(ctx, 12, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestClassroomService_GetAssignmentGrades(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/assignments/12/grades", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "assignment_name": "Intro to Binaries", + "assignment_url": "https://classroom.github.com/assignments/12", + "starter_code_url": "https://github.com/octocat/Hello-World", + "github_username": "octocat", + "roster_identifier": "student123", + "student_repository_name": "octocat/intro-to-binaries", + "student_repository_url": "https://github.com/octocat/intro-to-binaries", + "submission_timestamp": `+referenceTimeStr+`, + "points_awarded": 10, + "points_available": 10, + "group_name": "Team Alpha" + }, + { + "assignment_name": "Intro to Binaries", + "assignment_url": "https://classroom.github.com/assignments/12", + "starter_code_url": "https://github.com/octocat/Hello-World", + "github_username": "monalisa", + "roster_identifier": "student456", + "student_repository_name": "monalisa/intro-to-binaries", + "student_repository_url": "https://github.com/monalisa/intro-to-binaries", + "submission_timestamp": `+referenceTimeStr+`, + "points_awarded": 8, + "points_available": 10, + "group_name": "Team Beta" + } + ]`) + }) + + ctx := t.Context() + grades, _, err := client.Classroom.GetAssignmentGrades(ctx, 12) + if err != nil { + t.Errorf("Classroom.GetAssignmentGrades returned error: %v", err) + } + + want := []*AssignmentGrade{ + { + AssignmentName: Ptr("Intro to Binaries"), + AssignmentURL: Ptr("https://classroom.github.com/assignments/12"), + StarterCodeURL: Ptr("https://github.com/octocat/Hello-World"), + GithubUsername: Ptr("octocat"), + RosterIdentifier: Ptr("student123"), + StudentRepositoryName: Ptr("octocat/intro-to-binaries"), + StudentRepositoryURL: Ptr("https://github.com/octocat/intro-to-binaries"), + SubmissionTimestamp: &referenceTimestamp, + PointsAwarded: Ptr(10), + PointsAvailable: Ptr(10), + GroupName: Ptr("Team Alpha"), + }, + { + AssignmentName: Ptr("Intro to Binaries"), + AssignmentURL: Ptr("https://classroom.github.com/assignments/12"), + StarterCodeURL: Ptr("https://github.com/octocat/Hello-World"), + GithubUsername: Ptr("monalisa"), + RosterIdentifier: Ptr("student456"), + StudentRepositoryName: Ptr("monalisa/intro-to-binaries"), + StudentRepositoryURL: Ptr("https://github.com/monalisa/intro-to-binaries"), + SubmissionTimestamp: &referenceTimestamp, + PointsAwarded: Ptr(8), + PointsAvailable: Ptr(10), + GroupName: Ptr("Team Beta"), + }, + } + + if !cmp.Equal(grades, want) { + t.Errorf("Classroom.GetAssignmentGrades returned %+v, want %+v", grades, want) + } + + const methodName = "GetAssignmentGrades" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Classroom.GetAssignmentGrades(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Classroom.GetAssignmentGrades(ctx, 12) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/code_quality.go b/github/code_quality.go new file mode 100644 index 00000000000..69fa4c44e03 --- /dev/null +++ b/github/code_quality.go @@ -0,0 +1,183 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CodeQualityFindingRule represents the rule associated with a code quality finding. +type CodeQualityFindingRule struct { + ID string `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Help *string `json:"help,omitempty"` + Severity string `json:"severity"` + Category string `json:"category"` +} + +// CodeQualityFindingLocation represents the location of a code quality finding. +type CodeQualityFindingLocation struct { + Path string `json:"path"` + StartLine *int `json:"start_line,omitempty"` + EndLine *int `json:"end_line,omitempty"` + StartColumn *int `json:"start_column,omitempty"` + EndColumn *int `json:"end_column,omitempty"` +} + +// CodeQualityFindingMessage represents the message of a code quality finding. +type CodeQualityFindingMessage struct { + Text string `json:"text"` + Markdown string `json:"markdown"` +} + +// CodeQualityFinding represents a single code quality finding. +type CodeQualityFinding struct { + Number int `json:"number"` + State string `json:"state"` + URL string `json:"url"` + Rule CodeQualityFindingRule `json:"rule"` + Location CodeQualityFindingLocation `json:"location"` + Message CodeQualityFindingMessage `json:"message"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + +// ListCodeQualityFindingsOptions specifies the optional parameters to +// CodeQualityService.ListFindings. +type ListCodeQualityFindingsOptions struct { + State string `url:"state,omitempty"` + Direction string `url:"direction,omitempty"` + + ListCursorOptions +} + +// CodeQualityService handles communication with the code quality related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28 +type CodeQualityService service + +// CodeQualitySetupConfiguration represents a code quality setup configuration for a repository. +type CodeQualitySetupConfiguration struct { + State *string `json:"state,omitempty"` + Languages []string `json:"languages,omitempty"` + RunnerType *string `json:"runner_type,omitempty"` + RunnerLabel *string `json:"runner_label,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Schedule *string `json:"schedule,omitempty"` +} + +// CodeQualityUpdateSetupRequest specifies parameters to the +// CodeQualityService.UpdateSetup method. +type CodeQualityUpdateSetupRequest struct { + State *string `json:"state,omitempty"` + RunnerType *string `json:"runner_type,omitempty"` + RunnerLabel *string `json:"runner_label,omitempty"` + Languages []string `json:"languages,omitempty"` +} + +// CodeQualityUpdateSetupResponse represents a response from updating a code quality setup configuration. +type CodeQualityUpdateSetupResponse struct { + RunID *int64 `json:"run_id,omitempty"` + RunURL *string `json:"run_url,omitempty"` +} + +// GetSetup gets a code quality setup configuration for a repository. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28#get-a-code-quality-setup-configuration +// +//meta:operation GET /repos/{owner}/{repo}/code-quality/setup +func (s *CodeQualityService) GetSetup(ctx context.Context, owner, repo string) (*CodeQualitySetupConfiguration, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-quality/setup", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cfg *CodeQualitySetupConfiguration + resp, err := s.client.Do(req, &cfg) + if err != nil { + return nil, resp, err + } + + return cfg, resp, nil +} + +// UpdateSetup updates a code quality setup configuration for a repository. +// +// This method might return an AcceptedError and a status code of 202. This is because this is the status that GitHub +// returns to signify that it has now scheduled the update in a background task. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28#update-a-code-quality-setup-configuration +// +//meta:operation PATCH /repos/{owner}/{repo}/code-quality/setup +func (s *CodeQualityService) UpdateSetup(ctx context.Context, owner, repo string, body CodeQualityUpdateSetupRequest) (*CodeQualityUpdateSetupResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-quality/setup", owner, repo) + + request, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var result *CodeQualityUpdateSetupResponse + resp, err := s.client.Do(request, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// ListFindings lists code quality findings for a repository. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28#list-code-quality-findings-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-quality/findings +func (s *CodeQualityService) ListFindings(ctx context.Context, owner, repo string, opts *ListCodeQualityFindingsOptions) ([]*CodeQualityFinding, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-quality/findings", owner, repo) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var findings []*CodeQualityFinding + resp, err := s.client.Do(req, &findings) + if err != nil { + return nil, resp, err + } + + return findings, resp, nil +} + +// GetFinding gets a single code quality finding for a repository. +// +// GitHub API docs: https://docs.github.com/rest/code-quality/code-quality?apiVersion=2022-11-28#get-a-code-quality-finding +// +//meta:operation GET /repos/{owner}/{repo}/code-quality/findings/{finding_number} +func (s *CodeQualityService) GetFinding(ctx context.Context, owner, repo string, findingNumber int) (*CodeQualityFinding, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-quality/findings/%v", owner, repo, findingNumber) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var finding *CodeQualityFinding + resp, err := s.client.Do(req, &finding) + if err != nil { + return nil, resp, err + } + + return finding, resp, nil +} diff --git a/github/code_quality_test.go b/github/code_quality_test.go new file mode 100644 index 00000000000..2a90bf85702 --- /dev/null +++ b/github/code_quality_test.go @@ -0,0 +1,394 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodeQualityService_GetSetup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "state": "configured", + "languages": ["javascript-typescript", "python"], + "runner_type": "standard", + "runner_label": null, + "updated_at": `+referenceTimeStr+`, + "schedule": "weekly" + }`) + }) + + ctx := t.Context() + cfg, _, err := client.CodeQuality.GetSetup(ctx, "o", "r") + if err != nil { + t.Fatalf("CodeQuality.GetSetup returned error: %v", err) + } + + want := &CodeQualitySetupConfiguration{ + State: Ptr("configured"), + Languages: []string{"javascript-typescript", "python"}, + RunnerType: Ptr("standard"), + UpdatedAt: &referenceTimestamp, + Schedule: Ptr("weekly"), + } + if diff := cmp.Diff(want, cfg); diff != "" { + t.Errorf("CodeQuality.GetSetup mismatch (-want +got):\n%v", diff) + } + + const methodName = "GetSetup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeQuality.GetSetup(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeQuality.GetSetup(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeQualityService_UpdateSetup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CodeQualityUpdateSetupRequest{ + State: Ptr("configured"), + Languages: []string{"javascript-typescript", "python", "ruby"}, + } + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "run_id": 42, + "run_url": "https://api.github.com/repos/octocat/hello-world/actions/runs/42" + }`) + }) + + ctx := t.Context() + result, _, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", *input) + if err != nil { + t.Fatalf("CodeQuality.UpdateSetup returned error: %v", err) + } + + want := &CodeQualityUpdateSetupResponse{ + RunID: Ptr(int64(42)), + RunURL: Ptr("https://api.github.com/repos/octocat/hello-world/actions/runs/42"), + } + if diff := cmp.Diff(want, result); diff != "" { + t.Errorf("CodeQuality.UpdateSetup mismatch (-want +got):\n%v", diff) + } + + const methodName = "UpdateSetup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeQuality.UpdateSetup(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeQualityService_UpdateSetup_withRunnerLabel(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CodeQualityUpdateSetupRequest{ + State: Ptr("configured"), + RunnerType: Ptr("labeled"), + RunnerLabel: Ptr("my-runner"), + Languages: []string{"go", "python"}, + } + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "run_id": 99, + "run_url": "https://api.github.com/repos/octocat/hello-world/actions/runs/99" + }`) + }) + + ctx := t.Context() + result, _, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", *input) + if err != nil { + t.Fatalf("CodeQuality.UpdateSetup returned error: %v", err) + } + + want := &CodeQualityUpdateSetupResponse{ + RunID: Ptr(int64(99)), + RunURL: Ptr("https://api.github.com/repos/octocat/hello-world/actions/runs/99"), + } + if diff := cmp.Diff(want, result); diff != "" { + t.Errorf("CodeQuality.UpdateSetup mismatch (-want +got):\n%v", diff) + } +} + +func TestCodeQualityService_UpdateSetup_notConfigured(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CodeQualityUpdateSetupRequest{ + State: Ptr("not-configured"), + } + + mux.HandleFunc("/repos/o/r/code-quality/setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{}`) + }) + + ctx := t.Context() + _, _, err := client.CodeQuality.UpdateSetup(ctx, "o", "r", *input) + if err != nil { + t.Fatalf("CodeQuality.UpdateSetup returned error: %v", err) + } +} + +func TestCodeQualityService_GetSetup_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.CodeQuality.GetSetup(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestCodeQualityService_UpdateSetup_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.CodeQuality.UpdateSetup(ctx, "%", "r", CodeQualityUpdateSetupRequest{}) + testURLParseError(t, err) +} + +func TestCodeQualityService_ListFindings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-quality/findings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "open", + "direction": "desc", + }) + fmt.Fprint(w, `[ + { + "number": 1, + "state": "open", + "url": "https://api.github.com/repos/o/r/code-quality/findings/1", + "rule": { + "id": "rule-1", + "title": "Example Rule", + "description": "An example rule description", + "help": "How to fix it", + "severity": "warning", + "category": "maintainability" + }, + "location": { + "path": "src/main.go", + "start_line": 10, + "end_line": 10, + "start_column": 1, + "end_column": 20 + }, + "message": { + "text": "Issue found", + "markdown": "**Issue found**" + }, + "created_at": `+referenceTimeStr+` + } + ]`) + }) + + ctx := t.Context() + opts := &ListCodeQualityFindingsOptions{ + State: "open", + Direction: "desc", + } + findings, _, err := client.CodeQuality.ListFindings(ctx, "o", "r", opts) + if err != nil { + t.Fatalf("CodeQuality.ListFindings returned error: %v", err) + } + + want := []*CodeQualityFinding{ + { + Number: 1, + State: "open", + URL: "https://api.github.com/repos/o/r/code-quality/findings/1", + Rule: CodeQualityFindingRule{ + ID: "rule-1", + Title: "Example Rule", + Description: "An example rule description", + Help: Ptr("How to fix it"), + Severity: "warning", + Category: "maintainability", + }, + Location: CodeQualityFindingLocation{ + Path: "src/main.go", + StartLine: Ptr(10), + EndLine: Ptr(10), + StartColumn: Ptr(1), + EndColumn: Ptr(20), + }, + Message: CodeQualityFindingMessage{ + Text: "Issue found", + Markdown: "**Issue found**", + }, + CreatedAt: &referenceTimestamp, + }, + } + if diff := cmp.Diff(want, findings); diff != "" { + t.Errorf("CodeQuality.ListFindings mismatch (-want +got):\n%v", diff) + } + + const methodName = "ListFindings" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeQuality.ListFindings(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeQuality.ListFindings(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeQualityService_ListFindings_noOpts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-quality/findings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[]`) + }) + + ctx := t.Context() + findings, _, err := client.CodeQuality.ListFindings(ctx, "o", "r", nil) + if err != nil { + t.Fatalf("CodeQuality.ListFindings returned error: %v", err) + } + + if len(findings) != 0 { + t.Errorf("CodeQuality.ListFindings returned %v findings, want 0", len(findings)) + } +} + +func TestCodeQualityService_GetFinding(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-quality/findings/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "number": 1, + "state": "open", + "url": "https://api.github.com/repos/o/r/code-quality/findings/1", + "rule": { + "id": "rule-1", + "title": "Example Rule", + "description": "An example rule description", + "severity": "error", + "category": "reliability" + }, + "location": { + "path": "src/main.go", + "start_line": 5, + "end_line": 5 + }, + "message": { + "text": "Critical issue", + "markdown": "**Critical issue**" + }, + "created_at": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + finding, _, err := client.CodeQuality.GetFinding(ctx, "o", "r", 1) + if err != nil { + t.Fatalf("CodeQuality.GetFinding returned error: %v", err) + } + + want := &CodeQualityFinding{ + Number: 1, + State: "open", + URL: "https://api.github.com/repos/o/r/code-quality/findings/1", + Rule: CodeQualityFindingRule{ + ID: "rule-1", + Title: "Example Rule", + Description: "An example rule description", + Severity: "error", + Category: "reliability", + }, + Location: CodeQualityFindingLocation{ + Path: "src/main.go", + StartLine: Ptr(5), + EndLine: Ptr(5), + }, + Message: CodeQualityFindingMessage{ + Text: "Critical issue", + Markdown: "**Critical issue**", + }, + CreatedAt: &referenceTimestamp, + } + if diff := cmp.Diff(want, finding); diff != "" { + t.Errorf("CodeQuality.GetFinding mismatch (-want +got):\n%v", diff) + } + + const methodName = "GetFinding" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeQuality.GetFinding(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeQuality.GetFinding(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeQualityService_ListFindings_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.CodeQuality.ListFindings(ctx, "%", "r", nil) + testURLParseError(t, err) +} + +func TestCodeQualityService_GetFinding_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.CodeQuality.GetFinding(ctx, "%", "r", 1) + testURLParseError(t, err) +} diff --git a/github/code_scanning.go b/github/code_scanning.go new file mode 100644 index 00000000000..36f42357733 --- /dev/null +++ b/github/code_scanning.go @@ -0,0 +1,693 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" +) + +// CodeScanningService handles communication with the code scanning related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type CodeScanningService service + +// Rule represents the complete details of GitHub Code Scanning alert type. +type Rule struct { + ID *string `json:"id,omitempty"` + Severity *string `json:"severity,omitempty"` + Description *string `json:"description,omitempty"` + Name *string `json:"name,omitempty"` + SecuritySeverityLevel *string `json:"security_severity_level,omitempty"` + FullDescription *string `json:"full_description,omitempty"` + Tags []string `json:"tags,omitempty"` + Help *string `json:"help,omitempty"` +} + +// Location represents the exact location of the GitHub Code Scanning Alert in the scanned project. +type Location struct { + Path *string `json:"path,omitempty"` + StartLine *int `json:"start_line,omitempty"` + EndLine *int `json:"end_line,omitempty"` + StartColumn *int `json:"start_column,omitempty"` + EndColumn *int `json:"end_column,omitempty"` +} + +// Message is a part of MostRecentInstance struct which provides the appropriate message when any action is performed on the analysis object. +type Message struct { + Text *string `json:"text,omitempty"` +} + +// MostRecentInstance provides details of the most recent instance of this alert for the default branch or for the specified Git reference. +type MostRecentInstance struct { + Ref *string `json:"ref,omitempty"` + AnalysisKey *string `json:"analysis_key,omitempty"` + Category *string `json:"category,omitempty"` + Environment *string `json:"environment,omitempty"` + State *string `json:"state,omitempty"` + CommitSHA *string `json:"commit_sha,omitempty"` + Message *Message `json:"message,omitempty"` + Location *Location `json:"location,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Classifications []string `json:"classifications,omitempty"` +} + +// Tool represents the tool used to generate a GitHub Code Scanning Alert. +type Tool struct { + Name *string `json:"name,omitempty"` + GUID *string `json:"guid,omitempty"` + Version *string `json:"version,omitempty"` +} + +// Alert represents an individual GitHub Code Scanning Alert on a single repository. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type Alert struct { + Number *int `json:"number,omitempty"` + Repository *Repository `json:"repository,omitempty"` + RuleID *string `json:"rule_id,omitempty"` + RuleSeverity *string `json:"rule_severity,omitempty"` + RuleDescription *string `json:"rule_description,omitempty"` + Rule *Rule `json:"rule,omitempty"` + Tool *Tool `json:"tool,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + FixedAt *Timestamp `json:"fixed_at,omitempty"` + State *string `json:"state,omitempty"` + ClosedBy *User `json:"closed_by,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + MostRecentInstance *MostRecentInstance `json:"most_recent_instance,omitempty"` + Instances []*MostRecentInstance `json:"instances,omitempty"` + DismissedBy *User `json:"dismissed_by,omitempty"` + DismissedAt *Timestamp `json:"dismissed_at,omitempty"` + DismissedReason *string `json:"dismissed_reason,omitempty"` + DismissedComment *string `json:"dismissed_comment,omitempty"` + InstancesURL *string `json:"instances_url,omitempty"` +} + +// ID returns the ID associated with an alert. It is the number at the end of the security alert's URL. +func (a *Alert) ID() int64 { + if a == nil { + return 0 + } + + s := a.GetHTMLURL() + + // Check for an ID to parse at the end of the url + if i := strings.LastIndex(s, "/"); i >= 0 { + s = s[i+1:] + } + + // Return the alert ID as a 64-bit integer. Unable to convert or out of range returns 0. + id, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return 0 + } + + return id +} + +// AlertInstancesListOptions specifies optional parameters to the CodeScanningService.ListAlertInstances method. +type AlertInstancesListOptions struct { + // Return code scanning alert instances for a specific branch reference. + // The ref can be formatted as refs/heads/ or simply . To reference a pull request use refs/pull//merge + Ref string `url:"ref,omitempty"` + + ListOptions +} + +// AlertListOptions specifies optional parameters to the CodeScanningService.ListAlerts method. +type AlertListOptions struct { + // State of the code scanning alerts to list. Set to closed to list only closed code scanning alerts. Default: open + State string `url:"state,omitempty"` + + // Return code scanning alerts for a specific branch reference. + // The ref can be formatted as refs/heads/ or simply . To reference a pull request use refs/pull//merge + Ref string `url:"ref,omitempty"` + + // If specified, only code scanning alerts with this severity will be returned. Possible values are: critical, high, medium, low, warning, note, error. + Severity string `url:"severity,omitempty"` + + // The name of a code scanning tool. Only results by this tool will be listed. + ToolName string `url:"tool_name,omitempty"` + + // The GUID of a code scanning tool. Only results by this tool will be listed. + ToolGUID string `url:"tool_guid,omitempty"` + + // The direction to sort the results by. Possible values are: asc, desc. Default: desc. + Direction string `url:"direction,omitempty"` + + // The property by which to sort the results. Possible values are: created, updated. Default: created. + Sort string `url:"sort,omitempty"` + + ListCursorOptions + + // Add ListOptions so offset pagination with integer type "page" query parameter is accepted + // since ListCursorOptions accepts "page" as string only. + ListOptions +} + +// AnalysesListOptions specifies optional parameters to the CodeScanningService.ListAnalysesForRepo method. +type AnalysesListOptions struct { + // Return code scanning analyses belonging to the same SARIF upload. + SarifID *string `url:"sarif_id,omitempty"` + + // Return code scanning analyses for a specific branch reference. + // The ref can be formatted as refs/heads/ or simply . To reference a pull request use refs/pull//merge + Ref *string `url:"ref,omitempty"` + + ListOptions +} + +// CodeQLDatabase represents a metadata about the CodeQL database. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type CodeQLDatabase struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Language *string `json:"language,omitempty"` + Uploader *User `json:"uploader,omitempty"` + ContentType *string `json:"content_type,omitempty"` + Size *int64 `json:"size,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + URL *string `json:"url,omitempty"` +} + +// ScanningAnalysis represents an individual GitHub Code Scanning ScanningAnalysis on a single repository. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type ScanningAnalysis struct { + ID *int64 `json:"id,omitempty"` + Ref *string `json:"ref,omitempty"` + CommitSHA *string `json:"commit_sha,omitempty"` + AnalysisKey *string `json:"analysis_key,omitempty"` + Environment *string `json:"environment,omitempty"` + Error *string `json:"error,omitempty"` + Category *string `json:"category,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ResultsCount *int `json:"results_count,omitempty"` + RulesCount *int `json:"rules_count,omitempty"` + URL *string `json:"url,omitempty"` + SarifID *string `json:"sarif_id,omitempty"` + Tool *Tool `json:"tool,omitempty"` + Deletable *bool `json:"deletable,omitempty"` + Warning *string `json:"warning,omitempty"` +} + +// SarifAnalysis specifies the results of a code scanning job. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type SarifAnalysis struct { + CommitSHA string `json:"commit_sha"` + Ref string `json:"ref"` + Sarif string `json:"sarif"` + CheckoutURI *string `json:"checkout_uri,omitempty"` + StartedAt *Timestamp `json:"started_at,omitempty"` + ToolName *string `json:"tool_name,omitempty"` + Validate *bool `json:"validate,omitempty"` +} + +// CodeScanningAlertState specifies the state of a code scanning alert. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type CodeScanningAlertState struct { + // State sets the state of the code scanning alert and is a required field. + // You must also provide DismissedReason when you set the state to "dismissed". + // State can be one of: "open", "dismissed". + State string `json:"state"` + // DismissedReason represents the reason for dismissing or closing the alert. + // It is required when the state is "dismissed". + // It can be one of: "false positive", "won't fix", "used in tests". + DismissedReason *string `json:"dismissed_reason,omitempty"` + // DismissedComment is associated with the dismissal of the alert. + DismissedComment *string `json:"dismissed_comment,omitempty"` +} + +// SarifID identifies a sarif analysis upload. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28 +type SarifID struct { + ID *string `json:"id,omitempty"` + URL *string `json:"url,omitempty"` +} + +// ListAlertsForOrg lists code scanning alerts for an org. +// +// You must use an access token with the security_events scope to use this endpoint. GitHub Apps must have the security_events +// read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-code-scanning-alerts-for-an-organization +// +//meta:operation GET /orgs/{org}/code-scanning/alerts +func (s *CodeScanningService) ListAlertsForOrg(ctx context.Context, org string, opts *AlertListOptions) ([]*Alert, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-scanning/alerts", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*Alert + resp, err := s.client.Do(req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + +// ListAlertsForRepo lists code scanning alerts for a repository. +// +// Lists all open code scanning alerts for the default branch (usually master) and protected branches in a repository. +// You must use an access token with the security_events scope to use this endpoint. GitHub Apps must have the security_events +// read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-code-scanning-alerts-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/alerts +func (s *CodeScanningService) ListAlertsForRepo(ctx context.Context, owner, repo string, opts *AlertListOptions) ([]*Alert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*Alert + resp, err := s.client.Do(req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + +// GetAlert gets a single code scanning alert for a repository. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// The security alert_id is the number at the end of the security alert's URL. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#get-a-code-scanning-alert +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number} +func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string, id int64) (*Alert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var a *Alert + resp, err := s.client.Do(req, &a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// UpdateAlert updates the state of a single code scanning alert for a repository. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// The security alert_id is the number at the end of the security alert's URL. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-alert +// +//meta:operation PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number} +func (s *CodeScanningService) UpdateAlert(ctx context.Context, owner, repo string, id int64, body *CodeScanningAlertState) (*Alert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var a *Alert + resp, err := s.client.Do(req, &a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} + +// ListAlertInstances lists instances of a code scanning alert. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-instances-of-a-code-scanning-alert +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances +func (s *CodeScanningService) ListAlertInstances(ctx context.Context, owner, repo string, id int64, opts *AlertInstancesListOptions) ([]*MostRecentInstance, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v/instances", owner, repo, id) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alertInstances []*MostRecentInstance + resp, err := s.client.Do(req, &alertInstances) + if err != nil { + return nil, resp, err + } + + return alertInstances, resp, nil +} + +// UploadSarif uploads the result of code scanning job to GitHub. +// +// For the parameter sarif, you must first compress your SARIF file using gzip and then translate the contents of the file into a Base64 encoding string. +// You must use an access token with the security_events scope to use this endpoint. GitHub Apps must have the security_events +// write permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#upload-an-analysis-as-sarif-data +// +//meta:operation POST /repos/{owner}/{repo}/code-scanning/sarifs +func (s *CodeScanningService) UploadSarif(ctx context.Context, owner, repo string, body SarifAnalysis) (*SarifID, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/sarifs", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + // This will always return an error without unmarshaling the data + resp, err := s.client.Do(req, nil) + // Even though there was an error, we still return the response + // in case the caller wants to inspect it further. + // However, if the error is AcceptedError, decode it below before + // returning from this function and closing the response body. + var acceptedError *AcceptedError + if !errors.As(err, &acceptedError) { + return nil, resp, err + } + var sarifID *SarifID + decErr := json.Unmarshal(acceptedError.Raw, &sarifID) + if decErr != nil { + return nil, resp, decErr + } + + return sarifID, resp, nil +} + +// SARIFUpload represents information about a SARIF upload. +type SARIFUpload struct { + // `pending` files have not yet been processed, while `complete` means results from the SARIF have been stored. + // `failed` files have either not been processed at all, or could only be partially processed. + ProcessingStatus *string `json:"processing_status,omitempty"` + // The REST API URL for getting the analyses associated with the upload. + AnalysesURL *string `json:"analyses_url,omitempty"` +} + +// GetSARIF gets information about a SARIF upload. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#get-information-about-a-sarif-upload +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id} +func (s *CodeScanningService) GetSARIF(ctx context.Context, owner, repo, sarifID string) (*SARIFUpload, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/sarifs/%v", owner, repo, sarifID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var sarifUpload *SARIFUpload + resp, err := s.client.Do(req, &sarifUpload) + if err != nil { + return nil, resp, err + } + + return sarifUpload, resp, nil +} + +// ListAnalysesForRepo lists code scanning analyses for a repository. +// +// Lists the details of all code scanning analyses for a repository, starting with the most recent. +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-code-scanning-analyses-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/analyses +func (s *CodeScanningService) ListAnalysesForRepo(ctx context.Context, owner, repo string, opts *AnalysesListOptions) ([]*ScanningAnalysis, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/analyses", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var analyses []*ScanningAnalysis + resp, err := s.client.Do(req, &analyses) + if err != nil { + return nil, resp, err + } + + return analyses, resp, nil +} + +// GetAnalysis gets a single code scanning analysis for a repository. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// The security analysis_id is the ID of the analysis, as returned from the ListAnalysesForRepo operation. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#get-a-code-scanning-analysis-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id} +func (s *CodeScanningService) GetAnalysis(ctx context.Context, owner, repo string, id int64) (*ScanningAnalysis, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/analyses/%v", owner, repo, id) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var analysis *ScanningAnalysis + resp, err := s.client.Do(req, &analysis) + if err != nil { + return nil, resp, err + } + + return analysis, resp, nil +} + +// DeleteAnalysis represents a successful deletion of a code scanning analysis. +type DeleteAnalysis struct { + // Next deletable analysis in chain, without last analysis deletion confirmation + NextAnalysisURL *string `json:"next_analysis_url,omitempty"` + // Next deletable analysis in chain, with last analysis deletion confirmation + ConfirmDeleteURL *string `json:"confirm_delete_url,omitempty"` +} + +// DeleteAnalysis deletes a single code scanning analysis from a repository. +// +// You must use an access token with the repo scope to use this endpoint. +// GitHub Apps must have the security_events read permission to use this endpoint. +// +// The security analysis_id is the ID of the analysis, as returned from the ListAnalysesForRepo operation. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#delete-a-code-scanning-analysis-from-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id} +func (s *CodeScanningService) DeleteAnalysis(ctx context.Context, owner, repo string, id int64) (*DeleteAnalysis, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/analyses/%v", owner, repo, id) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + var deleteAnalysis *DeleteAnalysis + resp, err := s.client.Do(req, &deleteAnalysis) + if err != nil { + return nil, resp, err + } + + return deleteAnalysis, resp, nil +} + +// ListCodeQLDatabases lists the CodeQL databases that are available in a repository. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the contents read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-codeql-databases-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/codeql/databases +func (s *CodeScanningService) ListCodeQLDatabases(ctx context.Context, owner, repo string) ([]*CodeQLDatabase, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/codeql/databases", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codeqlDatabases []*CodeQLDatabase + resp, err := s.client.Do(req, &codeqlDatabases) + if err != nil { + return nil, resp, err + } + + return codeqlDatabases, resp, nil +} + +// GetCodeQLDatabase gets a CodeQL database for a language in a repository. +// +// You must use an access token with the security_events scope to use this endpoint. +// GitHub Apps must have the contents read permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#get-a-codeql-database-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/codeql/databases/{language} +func (s *CodeScanningService) GetCodeQLDatabase(ctx context.Context, owner, repo, language string) (*CodeQLDatabase, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/codeql/databases/%v", owner, repo, language) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codeqlDatabase *CodeQLDatabase + resp, err := s.client.Do(req, &codeqlDatabase) + if err != nil { + return nil, resp, err + } + + return codeqlDatabase, resp, nil +} + +// DeleteCodeQLDatabase deletes a CodeQL database for a language in a repository. +// +// You must use an access token with the repo scope to use this +// endpoint with private repos or the public_repo scope for public repos. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#delete-a-codeql-database +// +//meta:operation DELETE /repos/{owner}/{repo}/code-scanning/codeql/databases/{language} +func (s *CodeScanningService) DeleteCodeQLDatabase(ctx context.Context, owner, repo, language string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/codeql/databases/%v", owner, repo, language) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DefaultSetupConfiguration represents a code scanning default setup configuration. +type DefaultSetupConfiguration struct { + State *string `json:"state,omitempty"` + Languages []string `json:"languages,omitempty"` + QuerySuite *string `json:"query_suite,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// GetDefaultSetupConfiguration gets a code scanning default setup configuration. +// +// You must use an access token with the repo scope to use this +// endpoint with private repos or the public_repo scope for public repos. GitHub Apps must have the repo write +// permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#get-a-code-scanning-default-setup-configuration +// +//meta:operation GET /repos/{owner}/{repo}/code-scanning/default-setup +func (s *CodeScanningService) GetDefaultSetupConfiguration(ctx context.Context, owner, repo string) (*DefaultSetupConfiguration, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/default-setup", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var cfg *DefaultSetupConfiguration + resp, err := s.client.Do(req, &cfg) + if err != nil { + return nil, resp, err + } + + return cfg, resp, nil +} + +// UpdateDefaultSetupConfigurationOptions specifies parameters to the CodeScanningService.UpdateDefaultSetupConfiguration +// method. +type UpdateDefaultSetupConfigurationOptions struct { + State string `json:"state"` + QuerySuite *string `json:"query_suite,omitempty"` + Languages []string `json:"languages,omitempty"` +} + +// UpdateDefaultSetupConfigurationResponse represents a response from updating a code scanning default setup configuration. +type UpdateDefaultSetupConfigurationResponse struct { + RunID *int64 `json:"run_id,omitempty"` + RunURL *string `json:"run_url,omitempty"` +} + +// UpdateDefaultSetupConfiguration updates a code scanning default setup configuration. +// +// You must use an access token with the repo scope to use this +// endpoint with private repos or the public_repo scope for public repos. GitHub Apps must have the repo write +// permission to use this endpoint. +// +// This method might return an AcceptedError and a status code of 202. This is because this is the status that GitHub +// returns to signify that it has now scheduled the update of the pull request branch in a background task. +// +// GitHub API docs: https://docs.github.com/rest/code-scanning/code-scanning?apiVersion=2022-11-28#update-a-code-scanning-default-setup-configuration +// +//meta:operation PATCH /repos/{owner}/{repo}/code-scanning/default-setup +func (s *CodeScanningService) UpdateDefaultSetupConfiguration(ctx context.Context, owner, repo string, body *UpdateDefaultSetupConfigurationOptions) (*UpdateDefaultSetupConfigurationResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-scanning/default-setup", owner, repo) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var a *UpdateDefaultSetupConfigurationResponse + resp, err := s.client.Do(req, &a) + if err != nil { + return nil, resp, err + } + + return a, resp, nil +} diff --git a/github/code_scanning_test.go b/github/code_scanning_test.go new file mode 100644 index 00000000000..bc1321db4e2 --- /dev/null +++ b/github/code_scanning_test.go @@ -0,0 +1,1521 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodeScanningService_Alert_ID(t *testing.T) { + t.Parallel() + // Test: nil Alert ID == 0 + var a *Alert + id := a.ID() + var want int64 + if id != want { + t.Errorf("Alert.ID error returned %+v, want %+v", id, want) + } + + // Test: Valid HTMLURL + a = &Alert{ + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/88"), + } + id = a.ID() + want = 88 + if !cmp.Equal(id, want) { + t.Errorf("Alert.ID error returned %+v, want %+v", id, want) + } + + // Test: HTMLURL is nil + a = &Alert{} + id = a.ID() + want = 0 + if !cmp.Equal(id, want) { + t.Errorf("Alert.ID error returned %+v, want %+v", id, want) + } + + // Test: ID can't be parsed as an int + a = &Alert{ + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/bad88"), + } + id = a.ID() + want = 0 + if !cmp.Equal(id, want) { + t.Errorf("Alert.ID error returned %+v, want %+v", id, want) + } +} + +func TestCodeScanningService_UploadSarif(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + expectedSarifID := &SarifID{ + ID: Ptr("testid"), + URL: Ptr("https://example.com/testurl"), + } + + sarifAnalysis := SarifAnalysis{CommitSHA: "abc", Ref: "ref/head/main", Sarif: "abc", CheckoutURI: Ptr("uri"), StartedAt: &referenceTimestamp, ToolName: Ptr("codeql-cli"), Validate: Ptr(true)} + + mux.HandleFunc("/repos/o/r/code-scanning/sarifs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, sarifAnalysis) + w.WriteHeader(http.StatusAccepted) + respBody, _ := json.Marshal(expectedSarifID) + _, _ = w.Write(respBody) + }) + + ctx := t.Context() + respSarifID, _, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis) + if err != nil { + t.Errorf("CodeScanning.UploadSarif returned error: %v", err) + } + if !cmp.Equal(expectedSarifID, respSarifID) { + t.Errorf("Sarif response = %+v, want %+v", respSarifID, expectedSarifID) + } + + const methodName = "UploadSarif" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.UploadSarif(ctx, "\n", "\n", sarifAnalysis) + return err + }) + + testNewRequestAndDoFailureCategory(t, methodName, client, CodeScanningUploadCategory, func() (*Response, error) { + _, resp, err := client.CodeScanning.UploadSarif(ctx, "o", "r", sarifAnalysis) + return resp, err + }) +} + +func TestCodeScanningService_GetSARIF(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/sarifs/abc", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "processing_status": "s", + "analyses_url": "u" + }`) + }) + + ctx := t.Context() + sarifUpload, _, err := client.CodeScanning.GetSARIF(ctx, "o", "r", "abc") + if err != nil { + t.Errorf("CodeScanning.GetSARIF returned error: %v", err) + } + + want := &SARIFUpload{ + ProcessingStatus: Ptr("s"), + AnalysesURL: Ptr("u"), + } + if !cmp.Equal(sarifUpload, want) { + t.Errorf("CodeScanning.GetSARIF returned %+v, want %+v", sarifUpload, want) + } + + const methodName = "GetSARIF" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.GetSARIF(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.GetSARIF(ctx, "o", "r", "abc") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_ListAlertsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "tool_guid": "guid", "direction": "asc", "sort": "updated"}) + fmt.Fprint(w, `[{ + "repository": { + "id": 1, + "name": "n", + "url": "url" + }, + "rule_id":"js/trivial-conditional", + "rule_severity":"warning", + "rule_description":"Useless conditional", + "tool": { + "name": "CodeQL", + "guid": "guid", + "version": "1.4.0" + }, + "rule": { + "id": "js/trivial-conditional", + "severity": "warning", + "description": "Useless conditional", + "name": "js/trivial-conditional", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", + "html_url":"https://github.com/o/r/security/code-scanning/25" + }, + { + "rule_id":"js/useless-expression", + "rule_severity":"warning", + "rule_description":"Expression has no effect", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "rule": { + "id": "js/useless-expression", + "severity": "warning", + "description": "Expression has no effect", + "name": "js/useless-expression", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", + "html_url":"https://github.com/o/r/security/code-scanning/88" + }]`) + }) + + opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ToolGUID: "guid", Direction: "asc", Sort: "updated"} + ctx := t.Context() + alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err) + } + + want := []*Alert{ + { + Repository: &Repository{ + ID: Ptr(int64(1)), + URL: Ptr("url"), + Name: Ptr("n"), + }, + RuleID: Ptr("js/trivial-conditional"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Useless conditional"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: Ptr("guid"), Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("js/trivial-conditional"), + Severity: Ptr("warning"), + Description: Ptr("Useless conditional"), + Name: Ptr("js/trivial-conditional"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: Ptr("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/25"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/25"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + }, + { + RuleID: Ptr("js/useless-expression"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Expression has no effect"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("js/useless-expression"), + Severity: Ptr("warning"), + Description: Ptr("Expression has no effect"), + Name: Ptr("js/useless-expression"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: Ptr("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/88"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + }, + } + if !cmp.Equal(alerts, want) { + t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_ListAlertsForOrgLisCursorOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "per_page": "1", "before": "deadbeefb", "after": "deadbeefa"}) + fmt.Fprint(w, `[{ + "repository": { + "id": 1, + "name": "n", + "url": "url" + }, + "rule_id":"js/trivial-conditional", + "rule_severity":"warning", + "rule_description":"Useless conditional", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "rule": { + "id": "js/trivial-conditional", + "severity": "warning", + "description": "Useless conditional", + "name": "js/trivial-conditional", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", + "html_url":"https://github.com/o/r/security/code-scanning/25" + }]`) + }) + + opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ListCursorOptions: ListCursorOptions{PerPage: 1, Before: "deadbeefb", After: "deadbeefa"}} + ctx := t.Context() + alerts, _, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("CodeScanning.ListAlertsForOrg returned error: %v", err) + } + + want := []*Alert{ + { + Repository: &Repository{ + ID: Ptr(int64(1)), + URL: Ptr("url"), + Name: Ptr("n"), + }, + RuleID: Ptr("js/trivial-conditional"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Useless conditional"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("js/trivial-conditional"), + Severity: Ptr("warning"), + Description: Ptr("Useless conditional"), + Name: Ptr("js/trivial-conditional"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: Ptr("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/25"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/25"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + }, + } + if !cmp.Equal(alerts, want) { + t.Errorf("CodeScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListAlertsForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListAlertsForOrg(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_ListAlertsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "ref": "heads/master", "severity": "warning", "tool_name": "CodeQL", "tool_guid": "guid", "direction": "asc", "sort": "updated"}) + fmt.Fprint(w, `[{ + "rule_id":"js/trivial-conditional", + "rule_severity":"warning", + "rule_description":"Useless conditional", + "tool": { + "name": "CodeQL", + "guid": "guid", + "version": "1.4.0" + }, + "rule": { + "id": "js/trivial-conditional", + "severity": "warning", + "description": "Useless conditional", + "name": "js/trivial-conditional", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/25", + "html_url":"https://github.com/o/r/security/code-scanning/25" + }, + { + "rule_id":"js/useless-expression", + "rule_severity":"warning", + "rule_description":"Expression has no effect", + "tool": { + "name": "CodeQL", + "guid": "guid", + "version": "1.4.0" + }, + "rule": { + "id": "js/useless-expression", + "severity": "warning", + "description": "Expression has no effect", + "name": "js/useless-expression", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", + "html_url":"https://github.com/o/r/security/code-scanning/88" + }]`) + }) + + opts := &AlertListOptions{State: "open", Ref: "heads/master", Severity: "warning", ToolName: "CodeQL", ToolGUID: "guid", Direction: "asc", Sort: "updated"} + ctx := t.Context() + alerts, _, err := client.CodeScanning.ListAlertsForRepo(ctx, "o", "r", opts) + if err != nil { + t.Errorf("CodeScanning.ListAlertsForRepo returned error: %v", err) + } + + want := []*Alert{ + { + RuleID: Ptr("js/trivial-conditional"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Useless conditional"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: Ptr("guid"), Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("js/trivial-conditional"), + Severity: Ptr("warning"), + Description: Ptr("Useless conditional"), + Name: Ptr("js/trivial-conditional"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: Ptr("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/25"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/25"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + }, + { + RuleID: Ptr("js/useless-expression"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Expression has no effect"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: Ptr("guid"), Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("js/useless-expression"), + Severity: Ptr("warning"), + Description: Ptr("Expression has no effect"), + Name: Ptr("js/useless-expression"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: Ptr("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/88"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + }, + } + if !cmp.Equal(alerts, want) { + t.Errorf("CodeScanning.ListAlertsForRepo returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListAlertsForRepo(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_UpdateAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"rule_id":"js/useless-expression", + "rule_severity":"warning", + "rule_description":"Expression has no effect", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "rule": { + "id": "useless expression", + "severity": "warning", + "description": "Expression has no effect", + "name": "useless expression", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "dismissed", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"dismissed", + "dismissed_reason": "false positive", + "dismissed_comment": "This alert is not actually correct as sanitizer is used", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", + "html_url":"https://github.com/o/r/security/code-scanning/88"}`) + }) + + ctx := t.Context() + dismissedComment := Ptr("This alert is not actually correct as sanitizer is used") + dismissedReason := Ptr("false positive") + state := Ptr("dismissed") + stateInfo := &CodeScanningAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment} + alert, _, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo) + if err != nil { + t.Errorf("CodeScanning.UpdateAlert returned error: %v", err) + } + + want := &Alert{ + RuleID: Ptr("js/useless-expression"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Expression has no effect"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("useless expression"), + Severity: Ptr("warning"), + Description: Ptr("Expression has no effect"), + Name: Ptr("useless expression"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: state, + DismissedReason: dismissedReason, + DismissedComment: dismissedComment, + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/88"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("dismissed"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + } + if !cmp.Equal(alert, want) { + t.Errorf("CodeScanning.UpdateAlert returned %+v, want %+v", alert, want) + } + + const methodName = "UpdateAlert" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.UpdateAlert(ctx, "\n", "\n", -88, stateInfo) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.UpdateAlert(ctx, "o", "r", 88, stateInfo) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_ListAlertInstances(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/alerts/88/instances", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "ref": "refs/heads/main", + "analysis_key": ".github/workflows/codeql-analysis.yml:analyze", + "environment": "", + "category": ".github/workflows/codeql-analysis.yml:analyze", + "state": "open", + "fixed_at": null, + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + } + ]`) + }) + + opts := &AlertInstancesListOptions{Ref: "heads/main", ListOptions: ListOptions{Page: 1}} + ctx := t.Context() + instances, _, err := client.CodeScanning.ListAlertInstances(ctx, "o", "r", 88, opts) + if err != nil { + t.Errorf("CodeScanning.ListAlertInstances returned error: %v", err) + } + + want := []*MostRecentInstance{ + { + Ref: Ptr("refs/heads/main"), + AnalysisKey: Ptr(".github/workflows/codeql-analysis.yml:analyze"), + Category: Ptr(".github/workflows/codeql-analysis.yml:analyze"), + Environment: Ptr(""), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + } + if !cmp.Equal(instances, want) { + t.Errorf("CodeScanning.ListAlertInstances returned %+v, want %+v", instances, want) + } + + const methodName = "ListAlertInstances" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListAlertInstances(ctx, "\n", "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListAlertInstances(ctx, "o", "r", 88, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_GetAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/alerts/88", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "rule_id":"js/useless-expression", + "rule_severity":"warning", + "rule_description":"Expression has no effect", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "1.4.0" + }, + "rule": { + "id": "useless expression", + "severity": "warning", + "description": "Expression has no effect", + "name": "useless expression", + "full_description": "Expression has no effect", + "help": "Expression has no effect" + }, + "most_recent_instance": { + "ref": "refs/heads/main", + "state": "open", + "commit_sha": "abcdefg12345", + "message": { + "text": "This path depends on a user-provided value." + }, + "location": { + "path": "spec-main/api-session-spec.ts", + "start_line": 917, + "end_line": 917, + "start_column": 7, + "end_column": 18 + }, + "classifications": [ + "test" + ] + }, + "created_at":`+referenceTimeStr+`, + "state":"open", + "closed_by":null, + "closed_at":null, + "url":"https://api.github.com/repos/o/r/code-scanning/alerts/88", + "html_url":"https://github.com/o/r/security/code-scanning/88" + }`) + }) + + ctx := t.Context() + alert, _, err := client.CodeScanning.GetAlert(ctx, "o", "r", 88) + if err != nil { + t.Errorf("CodeScanning.GetAlert returned error: %v", err) + } + + want := &Alert{ + RuleID: Ptr("js/useless-expression"), + RuleSeverity: Ptr("warning"), + RuleDescription: Ptr("Expression has no effect"), + Tool: &Tool{Name: Ptr("CodeQL"), GUID: nil, Version: Ptr("1.4.0")}, + Rule: &Rule{ + ID: Ptr("useless expression"), + Severity: Ptr("warning"), + Description: Ptr("Expression has no effect"), + Name: Ptr("useless expression"), + FullDescription: Ptr("Expression has no effect"), + Help: Ptr("Expression has no effect"), + }, + CreatedAt: &referenceTimestamp, + State: Ptr("open"), + ClosedBy: nil, + ClosedAt: nil, + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/alerts/88"), + HTMLURL: Ptr("https://github.com/o/r/security/code-scanning/88"), + MostRecentInstance: &MostRecentInstance{ + Ref: Ptr("refs/heads/main"), + State: Ptr("open"), + CommitSHA: Ptr("abcdefg12345"), + Message: &Message{ + Text: Ptr("This path depends on a user-provided value."), + }, + Location: &Location{ + Path: Ptr("spec-main/api-session-spec.ts"), + StartLine: Ptr(917), + EndLine: Ptr(917), + StartColumn: Ptr(7), + EndColumn: Ptr(18), + }, + Classifications: []string{"test"}, + }, + } + if !cmp.Equal(alert, want) { + t.Errorf("CodeScanning.GetAlert returned %+v, want %+v", alert, want) + } + + const methodName = "GetAlert" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.GetAlert(ctx, "\n", "\n", -88) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.GetAlert(ctx, "o", "r", 88) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_ListAnalysesForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/analyses", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", "ref": "heads/master"}) + fmt.Fprint(w, `[ + { + "ref": "refs/heads/main", + "commit_sha": "d99612c3e1f2970085cfbaeadf8f010ef69bad83", + "analysis_key": ".github/workflows/codeql-analysis.yml:analyze", + "environment": "{\"language\":\"python\"}", + "error": "", + "category": ".github/workflows/codeql-analysis.yml:analyze/language:python", + "created_at": `+referenceTimeStr+`, + "results_count": 17, + "rules_count": 49, + "id": 201, + "url": "https://api.github.com/repos/o/r/code-scanning/analyses/201", + "sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "2.4.0" + }, + "deletable": true, + "warning": "" + }, + { + "ref": "refs/heads/my-branch", + "commit_sha": "c8cff6510d4d084fb1b4aa13b64b97ca12b07321", + "analysis_key": ".github/workflows/shiftleft.yml:build", + "environment": "{}", + "error": "", + "category": ".github/workflows/shiftleft.yml:build/", + "created_at": `+referenceTimeStr+`, + "results_count": 17, + "rules_count": 32, + "id": 200, + "url": "https://api.github.com/repos/o/r/code-scanning/analyses/200", + "sarif_id": "8981cd8e-b078-4ac3-a3be-1dad7dbd0b582", + "tool": { + "name": "Python Security ScanningAnalysis", + "guid": null, + "version": "1.2.0" + }, + "deletable": true, + "warning": "" + } + ]`) + }) + + opts := &AnalysesListOptions{SarifID: Ptr("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), Ref: Ptr("heads/master")} + ctx := t.Context() + analyses, _, err := client.CodeScanning.ListAnalysesForRepo(ctx, "o", "r", opts) + if err != nil { + t.Errorf("CodeScanning.ListAnalysesForRepo returned error: %v", err) + } + + want := []*ScanningAnalysis{ + { + ID: Ptr(int64(201)), + Ref: Ptr("refs/heads/main"), + CommitSHA: Ptr("d99612c3e1f2970085cfbaeadf8f010ef69bad83"), + AnalysisKey: Ptr(".github/workflows/codeql-analysis.yml:analyze"), + Environment: Ptr("{\"language\":\"python\"}"), + Error: Ptr(""), + Category: Ptr(".github/workflows/codeql-analysis.yml:analyze/language:python"), + CreatedAt: &referenceTimestamp, + ResultsCount: Ptr(17), + RulesCount: Ptr(49), + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/analyses/201"), + SarifID: Ptr("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), + Tool: &Tool{ + Name: Ptr("CodeQL"), + GUID: nil, + Version: Ptr("2.4.0"), + }, + Deletable: Ptr(true), + Warning: Ptr(""), + }, + { + ID: Ptr(int64(200)), + Ref: Ptr("refs/heads/my-branch"), + CommitSHA: Ptr("c8cff6510d4d084fb1b4aa13b64b97ca12b07321"), + AnalysisKey: Ptr(".github/workflows/shiftleft.yml:build"), + Environment: Ptr("{}"), + Error: Ptr(""), + Category: Ptr(".github/workflows/shiftleft.yml:build/"), + CreatedAt: &referenceTimestamp, + ResultsCount: Ptr(17), + RulesCount: Ptr(32), + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/analyses/200"), + SarifID: Ptr("8981cd8e-b078-4ac3-a3be-1dad7dbd0b582"), + Tool: &Tool{ + Name: Ptr("Python Security ScanningAnalysis"), + GUID: nil, + Version: Ptr("1.2.0"), + }, + Deletable: Ptr(true), + Warning: Ptr(""), + }, + } + if !cmp.Equal(analyses, want) { + t.Errorf("CodeScanning.ListAnalysesForRepo returned %+v, want %+v", analyses, want) + } + + const methodName = "ListAnalysesForRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListAnalysesForRepo(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListAnalysesForRepo(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_GetAnalysis(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/analyses/3602840", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "ref": "refs/heads/main", + "commit_sha": "c18c69115654ff0166991962832dc2bd7756e655", + "analysis_key": ".github/workflows/codeql-analysis.yml:analyze", + "environment": "{\"language\":\"javascript\"}", + "error": "", + "category": ".github/workflows/codeql-analysis.yml:analyze/language:javascript", + "created_at": `+referenceTimeStr+`, + "results_count": 3, + "rules_count": 67, + "id": 3602840, + "url": "https://api.github.com/repos/o/r/code-scanning/analyses/201", + "sarif_id": "47177e22-5596-11eb-80a1-c1e54ef945c6", + "tool": { + "name": "CodeQL", + "guid": null, + "version": "2.4.0" + }, + "deletable": true, + "warning": "" + }`) + }) + + ctx := t.Context() + analysis, _, err := client.CodeScanning.GetAnalysis(ctx, "o", "r", 3602840) + if err != nil { + t.Errorf("CodeScanning.GetAnalysis returned error: %v", err) + } + + want := &ScanningAnalysis{ + ID: Ptr(int64(3602840)), + Ref: Ptr("refs/heads/main"), + CommitSHA: Ptr("c18c69115654ff0166991962832dc2bd7756e655"), + AnalysisKey: Ptr(".github/workflows/codeql-analysis.yml:analyze"), + Environment: Ptr("{\"language\":\"javascript\"}"), + Error: Ptr(""), + Category: Ptr(".github/workflows/codeql-analysis.yml:analyze/language:javascript"), + CreatedAt: &referenceTimestamp, + ResultsCount: Ptr(3), + RulesCount: Ptr(67), + URL: Ptr("https://api.github.com/repos/o/r/code-scanning/analyses/201"), + SarifID: Ptr("47177e22-5596-11eb-80a1-c1e54ef945c6"), + Tool: &Tool{ + Name: Ptr("CodeQL"), + GUID: nil, + Version: Ptr("2.4.0"), + }, + Deletable: Ptr(true), + Warning: Ptr(""), + } + if !cmp.Equal(analysis, want) { + t.Errorf("CodeScanning.GetAnalysis returned %+v, want %+v", analysis, want) + } + + const methodName = "GetAnalysis" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.GetAnalysis(ctx, "\n", "\n", -123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.GetAnalysis(ctx, "o", "r", 3602840) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_DeleteAnalysis(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/analyses/40", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "next_analysis_url": "a", + "confirm_delete_url": "b" + }`) + }) + + ctx := t.Context() + analysis, _, err := client.CodeScanning.DeleteAnalysis(ctx, "o", "r", 40) + if err != nil { + t.Errorf("CodeScanning.DeleteAnalysis returned error: %v", err) + } + + want := &DeleteAnalysis{ + NextAnalysisURL: Ptr("a"), + ConfirmDeleteURL: Ptr("b"), + } + if !cmp.Equal(analysis, want) { + t.Errorf("CodeScanning.DeleteAnalysis returned %+v, want %+v", analysis, want) + } + + const methodName = "DeleteAnalysis" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.DeleteAnalysis(ctx, "\n", "\n", -123) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.DeleteAnalysis(ctx, "o", "r", 40) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_ListCodeQLDatabases(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "id": 1, + "name": "name", + "language": "language", + "uploader": { + "login": "a", + "id": 1, + "node_id": "b", + "avatar_url": "c", + "gravatar_id": "d", + "url": "e", + "html_url": "f", + "followers_url": "g", + "following_url": "h", + "gists_url": "i", + "starred_url": "j", + "subscriptions_url": "k", + "organizations_url": "l", + "repos_url": "m", + "events_url": "n", + "received_events_url": "o", + "type": "p", + "site_admin": false + }, + "content_type": "r", + "size": 1024, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "url": "s" + } + ]`) + }) + + ctx := t.Context() + databases, _, err := client.CodeScanning.ListCodeQLDatabases(ctx, "o", "r") + if err != nil { + t.Errorf("CodeScanning.ListCodeQLDatabases returned error: %v", err) + } + + want := []*CodeQLDatabase{ + { + ID: Ptr(int64(1)), + Name: Ptr("name"), + Language: Ptr("language"), + Uploader: &User{ + Login: Ptr("a"), + ID: Ptr(int64(1)), + NodeID: Ptr("b"), + AvatarURL: Ptr("c"), + GravatarID: Ptr("d"), + URL: Ptr("e"), + HTMLURL: Ptr("f"), + FollowersURL: Ptr("g"), + FollowingURL: Ptr("h"), + GistsURL: Ptr("i"), + StarredURL: Ptr("j"), + SubscriptionsURL: Ptr("k"), + OrganizationsURL: Ptr("l"), + ReposURL: Ptr("m"), + EventsURL: Ptr("n"), + ReceivedEventsURL: Ptr("o"), + Type: Ptr("p"), + SiteAdmin: Ptr(false), + }, + ContentType: Ptr("r"), + Size: Ptr(int64(1024)), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + URL: Ptr("s"), + }, + } + + if !cmp.Equal(databases, want) { + t.Errorf("CodeScanning.ListCodeQLDatabases returned %+v, want %+v", databases, want) + } + + const methodName = "ListCodeQLDatabases" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.ListCodeQLDatabases(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.ListCodeQLDatabases(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_GetCodeQLDatabase(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases/lang", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 1, + "name": "name", + "language": "language", + "uploader": { + "login": "a", + "id": 1, + "node_id": "b", + "avatar_url": "c", + "gravatar_id": "d", + "url": "e", + "html_url": "f", + "followers_url": "g", + "following_url": "h", + "gists_url": "i", + "starred_url": "j", + "subscriptions_url": "k", + "organizations_url": "l", + "repos_url": "m", + "events_url": "n", + "received_events_url": "o", + "type": "p", + "site_admin": false + }, + "content_type": "r", + "size": 1024, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "url": "s" + }`) + }) + + ctx := t.Context() + database, _, err := client.CodeScanning.GetCodeQLDatabase(ctx, "o", "r", "lang") + if err != nil { + t.Errorf("CodeScanning.GetCodeQLDatabase returned error: %v", err) + } + + want := &CodeQLDatabase{ + ID: Ptr(int64(1)), + Name: Ptr("name"), + Language: Ptr("language"), + Uploader: &User{ + Login: Ptr("a"), + ID: Ptr(int64(1)), + NodeID: Ptr("b"), + AvatarURL: Ptr("c"), + GravatarID: Ptr("d"), + URL: Ptr("e"), + HTMLURL: Ptr("f"), + FollowersURL: Ptr("g"), + FollowingURL: Ptr("h"), + GistsURL: Ptr("i"), + StarredURL: Ptr("j"), + SubscriptionsURL: Ptr("k"), + OrganizationsURL: Ptr("l"), + ReposURL: Ptr("m"), + EventsURL: Ptr("n"), + ReceivedEventsURL: Ptr("o"), + Type: Ptr("p"), + SiteAdmin: Ptr(false), + }, + ContentType: Ptr("r"), + Size: Ptr(int64(1024)), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + URL: Ptr("s"), + } + + if !cmp.Equal(database, want) { + t.Errorf("CodeScanning.GetCodeQLDatabase returned %+v, want %+v", database, want) + } + + const methodName = "GetCodeQLDatabase" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.GetCodeQLDatabase(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.GetCodeQLDatabase(ctx, "o", "r", "lang") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_DeleteCodeQLDatabase(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/codeql/databases/lang", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.CodeScanning.DeleteCodeQLDatabase(ctx, "o", "r", "lang") + if err != nil { + t.Errorf("CodeScanning.DeleteCodeQLDatabase returned error: %v", err) + } + + const methodName = "DeleteCodeQLDatabase" + testBadOptions(t, methodName, func() (err error) { + _, err = client.CodeScanning.DeleteCodeQLDatabase(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.CodeScanning.DeleteCodeQLDatabase(ctx, "o", "r", "lang") + }) +} + +func TestCodeScanningService_GetDefaultSetupConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/default-setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + _, err := fmt.Fprint(w, `{ + "state": "configured", + "languages": [ + "javascript", + "javascript-typescript", + "typescript" + ], + "query_suite": "default", + "updated_at": `+referenceTimeStr+` + }`) + if err != nil { + t.Fatal(err) + } + }) + + ctx := t.Context() + cfg, _, err := client.CodeScanning.GetDefaultSetupConfiguration(ctx, "o", "r") + if err != nil { + t.Errorf("CodeScanning.GetDefaultSetupConfiguration returned error: %v", err) + } + + want := &DefaultSetupConfiguration{ + State: Ptr("configured"), + Languages: []string{"javascript", "javascript-typescript", "typescript"}, + QuerySuite: Ptr("default"), + UpdatedAt: &referenceTimestamp, + } + if !cmp.Equal(cfg, want) { + t.Errorf("CodeScanning.GetDefaultSetupConfiguration returned %+v, want %+v", cfg, want) + } + + const methodName = "GetDefaultSetupConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.GetDefaultSetupConfiguration(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.GetDefaultSetupConfiguration(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodeScanningService_UpdateDefaultSetupConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/code-scanning/default-setup", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + _, err := fmt.Fprint(w, `{ + "run_id": 5301214200, + "run_url": "https://api.github.com/repos/o/r/actions/runs/5301214200" + }`) + if err != nil { + t.Fatal(err) + } + }) + + ctx := t.Context() + options := &UpdateDefaultSetupConfigurationOptions{ + State: "configured", + Languages: []string{"go"}, + QuerySuite: Ptr("default"), + } + got, _, err := client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "o", "r", options) + if err != nil { + t.Errorf("CodeScanning.UpdateDefaultSetupConfiguration returned error: %v", err) + } + + want := &UpdateDefaultSetupConfigurationResponse{ + RunID: Ptr(int64(5301214200)), + RunURL: Ptr("https://api.github.com/repos/o/r/actions/runs/5301214200"), + } + if !cmp.Equal(got, want) { + t.Errorf("CodeScanning.UpdateDefaultSetupConfiguration returned %+v, want %+v", got, want) + } + + const methodName = "UpdateDefaultSetupConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "\n", "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodeScanning.UpdateDefaultSetupConfiguration(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/codesofconduct.go b/github/codesofconduct.go new file mode 100644 index 00000000000..882e732c39b --- /dev/null +++ b/github/codesofconduct.go @@ -0,0 +1,85 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CodesOfConductService provides access to code-of-conduct-related functions in the GitHub API. +type CodesOfConductService service + +// CodeOfConduct represents a code of conduct. +type CodeOfConduct struct { + Name *string `json:"name,omitempty"` + Key *string `json:"key,omitempty"` + URL *string `json:"url,omitempty"` + Body *string `json:"body,omitempty"` +} + +func (c *CodeOfConduct) String() string { + return Stringify(c) +} + +// List returns all codes of conduct. +// +// GitHub API docs: https://docs.github.com/rest/codes-of-conduct/codes-of-conduct?apiVersion=2022-11-28#get-all-codes-of-conduct +// +//meta:operation GET /codes_of_conduct +func (s *CodesOfConductService) List(ctx context.Context) ([]*CodeOfConduct, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "codes_of_conduct", nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + var cs []*CodeOfConduct + resp, err := s.client.Do(req, &cs) + if err != nil { + return nil, resp, err + } + + return cs, resp, nil +} + +// ListCodesOfConduct returns all codes of conduct. +// +// Deprecated: Use CodesOfConductService.List instead. +func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { + return c.CodesOfConduct.List(ctx) +} + +// Get returns an individual code of conduct. +// +// GitHub API docs: https://docs.github.com/rest/codes-of-conduct/codes-of-conduct?apiVersion=2022-11-28#get-a-code-of-conduct +// +//meta:operation GET /codes_of_conduct/{key} +func (s *CodesOfConductService) Get(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { + u := fmt.Sprintf("codes_of_conduct/%v", key) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeCodesOfConductPreview) + + var coc *CodeOfConduct + resp, err := s.client.Do(req, &coc) + if err != nil { + return nil, resp, err + } + + return coc, resp, nil +} + +// GetCodeOfConduct returns an individual code of conduct. +// +// Deprecated: Use CodesOfConductService.Get instead. +func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { + return c.CodesOfConduct.Get(ctx, key) +} diff --git a/github/codesofconduct_test.go b/github/codesofconduct_test.go new file mode 100644 index 00000000000..281eb8d5749 --- /dev/null +++ b/github/codesofconduct_test.go @@ -0,0 +1,97 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodesOfConductService_List(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/codes_of_conduct", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `[{ + "key": "key", + "name": "name", + "url": "url"} + ]`) + }) + + ctx := t.Context() + cs, _, err := client.ListCodesOfConduct(ctx) + assertNilError(t, err) + + want := []*CodeOfConduct{ + { + Key: Ptr("key"), + Name: Ptr("name"), + URL: Ptr("url"), + }, + } + if !cmp.Equal(want, cs) { + t.Errorf("returned %+v, want %+v", cs, want) + } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodesOfConduct.List(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodesOfConductService_Get(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/codes_of_conduct/k", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) + fmt.Fprint(w, `{ + "key": "key", + "name": "name", + "url": "url", + "body": "body"}`, + ) + }) + + ctx := t.Context() + coc, _, err := client.GetCodeOfConduct(ctx, "k") + assertNilError(t, err) + + want := &CodeOfConduct{ + Key: Ptr("key"), + Name: Ptr("name"), + URL: Ptr("url"), + Body: Ptr("body"), + } + if !cmp.Equal(want, coc) { + t.Errorf("returned %+v, want %+v", coc, want) + } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.CodesOfConduct.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.CodesOfConduct.Get(ctx, "k") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/codespaces.go b/github/codespaces.go new file mode 100644 index 00000000000..b3f9097196d --- /dev/null +++ b/github/codespaces.go @@ -0,0 +1,591 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CodespacesService handles communication with the Codespaces related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/codespaces?apiVersion=2022-11-28 +type CodespacesService service + +// Codespace represents a codespace. +// +// GitHub API docs: https://docs.github.com/rest/codespaces?apiVersion=2022-11-28 +type Codespace struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + DisplayName *string `json:"display_name,omitempty"` + EnvironmentID *string `json:"environment_id,omitempty"` + Owner *User `json:"owner,omitempty"` + BillableOwner *User `json:"billable_owner,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Machine *CodespacesMachine `json:"machine,omitempty"` + DevcontainerPath *string `json:"devcontainer_path,omitempty"` + Prebuild *bool `json:"prebuild,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + LastUsedAt *Timestamp `json:"last_used_at,omitempty"` + State *string `json:"state,omitempty"` + URL *string `json:"url,omitempty"` + GitStatus *CodespacesGitStatus `json:"git_status,omitempty"` + Location *string `json:"location,omitempty"` + IdleTimeoutMinutes *int `json:"idle_timeout_minutes,omitempty"` + WebURL *string `json:"web_url,omitempty"` + MachinesURL *string `json:"machines_url,omitempty"` + StartURL *string `json:"start_url,omitempty"` + StopURL *string `json:"stop_url,omitempty"` + PullsURL *string `json:"pulls_url,omitempty"` + RecentFolders []string `json:"recent_folders,omitempty"` + RuntimeConstraints *CodespacesRuntimeConstraints `json:"runtime_constraints,omitempty"` + PendingOperation *bool `json:"pending_operation,omitempty"` + PendingOperationDisabledReason *string `json:"pending_operation_disabled_reason,omitempty"` + IdleTimeoutNotice *string `json:"idle_timeout_notice,omitempty"` + RetentionPeriodMinutes *int `json:"retention_period_minutes,omitempty"` + RetentionExpiresAt *Timestamp `json:"retention_expires_at,omitempty"` + LastKnownStopNotice *string `json:"last_known_stop_notice,omitempty"` +} + +// CodespacesGitStatus represents the git status of a codespace. +type CodespacesGitStatus struct { + Ahead *int `json:"ahead,omitempty"` + Behind *int `json:"behind,omitempty"` + HasUnpushedChanges *bool `json:"has_unpushed_changes,omitempty"` + HasUncommittedChanges *bool `json:"has_uncommitted_changes,omitempty"` + Ref *string `json:"ref,omitempty"` +} + +// CodespacesMachine represents the machine type of a codespace. +type CodespacesMachine struct { + Name *string `json:"name,omitempty"` + DisplayName *string `json:"display_name,omitempty"` + OperatingSystem *string `json:"operating_system,omitempty"` + StorageInBytes *int64 `json:"storage_in_bytes,omitempty"` + MemoryInBytes *int64 `json:"memory_in_bytes,omitempty"` + CPUs *int `json:"cpus,omitempty"` + PrebuildAvailability *string `json:"prebuild_availability,omitempty"` +} + +// CodespacesRuntimeConstraints represents the runtime constraints of a codespace. +type CodespacesRuntimeConstraints struct { + AllowedPortPrivacySettings []string `json:"allowed_port_privacy_settings,omitempty"` +} + +// ListCodespaces represents the response from the list codespaces endpoints. +type ListCodespaces struct { + TotalCount *int `json:"total_count,omitempty"` + Codespaces []*Codespace `json:"codespaces"` +} + +// ListInRepo lists codespaces for a user in a repository. +// +// Lists the codespaces associated with a specified repository and the authenticated user. +// You must authenticate using an access token with the codespace scope to use this endpoint. +// GitHub Apps must have read access to the codespaces repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#list-codespaces-in-a-repository-for-the-authenticated-user +// +//meta:operation GET /repos/{owner}/{repo}/codespaces +func (s *CodespacesService) ListInRepo(ctx context.Context, owner, repo string, opts *ListOptions) (*ListCodespaces, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codespaces *ListCodespaces + resp, err := s.client.Do(req, &codespaces) + if err != nil { + return nil, resp, err + } + + return codespaces, resp, nil +} + +// ListCodespacesOptions represents the options for listing codespaces for a user. +type ListCodespacesOptions struct { + ListOptions + RepositoryID int64 `url:"repository_id,omitempty"` +} + +// List lists codespaces for an authenticated user. +// +// Lists the authenticated user's codespaces. +// You must authenticate using an access token with the codespace scope to use this endpoint. +// GitHub Apps must have read access to the codespaces repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#list-codespaces-for-the-authenticated-user +// +//meta:operation GET /user/codespaces +func (s *CodespacesService) List(ctx context.Context, opts *ListCodespacesOptions) (*ListCodespaces, *Response, error) { + u := "user/codespaces" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codespaces *ListCodespaces + resp, err := s.client.Do(req, &codespaces) + if err != nil { + return nil, resp, err + } + + return codespaces, resp, nil +} + +// CreateCodespaceOptions represents options for the creation of a codespace in a repository. +type CreateCodespaceOptions struct { + Ref *string `json:"ref,omitempty"` + // Geo represents the geographic area for this codespace. + // If not specified, the value is assigned by IP. + // This property replaces location, which is being deprecated. + // Geo can be one of: `EuropeWest`, `SoutheastAsia`, `UsEast`, `UsWest`. + Geo *string `json:"geo,omitempty"` + ClientIP *string `json:"client_ip,omitempty"` + Machine *string `json:"machine,omitempty"` + DevcontainerPath *string `json:"devcontainer_path,omitempty"` + MultiRepoPermissionsOptOut *bool `json:"multi_repo_permissions_opt_out,omitempty"` + WorkingDirectory *string `json:"working_directory,omitempty"` + IdleTimeoutMinutes *int `json:"idle_timeout_minutes,omitempty"` + DisplayName *string `json:"display_name,omitempty"` + // RetentionPeriodMinutes represents the duration in minutes after codespace has gone idle in which it will be deleted. + // Must be integer minutes between 0 and 43200 (30 days). + RetentionPeriodMinutes *int `json:"retention_period_minutes,omitempty"` + Location *string `json:"location,omitempty"` +} + +// DevContainer represents a devcontainer configuration in a repository. +type DevContainer struct { + Path string `json:"path"` + Name *string `json:"name,omitempty"` + DisplayName *string `json:"display_name,omitempty"` +} + +// DevContainerConfigurations represents a list of devcontainer configurations in a repository. +type DevContainerConfigurations struct { + Devcontainers []*DevContainer `json:"devcontainers"` + TotalCount int64 `json:"total_count"` +} + +// CodespaceDefaults represents default settings for a Codespace. +type CodespaceDefaults struct { + Location string `json:"location"` + DevcontainerPath *string `json:"devcontainer_path,omitempty"` +} + +// CodespaceDefaultAttributes represents the default attributes for codespaces created by the user with the repository. +type CodespaceDefaultAttributes struct { + BillableOwner *User `json:"billable_owner"` + Defaults *CodespaceDefaults `json:"defaults"` +} + +// CodespaceGetDefaultAttributesOptions represents options for getting default attributes for a codespace. +type CodespaceGetDefaultAttributesOptions struct { + // Ref represents the branch or commit to check for a default devcontainer path. If not specified, the default branch will be checked. + Ref *string `url:"ref,omitempty"` + // ClientIP represents an alternative IP for default location auto-detection, such as when proxying a request. + ClientIP *string `url:"client_ip,omitempty"` +} + +// CodespacePullRequestOptions represents options for a CodespacePullRequest. +type CodespacePullRequestOptions struct { + // PullRequestNumber represents the pull request number. + PullRequestNumber int64 `json:"pull_request_number"` + // RepositoryID represents the repository ID for this codespace. + RepositoryID int64 `json:"repository_id"` +} + +// CodespaceCreateForUserOptions represents options for creating a codespace for the authenticated user. +type CodespaceCreateForUserOptions struct { + PullRequest *CodespacePullRequestOptions `json:"pull_request"` + // RepositoryID represents the repository ID for this codespace. + RepositoryID int64 `json:"repository_id"` + Ref *string `json:"ref,omitempty"` + Geo *string `json:"geo,omitempty"` + ClientIP *string `json:"client_ip,omitempty"` + RetentionPeriodMinutes *int `json:"retention_period_minutes,omitempty"` + Location *string `json:"location,omitempty"` + Machine *string `json:"machine,omitempty"` + DevcontainerPath *string `json:"devcontainer_path,omitempty"` + MultiRepoPermissionsOptOut *bool `json:"multi_repo_permissions_opt_out,omitempty"` + WorkingDirectory *string `json:"working_directory,omitempty"` + IdleTimeoutMinutes *int `json:"idle_timeout_minutes,omitempty"` + DisplayName *string `json:"display_name,omitempty"` +} + +// UpdateCodespaceOptions represents options for updating a codespace. +type UpdateCodespaceOptions struct { + // Machine represents a valid machine to transition this codespace to. + Machine *string `json:"machine,omitempty"` + // RecentFolders represents the recently opened folders inside the codespace. + // It is currently used by the clients to determine the folder path to load the codespace in. + RecentFolders []string `json:"recent_folders,omitempty"` +} + +// CodespaceExport represents an export of a codespace. +type CodespaceExport struct { + // Can be one of: `succeeded`, `failed`, `in_progress`. + State *string `json:"state,omitempty"` + CompletedAt *Timestamp `json:"completed_at,omitempty"` + Branch *string `json:"branch,omitempty"` + SHA *string `json:"sha,omitempty"` + ID *string `json:"id,omitempty"` + ExportURL *string `json:"export_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// PublishCodespaceOptions represents options for creating a repository from an unpublished codespace. +type PublishCodespaceOptions struct { + // Name represents the name of the new repository. + Name *string `json:"name,omitempty"` + // Private represents whether the new repository is private. Defaults to false. + Private *bool `json:"private,omitempty"` +} + +// CodespacePermissions represents a response indicating whether the permissions defined by a devcontainer have been accepted. +type CodespacePermissions struct { + Accepted bool `json:"accepted"` +} + +// CreateInRepo creates a codespace in a repository. +// +// Creates a codespace owned by the authenticated user in the specified repository. +// You must authenticate using an access token with the codespace scope to use this endpoint. +// GitHub Apps must have write access to the codespaces repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-codespace-in-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/codespaces +func (s *CodespacesService) CreateInRepo(ctx context.Context, owner, repo string, body *CreateCodespaceOptions) (*Codespace, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces", owner, repo) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Start starts a codespace. +// +// You must authenticate using an access token with the codespace scope to use this endpoint. +// GitHub Apps must have write access to the codespaces_lifecycle_admin repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#start-a-codespace-for-the-authenticated-user +// +//meta:operation POST /user/codespaces/{codespace_name}/start +func (s *CodespacesService) Start(ctx context.Context, codespaceName string) (*Codespace, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v/start", codespaceName) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Stop stops a codespace. +// +// You must authenticate using an access token with the codespace scope to use this endpoint. +// GitHub Apps must have write access to the codespaces_lifecycle_admin repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#stop-a-codespace-for-the-authenticated-user +// +//meta:operation POST /user/codespaces/{codespace_name}/stop +func (s *CodespacesService) Stop(ctx context.Context, codespaceName string) (*Codespace, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v/stop", codespaceName) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Delete deletes a codespace. +// +// You must authenticate using an access token with the codespace scope to use this endpoint. +// GitHub Apps must have write access to the codespaces repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#delete-a-codespace-for-the-authenticated-user +// +//meta:operation DELETE /user/codespaces/{codespace_name} +func (s *CodespacesService) Delete(ctx context.Context, codespaceName string) (*Response, error) { + u := fmt.Sprintf("user/codespaces/%v", codespaceName) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListDevContainerConfigurations lists devcontainer configurations in a repository for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#list-devcontainer-configurations-in-a-repository-for-the-authenticated-user +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/devcontainers +func (s *CodespacesService) ListDevContainerConfigurations(ctx context.Context, owner, repo string, opts *ListOptions) (*DevContainerConfigurations, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/devcontainers", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var devcontainers *DevContainerConfigurations + resp, err := s.client.Do(req, &devcontainers) + if err != nil { + return nil, resp, err + } + + return devcontainers, resp, nil +} + +// GetDefaultAttributes gets the default attributes for codespaces created by the user with the repository. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#get-default-attributes-for-a-codespace +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/new +func (s *CodespacesService) GetDefaultAttributes(ctx context.Context, owner, repo string, opts *CodespaceGetDefaultAttributesOptions) (*CodespaceDefaultAttributes, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/new", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var attributes *CodespaceDefaultAttributes + resp, err := s.client.Do(req, &attributes) + if err != nil { + return nil, resp, err + } + + return attributes, resp, nil +} + +// CheckPermissions checks whether the permissions defined by a given devcontainer configuration have been accepted by the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#check-if-permissions-defined-by-a-devcontainer-have-been-accepted-by-the-authenticated-user +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/permissions_check +func (s *CodespacesService) CheckPermissions(ctx context.Context, owner, repo, ref, devcontainerPath string) (*CodespacePermissions, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/permissions_check", owner, repo) + u, err := addOptions(u, &struct { + Ref string `url:"ref"` + DevcontainerPath string `url:"devcontainer_path"` + }{ + Ref: ref, + DevcontainerPath: devcontainerPath, + }) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *CodespacePermissions + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// CreateFromPullRequest creates a codespace owned by the authenticated user for the specified pull request. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-codespace-from-a-pull-request +// +//meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces +func (s *CodespacesService) CreateFromPullRequest(ctx context.Context, owner, repo string, pullNumber int, body *CreateCodespaceOptions) (*Codespace, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/codespaces", owner, repo, pullNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Create creates a new codespace, owned by the authenticated user. +// +// This method requires either RepositoryId OR a PullRequest but not both. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-codespace-for-the-authenticated-user +// +//meta:operation POST /user/codespaces +func (s *CodespacesService) Create(ctx context.Context, body *CodespaceCreateForUserOptions) (*Codespace, *Response, error) { + u := "user/codespaces" + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Get gets information about a user's codespace. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#get-a-codespace-for-the-authenticated-user +// +//meta:operation GET /user/codespaces/{codespace_name} +func (s *CodespacesService) Get(ctx context.Context, codespaceName string) (*Codespace, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v", codespaceName) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Update updates a codespace owned by the authenticated user. +// +// Only the codespace's machine type and recent folders can be modified using this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#update-a-codespace-for-the-authenticated-user +// +//meta:operation PATCH /user/codespaces/{codespace_name} +func (s *CodespacesService) Update(ctx context.Context, codespaceName string, body *UpdateCodespaceOptions) (*Codespace, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v", codespaceName) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// ExportCodespace triggers an export of the specified codespace and returns a URL and ID where the status of the export can be monitored. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#export-a-codespace-for-the-authenticated-user +// +//meta:operation POST /user/codespaces/{codespace_name}/exports +func (s *CodespacesService) ExportCodespace(ctx context.Context, codespaceName string) (*CodespaceExport, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v/exports", codespaceName) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var codespace *CodespaceExport + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// GetLatestCodespaceExport gets information about an export of a codespace. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#get-details-about-a-codespace-export +// +//meta:operation GET /user/codespaces/{codespace_name}/exports/{export_id} +func (s *CodespacesService) GetLatestCodespaceExport(ctx context.Context, codespaceName string) (*CodespaceExport, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v/exports/latest", codespaceName) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codespace *CodespaceExport + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} + +// Publish publishes an unpublished codespace, creating a new repository and assigning it to the codespace. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-repository-from-an-unpublished-codespace +// +//meta:operation POST /user/codespaces/{codespace_name}/publish +func (s *CodespacesService) Publish(ctx context.Context, codespaceName string, body *PublishCodespaceOptions) (*Codespace, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v/publish", codespaceName) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var codespace *Codespace + resp, err := s.client.Do(req, &codespace) + if err != nil { + return nil, resp, err + } + + return codespace, resp, nil +} diff --git a/github/codespaces_machines.go b/github/codespaces_machines.go new file mode 100644 index 00000000000..155beadd7ed --- /dev/null +++ b/github/codespaces_machines.go @@ -0,0 +1,74 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CodespacesMachines represent a list of machines. +type CodespacesMachines struct { + TotalCount int64 `json:"total_count"` + Machines []*CodespacesMachine `json:"machines"` +} + +// ListRepoMachineTypesOptions represent options for ListMachineTypesForRepository. +type ListRepoMachineTypesOptions struct { + // Ref represent the branch or commit to check for prebuild availability and devcontainer restrictions. + Ref *string `url:"ref,omitempty"` + // Location represent the location to check for available machines. Assigned by IP if not provided. + Location *string `url:"location,omitempty"` + // ClientIP represent the IP for location auto-detection when proxying a request + ClientIP *string `url:"client_ip,omitempty"` +} + +// ListRepositoryMachineTypes lists the machine types available for a given repository based on its configuration. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/machines?apiVersion=2022-11-28#list-available-machine-types-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/machines +func (s *CodespacesService) ListRepositoryMachineTypes(ctx context.Context, owner, repo string, opts *ListRepoMachineTypesOptions) (*CodespacesMachines, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/machines", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var machines *CodespacesMachines + resp, err := s.client.Do(req, &machines) + if err != nil { + return nil, resp, err + } + + return machines, resp, nil +} + +// ListCodespaceMachineTypes lists the machine types a codespace can transition to use. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/machines?apiVersion=2022-11-28#list-machine-types-for-a-codespace +// +//meta:operation GET /user/codespaces/{codespace_name}/machines +func (s *CodespacesService) ListCodespaceMachineTypes(ctx context.Context, codespaceName string) (*CodespacesMachines, *Response, error) { + u := fmt.Sprintf("user/codespaces/%v/machines", codespaceName) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var machines *CodespacesMachines + resp, err := s.client.Do(req, &machines) + if err != nil { + return nil, resp, err + } + + return machines, resp, nil +} diff --git a/github/codespaces_machines_test.go b/github/codespaces_machines_test.go new file mode 100644 index 00000000000..d413d168b93 --- /dev/null +++ b/github/codespaces_machines_test.go @@ -0,0 +1,149 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodespacesService_ListRepositoryMachineTypes(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/codespaces/machines", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "ref": "main", + "location": "WestUs2", + "client_ip": "1.2.3.4", + }) + fmt.Fprint(w, `{ + "total_count": 1, + "machines": [ + { + "name": "standardLinux", + "display_name": "4 cores, 8 GB RAM, 64 GB storage", + "operating_system": "linux", + "storage_in_bytes": 68719476736, + "memory_in_bytes": 17179869184, + "cpus": 4, + "prebuild_availability": "ready" + } + ] + }`) + }) + + ctx := t.Context() + opts := &ListRepoMachineTypesOptions{ + Ref: Ptr("main"), + Location: Ptr("WestUs2"), + ClientIP: Ptr("1.2.3.4"), + } + + got, _, err := client.Codespaces.ListRepositoryMachineTypes( + ctx, + "owner", + "repo", + opts, + ) + if err != nil { + t.Fatalf("Codespaces.ListRepositoryMachineTypes returned error: %v", err) + } + + want := &CodespacesMachines{ + TotalCount: 1, + Machines: []*CodespacesMachine{ + { + Name: Ptr("standardLinux"), + DisplayName: Ptr("4 cores, 8 GB RAM, 64 GB storage"), + OperatingSystem: Ptr("linux"), + StorageInBytes: Ptr(int64(68719476736)), + MemoryInBytes: Ptr(int64(17179869184)), + CPUs: Ptr(4), + PrebuildAvailability: Ptr("ready"), + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Codespaces.ListRepositoryMachineTypes returned %+v, want %+v", got, want) + } + + const methodName = "ListRepositoryMachineTypes" + testBadOptions(t, methodName, func() error { + _, _, err := client.Codespaces.ListRepositoryMachineTypes(ctx, "\n", "/n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ListRepositoryMachineTypes(ctx, "/n", "/n", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_ListCodespaceMachineTypes(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1/machines", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `{ + "total_count": 1, + "machines": [ + { + "name": "standardLinux", + "display_name": "4 cores, 8 GB RAM, 64 GB storage", + "operating_system": "linux", + "storage_in_bytes": 68719476736, + "memory_in_bytes": 17179869184, + "cpus": 4, + "prebuild_availability": "ready" + } + ] + }`) + }) + + ctx := t.Context() + got, _, err := client.Codespaces.ListCodespaceMachineTypes(ctx, "codespace_1") + if err != nil { + t.Fatalf("Codespaces.ListCodespaceMachineTypes returned error: %v", err) + } + + want := &CodespacesMachines{ + TotalCount: 1, + Machines: []*CodespacesMachine{ + { + Name: Ptr("standardLinux"), + DisplayName: Ptr("4 cores, 8 GB RAM, 64 GB storage"), + OperatingSystem: Ptr("linux"), + StorageInBytes: Ptr(int64(68719476736)), + MemoryInBytes: Ptr(int64(17179869184)), + CPUs: Ptr(4), + PrebuildAvailability: Ptr("ready"), + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Codespaces.ListCodespaceMachineTypes returned %+v, want %+v", got, want) + } + + const methodName = "ListCodespaceMachineTypes" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ListCodespaceMachineTypes(ctx, "/n") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/codespaces_orgs.go b/github/codespaces_orgs.go new file mode 100644 index 00000000000..429db12b995 --- /dev/null +++ b/github/codespaces_orgs.go @@ -0,0 +1,179 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CodespacesOrgAccessControlRequest represent request for SetOrgAccessControl. +type CodespacesOrgAccessControlRequest struct { + // Visibility represent which users can access codespaces in the organization. + // Can be one of: disabled, selected_members, all_members, all_members_and_outside_collaborators. + Visibility string `json:"visibility"` + // SelectedUsernames represent the usernames of the organization members who should have access to codespaces in the organization. + // Required when visibility is selected_members. + SelectedUsernames []string `json:"selected_usernames,omitzero"` +} + +// ListInOrg lists the codespaces associated to a specified organization. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#list-codespaces-for-the-organization +// +//meta:operation GET /orgs/{org}/codespaces +func (s *CodespacesService) ListInOrg(ctx context.Context, org string, opts *ListOptions) (*ListCodespaces, *Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codespaces *ListCodespaces + resp, err := s.client.Do(req, &codespaces) + if err != nil { + return nil, resp, err + } + + return codespaces, resp, nil +} + +// SetOrgAccessControl sets which users can access codespaces in an organization. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#manage-access-control-for-organization-codespaces +// +//meta:operation PUT /orgs/{org}/codespaces/access +func (s *CodespacesService) SetOrgAccessControl(ctx context.Context, org string, body CodespacesOrgAccessControlRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/access", org) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AddUsersToOrgAccess adds users to Codespaces access for an organization. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#add-users-to-codespaces-access-for-an-organization +// +//meta:operation POST /orgs/{org}/codespaces/access/selected_users +func (s *CodespacesService) AddUsersToOrgAccess(ctx context.Context, org string, usernames []string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/access/selected_users", org) + req, err := s.client.NewRequest(ctx, "POST", u, map[string][]string{"selected_usernames": usernames}) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// RemoveUsersFromOrgAccess removes users from Codespaces access for an organization. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#remove-users-from-codespaces-access-for-an-organization +// +//meta:operation DELETE /orgs/{org}/codespaces/access/selected_users +func (s *CodespacesService) RemoveUsersFromOrgAccess(ctx context.Context, org string, usernames []string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/access/selected_users", org) + req, err := s.client.NewRequest(ctx, "DELETE", u, map[string][]string{"selected_usernames": usernames}) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListUserCodespacesInOrg lists the codespaces that a member of an organization has for repositories in that organization. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#list-codespaces-for-a-user-in-organization +// +//meta:operation GET /orgs/{org}/members/{username}/codespaces +func (s *CodespacesService) ListUserCodespacesInOrg(ctx context.Context, org, username string, opts *ListOptions) (*ListCodespaces, *Response, error) { + u := fmt.Sprintf("orgs/%v/members/%v/codespaces", org, username) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codespaces *ListCodespaces + resp, err := s.client.Do(req, &codespaces) + if err != nil { + return nil, resp, err + } + + return codespaces, resp, nil +} + +// DeleteUserCodespaceInOrg deletes a user's codespace from the organization. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#delete-a-codespace-from-the-organization +// +//meta:operation DELETE /orgs/{org}/members/{username}/codespaces/{codespace_name} +func (s *CodespacesService) DeleteUserCodespaceInOrg(ctx context.Context, org, username, codespaceName string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/members/%v/codespaces/%v", org, username, codespaceName) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// StopUserCodespaceInOrg stops a codespace for an organization user. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organizations?apiVersion=2022-11-28#stop-a-codespace-for-an-organization-user +// +//meta:operation POST /orgs/{org}/members/{username}/codespaces/{codespace_name}/stop +func (s *CodespacesService) StopUserCodespaceInOrg(ctx context.Context, org, username, codespaceName string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/members/%v/codespaces/%v/stop", org, username, codespaceName) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/codespaces_orgs_test.go b/github/codespaces_orgs_test.go new file mode 100644 index 00000000000..f0661628eb2 --- /dev/null +++ b/github/codespaces_orgs_test.go @@ -0,0 +1,251 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodespacesService_ListInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o1/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"codespaces":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 1, PerPage: 10} + ctx := t.Context() + got, _, err := client.Codespaces.ListInOrg(ctx, "o1", opts) + if err != nil { + t.Fatalf("Codespaces.ListInOrg returned error: %v", err) + } + + want := &ListCodespaces{ + TotalCount: Ptr(1), + Codespaces: []*Codespace{ + {ID: Ptr(int64(1))}, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Codespaces.ListInOrg = %+v, want %+v", got, want) + } + const methodName = "ListInOrg" + testBadOptions(t, methodName, func() error { + _, _, err := client.Codespaces.ListInOrg(ctx, "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ListInOrg(ctx, "o1", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_SetOrgAccessControl(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := CodespacesOrgAccessControlRequest{ + Visibility: "selected_members", + SelectedUsernames: []string{"u1", "u2"}, + } + + mux.HandleFunc("/orgs/o1/codespaces/access", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, req) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Codespaces.SetOrgAccessControl(ctx, "o1", req) + if err != nil { + t.Fatalf("Codespaces.SetOrgAccessControl returned error: %v", err) + } + + const methodName = "SetOrgAccessControl" + testBadOptions(t, methodName, func() error { + _, err := client.Codespaces.SetOrgAccessControl(ctx, "\n", req) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Codespaces.SetOrgAccessControl(ctx, "o1", req) + }) +} + +func TestEnterpriseService_AddUsersToOrgAccess(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := []string{"u1"} + + mux.HandleFunc("/orgs/o1/codespaces/access/selected_users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, struct { + SelectedUsernames []string `json:"selected_usernames"` + }{ + SelectedUsernames: req, + }) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Codespaces.AddUsersToOrgAccess(ctx, "o1", req) + if err != nil { + t.Fatalf("AddUsersToOrgAccess returned error: %v", err) + } + if resp == nil { + t.Fatal("AddUsersToOrgAccess returned nil Response") + } + + const methodName = "AddUsersToOrgAccess" + testBadOptions(t, methodName, func() error { + _, err := client.Codespaces.AddUsersToOrgAccess(ctx, "\n", req) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Codespaces.AddUsersToOrgAccess(ctx, "o1", req) + }) +} + +func TestEnterpriseService_RemoveUsersFromOrgAccess(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := []string{"u1"} + + mux.HandleFunc("/orgs/o1/codespaces/access/selected_users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, struct { + SelectedUsernames []string `json:"selected_usernames"` + }{ + SelectedUsernames: req, + }) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Codespaces.RemoveUsersFromOrgAccess(ctx, "o1", req) + if err != nil { + t.Fatalf("RemoveUsersFromOrgAccess returned error: %v", err) + } + if resp == nil { + t.Fatal("RemoveUsersFromOrgAccess returned nil Response") + } + + const methodName = "RemoveUsersFromOrgAccess" + testBadOptions(t, methodName, func() error { + _, err := client.Codespaces.RemoveUsersFromOrgAccess(ctx, "\n", req) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Codespaces.RemoveUsersFromOrgAccess(ctx, "o1", req) + }) +} + +func TestCodespacesService_ListUserCodespacesInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o1/members/u1/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"codespaces":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 1, PerPage: 10} + ctx := t.Context() + got, _, err := client.Codespaces.ListUserCodespacesInOrg(ctx, "o1", "u1", opts) + if err != nil { + t.Fatalf("Codespaces.ListUserCodespacesInOrg returned error: %v", err) + } + + want := &ListCodespaces{ + TotalCount: Ptr(1), + Codespaces: []*Codespace{ + {ID: Ptr(int64(1))}, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Codespaces.ListUserCodespacesInOrg = %+v, want %+v", got, want) + } + const methodName = "ListUserCodespacesInOrg" + testBadOptions(t, methodName, func() error { + _, _, err := client.Codespaces.ListUserCodespacesInOrg(ctx, "\n", "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ListUserCodespacesInOrg(ctx, "o1", "u1", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteUserCodespaceInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o1/members/u1/codespaces/c1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Codespaces.DeleteUserCodespaceInOrg(ctx, "o1", "u1", "c1") + if err != nil { + t.Fatalf("DeleteUserCodespaceInOrg returned error: %v", err) + } + if resp == nil { + t.Fatal("DeleteUserCodespaceInOrg returned nil Response") + } + + const methodName = "DeleteUserCodespaceInOrg" + testBadOptions(t, methodName, func() error { + _, err := client.Codespaces.DeleteUserCodespaceInOrg(ctx, "\n", "u1", "c1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Codespaces.DeleteUserCodespaceInOrg(ctx, "o1", "u1", "c1") + }) +} + +func TestEnterpriseService_StopUserCodespaceInOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o1/members/u1/codespaces/c1/stop", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Codespaces.StopUserCodespaceInOrg(ctx, "o1", "u1", "c1") + if err != nil { + t.Fatalf("StopUserCodespaceInOrg returned error: %v", err) + } + if resp == nil { + t.Fatal("StopUserCodespaceInOrg returned nil Response") + } + + const methodName = "StopUserCodespaceInOrg" + testBadOptions(t, methodName, func() error { + _, err := client.Codespaces.StopUserCodespaceInOrg(ctx, "\n", "u1", "c1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Codespaces.StopUserCodespaceInOrg(ctx, "o1", "u1", "c1") + }) +} diff --git a/github/codespaces_secrets.go b/github/codespaces_secrets.go new file mode 100644 index 00000000000..0e5c578a66a --- /dev/null +++ b/github/codespaces_secrets.go @@ -0,0 +1,492 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" +) + +// ListUserSecrets list all secrets available for a users codespace +// +// Lists all secrets available for a user's Codespaces without revealing their encrypted values +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint +// GitHub Apps must have read access to the codespaces_user_secrets user permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#list-secrets-for-the-authenticated-user +// +//meta:operation GET /user/codespaces/secrets +func (s *CodespacesService) ListUserSecrets(ctx context.Context, opts *ListOptions) (*Secrets, *Response, error) { + u, err := addOptions("user/codespaces/secrets", opts) + if err != nil { + return nil, nil, err + } + return s.listSecrets(ctx, u) +} + +// ListOrgSecrets list all secrets available to an org +// +// Lists all Codespaces secrets available at the organization-level without revealing their encrypted values. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#list-organization-secrets +// +//meta:operation GET /orgs/{org}/codespaces/secrets +func (s *CodespacesService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/secrets", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + return s.listSecrets(ctx, u) +} + +// ListRepoSecrets list all secrets available to a repo +// +// Lists all secrets available in a repository without revealing their encrypted values. You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have write access to the codespaces_secrets repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/repository-secrets?apiVersion=2022-11-28#list-repository-secrets +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/secrets +func (s *CodespacesService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/secrets", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + return s.listSecrets(ctx, u) +} + +func (s *CodespacesService) listSecrets(ctx context.Context, url string) (*Secrets, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// GetUserPublicKey gets the users public key for encrypting codespace secrets +// +// Gets your public key, which you need to encrypt secrets. You need to encrypt a secret before you can create or update secrets. +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. +// GitHub Apps must have read access to the codespaces_user_secrets user permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#get-public-key-for-the-authenticated-user +// +//meta:operation GET /user/codespaces/secrets/public-key +func (s *CodespacesService) GetUserPublicKey(ctx context.Context) (*PublicKey, *Response, error) { + return s.getPublicKey(ctx, "user/codespaces/secrets/public-key") +} + +// GetOrgPublicKey gets the org public key for encrypting codespace secrets +// +// Gets a public key for an organization, which is required in order to encrypt secrets. You need to encrypt the value of a secret before you can create or update secrets. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#get-an-organization-public-key +// +//meta:operation GET /orgs/{org}/codespaces/secrets/public-key +func (s *CodespacesService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) { + return s.getPublicKey(ctx, fmt.Sprintf("orgs/%v/codespaces/secrets/public-key", org)) +} + +// GetRepoPublicKey gets the repo public key for encrypting codespace secrets +// +// Gets your public key, which you need to encrypt secrets. You need to encrypt a secret before you can create or update secrets. Anyone with read access to the repository can use this endpoint. If the repository is private you must use an access token with the repo scope. GitHub Apps must have write access to the codespaces_secrets repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/repository-secrets?apiVersion=2022-11-28#get-a-repository-public-key +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/secrets/public-key +func (s *CodespacesService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) { + return s.getPublicKey(ctx, fmt.Sprintf("repos/%v/%v/codespaces/secrets/public-key", owner, repo)) +} + +func (s *CodespacesService) getPublicKey(ctx context.Context, url string) (*PublicKey, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var publicKey *PublicKey + resp, err := s.client.Do(req, &publicKey) + if err != nil { + return nil, resp, err + } + + return publicKey, resp, nil +} + +// GetUserSecret gets a users codespace secret +// +// Gets a secret available to a user's codespaces without revealing its encrypted value. +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. +// GitHub Apps must have read access to the codespaces_user_secrets user permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#get-a-secret-for-the-authenticated-user +// +//meta:operation GET /user/codespaces/secrets/{secret_name} +func (s *CodespacesService) GetUserSecret(ctx context.Context, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("user/codespaces/secrets/%v", name) + return s.getSecret(ctx, u) +} + +// GetOrgSecret gets an org codespace secret +// +// Gets an organization secret without revealing its encrypted value. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#get-an-organization-secret +// +//meta:operation GET /orgs/{org}/codespaces/secrets/{secret_name} +func (s *CodespacesService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v", org, name) + return s.getSecret(ctx, u) +} + +// GetRepoSecret gets a repo codespace secret +// +// Gets a single repository secret without revealing its encrypted value. You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have write access to the codespaces_secrets repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/repository-secrets?apiVersion=2022-11-28#get-a-repository-secret +// +//meta:operation GET /repos/{owner}/{repo}/codespaces/secrets/{secret_name} +func (s *CodespacesService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/secrets/%v", owner, repo, name) + return s.getSecret(ctx, u) +} + +func (s *CodespacesService) getSecret(ctx context.Context, url string) (*Secret, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// CreateOrUpdateUserSecret creates or updates a users codespace secret +// +// Creates or updates a secret for a user's codespace with an encrypted value. Encrypt your secret using LibSodium. +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must also have Codespaces access to use this endpoint. +// GitHub Apps must have write access to the codespaces_user_secrets user permission and codespaces_secrets repository permission on all referenced repositories to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#create-or-update-a-secret-for-the-authenticated-user +// +//meta:operation PUT /user/codespaces/secrets/{secret_name} +func (s *CodespacesService) CreateOrUpdateUserSecret(ctx context.Context, body *EncryptedSecret) (*Response, error) { + if body == nil { + return nil, errors.New("encrypted secret must be provided") + } + + u := fmt.Sprintf("user/codespaces/secrets/%v", body.Name) + return s.createOrUpdateSecret(ctx, u, body) +} + +// CreateOrUpdateOrgSecret creates or updates an orgs codespace secret +// +// Creates or updates an organization secret with an encrypted value. Encrypt your secret using LibSodium. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret +// +//meta:operation PUT /orgs/{org}/codespaces/secrets/{secret_name} +func (s *CodespacesService) CreateOrUpdateOrgSecret(ctx context.Context, org string, body *EncryptedSecret) (*Response, error) { + if body == nil { + return nil, errors.New("encrypted secret must be provided") + } + + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v", org, body.Name) + return s.createOrUpdateSecret(ctx, u, body) +} + +// CreateOrUpdateRepoSecret creates or updates a repos codespace secret +// +// Creates or updates a repository secret with an encrypted value. Encrypt your secret using LibSodium. You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have write access to the codespaces_secrets repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/repository-secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret +// +//meta:operation PUT /repos/{owner}/{repo}/codespaces/secrets/{secret_name} +func (s *CodespacesService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, body *EncryptedSecret) (*Response, error) { + if body == nil { + return nil, errors.New("encrypted secret must be provided") + } + + u := fmt.Sprintf("repos/%v/%v/codespaces/secrets/%v", owner, repo, body.Name) + return s.createOrUpdateSecret(ctx, u, body) +} + +func (s *CodespacesService) createOrUpdateSecret(ctx context.Context, url string, body *EncryptedSecret) (*Response, error) { + req, err := s.client.NewRequest(ctx, "PUT", url, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DeleteUserSecret deletes a users codespace secret +// +// Deletes a secret from a user's codespaces using the secret name. Deleting the secret will remove access from all codespaces that were allowed to access the secret. +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. +// GitHub Apps must have write access to the codespaces_user_secrets user permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#delete-a-secret-for-the-authenticated-user +// +//meta:operation DELETE /user/codespaces/secrets/{secret_name} +func (s *CodespacesService) DeleteUserSecret(ctx context.Context, name string) (*Response, error) { + u := fmt.Sprintf("user/codespaces/secrets/%v", name) + return s.deleteSecret(ctx, u) +} + +// DeleteOrgSecret deletes an orgs codespace secret +// +// Deletes an organization secret using the secret name. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#delete-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/codespaces/secrets/{secret_name} +func (s *CodespacesService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v", org, name) + return s.deleteSecret(ctx, u) +} + +// DeleteRepoSecret deletes a repos codespace secret +// +// Deletes a secret in a repository using the secret name. You must authenticate using an access token with the repo scope to use this endpoint. GitHub Apps must have write access to the codespaces_secrets repository permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/repository-secrets?apiVersion=2022-11-28#delete-a-repository-secret +// +//meta:operation DELETE /repos/{owner}/{repo}/codespaces/secrets/{secret_name} +func (s *CodespacesService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/codespaces/secrets/%v", owner, repo, name) + return s.deleteSecret(ctx, u) +} + +func (s *CodespacesService) deleteSecret(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "DELETE", url, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListSelectedReposForUserSecret lists the repositories that have been granted the ability to use a user's codespace secret. +// +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. +// GitHub Apps must have read access to the codespaces_user_secrets user permission and write access to the codespaces_secrets repository permission on all referenced repositories to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#list-selected-repositories-for-a-user-secret +// +//meta:operation GET /user/codespaces/secrets/{secret_name}/repositories +func (s *CodespacesService) ListSelectedReposForUserSecret(ctx context.Context, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u := fmt.Sprintf("user/codespaces/secrets/%v/repositories", name) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + return s.listSelectedReposForSecret(ctx, u) +} + +// ListSelectedReposForOrgSecret lists the repositories that have been granted the ability to use an organization's codespace secret. +// +// Lists all repositories that have been selected when the visibility for repository access to a secret is set to selected. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#list-selected-repositories-for-an-organization-secret +// +//meta:operation GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories +func (s *CodespacesService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v/repositories", org, name) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + return s.listSelectedReposForSecret(ctx, u) +} + +func (s *CodespacesService) listSelectedReposForSecret(ctx context.Context, url string) (*SelectedReposList, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var repositories *SelectedReposList + resp, err := s.client.Do(req, &repositories) + if err != nil { + return nil, resp, err + } + + return repositories, resp, nil +} + +// SetSelectedReposForUserSecret sets the repositories that have been granted the ability to use a user's codespace secret. +// +// You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. +// GitHub Apps must have write access to the codespaces_user_secrets user permission and write access to the codespaces_secrets repository permission on all referenced repositories to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#set-selected-repositories-for-a-user-secret +// +//meta:operation PUT /user/codespaces/secrets/{secret_name}/repositories +func (s *CodespacesService) SetSelectedReposForUserSecret(ctx context.Context, name string, ids SelectedRepoIDs) (*Response, error) { + u := fmt.Sprintf("user/codespaces/secrets/%v/repositories", name) + return s.setSelectedRepoForSecret(ctx, u, ids) +} + +// SetSelectedReposForOrgSecret sets the repositories that have been granted the ability to use a user's codespace secret. +// +// Replaces all repositories for an organization secret when the visibility for repository access is set to selected. The visibility is set when you Create or update an organization secret. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#set-selected-repositories-for-an-organization-secret +// +//meta:operation PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories +func (s *CodespacesService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) { + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v/repositories", org, name) + return s.setSelectedRepoForSecret(ctx, u, ids) +} + +func (s *CodespacesService) setSelectedRepoForSecret(ctx context.Context, url string, ids SelectedRepoIDs) (*Response, error) { + type repoIDs struct { + SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"` + } + + req, err := s.client.NewRequest(ctx, "PUT", url, repoIDs{SelectedIDs: ids}) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AddSelectedRepoToUserSecret adds a repository to the list of repositories that have been granted the ability to use a user's codespace secret. +// +// Adds a repository to the selected repositories for a user's codespace secret. You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. GitHub Apps must have write access to the codespaces_user_secrets user permission and write access to the codespaces_secrets repository permission on the referenced repository to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#add-a-selected-repository-to-a-user-secret +// +//meta:operation PUT /user/codespaces/secrets/{secret_name}/repositories/{repository_id} +func (s *CodespacesService) AddSelectedRepoToUserSecret(ctx context.Context, name string, repo *Repository) (*Response, error) { + if repo == nil { + return nil, errors.New("repository must be provided") + } + if repo.ID == nil { + return nil, errors.New("id must be provided") + } + + u := fmt.Sprintf("user/codespaces/secrets/%v/repositories/%v", name, *repo.ID) + return s.addSelectedRepoToSecret(ctx, u) +} + +// AddSelectedRepoToOrgSecret adds a repository to the list of repositories that have been granted the ability to use an organization's codespace secret. +// +// Adds a repository to an organization secret when the visibility for repository access is set to selected. The visibility is set when you Create or update an organization secret. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#add-selected-repository-to-an-organization-secret +// +//meta:operation PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id} +func (s *CodespacesService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) { + if repo == nil { + return nil, errors.New("repository must be provided") + } + if repo.ID == nil { + return nil, errors.New("id must be provided") + } + + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v/repositories/%v", org, name, *repo.ID) + return s.addSelectedRepoToSecret(ctx, u) +} + +func (s *CodespacesService) addSelectedRepoToSecret(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "PUT", url, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// RemoveSelectedRepoFromUserSecret removes a repository from the list of repositories that have been granted the ability to use a user's codespace secret. +// +// Removes a repository from the selected repositories for a user's codespace secret. You must authenticate using an access token with the codespace or codespace:secrets scope to use this endpoint. User must have Codespaces access to use this endpoint. GitHub Apps must have write access to the codespaces_user_secrets user permission to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/secrets?apiVersion=2022-11-28#remove-a-selected-repository-from-a-user-secret +// +//meta:operation DELETE /user/codespaces/secrets/{secret_name}/repositories/{repository_id} +func (s *CodespacesService) RemoveSelectedRepoFromUserSecret(ctx context.Context, name string, repo *Repository) (*Response, error) { + if repo == nil { + return nil, errors.New("repository must be provided") + } + if repo.ID == nil { + return nil, errors.New("id must be provided") + } + + u := fmt.Sprintf("user/codespaces/secrets/%v/repositories/%v", name, *repo.ID) + return s.removeSelectedRepoFromSecret(ctx, u) +} + +// RemoveSelectedRepoFromOrgSecret removes a repository from the list of repositories that have been granted the ability to use an organization's codespace secret. +// +// Removes a repository from an organization secret when the visibility for repository access is set to selected. The visibility is set when you Create or update an organization secret. You must authenticate using an access token with the admin:org scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/codespaces/organization-secrets?apiVersion=2022-11-28#remove-selected-repository-from-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id} +func (s *CodespacesService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) { + if repo == nil { + return nil, errors.New("repository must be provided") + } + if repo.ID == nil { + return nil, errors.New("id must be provided") + } + + u := fmt.Sprintf("orgs/%v/codespaces/secrets/%v/repositories/%v", org, name, *repo.ID) + return s.removeSelectedRepoFromSecret(ctx, u) +} + +func (s *CodespacesService) removeSelectedRepoFromSecret(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "DELETE", url, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/codespaces_secrets_test.go b/github/codespaces_secrets_test.go new file mode 100644 index 00000000000..d495b7003d5 --- /dev/null +++ b/github/codespaces_secrets_test.go @@ -0,0 +1,791 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodespacesService_ListSecrets(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*Secrets, *Response, error) + badCall func(context.Context, *Client) (*Secrets, *Response, error) + methodName string + } + opts := &ListOptions{Page: 2, PerPage: 2} + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + }, + call: func(ctx context.Context, client *Client) (*Secrets, *Response, error) { + return client.Codespaces.ListUserSecrets(ctx, opts) + }, + methodName: "ListUserSecrets", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + }, + call: func(ctx context.Context, client *Client) (*Secrets, *Response, error) { + return client.Codespaces.ListOrgSecrets(ctx, "o", opts) + }, + badCall: func(ctx context.Context, client *Client) (*Secrets, *Response, error) { + return client.Codespaces.ListOrgSecrets(ctx, "\n", opts) + }, + methodName: "ListOrgSecrets", + }, + { + name: "Repo", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/repos/o/r/codespaces/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + }, + call: func(ctx context.Context, client *Client) (*Secrets, *Response, error) { + return client.Codespaces.ListRepoSecrets(ctx, "o", "r", opts) + }, + badCall: func(ctx context.Context, client *Client) (*Secrets, *Response, error) { + return client.Codespaces.ListRepoSecrets(ctx, "\n", "\n", opts) + }, + methodName: "ListRepoSecrets", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + secrets, _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Codespaces.%v returned %+v, want %+v", tt.methodName, secrets, want) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + got, resp, err := tt.call(ctx, client) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", tt.methodName, got) + } + return resp, err + }) + }) + } +} + +func TestCodespacesService_GetSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*Secret, *Response, error) + badCall func(context.Context, *Client) (*Secret, *Response, error) + methodName string + } + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + }, + call: func(ctx context.Context, client *Client) (*Secret, *Response, error) { + return client.Codespaces.GetUserSecret(ctx, "NAME") + }, + methodName: "GetUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + }, + call: func(ctx context.Context, client *Client) (*Secret, *Response, error) { + return client.Codespaces.GetOrgSecret(ctx, "o", "NAME") + }, + badCall: func(ctx context.Context, client *Client) (*Secret, *Response, error) { + return client.Codespaces.GetOrgSecret(ctx, "\n", "\n") + }, + methodName: "GetOrgSecret", + }, + { + name: "Repo", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/repos/o/r/codespaces/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + }, + call: func(ctx context.Context, client *Client) (*Secret, *Response, error) { + return client.Codespaces.GetRepoSecret(ctx, "o", "r", "NAME") + }, + badCall: func(ctx context.Context, client *Client) (*Secret, *Response, error) { + return client.Codespaces.GetRepoSecret(ctx, "\n", "\n", "\n") + }, + methodName: "GetRepoSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + secret, _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + want := &Secret{Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)} + if !cmp.Equal(secret, want) { + t.Errorf("Codespaces.%v returned %+v, want %+v", tt.methodName, secret, want) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + got, resp, err := tt.call(ctx, client) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", tt.methodName, got) + } + return resp, err + }) + }) + } +} + +func TestCodespacesService_CreateOrUpdateSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client, *EncryptedSecret) (*Response, error) + badCall func(context.Context, *Client, *EncryptedSecret) (*Response, error) + methodName string + } + + want := &EncryptedSecret{ + EncryptedValue: "QIv=", + KeyID: "1234", + } + + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + }, + call: func(ctx context.Context, client *Client, e *EncryptedSecret) (*Response, error) { + return client.Codespaces.CreateOrUpdateUserSecret(ctx, e) + }, + badCall: func(ctx context.Context, client *Client, _ *EncryptedSecret) (*Response, error) { + return client.Codespaces.CreateOrUpdateUserSecret(ctx, nil) + }, + methodName: "CreateOrUpdateUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + }, + call: func(ctx context.Context, client *Client, e *EncryptedSecret) (*Response, error) { + return client.Codespaces.CreateOrUpdateOrgSecret(ctx, "o", e) + }, + badCall: func(ctx context.Context, client *Client, e *EncryptedSecret) (*Response, error) { + return client.Codespaces.CreateOrUpdateOrgSecret(ctx, "\n", e) + }, + methodName: "CreateOrUpdateOrgSecret", + }, + { + name: "Repo", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/repos/o/r/codespaces/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + }, + call: func(ctx context.Context, client *Client, e *EncryptedSecret) (*Response, error) { + return client.Codespaces.CreateOrUpdateRepoSecret(ctx, "o", "r", e) + }, + badCall: func(ctx context.Context, client *Client, e *EncryptedSecret) (*Response, error) { + return client.Codespaces.CreateOrUpdateRepoSecret(ctx, "\n", "\n", e) + }, + methodName: "CreateOrUpdateRepoSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + input := &EncryptedSecret{ + Name: "NAME", + EncryptedValue: "QIv=", + KeyID: "1234", + } + ctx := t.Context() + _, err := tt.call(ctx, client, input) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, err = tt.badCall(ctx, client, input) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + return tt.call(ctx, client, input) + }) + }) + } +} + +func TestCodespacesService_DeleteSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*Response, error) + badCall func(context.Context, *Client) (*Response, error) + methodName string + } + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.DeleteUserSecret(ctx, "NAME") + }, + methodName: "DeleteUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.DeleteOrgSecret(ctx, "o", "NAME") + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.DeleteOrgSecret(ctx, "\n", "\n") + }, + methodName: "DeleteOrgSecret", + }, + { + name: "Repo", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/repos/o/r/codespaces/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.DeleteRepoSecret(ctx, "o", "r", "NAME") + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.DeleteRepoSecret(ctx, "\n", "\n", "\n") + }, + methodName: "DeleteRepoSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + return tt.call(ctx, client) + }) + }) + } +} + +func TestCodespacesService_GetPublicKey(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*PublicKey, *Response, error) + badCall func(context.Context, *Client) (*PublicKey, *Response, error) + methodName string + } + + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + }, + call: func(ctx context.Context, client *Client) (*PublicKey, *Response, error) { + return client.Codespaces.GetUserPublicKey(ctx) + }, + methodName: "GetUserPublicKey", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + }, + call: func(ctx context.Context, client *Client) (*PublicKey, *Response, error) { + return client.Codespaces.GetOrgPublicKey(ctx, "o") + }, + badCall: func(ctx context.Context, client *Client) (*PublicKey, *Response, error) { + return client.Codespaces.GetOrgPublicKey(ctx, "\n") + }, + methodName: "GetOrgPublicKey", + }, + { + name: "Repo", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/repos/o/r/codespaces/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + }, + call: func(ctx context.Context, client *Client) (*PublicKey, *Response, error) { + return client.Codespaces.GetRepoPublicKey(ctx, "o", "r") + }, + badCall: func(ctx context.Context, client *Client) (*PublicKey, *Response, error) { + return client.Codespaces.GetRepoPublicKey(ctx, "\n", "\n") + }, + methodName: "GetRepoPublicKey", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + key, _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Codespaces.%v returned %+v, want %+v", tt.methodName, key, want) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + got, resp, err := tt.call(ctx, client) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", tt.methodName, got) + } + return resp, err + }) + }) + } +} + +func TestCodespacesService_ListSelectedReposForSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*SelectedReposList, *Response, error) + badCall func(context.Context, *Client) (*SelectedReposList, *Response, error) + methodName string + } + opts := &ListOptions{Page: 2, PerPage: 2} + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + }, + call: func(ctx context.Context, client *Client) (*SelectedReposList, *Response, error) { + return client.Codespaces.ListSelectedReposForUserSecret(ctx, "NAME", opts) + }, + methodName: "ListSelectedReposForUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + }, + call: func(ctx context.Context, client *Client) (*SelectedReposList, *Response, error) { + return client.Codespaces.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + }, + badCall: func(ctx context.Context, client *Client) (*SelectedReposList, *Response, error) { + return client.Codespaces.ListSelectedReposForOrgSecret(ctx, "\n", "\n", opts) + }, + methodName: "ListSelectedReposForOrgSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + repos, _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + want := &SelectedReposList{ + TotalCount: Ptr(1), + Repositories: []*Repository{ + {ID: Ptr(int64(1))}, + }, + } + + if !cmp.Equal(repos, want) { + t.Errorf("Codespaces.%v returned %+v, want %+v", tt.methodName, repos, want) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + got, resp, err := tt.call(ctx, client) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", tt.methodName, got) + } + return resp, err + }) + }) + } +} + +func TestCodespacesService_SetSelectedReposForSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*Response, error) + badCall func(context.Context, *Client) (*Response, error) + methodName string + } + ids := SelectedRepoIDs{64780797} + want := struct { + SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"` + }{ + SelectedIDs: ids, + } + + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, want) + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.SetSelectedReposForUserSecret(ctx, "NAME", ids) + }, + methodName: "SetSelectedReposForUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, want) + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.SetSelectedReposForOrgSecret(ctx, "o", "NAME", ids) + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.SetSelectedReposForOrgSecret(ctx, "\n", "\n", ids) + }, + methodName: "SetSelectedReposForOrgSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + return tt.call(ctx, client) + }) + }) + } +} + +func TestCodespacesService_AddSelectedReposForSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*Response, error) + badCall func(context.Context, *Client) (*Response, error) + methodName string + } + repo := &Repository{ID: Ptr(int64(1234))} + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.AddSelectedRepoToUserSecret(ctx, "NAME", repo) + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.AddSelectedRepoToUserSecret(ctx, "NAME", &Repository{ID: nil}) + }, + methodName: "AddSelectedRepoToUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repo) + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.AddSelectedRepoToOrgSecret(ctx, "\n", "\n", repo) + }, + methodName: "AddSelectedRepoToOrgSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + return tt.call(ctx, client) + }) + }) + } +} + +func TestCodespacesService_RemoveSelectedReposFromSecret(t *testing.T) { + t.Parallel() + type test struct { + name string + handleFunc func(*http.ServeMux) + call func(context.Context, *Client) (*Response, error) + badCall func(context.Context, *Client) (*Response, error) + methodName string + } + repo := &Repository{ID: Ptr(int64(1234))} + tests := []*test{ + { + name: "User", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/user/codespaces/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.RemoveSelectedRepoFromUserSecret(ctx, "NAME", repo) + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.RemoveSelectedRepoFromUserSecret(ctx, "NAME", nil) + }, + methodName: "RemoveSelectedRepoFromUserSecret", + }, + { + name: "Org", + handleFunc: func(mux *http.ServeMux) { + mux.HandleFunc("/orgs/o/codespaces/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + }, + call: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repo) + }, + badCall: func(ctx context.Context, client *Client) (*Response, error) { + return client.Codespaces.RemoveSelectedRepoFromOrgSecret(ctx, "\n", "\n", repo) + }, + methodName: "RemoveSelectedRepoFromOrgSecret", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tt.handleFunc(mux) + + ctx := t.Context() + _, err := tt.call(ctx, client) + if err != nil { + t.Errorf("Codespaces.%v returned error: %v", tt.methodName, err) + } + + if tt.badCall != nil { + testBadOptions(t, tt.methodName, func() (err error) { + _, err = tt.badCall(ctx, client) + return err + }) + } + + testNewRequestAndDoFailure(t, tt.methodName, client, func() (*Response, error) { + return tt.call(ctx, client) + }) + }) + } +} diff --git a/github/codespaces_test.go b/github/codespaces_test.go new file mode 100644 index 00000000000..7be8eb434f8 --- /dev/null +++ b/github/codespaces_test.go @@ -0,0 +1,766 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCodespacesService_ListInRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + }) + fmt.Fprint(w, `{"total_count":2,"codespaces":[{"id":1,"name":"monalisa-octocat-hello-world-g4wpq6h95q","environment_id":"26a7c758-7299-4a73-b978-5a92a7ae98a0","owner":{"login":"octocat"},"billable_owner":{"login":"octocat"},"repository":{"id":1296269},"machine":{"name":"standardLinux","display_name":"4 cores, 8 GB RAM, 64 GB storage","operating_system":"linux","storage_in_bytes":68719476736,"memory_in_bytes":8589934592,"cpus":4},"prebuild":false,"devcontainer_path":".devcontainer/devcontainer.json","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"last_used_at":`+refTimeStr(1136178002)+`,"state":"Available","url":"https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q","git_status":{"ahead":0,"behind":0,"has_unpushed_changes":false,"has_uncommitted_changes":false,"ref":"main"},"location":"WestUs2","idle_timeout_minutes":60,"web_url":"https://monalisa-octocat-hello-world-g4wpq6h95q.github.dev","machines_url":"https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q/machines","start_url":"https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q/start","stop_url":"https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q/stop","recent_folders":["testfolder1","testfolder2"]},{"id":2}]}`) + }) + + opt := &ListOptions{Page: 1, PerPage: 2} + ctx := t.Context() + codespaces, _, err := client.Codespaces.ListInRepo(ctx, "owner", "repo", opt) + if err != nil { + t.Errorf("Codespaces.ListInRepo returned error: %v", err) + } + + want := &ListCodespaces{TotalCount: Ptr(2), Codespaces: []*Codespace{ + { + ID: Ptr(int64(1)), + Name: Ptr("monalisa-octocat-hello-world-g4wpq6h95q"), + EnvironmentID: Ptr("26a7c758-7299-4a73-b978-5a92a7ae98a0"), + Owner: &User{ + Login: Ptr("octocat"), + }, + BillableOwner: &User{ + Login: Ptr("octocat"), + }, + Repository: &Repository{ + ID: Ptr(int64(1296269)), + }, + Machine: &CodespacesMachine{ + Name: Ptr("standardLinux"), + DisplayName: Ptr("4 cores, 8 GB RAM, 64 GB storage"), + OperatingSystem: Ptr("linux"), + StorageInBytes: Ptr(int64(68719476736)), + MemoryInBytes: Ptr(int64(8589934592)), + CPUs: Ptr(4), + }, + Prebuild: Ptr(false), + DevcontainerPath: Ptr(".devcontainer/devcontainer.json"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + LastUsedAt: refTimestamp(1136178002), + State: Ptr("Available"), + URL: Ptr("https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q"), + GitStatus: &CodespacesGitStatus{ + Ahead: Ptr(0), + Behind: Ptr(0), + HasUnpushedChanges: Ptr(false), + HasUncommittedChanges: Ptr(false), + Ref: Ptr("main"), + }, + Location: Ptr("WestUs2"), + IdleTimeoutMinutes: Ptr(60), + WebURL: Ptr("https://monalisa-octocat-hello-world-g4wpq6h95q.github.dev"), + MachinesURL: Ptr("https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q/machines"), + StartURL: Ptr("https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q/start"), + StopURL: Ptr("https://api.github.com/user/codespaces/monalisa-octocat-hello-world-g4wpq6h95q/stop"), + RecentFolders: []string{ + "testfolder1", + "testfolder2", + }, + }, + { + ID: Ptr(int64(2)), + }, + }} + if !cmp.Equal(codespaces, want) { + t.Errorf("Codespaces.ListInRepo returned %+v, want %+v", codespaces, want) + } + + const methodName = "ListInRepo" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ListInRepo(ctx, "", "", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_List(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "2", + "repository_id": "1296269", + }) + fmt.Fprint(w, `{"total_count":1,"codespaces":[{"id":1, "repository": {"id": 1296269}}]}`) + }) + + opt := &ListCodespacesOptions{ListOptions: ListOptions{Page: 1, PerPage: 2}, RepositoryID: 1296269} + ctx := t.Context() + codespaces, _, err := client.Codespaces.List(ctx, opt) + if err != nil { + t.Errorf("Codespaces.List returned error: %v", err) + } + + want := &ListCodespaces{TotalCount: Ptr(1), Codespaces: []*Codespace{ + { + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(1296269)), + }, + }, + }} + if !cmp.Equal(codespaces, want) { + t.Errorf("Codespaces.List returned %+v, want %+v", codespaces, want) + } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.List(ctx, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_CreateInRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateCodespaceOptions{ + Ref: Ptr("main"), + Geo: Ptr("WestUs2"), + Machine: Ptr("standardLinux"), + IdleTimeoutMinutes: Ptr(60), + } + + mux.HandleFunc("/repos/owner/repo/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1, "repository": {"id": 1296269}}`) + }) + + ctx := t.Context() + codespace, _, err := client.Codespaces.CreateInRepo(ctx, "owner", "repo", input) + if err != nil { + t.Errorf("Codespaces.CreateInRepo returned error: %v", err) + } + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(1296269)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.CreateInRepo returned %+v, want %+v", codespace, want) + } + + const methodName = "CreateInRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Codespaces.CreateInRepo(ctx, "\n", "", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.CreateInRepo(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Start(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1/start", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1, "repository": {"id": 1296269}}`) + }) + ctx := t.Context() + codespace, _, err := client.Codespaces.Start(ctx, "codespace_1") + if err != nil { + t.Errorf("Codespaces.Start returned error: %v", err) + } + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(1296269)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.Start returned %+v, want %+v", codespace, want) + } + + const methodName = "Start" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Codespaces.Start(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.Start(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Stop(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1/stop", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1, "repository": {"id": 1296269}}`) + }) + ctx := t.Context() + codespace, _, err := client.Codespaces.Stop(ctx, "codespace_1") + if err != nil { + t.Errorf("Codespaces.Stop returned error: %v", err) + } + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(1296269)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.Stop returned %+v, want %+v", codespace, want) + } + + const methodName = "Stop" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Codespaces.Stop(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.Stop(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Delete(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Codespaces.Delete(ctx, "codespace_1") + if err != nil { + t.Errorf("Codespaces.Delete return error: %v", err) + } + + const methodName = "Delete" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Codespaces.Delete(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Codespaces.Delete(ctx, "codespace_1") + }) +} + +func TestCodespacesService_ListDevContainerConfigurations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/codespaces/devcontainers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "devcontainers": [{ + "path": ".devcontainer/foobar/devcontainer.json", + "name": "foobar", + "display_name": "foobar" + }] + }`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + + got, _, err := client.Codespaces.ListDevContainerConfigurations(ctx, "o", "r", opts) + if err != nil { + t.Fatalf("Codespaces.ListDevContainerConfigurations returned error: %v", err) + } + + want := &DevContainerConfigurations{ + TotalCount: 1, + Devcontainers: []*DevContainer{ + { + Path: ".devcontainer/foobar/devcontainer.json", + Name: Ptr("foobar"), + DisplayName: Ptr("foobar"), + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Codespaces.ListDevContainerConfigurations = %+v, want %+v", got, want) + } + + const methodName = "ListDevContainerConfigurations" + + testBadOptions(t, methodName, func() error { + _, _, err := client.Codespaces.ListDevContainerConfigurations(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ListDevContainerConfigurations(ctx, "e", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_GetDefaultAttributes(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/codespaces/new", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "billable_owner": { + "login": "user1", + "id": 1001, + "url": "https://example.com/user1" + }, + "defaults": { + "devcontainer_path": ".devcontainer/devcontainer.json", + "location": "WestUs2" + } + }`) + }) + + ctx := t.Context() + + opt := &CodespaceGetDefaultAttributesOptions{ + Ref: Ptr("main"), + ClientIP: Ptr("1.2.3.4"), + } + + got, _, err := client.Codespaces.GetDefaultAttributes(ctx, "o", "r", opt) + if err != nil { + t.Fatalf("Codespaces.GetDefaultAttributes returned error: %v", err) + } + + want := &CodespaceDefaultAttributes{ + BillableOwner: &User{ + Login: Ptr("user1"), + ID: Ptr(int64(1001)), + URL: Ptr("https://example.com/user1"), + }, + Defaults: &CodespaceDefaults{ + DevcontainerPath: Ptr(".devcontainer/devcontainer.json"), + Location: "WestUs2", + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Codespaces.GetDefaultAttributes = %+v, want %+v", got, want) + } + + const methodName = "GetDefaultAttributes" + + testBadOptions(t, methodName, func() error { + _, _, err := client.Codespaces.GetDefaultAttributes(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.GetDefaultAttributes(ctx, "e", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_CheckPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/codespaces/permissions_check", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"accepted": true}`) + }) + + ctx := t.Context() + hasPermission, _, err := client.Codespaces.CheckPermissions(ctx, "o", "r", "main", "path") + if err != nil { + t.Errorf("Codespaces.CheckPermissions returned error: %v", err) + } + + want := CodespacePermissions{Accepted: true} + if !cmp.Equal(hasPermission, &want) { + t.Errorf("Codespaces.CheckPermissions = %+v, want %+v", hasPermission, want) + } + + const methodName = "CheckPermissions" + + testBadOptions(t, methodName, func() error { + _, _, err := client.Codespaces.CheckPermissions(ctx, "\n", "\n", "main", "path") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.CheckPermissions(ctx, "o", "r", "main", "path") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_CreateFromPullRequest(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateCodespaceOptions{ + Machine: Ptr("standardLinux"), + IdleTimeoutMinutes: Ptr(60), + } + + mux.HandleFunc("/repos/owner/repo/pulls/42/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1, "repository": {"id": 1}}`) + }) + + ctx := t.Context() + codespace, _, err := client.Codespaces.CreateFromPullRequest(ctx, "owner", "repo", 42, input) + if err != nil { + t.Errorf("Codespaces.CreateFromPullRequest returned error: %v", err) + } + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(1)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.CreateFromPullRequest returned %+v, want %+v", codespace, want) + } + + const methodName = "CreateFromPullRequest" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Codespaces.CreateFromPullRequest(ctx, "\n", "", 0, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.CreateFromPullRequest(ctx, "o", "r", 42, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Create(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &CodespaceCreateForUserOptions{ + Ref: Ptr("main"), + Geo: Ptr("WestUs2"), + Machine: Ptr("standardLinux"), + IdleTimeoutMinutes: Ptr(60), + RepositoryID: int64(111), + PullRequest: nil, + } + + mux.HandleFunc("/user/codespaces", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, opt) + fmt.Fprint(w, `{"id":1,"repository":{"id":111}}`) + }) + + ctx := t.Context() + codespace, _, err := client.Codespaces.Create( + ctx, + opt, + ) + if err != nil { + t.Fatalf("Codespaces.Create returned error: %v", err) + } + + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(111)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.Create returned %+v, want %+v", codespace, want) + } + + const methodName = "Create" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.Create( + ctx, + opt, + ) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Get(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1,"repository":{"id":111}}`) + }) + + ctx := t.Context() + codespace, _, err := client.Codespaces.Get(ctx, "codespace_1") + if err != nil { + t.Fatalf("Codespaces.Get returned error: %v", err) + } + + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(111)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.Get returned %+v, want %+v", codespace, want) + } + + const methodName = "Get" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.Get(ctx, "codespace_1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Update(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &UpdateCodespaceOptions{ + Machine: Ptr("standardLinux"), + RecentFolders: []string{ + "folder1", + "folder2", + }, + } + + mux.HandleFunc("/user/codespaces/codespace_1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, opt) + fmt.Fprint(w, `{"id":1,"repository":{"id":111}}`) + }) + + ctx := t.Context() + codespace, _, err := client.Codespaces.Update( + ctx, + "codespace_1", + opt, + ) + if err != nil { + t.Fatalf("Codespaces.Update returned error: %v", err) + } + + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(111)), + }, + } + + if !cmp.Equal(codespace, want) { + t.Errorf("Codespaces.Update returned %+v, want %+v", codespace, want) + } + + const methodName = "Update" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.Update( + ctx, + "codespace_1", + opt, + ) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_ExportCodespace(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1/exports", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "state": "succeeded", + "completed_at": `+referenceTimeStr+`, + "branch": "main", + "export_url": "https://api.github.com/user/codespaces/:name/exports/latest" + }`) + }) + + ctx := t.Context() + export, _, err := client.Codespaces.ExportCodespace(ctx, "codespace_1") + if err != nil { + t.Fatalf("Codespaces.ExportCodespace returned error: %v", err) + } + + want := &CodespaceExport{ + State: Ptr("succeeded"), + CompletedAt: &referenceTimestamp, + Branch: Ptr("main"), + ExportURL: Ptr("https://api.github.com/user/codespaces/:name/exports/latest"), + } + + if !cmp.Equal(export, want) { + t.Errorf("Codespaces.ExportCodespace returned %+v, want %+v", export, want) + } + + const methodName = "ExportCodespace" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.ExportCodespace(ctx, "codespace_1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_GetLatestCodespaceExport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/codespaces/codespace_1/exports/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "state": "succeeded", + "completed_at": `+referenceTimeStr+`, + "branch": "main", + "export_url": "https://api.github.com/user/codespaces/:name/exports/latest" + }`) + }) + + ctx := t.Context() + export, _, err := client.Codespaces.GetLatestCodespaceExport(ctx, "codespace_1") + if err != nil { + t.Fatalf("Codespaces.GetLatestCodespaceExport returned error: %v", err) + } + + want := &CodespaceExport{ + State: Ptr("succeeded"), + CompletedAt: &referenceTimestamp, + Branch: Ptr("main"), + ExportURL: Ptr("https://api.github.com/user/codespaces/:name/exports/latest"), + } + + if !cmp.Equal(export, want) { + t.Errorf("Codespaces.GetLatestCodespaceExport returned %+v, want %+v", export, want) + } + + const methodName = "GetLatestCodespaceExport" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.GetLatestCodespaceExport(ctx, "codespace_1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCodespacesService_Publish(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &PublishCodespaceOptions{ + Name: Ptr("repo"), + Private: Ptr(true), + } + + mux.HandleFunc("/user/codespaces/codespace_1/publish", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, opt) + fmt.Fprint(w, `{"id":1,"repository":{"id":111}}`) + }) + + ctx := t.Context() + repo, _, err := client.Codespaces.Publish( + ctx, + "codespace_1", + opt, + ) + if err != nil { + t.Fatalf("Codespaces.Publish returned error: %v", err) + } + + want := &Codespace{ + ID: Ptr(int64(1)), + Repository: &Repository{ + ID: Ptr(int64(111)), + }, + } + if !cmp.Equal(repo, want) { + t.Errorf("Codespaces.Publish returned %+v, want %+v", repo, want) + } + + const methodName = "Publish" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Codespaces.Publish( + ctx, + "codespace_1", + opt, + ) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/copilot.go b/github/copilot.go new file mode 100644 index 00000000000..d5f0f085b8f --- /dev/null +++ b/github/copilot.go @@ -0,0 +1,1251 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "time" +) + +// CopilotService provides access to the Copilot-related functions +// in the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/copilot?apiVersion=2022-11-28 +type CopilotService service + +// CopilotOrganizationDetails represents the details of an organization's Copilot for Business subscription. +type CopilotOrganizationDetails struct { + SeatBreakdown *CopilotSeatBreakdown `json:"seat_breakdown"` + PublicCodeSuggestions string `json:"public_code_suggestions"` + CopilotChat string `json:"copilot_chat"` + SeatManagementSetting string `json:"seat_management_setting"` +} + +// CopilotSeatBreakdown represents the breakdown of Copilot for Business seats for the organization. +type CopilotSeatBreakdown struct { + Total int `json:"total"` + AddedThisCycle int `json:"added_this_cycle"` + PendingCancellation int `json:"pending_cancellation"` + PendingInvitation int `json:"pending_invitation"` + ActiveThisCycle int `json:"active_this_cycle"` + InactiveThisCycle int `json:"inactive_this_cycle"` +} + +// ListCopilotSeatsResponse represents the Copilot for Business seat assignments for an organization. +type ListCopilotSeatsResponse struct { + TotalSeats int64 `json:"total_seats"` + Seats []*CopilotSeatDetails `json:"seats"` +} + +// CopilotSeatDetails represents the details of a Copilot for Business seat. +type CopilotSeatDetails struct { + // Assignee can either be a User, Team, or Organization. + Assignee any `json:"assignee"` + AssigningTeam *Team `json:"assigning_team,omitempty"` + PendingCancellationDate *string `json:"pending_cancellation_date,omitempty"` + LastActivityAt *Timestamp `json:"last_activity_at,omitempty"` + LastActivityEditor *string `json:"last_activity_editor,omitempty"` + CreatedAt *Timestamp `json:"created_at"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + PlanType *string `json:"plan_type,omitempty"` +} + +// SeatAssignments represents the number of seats assigned. +type SeatAssignments struct { + SeatsCreated int `json:"seats_created"` +} + +// SeatCancellations represents the number of seats cancelled. +type SeatCancellations struct { + SeatsCancelled int `json:"seats_cancelled"` +} + +// CopilotMetricsListOptions represents the optional parameters to the CopilotService get metrics methods. +type CopilotMetricsListOptions struct { + Since *time.Time `url:"since,omitempty"` + Until *time.Time `url:"until,omitempty"` + + ListOptions +} + +// CopilotIDECodeCompletionsLanguage represents Copilot usage metrics for completions in the IDE for a language. +type CopilotIDECodeCompletionsLanguage struct { + Name string `json:"name"` + TotalEngagedUsers int `json:"total_engaged_users"` +} + +// CopilotIDECodeCompletionsModelLanguage represents Copilot usage metrics for completions in the IDE for a model and language. +type CopilotIDECodeCompletionsModelLanguage struct { + Name string `json:"name"` + TotalEngagedUsers int `json:"total_engaged_users"` + TotalCodeSuggestions int `json:"total_code_suggestions"` + TotalCodeAcceptances int `json:"total_code_acceptances"` + TotalCodeLinesSuggested int `json:"total_code_lines_suggested"` + TotalCodeLinesAccepted int `json:"total_code_lines_accepted"` +} + +// CopilotIDECodeCompletionsModel represents Copilot usage metrics for completions in the IDE for a model. +type CopilotIDECodeCompletionsModel struct { + Name string `json:"name"` + IsCustomModel bool `json:"is_custom_model"` + CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"` + TotalEngagedUsers int `json:"total_engaged_users"` + Languages []*CopilotIDECodeCompletionsModelLanguage `json:"languages"` +} + +// CopilotIDECodeCompletionsEditor represents Copilot usage metrics for completions in the IDE for an editor. +type CopilotIDECodeCompletionsEditor struct { + Name string `json:"name"` + TotalEngagedUsers int `json:"total_engaged_users"` + Models []*CopilotIDECodeCompletionsModel `json:"models"` +} + +// CopilotIDECodeCompletions represents Copilot usage metrics for Copilot code completions in the IDE, categorized by editor, model and language. +type CopilotIDECodeCompletions struct { + TotalEngagedUsers int `json:"total_engaged_users"` + Languages []*CopilotIDECodeCompletionsLanguage `json:"languages"` + Editors []*CopilotIDECodeCompletionsEditor `json:"editors"` +} + +// CopilotIDEChatModel represents Copilot usage metrics for chatting with a model in the IDE. +type CopilotIDEChatModel struct { + Name string `json:"name"` + IsCustomModel bool `json:"is_custom_model"` + CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"` + TotalEngagedUsers int `json:"total_engaged_users"` + TotalChats int `json:"total_chats"` + TotalChatInsertionEvents int `json:"total_chat_insertion_events"` + TotalChatCopyEvents int `json:"total_chat_copy_events"` +} + +// CopilotIDEChatEditor represents Copilot usage metrics for chatting with a model in the IDE, categorized by editor and model. +type CopilotIDEChatEditor struct { + Name string `json:"name"` + TotalEngagedUsers int `json:"total_engaged_users"` + Models []*CopilotIDEChatModel `json:"models"` +} + +// CopilotIDEChat represents Copilot usage metrics for Copilot Chat in the IDE, categorized by editor and model. +type CopilotIDEChat struct { + TotalEngagedUsers int `json:"total_engaged_users"` + Editors []*CopilotIDEChatEditor `json:"editors"` +} + +// CopilotDotcomChatModel represents Copilot usage metrics for chatting with a model in the webbrowser. +type CopilotDotcomChatModel struct { + Name string `json:"name"` + IsCustomModel bool `json:"is_custom_model"` + CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"` + TotalEngagedUsers int `json:"total_engaged_users"` + TotalChats int `json:"total_chats"` +} + +// CopilotDotcomChat represents Copilot usage metrics for Copilot Chat in the webbrowser, categorized by model. +type CopilotDotcomChat struct { + TotalEngagedUsers int `json:"total_engaged_users"` + Models []*CopilotDotcomChatModel `json:"models"` +} + +// CopilotDotcomPullRequestsModel represents Copilot usage metrics for pull requests in the webbrowser, categorized by model. +type CopilotDotcomPullRequestsModel struct { + Name string `json:"name"` + IsCustomModel bool `json:"is_custom_model"` + CustomModelTrainingDate *string `json:"custom_model_training_date,omitempty"` + TotalPRSummariesCreated int `json:"total_pr_summaries_created"` + TotalEngagedUsers int `json:"total_engaged_users"` +} + +// CopilotDotcomPullRequestsRepository represents Copilot usage metrics for pull requests in the webbrowser, categorized by repository. +type CopilotDotcomPullRequestsRepository struct { + Name string `json:"name"` + TotalEngagedUsers int `json:"total_engaged_users"` + Models []*CopilotDotcomPullRequestsModel `json:"models"` +} + +// CopilotDotcomPullRequests represents Copilot usage metrics for pull requests in the webbrowser, categorized by repository and model. +type CopilotDotcomPullRequests struct { + TotalEngagedUsers int `json:"total_engaged_users"` + Repositories []*CopilotDotcomPullRequestsRepository `json:"repositories"` +} + +// CopilotMetrics represents Copilot usage metrics for a given day. +type CopilotMetrics struct { + Date string `json:"date"` + TotalActiveUsers *int `json:"total_active_users,omitempty"` + TotalEngagedUsers *int `json:"total_engaged_users,omitempty"` + CopilotIDECodeCompletions *CopilotIDECodeCompletions `json:"copilot_ide_code_completions,omitempty"` + CopilotIDEChat *CopilotIDEChat `json:"copilot_ide_chat,omitempty"` + CopilotDotcomChat *CopilotDotcomChat `json:"copilot_dotcom_chat,omitempty"` + CopilotDotcomPullRequests *CopilotDotcomPullRequests `json:"copilot_dotcom_pull_requests,omitempty"` +} + +// CopilotMetricsReportOptions specifies the optional parameters for single-day metrics report endpoints. +type CopilotMetricsReportOptions struct { + Day string `url:"day"` // Required, format: YYYY-MM-DD +} + +// CopilotDailyMetricsReport represents the response from 1-day Copilot metrics report endpoints. +type CopilotDailyMetricsReport struct { + DownloadLinks []string `json:"download_links"` + ReportDay string `json:"report_day"` +} + +// CopilotMetricsReport represents the response from 28-day Copilot metrics report endpoints. +type CopilotMetricsReport struct { + DownloadLinks []string `json:"download_links"` + ReportStartDay string `json:"report_start_day"` + ReportEndDay string `json:"report_end_day"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (cp *CopilotSeatDetails) UnmarshalJSON(data []byte) error { + // Using an alias to avoid infinite recursion when calling json.Unmarshal + type alias CopilotSeatDetails + var seatDetail alias + + if err := json.Unmarshal(data, &seatDetail); err != nil { + return err + } + + cp.AssigningTeam = seatDetail.AssigningTeam + cp.PendingCancellationDate = seatDetail.PendingCancellationDate + cp.LastActivityAt = seatDetail.LastActivityAt + cp.LastActivityEditor = seatDetail.LastActivityEditor + cp.CreatedAt = seatDetail.CreatedAt + cp.UpdatedAt = seatDetail.UpdatedAt + cp.PlanType = seatDetail.PlanType + + switch v := seatDetail.Assignee.(type) { + case nil: + // Assignee can be null according to GitHub API specification. + // See: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#list-all-copilot-seat-assignments-for-an-organization + // Note: Copilot API is in public preview and subject to change. + cp.Assignee = nil + case map[string]any: + jsonData, err := json.Marshal(seatDetail.Assignee) + if err != nil { + return err + } + + if v["type"] == nil { + return errors.New("assignee type field is not set") + } + + if t, ok := v["type"].(string); ok && t == "User" { + var user *User + if err := json.Unmarshal(jsonData, &user); err != nil { + return err + } + cp.Assignee = user + } else if t, ok := v["type"].(string); ok && t == "Team" { + var team *Team + if err := json.Unmarshal(jsonData, &team); err != nil { + return err + } + cp.Assignee = team + } else if t, ok := v["type"].(string); ok && t == "Organization" { + var organization *Organization + if err := json.Unmarshal(jsonData, &organization); err != nil { + return err + } + cp.Assignee = organization + } else { + return fmt.Errorf("unsupported assignee type %v", v["type"]) + } + default: + return fmt.Errorf("unsupported assignee type %T", v) + } + + return nil +} + +// GetUser gets the User from the CopilotSeatDetails if the assignee is a user. +func (cp *CopilotSeatDetails) GetUser() (*User, bool) { u, ok := cp.Assignee.(*User); return u, ok } + +// GetTeam gets the Team from the CopilotSeatDetails if the assignee is a team. +func (cp *CopilotSeatDetails) GetTeam() (*Team, bool) { t, ok := cp.Assignee.(*Team); return t, ok } + +// GetOrganization gets the Organization from the CopilotSeatDetails if the assignee is an organization. +func (cp *CopilotSeatDetails) GetOrganization() (*Organization, bool) { + o, ok := cp.Assignee.(*Organization) + return o, ok +} + +// GetCopilotBilling gets Copilot for Business billing information and settings for an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#get-copilot-seat-information-and-settings-for-an-organization +// +//meta:operation GET /orgs/{org}/copilot/billing +func (s *CopilotService) GetCopilotBilling(ctx context.Context, org string) (*CopilotOrganizationDetails, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/billing", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var copilotDetails *CopilotOrganizationDetails + resp, err := s.client.Do(req, &copilotDetails) + if err != nil { + return nil, resp, err + } + + return copilotDetails, resp, nil +} + +// ListCopilotSeats lists Copilot for Business seat assignments for an organization. +// +// To paginate through all seats, populate 'Page' with the number of the last page. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#list-all-copilot-seat-assignments-for-an-organization +// +//meta:operation GET /orgs/{org}/copilot/billing/seats +func (s *CopilotService) ListCopilotSeats(ctx context.Context, org string, opts *ListOptions) (*ListCopilotSeatsResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/billing/seats", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var copilotSeats *ListCopilotSeatsResponse + resp, err := s.client.Do(req, &copilotSeats) + if err != nil { + return nil, resp, err + } + + return copilotSeats, resp, nil +} + +// ListCopilotEnterpriseSeats lists Copilot for Business seat assignments for an enterprise. +// +// To paginate through all seats, populate 'Page' with the number of the last page. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management?apiVersion=2022-11-28#list-all-copilot-seat-assignments-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/copilot/billing/seats +func (s *CopilotService) ListCopilotEnterpriseSeats(ctx context.Context, enterprise string, opts *ListOptions) (*ListCopilotSeatsResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/billing/seats", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var copilotSeats *ListCopilotSeatsResponse + resp, err := s.client.Do(req, &copilotSeats) + if err != nil { + return nil, resp, err + } + + return copilotSeats, resp, nil +} + +// ListOrganizationCopilotCodingAgentRepositoriesResponse represents the response from listing +// repositories enabled for the Copilot coding agent in an organization. +type ListOrganizationCopilotCodingAgentRepositoriesResponse struct { + TotalCount int `json:"total_count"` + Repositories []*Repository `json:"repositories"` +} + +// ListOrganizationCodingAgentRepositories lists repositories enabled for the Copilot coding agent in an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-coding-agent-management?apiVersion=2022-11-28#list-repositories-enabled-for-copilot-cloud-agent-in-an-organization +// +//meta:operation GET /orgs/{org}/copilot/coding-agent/permissions/repositories +func (s *CopilotService) ListOrganizationCodingAgentRepositories(ctx context.Context, org string, opts *ListOptions) (*ListOrganizationCopilotCodingAgentRepositoriesResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/coding-agent/permissions/repositories", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *ListOrganizationCopilotCodingAgentRepositoriesResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// CopilotOrganizationContentExclusionDetails lists all Copilot content exclusion +// rules for an organization, keyed by repository full name. Each value is the +// list of file paths excluded from Copilot for that repository. +type CopilotOrganizationContentExclusionDetails map[string][]string + +// GetOrganizationContentExclusionDetails gets the Copilot content exclusion rules for an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-content-exclusion-management?apiVersion=2022-11-28#get-copilot-content-exclusion-rules-for-an-organization +// +//meta:operation GET /orgs/{org}/copilot/content_exclusion +func (s *CopilotService) GetOrganizationContentExclusionDetails(ctx context.Context, org string) (CopilotOrganizationContentExclusionDetails, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/content_exclusion", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + details := CopilotOrganizationContentExclusionDetails{} + resp, err := s.client.Do(req, &details) + if err != nil { + return nil, resp, err + } + + return details, resp, nil +} + +// AddCopilotTeams adds teams to the Copilot for Business subscription for an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#add-teams-to-the-copilot-subscription-for-an-organization +// +//meta:operation POST /orgs/{org}/copilot/billing/selected_teams +func (s *CopilotService) AddCopilotTeams(ctx context.Context, org string, teamNames []string) (*SeatAssignments, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/billing/selected_teams", org) + + body := struct { + SelectedTeams []string `json:"selected_teams"` + }{ + SelectedTeams: teamNames, + } + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var seatAssignments *SeatAssignments + resp, err := s.client.Do(req, &seatAssignments) + if err != nil { + return nil, resp, err + } + + return seatAssignments, resp, nil +} + +// RemoveCopilotTeams removes teams from the Copilot for Business subscription for an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#remove-teams-from-the-copilot-subscription-for-an-organization +// +//meta:operation DELETE /orgs/{org}/copilot/billing/selected_teams +func (s *CopilotService) RemoveCopilotTeams(ctx context.Context, org string, teamNames []string) (*SeatCancellations, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/billing/selected_teams", org) + + body := struct { + SelectedTeams []string `json:"selected_teams"` + }{ + SelectedTeams: teamNames, + } + + req, err := s.client.NewRequest(ctx, "DELETE", u, body) + if err != nil { + return nil, nil, err + } + + var seatCancellations *SeatCancellations + resp, err := s.client.Do(req, &seatCancellations) + if err != nil { + return nil, resp, err + } + + return seatCancellations, resp, nil +} + +// AddCopilotUsers adds users to the Copilot for Business subscription for an organization +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#add-users-to-the-copilot-subscription-for-an-organization +// +//meta:operation POST /orgs/{org}/copilot/billing/selected_users +func (s *CopilotService) AddCopilotUsers(ctx context.Context, org string, users []string) (*SeatAssignments, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/billing/selected_users", org) + + body := struct { + SelectedUsernames []string `json:"selected_usernames"` + }{ + SelectedUsernames: users, + } + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var seatAssignments *SeatAssignments + resp, err := s.client.Do(req, &seatAssignments) + if err != nil { + return nil, resp, err + } + + return seatAssignments, resp, nil +} + +// RemoveCopilotUsers removes users from the Copilot for Business subscription for an organization. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#remove-users-from-the-copilot-subscription-for-an-organization +// +//meta:operation DELETE /orgs/{org}/copilot/billing/selected_users +func (s *CopilotService) RemoveCopilotUsers(ctx context.Context, org string, users []string) (*SeatCancellations, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/billing/selected_users", org) + + body := struct { + SelectedUsernames []string `json:"selected_usernames"` + }{ + SelectedUsernames: users, + } + + req, err := s.client.NewRequest(ctx, "DELETE", u, body) + if err != nil { + return nil, nil, err + } + + var seatCancellations *SeatCancellations + resp, err := s.client.Do(req, &seatCancellations) + if err != nil { + return nil, resp, err + } + + return seatCancellations, resp, nil +} + +// GetSeatDetails gets Copilot for Business seat assignment details for a user. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management?apiVersion=2022-11-28#get-copilot-seat-assignment-details-for-a-user +// +//meta:operation GET /orgs/{org}/members/{username}/copilot +func (s *CopilotService) GetSeatDetails(ctx context.Context, org, user string) (*CopilotSeatDetails, *Response, error) { + u := fmt.Sprintf("orgs/%v/members/%v/copilot", org, user) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var seatDetails *CopilotSeatDetails + resp, err := s.client.Do(req, &seatDetails) + if err != nil { + return nil, resp, err + } + + return seatDetails, resp, nil +} + +// GetEnterpriseMetrics gets Copilot usage metrics for an enterprise. +// +// Deprecated: This endpoint was closed down on April 2, 2026 for github.com. +// It may still be available for GitHub Enterprise Server. Use GetEnterpriseDailyMetricsReport +// or GetEnterpriseMetricsReport instead. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/copilot/metrics +func (s *CopilotService) GetEnterpriseMetrics(ctx context.Context, enterprise string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/metrics", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var metrics []*CopilotMetrics + resp, err := s.client.Do(req, &metrics) + if err != nil { + return nil, resp, err + } + + return metrics, resp, nil +} + +// GetEnterpriseTeamMetrics gets Copilot usage metrics for an enterprise team. +// +// Deprecated: This endpoint was closed down on April 2, 2026 for github.com. +// It may still be available for GitHub Enterprise Server. Use GetEnterpriseDailyMetricsReport +// or GetEnterpriseMetricsReport instead. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-an-enterprise-team +// +//meta:operation GET /enterprises/{enterprise}/team/{team_slug}/copilot/metrics +func (s *CopilotService) GetEnterpriseTeamMetrics(ctx context.Context, enterprise, team string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) { + u := fmt.Sprintf("enterprises/%v/team/%v/copilot/metrics", enterprise, team) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var metrics []*CopilotMetrics + resp, err := s.client.Do(req, &metrics) + if err != nil { + return nil, resp, err + } + + return metrics, resp, nil +} + +// GetOrganizationMetrics gets Copilot usage metrics for an organization. +// +// Deprecated: This endpoint was closed down on April 2, 2026 for github.com. +// It may still be available for GitHub Enterprise Server. Use GetOrganizationDailyMetricsReport +// or GetOrganizationMetricsReport instead. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-an-organization +// +//meta:operation GET /orgs/{org}/copilot/metrics +func (s *CopilotService) GetOrganizationMetrics(ctx context.Context, org string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/metrics", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var metrics []*CopilotMetrics + resp, err := s.client.Do(req, &metrics) + if err != nil { + return nil, resp, err + } + + return metrics, resp, nil +} + +// GetOrganizationTeamMetrics gets Copilot usage metrics for an organization team. +// +// Deprecated: This endpoint was closed down on April 2, 2026 for github.com. +// It may still be available for GitHub Enterprise Server. Use GetOrganizationDailyMetricsReport +// or GetOrganizationMetricsReport instead. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-a-team +// +//meta:operation GET /orgs/{org}/team/{team_slug}/copilot/metrics +func (s *CopilotService) GetOrganizationTeamMetrics(ctx context.Context, org, team string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) { + u := fmt.Sprintf("orgs/%v/team/%v/copilot/metrics", org, team) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var metrics []*CopilotMetrics + resp, err := s.client.Do(req, &metrics) + if err != nil { + return nil, resp, err + } + + return metrics, resp, nil +} + +// GetEnterpriseDailyMetricsReport gets a report containing Copilot metrics for a single day for an enterprise. +// +// Use DownloadDailyMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-enterprise-usage-metrics-for-a-specific-day +// +//meta:operation GET /enterprises/{enterprise}/copilot/metrics/reports/enterprise-1-day +func (s *CopilotService) GetEnterpriseDailyMetricsReport(ctx context.Context, enterprise string, opts *CopilotMetricsReportOptions) (*CopilotDailyMetricsReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/metrics/reports/enterprise-1-day", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotDailyMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetEnterpriseMetricsReport gets a report containing Copilot metrics for a 28-day rolling window for an enterprise. +// +// Use DownloadPeriodicMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-enterprise-usage-metrics +// +//meta:operation GET /enterprises/{enterprise}/copilot/metrics/reports/enterprise-28-day/latest +func (s *CopilotService) GetEnterpriseMetricsReport(ctx context.Context, enterprise string) (*CopilotMetricsReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/metrics/reports/enterprise-28-day/latest", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetEnterpriseUsersDailyMetricsReport gets a report containing Copilot user metrics for a single day for an enterprise. +// +// Use DownloadUserDailyMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-users-usage-metrics-for-a-specific-day +// +//meta:operation GET /enterprises/{enterprise}/copilot/metrics/reports/users-1-day +func (s *CopilotService) GetEnterpriseUsersDailyMetricsReport(ctx context.Context, enterprise string, opts *CopilotMetricsReportOptions) (*CopilotDailyMetricsReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/metrics/reports/users-1-day", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotDailyMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetEnterpriseUsersMetricsReport gets a report containing Copilot user metrics for a 28-day rolling window for an enterprise. +// +// Use DownloadUserPeriodicMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-users-usage-metrics +// +//meta:operation GET /enterprises/{enterprise}/copilot/metrics/reports/users-28-day/latest +func (s *CopilotService) GetEnterpriseUsersMetricsReport(ctx context.Context, enterprise string) (*CopilotMetricsReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/copilot/metrics/reports/users-28-day/latest", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetOrganizationDailyMetricsReport gets a report containing Copilot metrics for a single day for an organization. +// +// Use DownloadDailyMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-organization-usage-metrics-for-a-specific-day +// +//meta:operation GET /orgs/{org}/copilot/metrics/reports/organization-1-day +func (s *CopilotService) GetOrganizationDailyMetricsReport(ctx context.Context, org string, opts *CopilotMetricsReportOptions) (*CopilotDailyMetricsReport, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/metrics/reports/organization-1-day", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotDailyMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetOrganizationMetricsReport gets a report containing Copilot metrics for a 28-day rolling window for an organization. +// +// Use DownloadPeriodicMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-organization-usage-metrics +// +//meta:operation GET /orgs/{org}/copilot/metrics/reports/organization-28-day/latest +func (s *CopilotService) GetOrganizationMetricsReport(ctx context.Context, org string) (*CopilotMetricsReport, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/metrics/reports/organization-28-day/latest", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetOrganizationUsersDailyMetricsReport gets a report containing Copilot user metrics for a single day for an organization. +// +// Use DownloadUserDailyMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-organization-users-usage-metrics-for-a-specific-day +// +//meta:operation GET /orgs/{org}/copilot/metrics/reports/users-1-day +func (s *CopilotService) GetOrganizationUsersDailyMetricsReport(ctx context.Context, org string, opts *CopilotMetricsReportOptions) (*CopilotDailyMetricsReport, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/metrics/reports/users-1-day", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotDailyMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetOrganizationUsersMetricsReport gets a report containing Copilot user metrics for a 28-day rolling window for an organization. +// +// Use DownloadUserPeriodicMetrics to decode the payloads served at the returned download links. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-usage-metrics?apiVersion=2022-11-28#get-copilot-organization-users-usage-metrics +// +//meta:operation GET /orgs/{org}/copilot/metrics/reports/users-28-day/latest +func (s *CopilotService) GetOrganizationUsersMetricsReport(ctx context.Context, org string) (*CopilotMetricsReport, *Response, error) { + u := fmt.Sprintf("orgs/%v/copilot/metrics/reports/users-28-day/latest", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *CopilotMetricsReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// DownloadCopilotMetrics downloads a Copilot metrics report from the provided download link +// and decodes it as a []*CopilotMetrics. +// +// Deprecated: Use DownloadDailyMetrics, +// DownloadPeriodicMetrics, DownloadUserDailyMetrics, DownloadUserPeriodicMetrics instead. +// The payloads served at the download links returned by the new +// Get*MetricsReport endpoints on GitHub.com do not match the CopilotMetrics shape +// (see https://github.com/google/go-github/issues/4136). +// This method is retained +// for GitHub Enterprise Server installations that may still serve the legacy shape. +func (s *CopilotService) DownloadCopilotMetrics(ctx context.Context, url string) ([]*CopilotMetrics, *Response, error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + resp, err := s.client.BareDo(req) + if err != nil { + return nil, nil, err + } + defer resp.Body.Close() + + var metrics []*CopilotMetrics + if err := json.NewDecoder(resp.Body).Decode(&metrics); err != nil { + return nil, resp, err + } + + return metrics, resp, nil +} + +// CopilotMetricsPullRequests represents pull request totals in a Copilot metrics report. +type CopilotMetricsPullRequests struct { + TotalReviewed *int `json:"total_reviewed,omitempty"` + TotalCreated *int `json:"total_created,omitempty"` + TotalCreatedByCopilot *int `json:"total_created_by_copilot,omitempty"` + TotalReviewedByCopilot *int `json:"total_reviewed_by_copilot,omitempty"` + TotalMerged *int `json:"total_merged,omitempty"` + MedianMinutesToMerge *float64 `json:"median_minutes_to_merge,omitempty"` + TotalSuggestions *int `json:"total_suggestions,omitempty"` + TotalAppliedSuggestions *int `json:"total_applied_suggestions,omitempty"` + TotalMergedCreatedByCopilot *int `json:"total_merged_created_by_copilot,omitempty"` + MedianMinutesToMergeCopilotAuthored *float64 `json:"median_minutes_to_merge_copilot_authored,omitempty"` + TotalCopilotSuggestions *int `json:"total_copilot_suggestions,omitempty"` + TotalCopilotAppliedSuggestions *int `json:"total_copilot_applied_suggestions,omitempty"` + MedianMinutesToMergeCopilotReviewed *float64 `json:"median_minutes_to_merge_copilot_reviewed,omitempty"` + TotalMergedReviewedByCopilot *int `json:"total_merged_reviewed_by_copilot,omitempty"` +} + +// CopilotMetricsCodeActivity captures the code-generation activity counts and lines-of-code (LOC) +// suggestion totals shared across the per-IDE, per-feature, per-language, and per-model breakdowns +// in a Copilot metrics report. +type CopilotMetricsCodeActivity struct { + CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"` + CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"` + LOCSuggestedToAddSum *int `json:"loc_suggested_to_add_sum,omitempty"` + LOCSuggestedToDeleteSum *int `json:"loc_suggested_to_delete_sum,omitempty"` + LOCAddedSum *int `json:"loc_added_sum,omitempty"` + LOCDeletedSum *int `json:"loc_deleted_sum,omitempty"` +} + +// CopilotMetricsChatPanel captures per-mode chat panel interaction counts shared across daily, +// periodic, and user metrics reports. Each field is a subset of user_initiated_interaction_count +// attributed to that chat panel mode. +type CopilotMetricsChatPanel struct { + ChatPanelAgentMode *int `json:"chat_panel_agent_mode,omitempty"` + ChatPanelAskMode *int `json:"chat_panel_ask_mode,omitempty"` + ChatPanelCustomMode *int `json:"chat_panel_custom_mode,omitempty"` + ChatPanelEditMode *int `json:"chat_panel_edit_mode,omitempty"` + ChatPanelUnknownMode *int `json:"chat_panel_unknown_mode,omitempty"` +} + +// CopilotMetricsIDE represents per-IDE aggregate totals in a Copilot metrics report. +type CopilotMetricsIDE struct { + IDE string `json:"ide"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsCodeActivity +} + +// CopilotMetricsFeature represents per-feature aggregate totals in a Copilot metrics report. +type CopilotMetricsFeature struct { + Feature string `json:"feature"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsCodeActivity +} + +// CopilotMetricsLanguageFeature represents per-language-feature totals in a Copilot metrics report. +type CopilotMetricsLanguageFeature struct { + Language string `json:"language"` + Feature string `json:"feature"` + CopilotMetricsCodeActivity +} + +// CopilotMetricsLanguageModel represents per-language-model totals in a Copilot metrics report. +type CopilotMetricsLanguageModel struct { + Language string `json:"language"` + Model string `json:"model"` + CopilotMetricsCodeActivity +} + +// CopilotMetricsModelFeature represents per-model-feature totals in a Copilot metrics report. +type CopilotMetricsModelFeature struct { + Model string `json:"model"` + Feature string `json:"feature"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsCodeActivity +} + +// CopilotMetricsCLIVersion represents the last known Copilot CLI version seen in a metrics report. +type CopilotMetricsCLIVersion struct { + SampledAt *Timestamp `json:"sampled_at,omitempty"` + CLIVersion string `json:"cli_version"` +} + +// CopilotMetricsCLITokenUsage represents Copilot CLI token totals in a metrics report. +type CopilotMetricsCLITokenUsage struct { + AvgTokensPerRequest *float64 `json:"avg_tokens_per_request,omitempty"` + OutputTokensSum *int `json:"output_tokens_sum,omitempty"` + PromptTokensSum *int `json:"prompt_tokens_sum,omitempty"` +} + +// CopilotMetricsCLI represents Copilot CLI totals in a metrics report. +type CopilotMetricsCLI struct { + SessionCount *int `json:"session_count,omitempty"` + RequestCount *int `json:"request_count,omitempty"` + PromptCount *int `json:"prompt_count,omitempty"` + TokenUsage *CopilotMetricsCLITokenUsage `json:"token_usage,omitempty"` + LastKnownCLIVersion *CopilotMetricsCLIVersion `json:"last_known_cli_version,omitempty"` +} + +// CopilotDailyMetrics represents the payload downloaded from a 1-day Copilot usage metrics report. +type CopilotDailyMetrics struct { + Day string `json:"day"` + OrganizationID *string `json:"organization_id,omitempty"` + EnterpriseID *string `json:"enterprise_id,omitempty"` + DailyActiveCLIUsers *int `json:"daily_active_cli_users,omitempty"` + DailyActiveUsers *int `json:"daily_active_users,omitempty"` + DailyActiveCopilotCloudAgentUsers *int `json:"daily_active_copilot_cloud_agent_users,omitempty"` + WeeklyActiveUsers *int `json:"weekly_active_users,omitempty"` + WeeklyActiveCopilotCloudAgentUsers *int `json:"weekly_active_copilot_cloud_agent_users,omitempty"` + MonthlyActiveUsers *int `json:"monthly_active_users,omitempty"` + MonthlyActiveChatUsers *int `json:"monthly_active_chat_users,omitempty"` + MonthlyActiveAgentUsers *int `json:"monthly_active_agent_users,omitempty"` + MonthlyActiveCopilotCloudAgentUsers *int `json:"monthly_active_copilot_cloud_agent_users,omitempty"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsChatPanel + CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"` + CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"` + TotalsByIDE []*CopilotMetricsIDE `json:"totals_by_ide,omitempty"` + TotalsByFeature []*CopilotMetricsFeature `json:"totals_by_feature,omitempty"` + TotalsByLanguageFeature []*CopilotMetricsLanguageFeature `json:"totals_by_language_feature,omitempty"` + TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"` + TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"` + TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"` + LOCSuggestedToAddSum *int `json:"loc_suggested_to_add_sum,omitempty"` + LOCSuggestedToDeleteSum *int `json:"loc_suggested_to_delete_sum,omitempty"` + LOCAddedSum *int `json:"loc_added_sum,omitempty"` + LOCDeletedSum *int `json:"loc_deleted_sum,omitempty"` + PullRequests *CopilotMetricsPullRequests `json:"pull_requests,omitempty"` +} + +// CopilotPeriodicMetrics represents the payload downloaded from a multi-day (e.g. 28-day rolling) +// Copilot usage metrics report. The DayTotals field contains one CopilotDailyMetrics entry per day +// in the reporting window. Window-level metadata (ReportStartDay, ReportEndDay, CreatedAt) lives on +// this parent struct; each DayTotals entry only populates fields scoped to that day. +type CopilotPeriodicMetrics struct { + ReportStartDay string `json:"report_start_day"` + ReportEndDay string `json:"report_end_day"` + OrganizationID *string `json:"organization_id,omitempty"` + EnterpriseID *string `json:"enterprise_id,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + DayTotals []*CopilotDailyMetrics `json:"day_totals,omitempty"` +} + +// CopilotUserMetricsPluginVersion represents the last known plugin version used in an IDE by a Copilot user. +type CopilotUserMetricsPluginVersion struct { + SampledAt *Timestamp `json:"sampled_at,omitempty"` + Plugin string `json:"plugin"` + PluginVersion string `json:"plugin_version"` +} + +// CopilotUserMetricsIDEVersion represents the last known IDE version used by a Copilot user. +type CopilotUserMetricsIDEVersion struct { + SampledAt *Timestamp `json:"sampled_at,omitempty"` + IDEVersion string `json:"ide_version"` +} + +// CopilotUserMetricsIDE represents per-IDE totals for a single Copilot user in a user metrics report. +type CopilotUserMetricsIDE struct { + IDE string `json:"ide"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsCodeActivity + LastKnownPluginVersion *CopilotUserMetricsPluginVersion `json:"last_known_plugin_version,omitempty"` + LastKnownIDEVersion *CopilotUserMetricsIDEVersion `json:"last_known_ide_version,omitempty"` +} + +// CopilotUserDailyMetrics represents a single user's per-day Copilot usage metrics record from a +// 1-day user metrics report. User metrics reports are served as newline-delimited JSON. +type CopilotUserDailyMetrics struct { + UserID int `json:"user_id"` + UserLogin string `json:"user_login"` + Day string `json:"day"` + OrganizationID *string `json:"organization_id,omitempty"` + EnterpriseID *string `json:"enterprise_id,omitempty"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsChatPanel + CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"` + CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"` + TotalsByIDE []*CopilotUserMetricsIDE `json:"totals_by_ide,omitempty"` + TotalsByFeature []*CopilotMetricsFeature `json:"totals_by_feature,omitempty"` + TotalsByLanguageFeature []*CopilotMetricsLanguageFeature `json:"totals_by_language_feature,omitempty"` + TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"` + TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"` + TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"` + UsedAgent *bool `json:"used_agent,omitempty"` + UsedChat *bool `json:"used_chat,omitempty"` + UsedCLI *bool `json:"used_cli,omitempty"` + UsedCopilotCodeReviewActive *bool `json:"used_copilot_code_review_active,omitempty"` + UsedCopilotCodeReviewPassive *bool `json:"used_copilot_code_review_passive,omitempty"` + UsedCopilotCodingAgent *bool `json:"used_copilot_coding_agent,omitempty"` + LOCSuggestedToAddSum *int `json:"loc_suggested_to_add_sum,omitempty"` + LOCSuggestedToDeleteSum *int `json:"loc_suggested_to_delete_sum,omitempty"` + LOCAddedSum *int `json:"loc_added_sum,omitempty"` + LOCDeletedSum *int `json:"loc_deleted_sum,omitempty"` +} + +// CopilotUserPeriodicMetrics represents a single user's per-day Copilot usage metrics record from a +// multi-day (e.g. 28-day rolling) user metrics report. User metrics reports are served as +// newline-delimited JSON. +type CopilotUserPeriodicMetrics struct { + ReportStartDay string `json:"report_start_day"` + ReportEndDay string `json:"report_end_day"` + Day string `json:"day"` + OrganizationID *string `json:"organization_id,omitempty"` + EnterpriseID *string `json:"enterprise_id,omitempty"` + UserID int `json:"user_id"` + UserLogin string `json:"user_login"` + UserInitiatedInteractionCount *int `json:"user_initiated_interaction_count,omitempty"` + CopilotMetricsChatPanel + CodeGenerationActivityCount *int `json:"code_generation_activity_count,omitempty"` + CodeAcceptanceActivityCount *int `json:"code_acceptance_activity_count,omitempty"` + TotalsByIDE []*CopilotUserMetricsIDE `json:"totals_by_ide,omitempty"` + TotalsByFeature []*CopilotMetricsFeature `json:"totals_by_feature,omitempty"` + TotalsByLanguageFeature []*CopilotMetricsLanguageFeature `json:"totals_by_language_feature,omitempty"` + TotalsByLanguageModel []*CopilotMetricsLanguageModel `json:"totals_by_language_model,omitempty"` + TotalsByModelFeature []*CopilotMetricsModelFeature `json:"totals_by_model_feature,omitempty"` + TotalsByCLI *CopilotMetricsCLI `json:"totals_by_cli,omitempty"` + UsedAgent *bool `json:"used_agent,omitempty"` + UsedChat *bool `json:"used_chat,omitempty"` + UsedCLI *bool `json:"used_cli,omitempty"` + UsedCopilotCodeReviewActive *bool `json:"used_copilot_code_review_active,omitempty"` + UsedCopilotCodeReviewPassive *bool `json:"used_copilot_code_review_passive,omitempty"` + UsedCopilotCodingAgent *bool `json:"used_copilot_coding_agent,omitempty"` + LOCSuggestedToAddSum *int `json:"loc_suggested_to_add_sum,omitempty"` + LOCSuggestedToDeleteSum *int `json:"loc_suggested_to_delete_sum,omitempty"` + LOCAddedSum *int `json:"loc_added_sum,omitempty"` + LOCDeletedSum *int `json:"loc_deleted_sum,omitempty"` +} + +// fetchMetricsReport performs a GET against the provided download URL and returns the raw +// http.Response. The caller is responsible for closing the body. +func (s *CopilotService) fetchMetricsReport(ctx context.Context, url string) (*http.Response, *Response, error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + resp, err := s.client.client.Do(req) + if err != nil { + return nil, nil, err + } + + if err := CheckResponse(resp); err != nil { + resp.Body.Close() + return nil, newResponse(resp), err + } + + return resp, newResponse(resp), nil +} + +// decodeNDJSONMetrics streams a newline-delimited JSON response body into a slice of *T, +// returning a nil slice when the body is empty. +func decodeNDJSONMetrics[T any](r io.Reader) ([]*T, error) { + var records []*T + dec := json.NewDecoder(r) + for { + var rec *T + if err := dec.Decode(&rec); err != nil { + if errors.Is(err, io.EOF) { + break + } + return nil, err + } + records = append(records, rec) + } + return records, nil +} + +// DownloadDailyMetrics downloads the payload of a 1-day Copilot usage metrics report from a +// download link returned by GetEnterpriseDailyMetricsReport or GetOrganizationDailyMetricsReport. +func (s *CopilotService) DownloadDailyMetrics(ctx context.Context, url string) (*CopilotDailyMetrics, *Response, error) { + resp, r, err := s.fetchMetricsReport(ctx, url) + if err != nil { + return nil, r, err + } + defer resp.Body.Close() + + var metrics *CopilotDailyMetrics + if err := json.NewDecoder(resp.Body).Decode(&metrics); err != nil { + return nil, r, err + } + + return metrics, r, nil +} + +// DownloadPeriodicMetrics downloads the payload of a multi-day (e.g. 28-day rolling) Copilot +// usage metrics report from a download link returned by GetEnterpriseMetricsReport or +// GetOrganizationMetricsReport. +func (s *CopilotService) DownloadPeriodicMetrics(ctx context.Context, url string) (*CopilotPeriodicMetrics, *Response, error) { + resp, r, err := s.fetchMetricsReport(ctx, url) + if err != nil { + return nil, r, err + } + defer resp.Body.Close() + + var metrics *CopilotPeriodicMetrics + if err := json.NewDecoder(resp.Body).Decode(&metrics); err != nil { + return nil, r, err + } + + return metrics, r, nil +} + +// DownloadUserDailyMetrics downloads the payload of a 1-day Copilot user metrics report from a +// download link returned by GetEnterpriseUsersDailyMetricsReport or +// GetOrganizationUsersDailyMetricsReport. +// +// The response is newline-delimited JSON, with one CopilotUserDailyMetrics record per line. +func (s *CopilotService) DownloadUserDailyMetrics(ctx context.Context, url string) ([]*CopilotUserDailyMetrics, *Response, error) { + resp, r, err := s.fetchMetricsReport(ctx, url) + if err != nil { + return nil, r, err + } + defer resp.Body.Close() + + records, err := decodeNDJSONMetrics[CopilotUserDailyMetrics](resp.Body) + if err != nil { + return nil, r, err + } + return records, r, nil +} + +// DownloadUserPeriodicMetrics downloads the payload of a multi-day (e.g. 28-day rolling) Copilot +// user metrics report from a download link returned by GetEnterpriseUsersMetricsReport or +// GetOrganizationUsersMetricsReport. +// +// The response is newline-delimited JSON, with one CopilotUserPeriodicMetrics record per +// user-day in the reporting window. +func (s *CopilotService) DownloadUserPeriodicMetrics(ctx context.Context, url string) ([]*CopilotUserPeriodicMetrics, *Response, error) { + resp, r, err := s.fetchMetricsReport(ctx, url) + if err != nil { + return nil, r, err + } + defer resp.Body.Close() + + records, err := decodeNDJSONMetrics[CopilotUserPeriodicMetrics](resp.Body) + if err != nil { + return nil, r, err + } + return records, r, nil +} diff --git a/github/copilot_cloud_agent.go b/github/copilot_cloud_agent.go new file mode 100644 index 00000000000..b323af5efc8 --- /dev/null +++ b/github/copilot_cloud_agent.go @@ -0,0 +1,51 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CopilotCloudAgentConfiguration represents the Copilot cloud agent configuration for a repository. +type CopilotCloudAgentConfiguration struct { + MCPConfiguration any `json:"mcp_configuration"` + EnabledTools *CopilotCloudAgentEnabledTools `json:"enabled_tools"` + RequireActionsWorkflowApproval bool `json:"require_actions_workflow_approval"` + IsFirewallEnabled bool `json:"is_firewall_enabled"` + IsFirewallRecommendedAllowlistEnabled bool `json:"is_firewall_recommended_allowlist_enabled"` + CustomAllowlist []string `json:"custom_allowlist"` +} + +// CopilotCloudAgentEnabledTools represents the enabled review tools for Copilot cloud agent. +type CopilotCloudAgentEnabledTools struct { + Codeql bool `json:"codeql"` + CopilotCodeReview bool `json:"copilot_code_review"` + SecretScanning bool `json:"secret_scanning"` + DependencyVulnerabilityChecks bool `json:"dependency_vulnerability_checks"` +} + +// GetCloudAgentConfiguration gets the Copilot cloud agent configuration for a repository. +// +// GitHub API docs: https://docs.github.com/rest/copilot/copilot-cloud-agent-management?apiVersion=2022-11-28#get-copilot-cloud-agent-configuration-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/copilot/cloud-agent/configuration +func (s *CopilotService) GetCloudAgentConfiguration(ctx context.Context, owner, repo string) (*CopilotCloudAgentConfiguration, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/copilot/cloud-agent/configuration", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var config *CopilotCloudAgentConfiguration + resp, err := s.client.Do(req, &config) + if err != nil { + return nil, resp, err + } + + return config, resp, nil +} diff --git a/github/copilot_cloud_agent_test.go b/github/copilot_cloud_agent_test.go new file mode 100644 index 00000000000..f29cedad554 --- /dev/null +++ b/github/copilot_cloud_agent_test.go @@ -0,0 +1,239 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCopilotService_GetCloudAgentConfiguration(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + responseBody string + want *CopilotCloudAgentConfiguration + wantErr bool + }{ + { + name: "with null mcp_configuration", + responseBody: `{ + "mcp_configuration": null, + "enabled_tools": { + "codeql": true, + "copilot_code_review": true, + "secret_scanning": true, + "dependency_vulnerability_checks": true + }, + "require_actions_workflow_approval": true, + "is_firewall_enabled": true, + "is_firewall_recommended_allowlist_enabled": true, + "custom_allowlist": [] + }`, + want: &CopilotCloudAgentConfiguration{ + MCPConfiguration: nil, + EnabledTools: &CopilotCloudAgentEnabledTools{ + Codeql: true, + CopilotCodeReview: true, + SecretScanning: true, + DependencyVulnerabilityChecks: true, + }, + RequireActionsWorkflowApproval: true, + IsFirewallEnabled: true, + IsFirewallRecommendedAllowlistEnabled: true, + CustomAllowlist: []string{}, + }, + wantErr: false, + }, + { + name: "with active mcp_configuration object", + responseBody: `{ + "mcp_configuration": { + "type": "resource", + "uri": "stdio://server" + }, + "enabled_tools": { + "codeql": true, + "copilot_code_review": true, + "secret_scanning": true, + "dependency_vulnerability_checks": true + }, + "require_actions_workflow_approval": true, + "is_firewall_enabled": true, + "is_firewall_recommended_allowlist_enabled": true, + "custom_allowlist": [] + }`, + want: &CopilotCloudAgentConfiguration{ + MCPConfiguration: map[string]any{ + "type": "resource", + "uri": "stdio://server", + }, + EnabledTools: &CopilotCloudAgentEnabledTools{ + Codeql: true, + CopilotCodeReview: true, + SecretScanning: true, + DependencyVulnerabilityChecks: true, + }, + RequireActionsWorkflowApproval: true, + IsFirewallEnabled: true, + IsFirewallRecommendedAllowlistEnabled: true, + CustomAllowlist: []string{}, + }, + wantErr: false, + }, + { + name: "with custom allowlist", + responseBody: `{ + "mcp_configuration": null, + "enabled_tools": { + "codeql": false, + "copilot_code_review": true, + "secret_scanning": false, + "dependency_vulnerability_checks": true + }, + "require_actions_workflow_approval": false, + "is_firewall_enabled": true, + "is_firewall_recommended_allowlist_enabled": true, + "custom_allowlist": ["example.com"] + }`, + want: &CopilotCloudAgentConfiguration{ + MCPConfiguration: nil, + EnabledTools: &CopilotCloudAgentEnabledTools{ + Codeql: false, + CopilotCodeReview: true, + SecretScanning: false, + DependencyVulnerabilityChecks: true, + }, + RequireActionsWorkflowApproval: false, + IsFirewallEnabled: true, + IsFirewallRecommendedAllowlistEnabled: true, + CustomAllowlist: []string{"example.com"}, + }, + wantErr: false, + }, + { + name: "all tools disabled", + responseBody: `{ + "mcp_configuration": null, + "enabled_tools": { + "codeql": false, + "copilot_code_review": false, + "secret_scanning": false, + "dependency_vulnerability_checks": false + }, + "require_actions_workflow_approval": false, + "is_firewall_enabled": false, + "is_firewall_recommended_allowlist_enabled": false, + "custom_allowlist": [] + }`, + want: &CopilotCloudAgentConfiguration{ + MCPConfiguration: nil, + EnabledTools: &CopilotCloudAgentEnabledTools{ + Codeql: false, + CopilotCodeReview: false, + SecretScanning: false, + DependencyVulnerabilityChecks: false, + }, + RequireActionsWorkflowApproval: false, + IsFirewallEnabled: false, + IsFirewallRecommendedAllowlistEnabled: false, + CustomAllowlist: []string{}, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/copilot/cloud-agent/configuration", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, tt.responseBody) + }) + + ctx := t.Context() + config, _, err := client.Copilot.GetCloudAgentConfiguration(ctx, "o", "r") + if (err != nil) != tt.wantErr { + t.Errorf("GetCloudAgentConfiguration returned error: %v, wantErr: %v", err, tt.wantErr) + } + + if !cmp.Equal(config, tt.want) { + t.Errorf("GetCloudAgentConfiguration returned %+v, want %+v", config, tt.want) + } + }) + } +} + +func TestCopilotService_GetCloudAgentConfiguration_BadOptions(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + const methodName = "GetCloudAgentConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetCloudAgentConfiguration(ctx, "\n", "\n") + return err + }) +} + +func TestCopilotService_GetCloudAgentConfiguration_NewRequestFailure(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + const methodName = "GetCloudAgentConfiguration" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + ctx := t.Context() + got, resp, err := client.Copilot.GetCloudAgentConfiguration(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCopilotService_GetCloudAgentConfiguration_InvalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Copilot.GetCloudAgentConfiguration(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestCopilotService_GetCloudAgentConfiguration_InvalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Copilot.GetCloudAgentConfiguration(ctx, "o", "%") + testURLParseError(t, err) +} + +func TestCopilotService_GetCloudAgentConfiguration_MalformedJSON(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/copilot/cloud-agent/configuration", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{invalid json}`) + }) + + ctx := t.Context() + config, _, err := client.Copilot.GetCloudAgentConfiguration(ctx, "o", "r") + if err == nil { + t.Error("Expected error from malformed JSON") + } + if config != nil { + t.Errorf("GetCloudAgentConfiguration should return nil on error, got %+v", config) + } +} diff --git a/github/copilot_test.go b/github/copilot_test.go new file mode 100644 index 00000000000..8f3cfef2fac --- /dev/null +++ b/github/copilot_test.go @@ -0,0 +1,3462 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestCopilotSeatDetails_UnmarshalJSON(t *testing.T) { + t.Parallel() + tests := []struct { + name string + data string + want *CopilotSeatDetails + wantErr bool + }{ + { + name: "Invalid JSON", + data: `{`, + want: nil, + wantErr: true, + }, + { + name: "Invalid top level type", + data: `{ + "assignee": { + "type": "User", + "name": "octokittens", + "id": 1 + }, + "assigning_team": "this should be an object" + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + { + name: "No Type Field", + data: `{ + "assignee": { + "name": "octokittens", + "id": 1 + } + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + { + name: "Null Assignee", + data: `{ + "assignee": null + }`, + want: &CopilotSeatDetails{ + Assignee: nil, + }, + wantErr: false, + }, + { + name: "Invalid Assignee Field Type", + data: `{ + "assignee": "test" + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + { + name: "Invalid Assignee Type", + data: `{ + "assignee": { + "name": "octokittens", + "id": 1, + "type": [] + } + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + { + name: "Invalid User", + data: `{ + "assignee": { + "type": "User", + "id": "bad" + } + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + { + name: "Invalid Team", + data: `{ + "assignee": { + "type": "Team", + "id": "bad" + } + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + { + name: "Invalid Organization", + data: `{ + "assignee": { + "type": "Organization", + "id": "bad" + } + }`, + want: &CopilotSeatDetails{}, + wantErr: true, + }, + } + + for _, tc := range tests { + var seatDetails *CopilotSeatDetails + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := json.Unmarshal([]byte(tc.data), &seatDetails) + if err == nil && tc.wantErr { + t.Error("CopilotSeatDetails.UnmarshalJSON returned nil instead of an error") + } + if err != nil && !tc.wantErr { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + if !cmp.Equal(tc.want, seatDetails) { + t.Errorf("CopilotSeatDetails.UnmarshalJSON expected %+v, got %+v", tc.want, seatDetails) + } + }) + } +} + +func TestCopilotService_GetSeatDetailsUser(t *testing.T) { + t.Parallel() + data := `{ + "assignee": { + "type": "User", + "id": 1 + } + }` + + var seatDetails *CopilotSeatDetails + + err := json.Unmarshal([]byte(data), &seatDetails) + if err != nil { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + + want := &User{ + ID: Ptr(int64(1)), + Type: Ptr("User"), + } + + if got, ok := seatDetails.GetUser(); ok && !cmp.Equal(got, want) { + t.Errorf("CopilotSeatDetails.GetTeam returned %+v, want %+v", got, want) + } else if !ok { + t.Error("CopilotSeatDetails.GetUser returned false, expected true") + } + + data = `{ + "assignee": { + "type": "Organization", + "id": 1 + } + }` + + bad := &Organization{ + ID: Ptr(int64(1)), + Type: Ptr("Organization"), + } + + err = json.Unmarshal([]byte(data), seatDetails) + if err != nil { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + + if got, ok := seatDetails.GetUser(); ok { + t.Errorf("CopilotSeatDetails.GetUser returned true, expected false. Returned %v, expected %v", got, bad) + } +} + +func TestCopilotService_GetSeatDetailsTeam(t *testing.T) { + t.Parallel() + data := `{ + "assignee": { + "type": "Team", + "id": 1 + } + }` + + var seatDetails *CopilotSeatDetails + + err := json.Unmarshal([]byte(data), &seatDetails) + if err != nil { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + + want := &Team{ + ID: Ptr(int64(1)), + Type: Ptr("Team"), + } + + if got, ok := seatDetails.GetTeam(); ok && !cmp.Equal(got, want) { + t.Errorf("CopilotSeatDetails.GetTeam returned %+v, want %+v", got, want) + } else if !ok { + t.Error("CopilotSeatDetails.GetTeam returned false, expected true") + } + + data = `{ + "assignee": { + "type": "User", + "id": 1 + } + }` + + bad := &User{ + ID: Ptr(int64(1)), + Type: Ptr("User"), + } + + err = json.Unmarshal([]byte(data), seatDetails) + if err != nil { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + + if got, ok := seatDetails.GetTeam(); ok { + t.Errorf("CopilotSeatDetails.GetTeam returned true, expected false. Returned %v, expected %v", got, bad) + } +} + +func TestCopilotService_GetSeatDetailsOrganization(t *testing.T) { + t.Parallel() + data := `{ + "assignee": { + "type": "Organization", + "id": 1 + } + }` + + var seatDetails *CopilotSeatDetails + + err := json.Unmarshal([]byte(data), &seatDetails) + if err != nil { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + + want := &Organization{ + ID: Ptr(int64(1)), + Type: Ptr("Organization"), + } + + if got, ok := seatDetails.GetOrganization(); ok && !cmp.Equal(got, want) { + t.Errorf("CopilotSeatDetails.GetOrganization returned %+v, want %+v", got, want) + } else if !ok { + t.Error("CopilotSeatDetails.GetOrganization returned false, expected true") + } + + data = `{ + "assignee": { + "type": "Team", + "id": 1 + } + }` + + bad := &Team{ + ID: Ptr(int64(1)), + } + + err = json.Unmarshal([]byte(data), seatDetails) + if err != nil { + t.Errorf("CopilotSeatDetails.UnmarshalJSON returned an unexpected error: %v", err) + } + + if got, ok := seatDetails.GetOrganization(); ok { + t.Errorf("CopilotSeatDetails.GetOrganization returned true, expected false. Returned %v, expected %v", got, bad) + } +} + +func TestCopilotService_GetCopilotBilling(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/billing", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "seat_breakdown": { + "total": 12, + "added_this_cycle": 9, + "pending_invitation": 0, + "pending_cancellation": 0, + "active_this_cycle": 12, + "inactive_this_cycle": 11 + }, + "seat_management_setting": "assign_selected", + "public_code_suggestions": "block" + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetCopilotBilling(ctx, "o") + if err != nil { + t.Errorf("Copilot.GetCopilotBilling returned error: %v", err) + } + + want := &CopilotOrganizationDetails{ + SeatBreakdown: &CopilotSeatBreakdown{ + Total: 12, + AddedThisCycle: 9, + PendingInvitation: 0, + PendingCancellation: 0, + ActiveThisCycle: 12, + InactiveThisCycle: 11, + }, + PublicCodeSuggestions: "block", + CopilotChat: "", + SeatManagementSetting: "assign_selected", + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetCopilotBilling returned %+v, want %+v", got, want) + } + + const methodName = "GetCopilotBilling" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetCopilotBilling(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetCopilotBilling(ctx, "o") + if got != nil { + t.Errorf("Copilot.GetCopilotBilling returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_ListCopilotSeats(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/billing/seats", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "per_page": "100", + "page": "1", + }) + fmt.Fprint(w, `{ + "total_seats": 4, + "seats": [ + { + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": null, + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "assignee": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "assigning_team": { + "id": 1, + "node_id": "MDQ6VGVhbTE=", + "url": "https://api.github.com/teams/1", + "html_url": "https://github.com/orgs/github/teams/justice-league", + "name": "Justice League", + "slug": "justice-league", + "description": "A great team.", + "privacy": "closed", + "notification_setting": "notifications_enabled", + "permission": "admin", + "members_url": "https://api.github.com/teams/1/members{/member}", + "repositories_url": "https://api.github.com/teams/1/repos", + "parent": null + } + }, + { + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": "2021-11-01", + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "assignee": { + "login": "octokitten", + "id": 1, + "node_id": "MDQ76VNlcjE=", + "avatar_url": "https://github.com/images/error/octokitten_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octokitten", + "html_url": "https://github.com/octokitten", + "followers_url": "https://api.github.com/users/octokitten/followers", + "following_url": "https://api.github.com/users/octokitten/following{/other_user}", + "gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octokitten/subscriptions", + "organizations_url": "https://api.github.com/users/octokitten/orgs", + "repos_url": "https://api.github.com/users/octokitten/repos", + "events_url": "https://api.github.com/users/octokitten/events{/privacy}", + "received_events_url": "https://api.github.com/users/octokitten/received_events", + "type": "User", + "site_admin": false + } + }, + { + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": "2021-11-01", + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "assignee": { + "name": "octokittens", + "id": 1, + "type": "Team" + } + }, + { + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": "2021-11-01", + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "assignee": { + "name": "octocats", + "id": 1, + "type": "Organization" + } + } + ] + }`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 100} + got, _, err := client.Copilot.ListCopilotSeats(ctx, "o", opts) + if err != nil { + t.Errorf("Copilot.ListCopilotSeats returned error: %v", err) + } + + want := &ListCopilotSeatsResponse{ + TotalSeats: 4, + Seats: []*CopilotSeatDetails{ + { + Assignee: &User{ + Login: Ptr("octocat"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VXNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octocat"), + HTMLURL: Ptr("https://github.com/octocat"), + FollowersURL: Ptr("https://api.github.com/users/octocat/followers"), + FollowingURL: Ptr("https://api.github.com/users/octocat/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octocat/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octocat/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octocat/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octocat/orgs"), + ReposURL: Ptr("https://api.github.com/users/octocat/repos"), + EventsURL: Ptr("https://api.github.com/users/octocat/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octocat/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + AssigningTeam: &Team{ + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VGVhbTE="), + URL: Ptr("https://api.github.com/teams/1"), + HTMLURL: Ptr("https://github.com/orgs/github/teams/justice-league"), + Name: Ptr("Justice League"), + Slug: Ptr("justice-league"), + Description: Ptr("A great team."), + Privacy: Ptr("closed"), + Permission: Ptr("admin"), + NotificationSetting: Ptr("notifications_enabled"), + MembersURL: Ptr("https://api.github.com/teams/1/members{/member}"), + RepositoriesURL: Ptr("https://api.github.com/teams/1/repos"), + Parent: nil, + }, + + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: nil, + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + }, + { + Assignee: &User{ + Login: Ptr("octokitten"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ76VNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octokitten_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octokitten"), + HTMLURL: Ptr("https://github.com/octokitten"), + FollowersURL: Ptr("https://api.github.com/users/octokitten/followers"), + FollowingURL: Ptr("https://api.github.com/users/octokitten/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octokitten/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octokitten/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octokitten/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octokitten/orgs"), + ReposURL: Ptr("https://api.github.com/users/octokitten/repos"), + EventsURL: Ptr("https://api.github.com/users/octokitten/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octokitten/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + AssigningTeam: nil, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: Ptr("2021-11-01"), + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + }, + { + Assignee: &Team{ + ID: Ptr(int64(1)), + Name: Ptr("octokittens"), + Type: Ptr("Team"), + }, + AssigningTeam: nil, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: Ptr("2021-11-01"), + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + }, + { + Assignee: &Organization{ + ID: Ptr(int64(1)), + Name: Ptr("octocats"), + Type: Ptr("Organization"), + }, + AssigningTeam: nil, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: Ptr("2021-11-01"), + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + }, + }, + } + + assertNoDiff(t, want, got) + + const methodName = "ListCopilotSeats" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.ListCopilotSeats(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.ListCopilotSeats(ctx, "o", opts) + if got != nil { + t.Errorf("Copilot.ListCopilotSeats returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_ListCopilotEnterpriseSeats(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/billing/seats", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "per_page": "100", + "page": "1", + }) + fmt.Fprint(w, `{ + "total_seats": 2, + "seats": [ + { + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": null, + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "plan_type": "business", + "assignee": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "assigning_team": { + "id": 1, + "node_id": "MDQ6VGVhbTE=", + "url": "https://api.github.com/teams/1", + "html_url": "https://github.com/orgs/github/teams/justice-league", + "name": "Justice League", + "slug": "justice-league", + "description": "A great team.", + "privacy": "closed", + "notification_setting": "notifications_enabled", + "permission": "admin", + "members_url": "https://api.github.com/teams/1/members{/member}", + "repositories_url": "https://api.github.com/teams/1/repos", + "parent": null + } + }, + { + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": "2021-11-01", + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "assignee": { + "login": "octokitten", + "id": 1, + "node_id": "MDQ76VNlcjE=", + "avatar_url": "https://github.com/images/error/octokitten_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octokitten", + "html_url": "https://github.com/octokitten", + "followers_url": "https://api.github.com/users/octokitten/followers", + "following_url": "https://api.github.com/users/octokitten/following{/other_user}", + "gists_url": "https://api.github.com/users/octokitten/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octokitten/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octokitten/subscriptions", + "organizations_url": "https://api.github.com/users/octokitten/orgs", + "repos_url": "https://api.github.com/users/octokitten/repos", + "events_url": "https://api.github.com/users/octokitten/events{/privacy}", + "received_events_url": "https://api.github.com/users/octokitten/received_events", + "type": "User", + "site_admin": false + } + } + ] + }`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 100} + got, _, err := client.Copilot.ListCopilotEnterpriseSeats(ctx, "e", opts) + if err != nil { + t.Errorf("Copilot.ListCopilotEnterpriseSeats returned error: %v", err) + } + + want := &ListCopilotSeatsResponse{ + TotalSeats: 2, + Seats: []*CopilotSeatDetails{ + { + Assignee: &User{ + Login: Ptr("octocat"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VXNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octocat"), + HTMLURL: Ptr("https://github.com/octocat"), + FollowersURL: Ptr("https://api.github.com/users/octocat/followers"), + FollowingURL: Ptr("https://api.github.com/users/octocat/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octocat/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octocat/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octocat/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octocat/orgs"), + ReposURL: Ptr("https://api.github.com/users/octocat/repos"), + EventsURL: Ptr("https://api.github.com/users/octocat/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octocat/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + AssigningTeam: &Team{ + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VGVhbTE="), + URL: Ptr("https://api.github.com/teams/1"), + HTMLURL: Ptr("https://github.com/orgs/github/teams/justice-league"), + Name: Ptr("Justice League"), + Slug: Ptr("justice-league"), + Description: Ptr("A great team."), + Privacy: Ptr("closed"), + NotificationSetting: Ptr("notifications_enabled"), + Permission: Ptr("admin"), + MembersURL: Ptr("https://api.github.com/teams/1/members{/member}"), + RepositoriesURL: Ptr("https://api.github.com/teams/1/repos"), + Parent: nil, + }, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: nil, + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + PlanType: Ptr("business"), + }, + { + Assignee: &User{ + Login: Ptr("octokitten"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ76VNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octokitten_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octokitten"), + HTMLURL: Ptr("https://github.com/octokitten"), + FollowersURL: Ptr("https://api.github.com/users/octokitten/followers"), + FollowingURL: Ptr("https://api.github.com/users/octokitten/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octokitten/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octokitten/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octokitten/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octokitten/orgs"), + ReposURL: Ptr("https://api.github.com/users/octokitten/repos"), + EventsURL: Ptr("https://api.github.com/users/octokitten/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octokitten/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + AssigningTeam: nil, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: Ptr("2021-11-01"), + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + PlanType: nil, + }, + }, + } + + if !cmp.Equal(got, want) { + log.Printf("got: %+v", got.Seats[1]) + log.Printf("want: %+v", want.Seats[1]) + t.Errorf("Copilot.ListCopilotEnterpriseSeats returned %+v, want %+v", got, want) + } + + const methodName = "ListCopilotEnterpriseSeats" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.ListCopilotEnterpriseSeats(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.ListCopilotEnterpriseSeats(ctx, "e", opts) + if got != nil { + t.Errorf("Copilot.ListCopilotEnterpriseSeats returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_ListOrganizationCodingAgentRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/coding-agent/permissions/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "per_page": "100", + "page": "1", + }) + fmt.Fprint(w, `{ + "total_count": 2, + "repositories": [ + {"id": 1, "name": "Hello-World", "full_name": "octocat/Hello-World"}, + {"id": 2, "name": "Hello-World-2", "full_name": "octocat/Hello-World-2"} + ] + }`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 100} + got, _, err := client.Copilot.ListOrganizationCodingAgentRepositories(ctx, "o", opts) + if err != nil { + t.Errorf("Copilot.ListOrganizationCodingAgentRepositories returned error: %v", err) + } + + want := &ListOrganizationCopilotCodingAgentRepositoriesResponse{ + TotalCount: 2, + Repositories: []*Repository{ + {ID: Ptr(int64(1)), Name: Ptr("Hello-World"), FullName: Ptr("octocat/Hello-World")}, + {ID: Ptr(int64(2)), Name: Ptr("Hello-World-2"), FullName: Ptr("octocat/Hello-World-2")}, + }, + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.ListOrganizationCodingAgentRepositories returned %+v, want %+v", got, want) + } + + const methodName = "ListOrganizationCodingAgentRepositories" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.ListOrganizationCodingAgentRepositories(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.ListOrganizationCodingAgentRepositories(ctx, "o", opts) + if got != nil { + t.Errorf("Copilot.ListOrganizationCodingAgentRepositories returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationContentExclusionDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/content_exclusion", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "octo-repo": ["/src/some-dir/kernel.rs"], + "octo-repo-2": ["/docs/secret.md", "**/*.env"] + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetOrganizationContentExclusionDetails(ctx, "o") + if err != nil { + t.Errorf("Copilot.GetOrganizationContentExclusionDetails returned error: %v", err) + } + + want := CopilotOrganizationContentExclusionDetails{ + "octo-repo": {"/src/some-dir/kernel.rs"}, + "octo-repo-2": {"/docs/secret.md", "**/*.env"}, + } + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationContentExclusionDetails returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationContentExclusionDetails" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationContentExclusionDetails(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationContentExclusionDetails(ctx, "o") + if got != nil { + t.Errorf("Copilot.GetOrganizationContentExclusionDetails returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_AddCopilotTeams(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []string{"team1", "team2"} + + mux.HandleFunc("/orgs/o/copilot/billing/selected_teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, struct { + SelectedTeams []string `json:"selected_teams"` + }{ + SelectedTeams: input, + }) + fmt.Fprint(w, `{"seats_created": 2}`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.AddCopilotTeams(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.AddCopilotTeams returned error: %v", err) + } + + want := &SeatAssignments{SeatsCreated: 2} + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.AddCopilotTeams returned %+v, want %+v", got, want) + } + + const methodName = "AddCopilotTeams" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.AddCopilotTeams(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.AddCopilotTeams(ctx, "o", input) + if got != nil { + t.Errorf("Copilot.AddCopilotTeams returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_RemoveCopilotTeams(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []string{"user1", "user2"} + + mux.HandleFunc("/orgs/o/copilot/billing/selected_teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, struct { + SelectedTeams []string `json:"selected_teams"` + }{ + SelectedTeams: input, + }) + fmt.Fprint(w, `{"seats_cancelled": 2}`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.RemoveCopilotTeams(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.RemoveCopilotTeams returned error: %v", err) + } + + want := &SeatCancellations{SeatsCancelled: 2} + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.RemoveCopilotTeams returned %+v, want %+v", got, want) + } + + const methodName = "RemoveCopilotTeams" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.RemoveCopilotTeams(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.RemoveCopilotTeams(ctx, "o", []string{"team1", "team2"}) + if got != nil { + t.Errorf("Copilot.RemoveCopilotTeams returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_AddCopilotUsers(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []string{"user1", "user2"} + + mux.HandleFunc("/orgs/o/copilot/billing/selected_users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, struct { + SelectedTeams []string `json:"selected_usernames"` + }{ + SelectedTeams: input, + }) + fmt.Fprint(w, `{"seats_created": 2}`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.AddCopilotUsers(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.AddCopilotUsers returned error: %v", err) + } + + want := &SeatAssignments{SeatsCreated: 2} + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.AddCopilotUsers returned %+v, want %+v", got, want) + } + + const methodName = "AddCopilotUsers" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.AddCopilotUsers(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.AddCopilotUsers(ctx, "o", input) + if got != nil { + t.Errorf("Copilot.AddCopilotUsers returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_RemoveCopilotUsers(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []string{"user1", "user2"} + + mux.HandleFunc("/orgs/o/copilot/billing/selected_users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, struct { + SelectedTeams []string `json:"selected_usernames"` + }{ + SelectedTeams: input, + }) + fmt.Fprint(w, `{"seats_cancelled": 2}`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.RemoveCopilotUsers(ctx, "o", input) + if err != nil { + t.Errorf("Copilot.RemoveCopilotUsers returned error: %v", err) + } + + want := &SeatCancellations{SeatsCancelled: 2} + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.RemoveCopilotUsers returned %+v, want %+v", got, want) + } + + const methodName = "RemoveCopilotUsers" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.RemoveCopilotUsers(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.RemoveCopilotUsers(ctx, "o", input) + if got != nil { + t.Errorf("Copilot.RemoveCopilotUsers returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetSeatDetails(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/members/u/copilot", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "pending_cancellation_date": null, + "last_activity_at": `+refTimeStr(1136178002)+`, + "last_activity_editor": "vscode/1.77.3/copilot/1.86.82", + "assignee": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "assigning_team": { + "id": 1, + "node_id": "MDQ6VGVhbTE=", + "url": "https://api.github.com/teams/1", + "html_url": "https://github.com/orgs/github/teams/justice-league", + "name": "Justice League", + "slug": "justice-league", + "description": "A great team.", + "privacy": "closed", + "notification_setting": "notifications_enabled", + "permission": "admin", + "members_url": "https://api.github.com/teams/1/members{/member}", + "repositories_url": "https://api.github.com/teams/1/repos", + "parent": null + } + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetSeatDetails(ctx, "o", "u") + if err != nil { + t.Errorf("Copilot.GetSeatDetails returned error: %v", err) + } + + want := &CopilotSeatDetails{ + Assignee: &User{ + Login: Ptr("octocat"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VXNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octocat"), + HTMLURL: Ptr("https://github.com/octocat"), + FollowersURL: Ptr("https://api.github.com/users/octocat/followers"), + FollowingURL: Ptr("https://api.github.com/users/octocat/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octocat/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octocat/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octocat/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octocat/orgs"), + ReposURL: Ptr("https://api.github.com/users/octocat/repos"), + EventsURL: Ptr("https://api.github.com/users/octocat/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octocat/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + AssigningTeam: &Team{ + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VGVhbTE="), + URL: Ptr("https://api.github.com/teams/1"), + HTMLURL: Ptr("https://github.com/orgs/github/teams/justice-league"), + Name: Ptr("Justice League"), + Slug: Ptr("justice-league"), + Description: Ptr("A great team."), + Privacy: Ptr("closed"), + NotificationSetting: Ptr("notifications_enabled"), + Permission: Ptr("admin"), + MembersURL: Ptr("https://api.github.com/teams/1/members{/member}"), + RepositoriesURL: Ptr("https://api.github.com/teams/1/repos"), + Parent: nil, + }, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + PendingCancellationDate: nil, + LastActivityAt: refTimestamp(1136178002), + LastActivityEditor: Ptr("vscode/1.77.3/copilot/1.86.82"), + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetSeatDetails returned %+v, want %+v", got, want) + } + + const methodName = "GetSeatDetails" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetSeatDetails(ctx, "\n", "u") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetSeatDetails(ctx, "o", "u") + if got != nil { + t.Errorf("Copilot.GetSeatDetails returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/metrics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "date": "2024-06-24", + "total_active_users": 24, + "total_engaged_users": 20, + "copilot_ide_code_completions": { + "total_engaged_users": 20, + "languages": [ + { + "name": "python", + "total_engaged_users": 10 + }, + { + "name": "ruby", + "total_engaged_users": 10 + } + ], + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 13, + "languages": [ + { + "name": "python", + "total_engaged_users": 6, + "total_code_suggestions": 249, + "total_code_acceptances": 123, + "total_code_lines_suggested": 225, + "total_code_lines_accepted": 135 + }, + { + "name": "ruby", + "total_engaged_users": 7, + "total_code_suggestions": 496, + "total_code_acceptances": 253, + "total_code_lines_suggested": 520, + "total_code_lines_accepted": 270 + } + ] + } + ] + }, + { + "name": "neovim", + "total_engaged_users": 7, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "languages": [ + { + "name": "typescript", + "total_engaged_users": 3, + "total_code_suggestions": 112, + "total_code_acceptances": 56, + "total_code_lines_suggested": 143, + "total_code_lines_accepted": 61 + }, + { + "name": "go", + "total_engaged_users": 4, + "total_code_suggestions": 132, + "total_code_acceptances": 67, + "total_code_lines_suggested": 154, + "total_code_lines_accepted": 72 + } + ] + } + ] + } + ] + }, + "copilot_ide_chat": { + "total_engaged_users": 13, + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 12, + "total_chats": 45, + "total_chat_insertion_events": 12, + "total_chat_copy_events": 16 + }, + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_engaged_users": 1, + "total_chats": 10, + "total_chat_insertion_events": 11, + "total_chat_copy_events": 3 + } + ] + } + ] + }, + "copilot_dotcom_chat": { + "total_engaged_users": 14, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 14, + "total_chats": 38 + } + ] + }, + "copilot_dotcom_pull_requests": { + "total_engaged_users": 12, + "repositories": [ + { + "name": "demo/repo1", + "total_engaged_users": 8, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_pr_summaries_created": 6, + "total_engaged_users": 8 + } + ] + }, + { + "name": "demo/repo2", + "total_engaged_users": 4, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_pr_summaries_created": 10, + "total_engaged_users": 4 + } + ] + } + ] + } + } + ]`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetEnterpriseMetrics(ctx, "e", &CopilotMetricsListOptions{}) + if err != nil { + t.Errorf("Copilot.GetEnterpriseMetrics returned error: %v", err) + } + + want := []*CopilotMetrics{ + { + Date: "2024-06-24", + TotalActiveUsers: Ptr(24), + TotalEngagedUsers: Ptr(20), + CopilotIDECodeCompletions: &CopilotIDECodeCompletions{ + TotalEngagedUsers: 20, + Languages: []*CopilotIDECodeCompletionsLanguage{ + { + Name: "python", + TotalEngagedUsers: 10, + }, + { + Name: "ruby", + TotalEngagedUsers: 10, + }, + }, + Editors: []*CopilotIDECodeCompletionsEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 13, + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "python", + TotalEngagedUsers: 6, + TotalCodeSuggestions: 249, + TotalCodeAcceptances: 123, + TotalCodeLinesSuggested: 225, + TotalCodeLinesAccepted: 135, + }, + { + Name: "ruby", + TotalEngagedUsers: 7, + TotalCodeSuggestions: 496, + TotalCodeAcceptances: 253, + TotalCodeLinesSuggested: 520, + TotalCodeLinesAccepted: 270, + }, + }, + }, + }, + }, + { + Name: "neovim", + TotalEngagedUsers: 7, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "typescript", + TotalEngagedUsers: 3, + TotalCodeSuggestions: 112, + TotalCodeAcceptances: 56, + TotalCodeLinesSuggested: 143, + TotalCodeLinesAccepted: 61, + }, + { + Name: "go", + TotalEngagedUsers: 4, + TotalCodeSuggestions: 132, + TotalCodeAcceptances: 67, + TotalCodeLinesSuggested: 154, + TotalCodeLinesAccepted: 72, + }, + }, + }, + }, + }, + }, + }, + CopilotIDEChat: &CopilotIDEChat{ + TotalEngagedUsers: 13, + Editors: []*CopilotIDEChatEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDEChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 12, + TotalChats: 45, + TotalChatInsertionEvents: 12, + TotalChatCopyEvents: 16, + }, + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalEngagedUsers: 1, + TotalChats: 10, + TotalChatInsertionEvents: 11, + TotalChatCopyEvents: 3, + }, + }, + }, + }, + }, + CopilotDotcomChat: &CopilotDotcomChat{ + TotalEngagedUsers: 14, + Models: []*CopilotDotcomChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 14, + TotalChats: 38, + }, + }, + }, + CopilotDotcomPullRequests: &CopilotDotcomPullRequests{ + TotalEngagedUsers: 12, + Repositories: []*CopilotDotcomPullRequestsRepository{ + { + Name: "demo/repo1", + TotalEngagedUsers: 8, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalPRSummariesCreated: 6, + TotalEngagedUsers: 8, + }, + }, + }, + { + Name: "demo/repo2", + TotalEngagedUsers: 4, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalPRSummariesCreated: 10, + TotalEngagedUsers: 4, + }, + }, + }, + }, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseMetrics returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseMetrics" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseMetrics(ctx, "\n", &CopilotMetricsListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseMetrics(ctx, "e", &CopilotMetricsListOptions{}) + if got != nil { + t.Errorf("Copilot.GetEnterpriseMetrics returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseTeamMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/team/t/copilot/metrics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "date": "2024-06-24", + "total_active_users": 24, + "total_engaged_users": 20, + "copilot_ide_code_completions": { + "total_engaged_users": 20, + "languages": [ + { + "name": "python", + "total_engaged_users": 10 + }, + { + "name": "ruby", + "total_engaged_users": 10 + } + ], + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 13, + "languages": [ + { + "name": "python", + "total_engaged_users": 6, + "total_code_suggestions": 249, + "total_code_acceptances": 123, + "total_code_lines_suggested": 225, + "total_code_lines_accepted": 135 + }, + { + "name": "ruby", + "total_engaged_users": 7, + "total_code_suggestions": 496, + "total_code_acceptances": 253, + "total_code_lines_suggested": 520, + "total_code_lines_accepted": 270 + } + ] + } + ] + }, + { + "name": "neovim", + "total_engaged_users": 7, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "languages": [ + { + "name": "typescript", + "total_engaged_users": 3, + "total_code_suggestions": 112, + "total_code_acceptances": 56, + "total_code_lines_suggested": 143, + "total_code_lines_accepted": 61 + }, + { + "name": "go", + "total_engaged_users": 4, + "total_code_suggestions": 132, + "total_code_acceptances": 67, + "total_code_lines_suggested": 154, + "total_code_lines_accepted": 72 + } + ] + } + ] + } + ] + }, + "copilot_ide_chat": { + "total_engaged_users": 13, + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 12, + "total_chats": 45, + "total_chat_insertion_events": 12, + "total_chat_copy_events": 16 + }, + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_engaged_users": 1, + "total_chats": 10, + "total_chat_insertion_events": 11, + "total_chat_copy_events": 3 + } + ] + } + ] + }, + "copilot_dotcom_chat": { + "total_engaged_users": 14, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 14, + "total_chats": 38 + } + ] + }, + "copilot_dotcom_pull_requests": { + "total_engaged_users": 12, + "repositories": [ + { + "name": "demo/repo1", + "total_engaged_users": 8, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_pr_summaries_created": 6, + "total_engaged_users": 8 + } + ] + }, + { + "name": "demo/repo2", + "total_engaged_users": 4, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_pr_summaries_created": 10, + "total_engaged_users": 4 + } + ] + } + ] + } + } + ]`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetEnterpriseTeamMetrics(ctx, "e", "t", &CopilotMetricsListOptions{}) + if err != nil { + t.Errorf("Copilot.GetEnterpriseTeamMetrics returned error: %v", err) + } + + want := []*CopilotMetrics{ + { + Date: "2024-06-24", + TotalActiveUsers: Ptr(24), + TotalEngagedUsers: Ptr(20), + CopilotIDECodeCompletions: &CopilotIDECodeCompletions{ + TotalEngagedUsers: 20, + Languages: []*CopilotIDECodeCompletionsLanguage{ + { + Name: "python", + TotalEngagedUsers: 10, + }, + { + Name: "ruby", + TotalEngagedUsers: 10, + }, + }, + Editors: []*CopilotIDECodeCompletionsEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 13, + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "python", + TotalEngagedUsers: 6, + TotalCodeSuggestions: 249, + TotalCodeAcceptances: 123, + TotalCodeLinesSuggested: 225, + TotalCodeLinesAccepted: 135, + }, + { + Name: "ruby", + TotalEngagedUsers: 7, + TotalCodeSuggestions: 496, + TotalCodeAcceptances: 253, + TotalCodeLinesSuggested: 520, + TotalCodeLinesAccepted: 270, + }, + }, + }, + }, + }, + { + Name: "neovim", + TotalEngagedUsers: 7, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "typescript", + TotalEngagedUsers: 3, + TotalCodeSuggestions: 112, + TotalCodeAcceptances: 56, + TotalCodeLinesSuggested: 143, + TotalCodeLinesAccepted: 61, + }, + { + Name: "go", + TotalEngagedUsers: 4, + TotalCodeSuggestions: 132, + TotalCodeAcceptances: 67, + TotalCodeLinesSuggested: 154, + TotalCodeLinesAccepted: 72, + }, + }, + }, + }, + }, + }, + }, + CopilotIDEChat: &CopilotIDEChat{ + TotalEngagedUsers: 13, + Editors: []*CopilotIDEChatEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDEChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 12, + TotalChats: 45, + TotalChatInsertionEvents: 12, + TotalChatCopyEvents: 16, + }, + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalEngagedUsers: 1, + TotalChats: 10, + TotalChatInsertionEvents: 11, + TotalChatCopyEvents: 3, + }, + }, + }, + }, + }, + CopilotDotcomChat: &CopilotDotcomChat{ + TotalEngagedUsers: 14, + Models: []*CopilotDotcomChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 14, + TotalChats: 38, + }, + }, + }, + CopilotDotcomPullRequests: &CopilotDotcomPullRequests{ + TotalEngagedUsers: 12, + Repositories: []*CopilotDotcomPullRequestsRepository{ + { + Name: "demo/repo1", + TotalEngagedUsers: 8, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalPRSummariesCreated: 6, + TotalEngagedUsers: 8, + }, + }, + }, + { + Name: "demo/repo2", + TotalEngagedUsers: 4, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalPRSummariesCreated: 10, + TotalEngagedUsers: 4, + }, + }, + }, + }, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseTeamMetrics returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseTeamMetrics" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseTeamMetrics(ctx, "\n", "t", &CopilotMetricsListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseTeamMetrics(ctx, "e", "t", &CopilotMetricsListOptions{}) + if got != nil { + t.Errorf("Copilot.GetEnterpriseTeamMetrics returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/metrics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "date": "2024-06-24", + "total_active_users": 24, + "total_engaged_users": 20, + "copilot_ide_code_completions": { + "total_engaged_users": 20, + "languages": [ + { + "name": "python", + "total_engaged_users": 10 + }, + { + "name": "ruby", + "total_engaged_users": 10 + } + ], + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 13, + "languages": [ + { + "name": "python", + "total_engaged_users": 6, + "total_code_suggestions": 249, + "total_code_acceptances": 123, + "total_code_lines_suggested": 225, + "total_code_lines_accepted": 135 + }, + { + "name": "ruby", + "total_engaged_users": 7, + "total_code_suggestions": 496, + "total_code_acceptances": 253, + "total_code_lines_suggested": 520, + "total_code_lines_accepted": 270 + } + ] + } + ] + }, + { + "name": "neovim", + "total_engaged_users": 7, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "languages": [ + { + "name": "typescript", + "total_engaged_users": 3, + "total_code_suggestions": 112, + "total_code_acceptances": 56, + "total_code_lines_suggested": 143, + "total_code_lines_accepted": 61 + }, + { + "name": "go", + "total_engaged_users": 4, + "total_code_suggestions": 132, + "total_code_acceptances": 67, + "total_code_lines_suggested": 154, + "total_code_lines_accepted": 72 + } + ] + } + ] + } + ] + }, + "copilot_ide_chat": { + "total_engaged_users": 13, + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 12, + "total_chats": 45, + "total_chat_insertion_events": 12, + "total_chat_copy_events": 16 + }, + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_engaged_users": 1, + "total_chats": 10, + "total_chat_insertion_events": 11, + "total_chat_copy_events": 3 + } + ] + } + ] + }, + "copilot_dotcom_chat": { + "total_engaged_users": 14, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 14, + "total_chats": 38 + } + ] + }, + "copilot_dotcom_pull_requests": { + "total_engaged_users": 12, + "repositories": [ + { + "name": "demo/repo1", + "total_engaged_users": 8, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_pr_summaries_created": 6, + "total_engaged_users": 8 + } + ] + }, + { + "name": "demo/repo2", + "total_engaged_users": 4, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_pr_summaries_created": 10, + "total_engaged_users": 4 + } + ] + } + ] + } + } + ]`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetOrganizationMetrics(ctx, "o", &CopilotMetricsListOptions{}) + if err != nil { + t.Errorf("Copilot.GetOrganizationMetrics returned error: %v", err) + } + + want := []*CopilotMetrics{ + { + Date: "2024-06-24", + TotalActiveUsers: Ptr(24), + TotalEngagedUsers: Ptr(20), + CopilotIDECodeCompletions: &CopilotIDECodeCompletions{ + TotalEngagedUsers: 20, + Languages: []*CopilotIDECodeCompletionsLanguage{ + { + Name: "python", + TotalEngagedUsers: 10, + }, + { + Name: "ruby", + TotalEngagedUsers: 10, + }, + }, + Editors: []*CopilotIDECodeCompletionsEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 13, + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "python", + TotalEngagedUsers: 6, + TotalCodeSuggestions: 249, + TotalCodeAcceptances: 123, + TotalCodeLinesSuggested: 225, + TotalCodeLinesAccepted: 135, + }, + { + Name: "ruby", + TotalEngagedUsers: 7, + TotalCodeSuggestions: 496, + TotalCodeAcceptances: 253, + TotalCodeLinesSuggested: 520, + TotalCodeLinesAccepted: 270, + }, + }, + }, + }, + }, + { + Name: "neovim", + TotalEngagedUsers: 7, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "typescript", + TotalEngagedUsers: 3, + TotalCodeSuggestions: 112, + TotalCodeAcceptances: 56, + TotalCodeLinesSuggested: 143, + TotalCodeLinesAccepted: 61, + }, + { + Name: "go", + TotalEngagedUsers: 4, + TotalCodeSuggestions: 132, + TotalCodeAcceptances: 67, + TotalCodeLinesSuggested: 154, + TotalCodeLinesAccepted: 72, + }, + }, + }, + }, + }, + }, + }, + CopilotIDEChat: &CopilotIDEChat{ + TotalEngagedUsers: 13, + Editors: []*CopilotIDEChatEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDEChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 12, + TotalChats: 45, + TotalChatInsertionEvents: 12, + TotalChatCopyEvents: 16, + }, + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalEngagedUsers: 1, + TotalChats: 10, + TotalChatInsertionEvents: 11, + TotalChatCopyEvents: 3, + }, + }, + }, + }, + }, + CopilotDotcomChat: &CopilotDotcomChat{ + TotalEngagedUsers: 14, + Models: []*CopilotDotcomChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 14, + TotalChats: 38, + }, + }, + }, + CopilotDotcomPullRequests: &CopilotDotcomPullRequests{ + TotalEngagedUsers: 12, + Repositories: []*CopilotDotcomPullRequestsRepository{ + { + Name: "demo/repo1", + TotalEngagedUsers: 8, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalPRSummariesCreated: 6, + TotalEngagedUsers: 8, + }, + }, + }, + { + Name: "demo/repo2", + TotalEngagedUsers: 4, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalPRSummariesCreated: 10, + TotalEngagedUsers: 4, + }, + }, + }, + }, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationMetrics returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationMetrics" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationMetrics(ctx, "\n", &CopilotMetricsListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationMetrics(ctx, "o", &CopilotMetricsListOptions{}) + if got != nil { + t.Errorf("Copilot.GetOrganizationMetrics returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationTeamMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/team/t/copilot/metrics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "date": "2024-06-24", + "total_active_users": 24, + "total_engaged_users": 20, + "copilot_ide_code_completions": { + "total_engaged_users": 20, + "languages": [ + { + "name": "python", + "total_engaged_users": 10 + }, + { + "name": "ruby", + "total_engaged_users": 10 + } + ], + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 13, + "languages": [ + { + "name": "python", + "total_engaged_users": 6, + "total_code_suggestions": 249, + "total_code_acceptances": 123, + "total_code_lines_suggested": 225, + "total_code_lines_accepted": 135 + }, + { + "name": "ruby", + "total_engaged_users": 7, + "total_code_suggestions": 496, + "total_code_acceptances": 253, + "total_code_lines_suggested": 520, + "total_code_lines_accepted": 270 + } + ] + } + ] + }, + { + "name": "neovim", + "total_engaged_users": 7, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "languages": [ + { + "name": "typescript", + "total_engaged_users": 3, + "total_code_suggestions": 112, + "total_code_acceptances": 56, + "total_code_lines_suggested": 143, + "total_code_lines_accepted": 61 + }, + { + "name": "go", + "total_engaged_users": 4, + "total_code_suggestions": 132, + "total_code_acceptances": 67, + "total_code_lines_suggested": 154, + "total_code_lines_accepted": 72 + } + ] + } + ] + } + ] + }, + "copilot_ide_chat": { + "total_engaged_users": 13, + "editors": [ + { + "name": "vscode", + "total_engaged_users": 13, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 12, + "total_chats": 45, + "total_chat_insertion_events": 12, + "total_chat_copy_events": 16 + }, + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_engaged_users": 1, + "total_chats": 10, + "total_chat_insertion_events": 11, + "total_chat_copy_events": 3 + } + ] + } + ] + }, + "copilot_dotcom_chat": { + "total_engaged_users": 14, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 14, + "total_chats": 38 + } + ] + }, + "copilot_dotcom_pull_requests": { + "total_engaged_users": 12, + "repositories": [ + { + "name": "demo/repo1", + "total_engaged_users": 8, + "models": [ + { + "name": "default", + "is_custom_model": false, + "custom_model_training_date": null, + "total_pr_summaries_created": 6, + "total_engaged_users": 8 + } + ] + }, + { + "name": "demo/repo2", + "total_engaged_users": 4, + "models": [ + { + "name": "a-custom-model", + "is_custom_model": true, + "custom_model_training_date": "2024-02-01", + "total_pr_summaries_created": 10, + "total_engaged_users": 4 + } + ] + } + ] + } + } + ]`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetOrganizationTeamMetrics(ctx, "o", "t", &CopilotMetricsListOptions{}) + if err != nil { + t.Errorf("Copilot.GetOrganizationTeamMetrics returned error: %v", err) + } + + want := []*CopilotMetrics{ + { + Date: "2024-06-24", + TotalActiveUsers: Ptr(24), + TotalEngagedUsers: Ptr(20), + CopilotIDECodeCompletions: &CopilotIDECodeCompletions{ + TotalEngagedUsers: 20, + Languages: []*CopilotIDECodeCompletionsLanguage{ + { + Name: "python", + TotalEngagedUsers: 10, + }, + { + Name: "ruby", + TotalEngagedUsers: 10, + }, + }, + Editors: []*CopilotIDECodeCompletionsEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 13, + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "python", + TotalEngagedUsers: 6, + TotalCodeSuggestions: 249, + TotalCodeAcceptances: 123, + TotalCodeLinesSuggested: 225, + TotalCodeLinesAccepted: 135, + }, + { + Name: "ruby", + TotalEngagedUsers: 7, + TotalCodeSuggestions: 496, + TotalCodeAcceptances: 253, + TotalCodeLinesSuggested: 520, + TotalCodeLinesAccepted: 270, + }, + }, + }, + }, + }, + { + Name: "neovim", + TotalEngagedUsers: 7, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "typescript", + TotalEngagedUsers: 3, + TotalCodeSuggestions: 112, + TotalCodeAcceptances: 56, + TotalCodeLinesSuggested: 143, + TotalCodeLinesAccepted: 61, + }, + { + Name: "go", + TotalEngagedUsers: 4, + TotalCodeSuggestions: 132, + TotalCodeAcceptances: 67, + TotalCodeLinesSuggested: 154, + TotalCodeLinesAccepted: 72, + }, + }, + }, + }, + }, + }, + }, + CopilotIDEChat: &CopilotIDEChat{ + TotalEngagedUsers: 13, + Editors: []*CopilotIDEChatEditor{ + { + Name: "vscode", + TotalEngagedUsers: 13, + Models: []*CopilotIDEChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 12, + TotalChats: 45, + TotalChatInsertionEvents: 12, + TotalChatCopyEvents: 16, + }, + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalEngagedUsers: 1, + TotalChats: 10, + TotalChatInsertionEvents: 11, + TotalChatCopyEvents: 3, + }, + }, + }, + }, + }, + CopilotDotcomChat: &CopilotDotcomChat{ + TotalEngagedUsers: 14, + Models: []*CopilotDotcomChatModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalEngagedUsers: 14, + TotalChats: 38, + }, + }, + }, + CopilotDotcomPullRequests: &CopilotDotcomPullRequests{ + TotalEngagedUsers: 12, + Repositories: []*CopilotDotcomPullRequestsRepository{ + { + Name: "demo/repo1", + TotalEngagedUsers: 8, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "default", + IsCustomModel: false, + CustomModelTrainingDate: nil, + TotalPRSummariesCreated: 6, + TotalEngagedUsers: 8, + }, + }, + }, + { + Name: "demo/repo2", + TotalEngagedUsers: 4, + Models: []*CopilotDotcomPullRequestsModel{ + { + Name: "a-custom-model", + IsCustomModel: true, + CustomModelTrainingDate: Ptr("2024-02-01"), + TotalPRSummariesCreated: 10, + TotalEngagedUsers: 4, + }, + }, + }, + }, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationTeamMetrics returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationTeamMetrics" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationTeamMetrics(ctx, "\n", "\n", &CopilotMetricsListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationTeamMetrics(ctx, "o", "t", &CopilotMetricsListOptions{}) + if got != nil { + t.Errorf("Copilot.GetOrganizationTeamMetrics returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseDailyMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/metrics/reports/enterprise-1-day", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"day": "2025-07-01"}) + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_day": "2025-07-01" + }`) + }) + + ctx := t.Context() + opts := &CopilotMetricsReportOptions{Day: "2025-07-01"} + got, _, err := client.Copilot.GetEnterpriseDailyMetricsReport(ctx, "e", opts) + if err != nil { + t.Errorf("Copilot.GetEnterpriseDailyMetricsReport returned error: %v", err) + } + + want := &CopilotDailyMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportDay: "2025-07-01", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseDailyMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseDailyMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseDailyMetricsReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseDailyMetricsReport(ctx, "e", opts) + if got != nil { + t.Errorf("Copilot.GetEnterpriseDailyMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/metrics/reports/enterprise-28-day/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_start_day": "2025-07-01", + "report_end_day": "2025-07-28" + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetEnterpriseMetricsReport(ctx, "e") + if err != nil { + t.Errorf("Copilot.GetEnterpriseMetricsReport returned error: %v", err) + } + + want := &CopilotMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportStartDay: "2025-07-01", + ReportEndDay: "2025-07-28", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseMetricsReport(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseMetricsReport(ctx, "e") + if got != nil { + t.Errorf("Copilot.GetEnterpriseMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseUsersDailyMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/metrics/reports/users-1-day", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"day": "2025-07-01"}) + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_day": "2025-07-01" + }`) + }) + + ctx := t.Context() + opts := &CopilotMetricsReportOptions{Day: "2025-07-01"} + got, _, err := client.Copilot.GetEnterpriseUsersDailyMetricsReport(ctx, "e", opts) + if err != nil { + t.Errorf("Copilot.GetEnterpriseUsersDailyMetricsReport returned error: %v", err) + } + + want := &CopilotDailyMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportDay: "2025-07-01", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseUsersDailyMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseUsersDailyMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseUsersDailyMetricsReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseUsersDailyMetricsReport(ctx, "e", opts) + if got != nil { + t.Errorf("Copilot.GetEnterpriseUsersDailyMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetEnterpriseUsersMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/copilot/metrics/reports/users-28-day/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_start_day": "2025-07-01", + "report_end_day": "2025-07-28" + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetEnterpriseUsersMetricsReport(ctx, "e") + if err != nil { + t.Errorf("Copilot.GetEnterpriseUsersMetricsReport returned error: %v", err) + } + + want := &CopilotMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportStartDay: "2025-07-01", + ReportEndDay: "2025-07-28", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetEnterpriseUsersMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetEnterpriseUsersMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetEnterpriseUsersMetricsReport(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetEnterpriseUsersMetricsReport(ctx, "e") + if got != nil { + t.Errorf("Copilot.GetEnterpriseUsersMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationDailyMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/metrics/reports/organization-1-day", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"day": "2025-07-01"}) + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_day": "2025-07-01" + }`) + }) + + ctx := t.Context() + opts := &CopilotMetricsReportOptions{Day: "2025-07-01"} + got, _, err := client.Copilot.GetOrganizationDailyMetricsReport(ctx, "o", opts) + if err != nil { + t.Errorf("Copilot.GetOrganizationDailyMetricsReport returned error: %v", err) + } + + want := &CopilotDailyMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportDay: "2025-07-01", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationDailyMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationDailyMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationDailyMetricsReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationDailyMetricsReport(ctx, "o", opts) + if got != nil { + t.Errorf("Copilot.GetOrganizationDailyMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/metrics/reports/organization-28-day/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_start_day": "2025-07-01", + "report_end_day": "2025-07-28" + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetOrganizationMetricsReport(ctx, "o") + if err != nil { + t.Errorf("Copilot.GetOrganizationMetricsReport returned error: %v", err) + } + + want := &CopilotMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportStartDay: "2025-07-01", + ReportEndDay: "2025-07-28", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationMetricsReport(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationMetricsReport(ctx, "o") + if got != nil { + t.Errorf("Copilot.GetOrganizationMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationUsersDailyMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/metrics/reports/users-1-day", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"day": "2025-07-01"}) + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_day": "2025-07-01" + }`) + }) + + ctx := t.Context() + opts := &CopilotMetricsReportOptions{Day: "2025-07-01"} + got, _, err := client.Copilot.GetOrganizationUsersDailyMetricsReport(ctx, "o", opts) + if err != nil { + t.Errorf("Copilot.GetOrganizationUsersDailyMetricsReport returned error: %v", err) + } + + want := &CopilotDailyMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportDay: "2025-07-01", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationUsersDailyMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationUsersDailyMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationUsersDailyMetricsReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationUsersDailyMetricsReport(ctx, "o", opts) + if got != nil { + t.Errorf("Copilot.GetOrganizationUsersDailyMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_GetOrganizationUsersMetricsReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/copilot/metrics/reports/users-28-day/latest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "download_links": ["https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"], + "report_start_day": "2025-07-01", + "report_end_day": "2025-07-28" + }`) + }) + + ctx := t.Context() + got, _, err := client.Copilot.GetOrganizationUsersMetricsReport(ctx, "o") + if err != nil { + t.Errorf("Copilot.GetOrganizationUsersMetricsReport returned error: %v", err) + } + + want := &CopilotMetricsReport{ + DownloadLinks: []string{"https://example.com/copilot-usage-report-1.json", "https://example.com/copilot-usage-report-2.json"}, + ReportStartDay: "2025-07-01", + ReportEndDay: "2025-07-28", + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.GetOrganizationUsersMetricsReport returned %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationUsersMetricsReport" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Copilot.GetOrganizationUsersMetricsReport(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Copilot.GetOrganizationUsersMetricsReport(ctx, "o") + if got != nil { + t.Errorf("Copilot.GetOrganizationUsersMetricsReport returned %+v, want nil", got) + } + return resp, err + }) +} + +func TestCopilotService_DownloadCopilotMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/path/to/download", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "date": "2023-01-01", + "total_active_users": 100, + "total_engaged_users": 50, + "copilot_ide_code_completions": { + "total_engaged_users": 50, + "languages": [ + { + "name": "go", + "total_engaged_users": 10 + } + ], + "editors": [ + { + "name": "vscode", + "total_engaged_users": 10, + "models": [ + { + "name": "model1", + "is_custom_model": false, + "custom_model_training_date": null, + "total_engaged_users": 10, + "languages": [ + { + "name": "go", + "total_engaged_users": 10, + "total_code_suggestions": 100, + "total_code_acceptances": 50, + "total_code_lines_suggested": 1000, + "total_code_lines_accepted": 500 + } + ] + } + ] + } + ] + } + }]`) + }) + + ctx := t.Context() + url := client.baseURL.String() + "path/to/download" + got, resp, err := client.Copilot.DownloadCopilotMetrics(ctx, url) + if err != nil { + t.Errorf("Copilot.DownloadCopilotMetrics returned error: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("Copilot.DownloadCopilotMetrics returned status code: %v", resp.StatusCode) + } + + want := []*CopilotMetrics{ + { + Date: "2023-01-01", + TotalActiveUsers: Ptr(100), + TotalEngagedUsers: Ptr(50), + CopilotIDECodeCompletions: &CopilotIDECodeCompletions{ + TotalEngagedUsers: 50, + Languages: []*CopilotIDECodeCompletionsLanguage{ + { + Name: "go", + TotalEngagedUsers: 10, + }, + }, + Editors: []*CopilotIDECodeCompletionsEditor{ + { + Name: "vscode", + TotalEngagedUsers: 10, + Models: []*CopilotIDECodeCompletionsModel{ + { + Name: "model1", + IsCustomModel: false, + TotalEngagedUsers: 10, + Languages: []*CopilotIDECodeCompletionsModelLanguage{ + { + Name: "go", + TotalEngagedUsers: 10, + TotalCodeSuggestions: 100, + TotalCodeAcceptances: 50, + TotalCodeLinesSuggested: 1000, + TotalCodeLinesAccepted: 500, + }, + }, + }, + }, + }, + }, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.DownloadCopilotMetrics returned %+v, want %+v", got, want) + } + + // Test unexpected status code + mux.HandleFunc("/path/to/download/error", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + urlErr := client.baseURL.String() + "path/to/download/error" + _, _, err = client.Copilot.DownloadCopilotMetrics(ctx, urlErr) + if err == nil { + t.Error("Copilot.DownloadCopilotMetrics expected error but got none") + } + + // Test invalid URL (fails http.NewRequestWithContext) + _, _, err = client.Copilot.DownloadCopilotMetrics(ctx, "\n") + if err == nil { + t.Error("Copilot.DownloadCopilotMetrics expected error for invalid URL, got none") + } + + // Test invalid scheme (fails client.Do) + _, _, err = client.Copilot.DownloadCopilotMetrics(ctx, "invalid-scheme://test") + if err == nil { + t.Error("Copilot.DownloadCopilotMetrics expected error for invalid scheme, got none") + } + + // Test json decoding error + mux.HandleFunc("/path/to/download/badjson", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{invalid JSON`) + }) + urlBadJSON := client.baseURL.String() + "path/to/download/badjson" + _, _, err = client.Copilot.DownloadCopilotMetrics(ctx, urlBadJSON) + if err == nil { + t.Error("Copilot.DownloadCopilotMetrics expected error for bad JSON, got none") + } +} + +func TestCopilotService_DownloadDailyMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/path/to/daily", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "day": "2026-04-01", + "organization_id": "123", + "daily_active_cli_users": 2, + "daily_active_users": 10, + "weekly_active_users": 20, + "monthly_active_users": 30, + "chat_panel_ask_mode": 4, + "totals_by_ide": [ + {"ide": "vscode", "user_initiated_interaction_count": 5, "loc_added_sum": 100} + ], + "totals_by_feature": [ + {"feature": "completion", "user_initiated_interaction_count": 5}, + {"feature": "agent_edit", "loc_added_sum": 7, "loc_deleted_sum": 2} + ], + "totals_by_language_feature": [ + {"language": "go", "feature": "completion", "code_generation_activity_count": 3} + ], + "totals_by_language_model": [ + {"language": "go", "model": "m1", "code_generation_activity_count": 3} + ], + "totals_by_model_feature": [ + {"model": "m1", "feature": "completion", "user_initiated_interaction_count": 5} + ], + "totals_by_cli": { + "session_count": 3, + "request_count": 4, + "prompt_count": 2, + "token_usage": { + "avg_tokens_per_request": 4123.5, + "output_tokens_sum": 7000, + "prompt_tokens_sum": 9494 + } + }, + "loc_added_sum": 100, + "pull_requests": { + "total_reviewed": 1, + "total_created": 2, + "median_minutes_to_merge": 12.5, + "median_minutes_to_merge_copilot_authored": 4.5, + "median_minutes_to_merge_copilot_reviewed": 6.5 + } + }`) + }) + + ctx := t.Context() + url := client.baseURL.String() + "path/to/daily" + got, resp, err := client.Copilot.DownloadDailyMetrics(ctx, url) + if err != nil { + t.Errorf("Copilot.DownloadDailyMetrics returned error: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("Copilot.DownloadDailyMetrics returned status code: %v", resp.StatusCode) + } + + want := &CopilotDailyMetrics{ + Day: "2026-04-01", + OrganizationID: Ptr("123"), + DailyActiveCLIUsers: Ptr(2), + DailyActiveUsers: Ptr(10), + WeeklyActiveUsers: Ptr(20), + MonthlyActiveUsers: Ptr(30), + CopilotMetricsChatPanel: CopilotMetricsChatPanel{ + ChatPanelAskMode: Ptr(4), + }, + TotalsByIDE: []*CopilotMetricsIDE{ + {IDE: "vscode", UserInitiatedInteractionCount: Ptr(5), CopilotMetricsCodeActivity: CopilotMetricsCodeActivity{LOCAddedSum: Ptr(100)}}, + }, + TotalsByFeature: []*CopilotMetricsFeature{ + {Feature: "completion", UserInitiatedInteractionCount: Ptr(5)}, + {Feature: "agent_edit", CopilotMetricsCodeActivity: CopilotMetricsCodeActivity{LOCAddedSum: Ptr(7), LOCDeletedSum: Ptr(2)}}, + }, + TotalsByLanguageFeature: []*CopilotMetricsLanguageFeature{ + {Language: "go", Feature: "completion", CopilotMetricsCodeActivity: CopilotMetricsCodeActivity{CodeGenerationActivityCount: Ptr(3)}}, + }, + TotalsByLanguageModel: []*CopilotMetricsLanguageModel{ + {Language: "go", Model: "m1", CopilotMetricsCodeActivity: CopilotMetricsCodeActivity{CodeGenerationActivityCount: Ptr(3)}}, + }, + TotalsByModelFeature: []*CopilotMetricsModelFeature{ + {Model: "m1", Feature: "completion", UserInitiatedInteractionCount: Ptr(5)}, + }, + TotalsByCLI: &CopilotMetricsCLI{ + SessionCount: Ptr(3), + RequestCount: Ptr(4), + PromptCount: Ptr(2), + TokenUsage: &CopilotMetricsCLITokenUsage{ + AvgTokensPerRequest: Ptr(4123.5), + OutputTokensSum: Ptr(7000), + PromptTokensSum: Ptr(9494), + }, + }, + LOCAddedSum: Ptr(100), + PullRequests: &CopilotMetricsPullRequests{ + TotalReviewed: Ptr(1), + TotalCreated: Ptr(2), + MedianMinutesToMerge: Ptr(12.5), + MedianMinutesToMergeCopilotAuthored: Ptr(4.5), + MedianMinutesToMergeCopilotReviewed: Ptr(6.5), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.DownloadDailyMetrics returned %+v, want %+v", got, want) + } + + mux.HandleFunc("/path/to/daily/error", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + if _, _, err := client.Copilot.DownloadDailyMetrics(ctx, client.baseURL.String()+"path/to/daily/error"); err == nil { + t.Error("Copilot.DownloadDailyMetrics expected error but got none") + } + if _, _, err := client.Copilot.DownloadDailyMetrics(ctx, "\n"); err == nil { + t.Error("Copilot.DownloadDailyMetrics expected error for invalid URL, got none") + } + if _, _, err := client.Copilot.DownloadDailyMetrics(ctx, "invalid-scheme://test"); err == nil { + t.Error("Copilot.DownloadDailyMetrics expected error for invalid scheme, got none") + } + + mux.HandleFunc("/path/to/daily/badjson", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{invalid`) + }) + if _, _, err := client.Copilot.DownloadDailyMetrics(ctx, client.baseURL.String()+"path/to/daily/badjson"); err == nil { + t.Error("Copilot.DownloadDailyMetrics expected error for bad JSON, got none") + } +} + +func TestCopilotService_DownloadPeriodicMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/path/to/periodic", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "report_start_day": "2026-03-05", + "report_end_day": "2026-04-01", + "organization_id": "123", + "created_at": `+refTimeStr(1136178000)+`, + "day_totals": [ + { + "day": "2026-03-05", + "daily_active_cli_users": 2, + "daily_active_users": 5, + "totals_by_cli": { + "session_count": 1, + "request_count": 2, + "prompt_count": 1, + "token_usage": { + "avg_tokens_per_request": 4000.0, + "output_tokens_sum": 5000, + "prompt_tokens_sum": 3000 + } + }, + "pull_requests": { + "median_minutes_to_merge": 8.5, + "median_minutes_to_merge_copilot_authored": 5.0, + "median_minutes_to_merge_copilot_reviewed": 7.0 + } + }, + {"day": "2026-03-06", "daily_active_users": 7} + ] + }`) + }) + + ctx := t.Context() + url := client.baseURL.String() + "path/to/periodic" + got, resp, err := client.Copilot.DownloadPeriodicMetrics(ctx, url) + if err != nil { + t.Errorf("Copilot.DownloadPeriodicMetrics returned error: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("Copilot.DownloadPeriodicMetrics returned status code: %v", resp.StatusCode) + } + + want := &CopilotPeriodicMetrics{ + ReportStartDay: "2026-03-05", + ReportEndDay: "2026-04-01", + OrganizationID: Ptr("123"), + CreatedAt: refTimestamp(1136178000), + DayTotals: []*CopilotDailyMetrics{ + { + Day: "2026-03-05", + DailyActiveCLIUsers: Ptr(2), + DailyActiveUsers: Ptr(5), + TotalsByCLI: &CopilotMetricsCLI{ + SessionCount: Ptr(1), + RequestCount: Ptr(2), + PromptCount: Ptr(1), + TokenUsage: &CopilotMetricsCLITokenUsage{ + AvgTokensPerRequest: Ptr(4000.0), + OutputTokensSum: Ptr(5000), + PromptTokensSum: Ptr(3000), + }, + }, + PullRequests: &CopilotMetricsPullRequests{ + MedianMinutesToMerge: Ptr(8.5), + MedianMinutesToMergeCopilotAuthored: Ptr(5.0), + MedianMinutesToMergeCopilotReviewed: Ptr(7.0), + }, + }, + {Day: "2026-03-06", DailyActiveUsers: Ptr(7)}, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.DownloadPeriodicMetrics returned %+v, want %+v", got, want) + } + + mux.HandleFunc("/path/to/periodic/error", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + if _, _, err := client.Copilot.DownloadPeriodicMetrics(ctx, client.baseURL.String()+"path/to/periodic/error"); err == nil { + t.Error("Copilot.DownloadPeriodicMetrics expected error but got none") + } + if _, _, err := client.Copilot.DownloadPeriodicMetrics(ctx, "\n"); err == nil { + t.Error("Copilot.DownloadPeriodicMetrics expected error for invalid URL, got none") + } + if _, _, err := client.Copilot.DownloadPeriodicMetrics(ctx, "invalid-scheme://test"); err == nil { + t.Error("Copilot.DownloadPeriodicMetrics expected error for invalid scheme, got none") + } + + mux.HandleFunc("/path/to/periodic/badjson", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{invalid`) + }) + if _, _, err := client.Copilot.DownloadPeriodicMetrics(ctx, client.baseURL.String()+"path/to/periodic/badjson"); err == nil { + t.Error("Copilot.DownloadPeriodicMetrics expected error for bad JSON, got none") + } +} + +func TestCopilotService_DownloadUserDailyMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/path/to/users-daily", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"user_id":1,"user_login":"alice","day":"2026-04-01","user_initiated_interaction_count":5,"chat_panel_edit_mode":2,"used_chat":true,"used_cli":true,"used_copilot_code_review_active":true,"totals_by_cli":{"session_count":2,"request_count":2,"prompt_count":1,"last_known_cli_version":{"sampled_at":`+refTimeStr(1136178000)+`,"cli_version":"1.0.8"}},"totals_by_ide":[{"ide":"vscode","user_initiated_interaction_count":5,"last_known_plugin_version":{"sampled_at":`+refTimeStr(1136178001)+`,"plugin":"copilot","plugin_version":"1.0.0"},"last_known_ide_version":{"sampled_at":`+refTimeStr(1136178002)+`,"ide_version":"1.90"}}]} +{"user_id":2,"user_login":"bob","day":"2026-04-01","used_agent":true,"used_copilot_code_review_passive":true} +`) + }) + + ctx := t.Context() + url := client.baseURL.String() + "path/to/users-daily" + got, resp, err := client.Copilot.DownloadUserDailyMetrics(ctx, url) + if err != nil { + t.Errorf("Copilot.DownloadUserDailyMetrics returned error: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("Copilot.DownloadUserDailyMetrics returned status code: %v", resp.StatusCode) + } + + want := []*CopilotUserDailyMetrics{ + { + UserID: 1, + UserLogin: "alice", + Day: "2026-04-01", + UserInitiatedInteractionCount: Ptr(5), + CopilotMetricsChatPanel: CopilotMetricsChatPanel{ + ChatPanelEditMode: Ptr(2), + }, + UsedChat: Ptr(true), + UsedCLI: Ptr(true), + UsedCopilotCodeReviewActive: Ptr(true), + TotalsByCLI: &CopilotMetricsCLI{ + SessionCount: Ptr(2), + RequestCount: Ptr(2), + PromptCount: Ptr(1), + LastKnownCLIVersion: &CopilotMetricsCLIVersion{ + SampledAt: refTimestamp(1136178000), + CLIVersion: "1.0.8", + }, + }, + TotalsByIDE: []*CopilotUserMetricsIDE{ + { + IDE: "vscode", + UserInitiatedInteractionCount: Ptr(5), + LastKnownPluginVersion: &CopilotUserMetricsPluginVersion{ + SampledAt: refTimestamp(1136178001), + Plugin: "copilot", + PluginVersion: "1.0.0", + }, + LastKnownIDEVersion: &CopilotUserMetricsIDEVersion{ + SampledAt: refTimestamp(1136178002), + IDEVersion: "1.90", + }, + }, + }, + }, + { + UserID: 2, + UserLogin: "bob", + Day: "2026-04-01", + UsedAgent: Ptr(true), + UsedCopilotCodeReviewPassive: Ptr(true), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.DownloadUserDailyMetrics returned %+v, want %+v", got, want) + } + + // Empty body parses to a nil slice (no records). + mux.HandleFunc("/path/to/users-daily/empty", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + gotEmpty, _, err := client.Copilot.DownloadUserDailyMetrics(ctx, client.baseURL.String()+"path/to/users-daily/empty") + if err != nil { + t.Errorf("Copilot.DownloadUserDailyMetrics empty body returned error: %v", err) + } + if gotEmpty != nil { + t.Errorf("Copilot.DownloadUserDailyMetrics empty body returned %+v, want nil", gotEmpty) + } + + mux.HandleFunc("/path/to/users-daily/error", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + if _, _, err := client.Copilot.DownloadUserDailyMetrics(ctx, client.baseURL.String()+"path/to/users-daily/error"); err == nil { + t.Error("Copilot.DownloadUserDailyMetrics expected error but got none") + } + if _, _, err := client.Copilot.DownloadUserDailyMetrics(ctx, "\n"); err == nil { + t.Error("Copilot.DownloadUserDailyMetrics expected error for invalid URL, got none") + } + if _, _, err := client.Copilot.DownloadUserDailyMetrics(ctx, "invalid-scheme://test"); err == nil { + t.Error("Copilot.DownloadUserDailyMetrics expected error for invalid scheme, got none") + } + + // A malformed line causes the stream decode to fail. + mux.HandleFunc("/path/to/users-daily/badjson", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, "{\"user_id\":1,\"day\":\"2026-04-01\"}\n{bad\n") + }) + if _, _, err := client.Copilot.DownloadUserDailyMetrics(ctx, client.baseURL.String()+"path/to/users-daily/badjson"); err == nil { + t.Error("Copilot.DownloadUserDailyMetrics expected error for bad JSON, got none") + } +} + +func TestCopilotService_DownloadUserPeriodicMetrics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/path/to/users-periodic", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"report_start_day":"2026-03-05","report_end_day":"2026-04-01","day":"2026-03-05","user_id":1,"user_login":"alice","user_initiated_interaction_count":3,"used_copilot_code_review_active":true} +{"report_start_day":"2026-03-05","report_end_day":"2026-04-01","day":"2026-03-06","user_id":1,"user_login":"alice","used_cli":true,"used_copilot_code_review_passive":true,"used_copilot_coding_agent":true,"totals_by_cli":{"session_count":1,"request_count":3,"prompt_count":2,"token_usage":{"avg_tokens_per_request":1200.5,"output_tokens_sum":2400,"prompt_tokens_sum":1201}}} +`) + }) + + ctx := t.Context() + url := client.baseURL.String() + "path/to/users-periodic" + got, resp, err := client.Copilot.DownloadUserPeriodicMetrics(ctx, url) + if err != nil { + t.Errorf("Copilot.DownloadUserPeriodicMetrics returned error: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("Copilot.DownloadUserPeriodicMetrics returned status code: %v", resp.StatusCode) + } + + want := []*CopilotUserPeriodicMetrics{ + { + ReportStartDay: "2026-03-05", + ReportEndDay: "2026-04-01", + Day: "2026-03-05", + UserID: 1, + UserLogin: "alice", + UserInitiatedInteractionCount: Ptr(3), + UsedCopilotCodeReviewActive: Ptr(true), + }, + { + ReportStartDay: "2026-03-05", + ReportEndDay: "2026-04-01", + Day: "2026-03-06", + UserID: 1, + UserLogin: "alice", + UsedCLI: Ptr(true), + UsedCopilotCodeReviewPassive: Ptr(true), + UsedCopilotCodingAgent: Ptr(true), + TotalsByCLI: &CopilotMetricsCLI{ + SessionCount: Ptr(1), + RequestCount: Ptr(3), + PromptCount: Ptr(2), + TokenUsage: &CopilotMetricsCLITokenUsage{ + AvgTokensPerRequest: Ptr(1200.5), + OutputTokensSum: Ptr(2400), + PromptTokensSum: Ptr(1201), + }, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Copilot.DownloadUserPeriodicMetrics returned %+v, want %+v", got, want) + } + + // Empty body parses to a nil slice (no records). + mux.HandleFunc("/path/to/users-periodic/empty", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + gotEmpty, _, err := client.Copilot.DownloadUserPeriodicMetrics(ctx, client.baseURL.String()+"path/to/users-periodic/empty") + if err != nil { + t.Errorf("Copilot.DownloadUserPeriodicMetrics empty body returned error: %v", err) + } + if gotEmpty != nil { + t.Errorf("Copilot.DownloadUserPeriodicMetrics empty body returned %+v, want nil", gotEmpty) + } + + mux.HandleFunc("/path/to/users-periodic/error", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + if _, _, err := client.Copilot.DownloadUserPeriodicMetrics(ctx, client.baseURL.String()+"path/to/users-periodic/error"); err == nil { + t.Error("Copilot.DownloadUserPeriodicMetrics expected error but got none") + } + if _, _, err := client.Copilot.DownloadUserPeriodicMetrics(ctx, "\n"); err == nil { + t.Error("Copilot.DownloadUserPeriodicMetrics expected error for invalid URL, got none") + } + if _, _, err := client.Copilot.DownloadUserPeriodicMetrics(ctx, "invalid-scheme://test"); err == nil { + t.Error("Copilot.DownloadUserPeriodicMetrics expected error for invalid scheme, got none") + } + + mux.HandleFunc("/path/to/users-periodic/badjson", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, "{not json\n") + }) + if _, _, err := client.Copilot.DownloadUserPeriodicMetrics(ctx, client.baseURL.String()+"path/to/users-periodic/badjson"); err == nil { + t.Error("Copilot.DownloadUserPeriodicMetrics expected error for bad JSON, got none") + } +} diff --git a/github/credentials.go b/github/credentials.go new file mode 100644 index 00000000000..d785edc0dcb --- /dev/null +++ b/github/credentials.go @@ -0,0 +1,37 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// CredentialsService handles credentials related methods of the GitHub API. +type CredentialsService service + +// revokeCredentialsRequest represents the request body for revoking credentials. +type revokeCredentialsRequest struct { + // The list of credential strings (tokens) to revoke. + Credentials []string `json:"credentials"` +} + +// Revoke revokes a list of credentials. +// +// GitHub API docs: https://docs.github.com/rest/credentials/revoke?apiVersion=2022-11-28#revoke-a-list-of-credentials +// +//meta:operation POST /credentials/revoke +func (s *CredentialsService) Revoke(ctx context.Context, credentials []string) (*Response, error) { + u := "credentials/revoke" + + reqBody := &revokeCredentialsRequest{Credentials: credentials} + + req, err := s.client.NewRequest(ctx, "POST", u, reqBody) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/credentials_test.go b/github/credentials_test.go new file mode 100644 index 00000000000..286a94a4527 --- /dev/null +++ b/github/credentials_test.go @@ -0,0 +1,45 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "net/http" + "testing" +) + +func TestCredentialsService_Revoke(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + creds := []string{ + "ghp_1234567890abcdef1234567890abcdef12345678", + "ghp_abcdef1234567890abcdef1234567890abcdef12", + } + + mux.HandleFunc("/credentials/revoke", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, &revokeCredentialsRequest{Credentials: creds}) + w.WriteHeader(http.StatusAccepted) + }) + + ctx := t.Context() + resp, err := client.Credentials.Revoke(ctx, creds) + if !errors.As(err, new(*AcceptedError)) { + t.Errorf("Credentials.Revoke returned error: %v (want AcceptedError)", err) + } + if resp == nil { + t.Fatal("Credentials.Revoke returned nil response") + } + if resp.StatusCode != http.StatusAccepted { + t.Errorf("Credentials.Revoke returned status %v, want %v", resp.StatusCode, http.StatusAccepted) + } + + const methodName = "Revoke" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Credentials.Revoke(ctx, []string{"a"}) + }) +} diff --git a/github/dependabot.go b/github/dependabot.go new file mode 100644 index 00000000000..5e4f7354403 --- /dev/null +++ b/github/dependabot.go @@ -0,0 +1,12 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +// DependabotService handles communication with the Dependabot related +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/dependabot?apiVersion=2022-11-28 +type DependabotService service diff --git a/github/dependabot_alerts.go b/github/dependabot_alerts.go new file mode 100644 index 00000000000..6cc37b2c367 --- /dev/null +++ b/github/dependabot_alerts.go @@ -0,0 +1,195 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// Dependency represents the vulnerable dependency. +type Dependency struct { + Package *VulnerabilityPackage `json:"package,omitempty"` + ManifestPath *string `json:"manifest_path,omitempty"` + Scope *string `json:"scope,omitempty"` +} + +// AdvisoryCVSS represents the advisory pertaining to the Common Vulnerability Scoring System. +type AdvisoryCVSS struct { + Score *float64 `json:"score,omitempty"` + VectorString *string `json:"vector_string,omitempty"` +} + +// AdvisoryCVSSSeverities represents the advisory pertaining to the Common Vulnerability Scoring System +// for each supported CVSS version. +type AdvisoryCVSSSeverities struct { + CVSSV3 *AdvisoryCVSS `json:"cvss_v3,omitempty"` + CVSSV4 *AdvisoryCVSS `json:"cvss_v4,omitempty"` +} + +// AdvisoryCWEs represent the advisory pertaining to Common Weakness Enumeration. +type AdvisoryCWEs struct { + CWEID *string `json:"cwe_id,omitempty"` + Name *string `json:"name,omitempty"` +} + +// AdvisoryEPSS represents the advisory pertaining to the Exploit Prediction Scoring System. +// +// For more information, see: +// https://github.blog/changelog/2024-10-10-epss-scores-in-the-github-advisory-database/ +type AdvisoryEPSS struct { + Percentage float64 `json:"percentage"` + Percentile float64 `json:"percentile"` +} + +// DependabotSecurityAdvisory represents the GitHub Security Advisory. +type DependabotSecurityAdvisory struct { + GHSAID *string `json:"ghsa_id,omitempty"` + CVEID *string `json:"cve_id,omitempty"` + Summary *string `json:"summary,omitempty"` + Description *string `json:"description,omitempty"` + Vulnerabilities []*AdvisoryVulnerability `json:"vulnerabilities,omitempty"` + Severity *string `json:"severity,omitempty"` + Classification *string `json:"classification,omitempty"` + CVSS *AdvisoryCVSS `json:"cvss,omitempty"` + CVSSSeverities *AdvisoryCVSSSeverities `json:"cvss_severities,omitempty"` + CWEs []*AdvisoryCWEs `json:"cwes,omitempty"` + EPSS *AdvisoryEPSS `json:"epss,omitempty"` + Identifiers []*AdvisoryIdentifier `json:"identifiers,omitempty"` + References []*AdvisoryReference `json:"references,omitempty"` + PublishedAt *Timestamp `json:"published_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + WithdrawnAt *Timestamp `json:"withdrawn_at,omitempty"` +} + +// DependabotAlert represents a Dependabot alert. +type DependabotAlert struct { + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + Dependency *Dependency `json:"dependency,omitempty"` + SecurityAdvisory *DependabotSecurityAdvisory `json:"security_advisory,omitempty"` + SecurityVulnerability *AdvisoryVulnerability `json:"security_vulnerability,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + DismissedAt *Timestamp `json:"dismissed_at,omitempty"` + DismissedBy *User `json:"dismissed_by,omitempty"` + DismissedReason *string `json:"dismissed_reason,omitempty"` + DismissedComment *string `json:"dismissed_comment,omitempty"` + FixedAt *Timestamp `json:"fixed_at,omitempty"` + AutoDismissedAt *Timestamp `json:"auto_dismissed_at,omitempty"` + // The repository is always empty for events + Repository *Repository `json:"repository,omitempty"` +} + +// DependabotAlertState represents the state of a Dependabot alert to update. +type DependabotAlertState struct { + // The state of the Dependabot alert. A dismissed_reason must be provided when setting the state to dismissed. + State string `json:"state"` + // Required when state is dismissed. A reason for dismissing the alert. + // Can be one of: fix_started, inaccurate, no_bandwidth, not_used, tolerable_risk + DismissedReason *string `json:"dismissed_reason,omitempty"` + // An optional comment associated with dismissing the alert. + DismissedComment *string `json:"dismissed_comment,omitempty"` +} + +// ListAlertsOptions specifies the optional parameters to the DependabotService.ListRepoAlerts +// and DependabotService.ListOrgAlerts methods. +type ListAlertsOptions struct { + State *string `url:"state,omitempty"` + Severity *string `url:"severity,omitempty"` + Ecosystem *string `url:"ecosystem,omitempty"` + Package *string `url:"package,omitempty"` + Scope *string `url:"scope,omitempty"` + Sort *string `url:"sort,omitempty"` + Direction *string `url:"direction,omitempty"` + + ListOptions + ListCursorOptions +} + +func (s *DependabotService) listAlerts(ctx context.Context, url string, opts *ListAlertsOptions) ([]*DependabotAlert, *Response, error) { + u, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*DependabotAlert + resp, err := s.client.Do(req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + +// ListRepoAlerts lists all Dependabot alerts of a repository. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/alerts?apiVersion=2022-11-28#list-dependabot-alerts-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/dependabot/alerts +func (s *DependabotService) ListRepoAlerts(ctx context.Context, owner, repo string, opts *ListAlertsOptions) ([]*DependabotAlert, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/dependabot/alerts", owner, repo) + return s.listAlerts(ctx, url, opts) +} + +// ListOrgAlerts lists all Dependabot alerts of an organization. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/alerts?apiVersion=2022-11-28#list-dependabot-alerts-for-an-organization +// +//meta:operation GET /orgs/{org}/dependabot/alerts +func (s *DependabotService) ListOrgAlerts(ctx context.Context, org string, opts *ListAlertsOptions) ([]*DependabotAlert, *Response, error) { + url := fmt.Sprintf("orgs/%v/dependabot/alerts", org) + return s.listAlerts(ctx, url, opts) +} + +// GetRepoAlert gets a single repository Dependabot alert. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/alerts?apiVersion=2022-11-28#get-a-dependabot-alert +// +//meta:operation GET /repos/{owner}/{repo}/dependabot/alerts/{alert_number} +func (s *DependabotService) GetRepoAlert(ctx context.Context, owner, repo string, number int) (*DependabotAlert, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/dependabot/alerts/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var alert *DependabotAlert + resp, err := s.client.Do(req, &alert) + if err != nil { + return nil, resp, err + } + + return alert, resp, nil +} + +// UpdateAlert updates a Dependabot alert. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/alerts?apiVersion=2022-11-28#update-a-dependabot-alert +// +//meta:operation PATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number} +func (s *DependabotService) UpdateAlert(ctx context.Context, owner, repo string, number int, body *DependabotAlertState) (*DependabotAlert, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/dependabot/alerts/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "PATCH", url, body) + if err != nil { + return nil, nil, err + } + + var alert *DependabotAlert + resp, err := s.client.Do(req, &alert) + if err != nil { + return nil, resp, err + } + + return alert, resp, nil +} diff --git a/github/dependabot_alerts_test.go b/github/dependabot_alerts_test.go new file mode 100644 index 00000000000..d5f09ec8e88 --- /dev/null +++ b/github/dependabot_alerts_test.go @@ -0,0 +1,178 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDependabotService_ListRepoAlerts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open"}) + fmt.Fprint(w, `[{"number":1,"state":"open"},{"number":42,"state":"fixed"}]`) + }) + + opts := &ListAlertsOptions{State: Ptr("open")} + ctx := t.Context() + alerts, _, err := client.Dependabot.ListRepoAlerts(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Dependabot.ListRepoAlerts returned error: %v", err) + } + + want := []*DependabotAlert{ + {Number: Ptr(1), State: Ptr("open")}, + {Number: Ptr(42), State: Ptr("fixed")}, + } + if !cmp.Equal(alerts, want) { + t.Errorf("Dependabot.ListRepoAlerts returned %+v, want %+v", alerts, want) + } + + const methodName = "ListRepoAlerts" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.ListRepoAlerts(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.ListRepoAlerts(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_GetRepoAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/alerts/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"number":42,"state":"fixed"}`) + }) + + ctx := t.Context() + alert, _, err := client.Dependabot.GetRepoAlert(ctx, "o", "r", 42) + if err != nil { + t.Errorf("Dependabot.GetRepoAlert returned error: %v", err) + } + + want := &DependabotAlert{ + Number: Ptr(42), + State: Ptr("fixed"), + } + if !cmp.Equal(alert, want) { + t.Errorf("Dependabot.GetRepoAlert returned %+v, want %+v", alert, want) + } + + const methodName = "GetRepoAlert" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.GetRepoAlert(ctx, "\n", "\n", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.GetRepoAlert(ctx, "o", "r", 42) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_ListOrgAlerts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open"}) + fmt.Fprint(w, `[{"number":1,"state":"open"},{"number":42,"state":"fixed"}]`) + }) + + opts := &ListAlertsOptions{State: Ptr("open")} + ctx := t.Context() + alerts, _, err := client.Dependabot.ListOrgAlerts(ctx, "o", opts) + if err != nil { + t.Errorf("Dependabot.ListOrgAlerts returned error: %v", err) + } + + want := []*DependabotAlert{ + {Number: Ptr(1), State: Ptr("open")}, + {Number: Ptr(42), State: Ptr("fixed")}, + } + if !cmp.Equal(alerts, want) { + t.Errorf("Dependabot.ListOrgAlerts returned %+v, want %+v", alerts, want) + } + + const methodName = "ListOrgAlerts" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.ListOrgAlerts(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.ListOrgAlerts(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_UpdateAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + state := Ptr("dismissed") + dismissedReason := Ptr("no_bandwidth") + dismissedComment := Ptr("no time to fix this") + + alertState := &DependabotAlertState{State: *state, DismissedReason: dismissedReason, DismissedComment: dismissedComment} + + mux.HandleFunc("/repos/o/r/dependabot/alerts/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"number":42,"state":"dismissed","dismissed_reason":"no_bandwidth","dismissed_comment":"no time to fix this"}`) + }) + + ctx := t.Context() + alert, _, err := client.Dependabot.UpdateAlert(ctx, "o", "r", 42, alertState) + if err != nil { + t.Errorf("Dependabot.UpdateAlert returned error: %v", err) + } + + want := &DependabotAlert{ + Number: Ptr(42), + State: Ptr("dismissed"), + DismissedReason: Ptr("no_bandwidth"), + DismissedComment: Ptr("no time to fix this"), + } + if !cmp.Equal(alert, want) { + t.Errorf("Dependabot.UpdateAlert returned %+v, want %+v", alert, want) + } + + const methodName = "UpdateAlert" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.UpdateAlert(ctx, "\n", "\n", 0, alertState) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.UpdateAlert(ctx, "o", "r", 42, alertState) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/dependabot_secrets.go b/github/dependabot_secrets.go new file mode 100644 index 00000000000..03d3f9e2aeb --- /dev/null +++ b/github/dependabot_secrets.go @@ -0,0 +1,311 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetRepoPublicKey gets a public key that should be used for Dependabot secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#get-a-repository-public-key +// +//meta:operation GET /repos/{owner}/{repo}/dependabot/secrets/public-key +func (s *DependabotService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/dependabot/secrets/public-key", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pubKey *PublicKey + resp, err := s.client.Do(req, &pubKey) + if err != nil { + return nil, resp, err + } + + return pubKey, resp, nil +} + +// GetOrgPublicKey gets a public key that should be used for Dependabot secret encryption. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#get-an-organization-public-key +// +//meta:operation GET /orgs/{org}/dependabot/secrets/public-key +func (s *DependabotService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/public-key", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pubKey *PublicKey + resp, err := s.client.Do(req, &pubKey) + if err != nil { + return nil, resp, err + } + + return pubKey, resp, nil +} + +// ListRepoSecrets lists all Dependabot secrets available in a repository +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#list-repository-secrets +// +//meta:operation GET /repos/{owner}/{repo}/dependabot/secrets +func (s *DependabotService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/dependabot/secrets", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// ListOrgSecrets lists all Dependabot secrets available in an organization +// without revealing their encrypted values. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#list-organization-secrets +// +//meta:operation GET /orgs/{org}/dependabot/secrets +func (s *DependabotService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secrets *Secrets + resp, err := s.client.Do(req, &secrets) + if err != nil { + return nil, resp, err + } + + return secrets, resp, nil +} + +// GetRepoSecret gets a single repository Dependabot secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#get-a-repository-secret +// +//meta:operation GET /repos/{owner}/{repo}/dependabot/secrets/{secret_name} +func (s *DependabotService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// GetOrgSecret gets a single organization Dependabot secret without revealing its encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#get-an-organization-secret +// +//meta:operation GET /orgs/{org}/dependabot/secrets/{secret_name} +func (s *DependabotService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secret *Secret + resp, err := s.client.Do(req, &secret) + if err != nil { + return nil, resp, err + } + + return secret, resp, nil +} + +// CreateOrUpdateRepoSecret creates or updates a repository Dependabot secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret +// +//meta:operation PUT /repos/{owner}/{repo}/dependabot/secrets/{secret_name} +func (s *DependabotService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo, name string, body SecretRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateOrUpdateOrgSecret creates or updates an organization Dependabot secret with an encrypted value. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#create-or-update-an-organization-secret +// +//meta:operation PUT /orgs/{org}/dependabot/secrets/{secret_name} +func (s *DependabotService) CreateOrUpdateOrgSecret(ctx context.Context, org, name string, body SecretOrgRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, name) + + var bodyOverride any = body + if len(body.SelectedRepositoryIDs) > 0 { + repoIDs := make([]string, len(body.SelectedRepositoryIDs)) + for i, id := range body.SelectedRepositoryIDs { + repoIDs[i] = fmt.Sprintf("%v", id) + } + + bodyOverride = struct { + SecretOrgRequest + SelectedRepositoryIDs []string `json:"selected_repository_ids,omitzero"` + }{ + SecretOrgRequest: body, + SelectedRepositoryIDs: repoIDs, + } + } + + req, err := s.client.NewRequest(ctx, "PUT", u, bodyOverride) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteRepoSecret deletes a Dependabot secret in a repository using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#delete-a-repository-secret +// +//meta:operation DELETE /repos/{owner}/{repo}/dependabot/secrets/{secret_name} +func (s *DependabotService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/dependabot/secrets/%v", owner, repo, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteOrgSecret deletes a Dependabot secret in an organization using the secret name. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#delete-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/dependabot/secrets/{secret_name} +func (s *DependabotService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v", org, name) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListSelectedReposForOrgSecret lists all repositories that have access to a Dependabot secret. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#list-selected-repositories-for-an-organization-secret +// +//meta:operation GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories +func (s *DependabotService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories", org, name) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *SelectedReposList + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// SetSelectedReposForOrgSecret sets the repositories that have access to a Dependabot secret. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#set-selected-repositories-for-an-organization-secret +// +//meta:operation PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories +func (s *DependabotService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories", org, name) + + type repoIDs struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + } + + req, err := s.client.NewRequest(ctx, "PUT", u, repoIDs{SelectedIDs: ids}) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddSelectedRepoToOrgSecret adds a repository to an organization Dependabot secret. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#add-selected-repository-to-an-organization-secret +// +//meta:operation PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id} +func (s *DependabotService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories/%v", org, name, repoID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveSelectedRepoFromOrgSecret removes a repository from an organization Dependabot secret. +// +// GitHub API docs: https://docs.github.com/rest/dependabot/secrets?apiVersion=2022-11-28#remove-selected-repository-from-an-organization-secret +// +//meta:operation DELETE /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id} +func (s *DependabotService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/dependabot/secrets/%v/repositories/%v", org, name, repoID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/dependabot_secrets_test.go b/github/dependabot_secrets_test.go new file mode 100644 index 00000000000..66ecb474adc --- /dev/null +++ b/github/dependabot_secrets_test.go @@ -0,0 +1,553 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDependabotService_GetRepoPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r") + if err != nil { + t.Errorf("Dependabot.GetRepoPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Dependabot.GetRepoPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetRepoPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.GetRepoPublicKey(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_GetRepoPublicKeyNumeric(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":1234,"key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r") + if err != nil { + t.Errorf("Dependabot.GetRepoPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("1234"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Dependabot.GetRepoPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetRepoPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.GetRepoPublicKey(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.GetRepoPublicKey(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_ListRepoSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"secrets":[{"name":"A","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`},{"name":"B","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Dependabot.ListRepoSecrets(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Dependabot.ListRepoSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 4, + Secrets: []*Secret{ + {Name: "A", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001)}, + {Name: "B", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003)}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Dependabot.ListRepoSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListRepoSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.ListRepoSecrets(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.ListRepoSecrets(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_GetRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`}`) + }) + + ctx := t.Context() + secret, _, err := client.Dependabot.GetRepoSecret(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Dependabot.GetRepoSecret returned error: %v", err) + } + + want := &Secret{ + Name: "NAME", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + } + if !cmp.Equal(secret, want) { + t.Errorf("Dependabot.GetRepoSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.GetRepoSecret(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.GetRepoSecret(ctx, "o", "r", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_CreateOrUpdateRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretRequest{ + EncryptedValue: "QIv=", + KeyID: "1234", + } + + mux.HandleFunc("/repos/o/r/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := SecretRequest{ + EncryptedValue: input.EncryptedValue, + KeyID: input.KeyID, + } + testJSONBody(t, r, want) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Dependabot.CreateOrUpdateRepoSecret(ctx, "o", "r", "NAME", input) + if err != nil { + t.Errorf("Dependabot.CreateOrUpdateRepoSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.CreateOrUpdateRepoSecret(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.CreateOrUpdateRepoSecret(ctx, "o", "r", "NAME", input) + }) +} + +func TestDependabotService_DeleteRepoSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/dependabot/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Dependabot.DeleteRepoSecret(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Dependabot.DeleteRepoSecret returned error: %v", err) + } + + const methodName = "DeleteRepoSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.DeleteRepoSecret(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.DeleteRepoSecret(ctx, "o", "r", "NAME") + }) +} + +func TestDependabotService_GetOrgPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"012345678","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Dependabot.GetOrgPublicKey(ctx, "o") + if err != nil { + t.Errorf("Dependabot.GetOrgPublicKey returned error: %v", err) + } + + want := &PublicKey{KeyID: Ptr("012345678"), Key: Ptr("2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234")} + if !cmp.Equal(key, want) { + t.Errorf("Dependabot.GetOrgPublicKey returned %+v, want %+v", key, want) + } + + const methodName = "GetOrgPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.GetOrgPublicKey(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.GetOrgPublicKey(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_ListOrgSecrets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"secrets":[{"name":"GIST_ID","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"private"},{"name":"DEPLOY_TOKEN","created_at":`+refTimeStr(1136178002)+`,"updated_at":`+refTimeStr(1136178003)+`,"visibility":"all"},{"name":"GH_TOKEN","created_at":`+refTimeStr(1136178004)+`,"updated_at":`+refTimeStr(1136178005)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + secrets, _, err := client.Dependabot.ListOrgSecrets(ctx, "o", opts) + if err != nil { + t.Errorf("Dependabot.ListOrgSecrets returned error: %v", err) + } + + want := &Secrets{ + TotalCount: 3, + Secrets: []*Secret{ + {Name: "GIST_ID", CreatedAt: *refTimestamp(1136178000), UpdatedAt: *refTimestamp(1136178001), Visibility: "private"}, + {Name: "DEPLOY_TOKEN", CreatedAt: *refTimestamp(1136178002), UpdatedAt: *refTimestamp(1136178003), Visibility: "all"}, + {Name: "GH_TOKEN", CreatedAt: *refTimestamp(1136178004), UpdatedAt: *refTimestamp(1136178005), Visibility: "selected", SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories"}, + }, + } + if !cmp.Equal(secrets, want) { + t.Errorf("Dependabot.ListOrgSecrets returned %+v, want %+v", secrets, want) + } + + const methodName = "ListOrgSecrets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.ListOrgSecrets(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.ListOrgSecrets(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_GetOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","created_at":`+refTimeStr(1136178000)+`,"updated_at":`+refTimeStr(1136178001)+`,"visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories"}`) + }) + + ctx := t.Context() + secret, _, err := client.Dependabot.GetOrgSecret(ctx, "o", "NAME") + if err != nil { + t.Errorf("Dependabot.GetOrgSecret returned error: %v", err) + } + + want := &Secret{ + Name: "NAME", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + Visibility: "selected", + SelectedRepositoriesURL: "https://api.github.com/orgs/octo-org/dependabot/secrets/SUPER_SECRET/repositories", + } + if !cmp.Equal(secret, want) { + t.Errorf("Dependabot.GetOrgSecret returned %+v, want %+v", secret, want) + } + + const methodName = "GetOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.GetOrgSecret(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.GetOrgSecret(ctx, "o", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_CreateOrUpdateOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretOrgRequest{ + EncryptedValue: "QIv=", + KeyID: "1234", + Visibility: "selected", + SelectedRepositoryIDs: []int64{1296269, 1269280}, + } + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + want := SecretOrgRequest{ + EncryptedValue: input.EncryptedValue, + KeyID: input.KeyID, + Visibility: input.Visibility, + } + testJSONBody(t, r, struct { + SecretOrgRequest + SelectedRepositoryIDs []string `json:"selected_repository_ids,omitempty"` + }{ + SecretOrgRequest: want, + SelectedRepositoryIDs: []string{"1296269", "1269280"}, + }) + w.WriteHeader(http.StatusCreated) + }) + + ctx := t.Context() + _, err := client.Dependabot.CreateOrUpdateOrgSecret(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Dependabot.CreateOrUpdateOrgSecret returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.CreateOrUpdateOrgSecret(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.CreateOrUpdateOrgSecret(ctx, "o", "NAME", input) + }) +} + +func TestDependabotService_ListSelectedReposForOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + repos, _, err := client.Dependabot.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + if err != nil { + t.Errorf("Dependabot.ListSelectedReposForOrgSecret returned error: %v", err) + } + + want := &SelectedReposList{ + TotalCount: Ptr(1), + Repositories: []*Repository{ + {ID: Ptr(int64(1))}, + }, + } + if !cmp.Equal(repos, want) { + t.Errorf("Dependabot.ListSelectedReposForOrgSecret returned %+v, want %+v", repos, want) + } + + const methodName = "ListSelectedReposForOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Dependabot.ListSelectedReposForOrgSecret(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Dependabot.ListSelectedReposForOrgSecret(ctx, "o", "NAME", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestDependabotService_SetSelectedReposForOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{64780797} + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testJSONBody(t, r, struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + }{ + SelectedIDs: input, + }) + }) + + ctx := t.Context() + _, err := client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", input) + if err != nil { + t.Errorf("Dependabot.SetSelectedReposForOrgSecret returned error: %v", err) + } + + const methodName = "SetSelectedReposForOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.SetSelectedReposForOrgSecret(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.SetSelectedReposForOrgSecret(ctx, "o", "NAME", input) + }) +} + +func TestDependabotService_AddSelectedRepoToOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + repoID := int64(1234) + ctx := t.Context() + _, err := client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repoID) + if err != nil { + t.Errorf("Dependabot.AddSelectedRepoToOrgSecret returned error: %v", err) + } + + const methodName = "AddSelectedRepoToOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", 0) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "\n", "\n", repoID) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.AddSelectedRepoToOrgSecret(ctx, "o", "NAME", repoID) + }) +} + +func TestDependabotService_RemoveSelectedRepoFromOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME/repositories/1234", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + repoID := int64(1234) + ctx := t.Context() + _, err := client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repoID) + if err != nil { + t.Errorf("Dependabot.RemoveSelectedRepoFromOrgSecret returned error: %v", err) + } + + const methodName = "RemoveSelectedRepoFromOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", 0) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "\n", "\n", repoID) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.RemoveSelectedRepoFromOrgSecret(ctx, "o", "NAME", repoID) + }) +} + +func TestDependabotService_DeleteOrgSecret(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/dependabot/secrets/NAME", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Dependabot.DeleteOrgSecret(ctx, "o", "NAME") + if err != nil { + t.Errorf("Dependabot.DeleteOrgSecret returned error: %v", err) + } + + const methodName = "DeleteOrgSecret" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Dependabot.DeleteOrgSecret(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Dependabot.DeleteOrgSecret(ctx, "o", "NAME") + }) +} diff --git a/github/dependency_graph.go b/github/dependency_graph.go new file mode 100644 index 00000000000..f145799a1c7 --- /dev/null +++ b/github/dependency_graph.go @@ -0,0 +1,129 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// DependencyGraphService handles communication with the dependency graph +// related methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph?apiVersion=2022-11-28 +type DependencyGraphService service + +// SBOM represents a software bill of materials, which describes the +// packages/libraries that a repository depends on. +type SBOM struct { + SBOM *SBOMInfo `json:"sbom,omitempty"` +} + +// CreationInfo represents when the SBOM was created and who created it. +type CreationInfo struct { + Created *Timestamp `json:"created,omitempty"` + Creators []string `json:"creators,omitempty"` +} + +// RepoDependencies represents the dependencies of a repo. +type RepoDependencies struct { + SPDXID *string `json:"SPDXID,omitempty"` + // Package name + Name *string `json:"name,omitempty"` + VersionInfo *string `json:"versionInfo,omitempty"` + DownloadLocation *string `json:"downloadLocation,omitempty"` + FilesAnalyzed *bool `json:"filesAnalyzed,omitempty"` + LicenseConcluded *string `json:"licenseConcluded,omitempty"` + LicenseDeclared *string `json:"licenseDeclared,omitempty"` + ExternalRefs []*PackageExternalRef `json:"externalRefs,omitempty"` +} + +// PackageExternalRef allows an Package to reference an external sources of additional information, +// like asset identifiers, or downloadable content that are relevant to the package, +// Example for identifiers (e.g., PURL/SWID/CPE) for a package in the SBOM. +// https://spdx.github.io/spdx-spec/v2.3/package-information/#721-external-reference-field +type PackageExternalRef struct { + // ReferenceCategory specifies the external reference categories such + // SECURITY", "PACKAGE-MANAGER", "PERSISTENT-ID", or "OTHER" + // Example: "PACKAGE-MANAGER" + ReferenceCategory string `json:"referenceCategory"` + + // ReferenceType specifies the type of external reference. + // For PACKAGE-MANAGER, it could be "purl"; other types include "cpe22Type", "swid", etc. + ReferenceType string `json:"referenceType"` + + // ReferenceLocator is the actual unique identifier or URI for the external reference. + // Example: "pkg:golang/github.com/spf13/cobra@1.8.1" + ReferenceLocator string `json:"referenceLocator"` +} + +// SBOMRelationship provides information about the relationship between two SPDX elements. +// Element could be packages or files in the SBOM. +// For example, to represent a relationship between two different Files, between a Package and a File, +// between two Packages, or between one SPDXDocument and another SPDXDocument. +// https://spdx.github.io/spdx-spec/v2.3/relationships-between-SPDX-elements/ +type SBOMRelationship struct { + // SPDXElementID is the identifier of the SPDX element that has a relationship. + // Example: "SPDXRef-github-interlynk-io-sbomqs-main-f43c98" + SPDXElementID string `json:"spdxElementId"` + + // RelatedSPDXElement is the identifier of the related SPDX element. + // Example: "SPDXRef-golang-github.comspf13-cobra-1.8.1-75c946" + RelatedSPDXElement string `json:"relatedSpdxElement"` + + // RelationshipType describes the type of relationship between the two elements. + // Such as "DEPENDS_ON", "DESCRIBES", "CONTAINS", etc., as defined by SPDX 2.3. + // Example: "DEPENDS_ON", "CONTAINS", "DESCRIBES", etc. + RelationshipType string `json:"relationshipType"` +} + +// SBOMInfo represents a software bill of materials (SBOM) using SPDX. +// SPDX is an open standard for SBOMs that +// identifies and catalogs components, licenses, copyrights, security +// references, and other metadata relating to software. +type SBOMInfo struct { + SPDXID *string `json:"SPDXID,omitempty"` + SPDXVersion *string `json:"spdxVersion,omitempty"` + CreationInfo *CreationInfo `json:"creationInfo,omitempty"` + + // Repo name + Name *string `json:"name,omitempty"` + DataLicense *string `json:"dataLicense,omitempty"` + DocumentDescribes []string `json:"documentDescribes,omitempty"` + DocumentNamespace *string `json:"documentNamespace,omitempty"` + + // List of packages dependencies + Packages []*RepoDependencies `json:"packages,omitempty"` + + // List of relationships between packages + Relationships []*SBOMRelationship `json:"relationships,omitempty"` +} + +func (s SBOM) String() string { + return Stringify(s) +} + +// GetSBOM fetches the software bill of materials for a repository. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/sboms?apiVersion=2022-11-28#export-a-software-bill-of-materials-sbom-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/dependency-graph/sbom +func (s *DependencyGraphService) GetSBOM(ctx context.Context, owner, repo string) (*SBOM, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/dependency-graph/sbom", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var sbom *SBOM + resp, err := s.client.Do(req, &sbom) + if err != nil { + return nil, resp, err + } + + return sbom, resp, nil +} diff --git a/github/dependency_graph_snapshots.go b/github/dependency_graph_snapshots.go new file mode 100644 index 00000000000..7786c36252a --- /dev/null +++ b/github/dependency_graph_snapshots.go @@ -0,0 +1,124 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// DependencyGraphSnapshotResolvedDependency represents a resolved dependency in a dependency graph snapshot. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshotResolvedDependency struct { + PackageURL *string `json:"package_url,omitempty"` + // User-defined metadata to store domain-specific information limited to 8 keys with scalar values. + // This metadata overrides auto-detected values from the package URL and GitHub's database. + // Common fields include: + // - "licenses": license information (e.g., "MIT", "Apache-2.0") + // - "name": package name + // - "version": package version + // - "manager": package manager (e.g., "npm", "pip", "maven") + // - "description": package description + Metadata map[string]any `json:"metadata,omitempty"` + // Represents whether the dependency is requested directly by the manifest or is a dependency of another dependency. + // Can have the following values: + // - "direct": indicates that the dependency is requested directly by the manifest. + // - "indirect": indicates that the dependency is a dependency of another dependency. + Relationship *string `json:"relationship,omitempty"` + // Represents whether the dependency is required for the primary build artifact or is only used for development. + // Can have the following values: + // - "runtime": indicates that the dependency is required for the primary build artifact. + // - "development": indicates that the dependency is only used for development. + Scope *string `json:"scope,omitempty"` + Dependencies []string `json:"dependencies,omitempty"` +} + +// DependencyGraphSnapshotJob represents the job that created the snapshot. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshotJob struct { + Correlator *string `json:"correlator,omitempty"` + ID *string `json:"id,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + +// DependencyGraphSnapshotDetector represents a description of the detector used. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshotDetector struct { + Name *string `json:"name,omitempty"` + Version *string `json:"version,omitempty"` + URL *string `json:"url,omitempty"` +} + +// DependencyGraphSnapshotManifestFile represents the file declaring the repository's dependencies. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshotManifestFile struct { + SourceLocation *string `json:"source_location,omitempty"` +} + +// DependencyGraphSnapshotManifest represents a collection of related dependencies declared in a file or representing a logical group of dependencies. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshotManifest struct { + Name *string `json:"name,omitempty"` + File *DependencyGraphSnapshotManifestFile `json:"file,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` + Resolved map[string]*DependencyGraphSnapshotResolvedDependency `json:"resolved,omitempty"` +} + +// DependencyGraphSnapshot represent a snapshot of a repository's dependencies. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshot struct { + Version int `json:"version"` + Sha *string `json:"sha,omitempty"` + Ref *string `json:"ref,omitempty"` + Job *DependencyGraphSnapshotJob `json:"job,omitempty"` + Detector *DependencyGraphSnapshotDetector `json:"detector,omitempty"` + Scanned *Timestamp `json:"scanned,omitempty"` + Metadata map[string]any `json:"metadata,omitempty"` + Manifests map[string]*DependencyGraphSnapshotManifest `json:"manifests,omitempty"` +} + +// DependencyGraphSnapshotCreationData represents the dependency snapshot's creation result. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +type DependencyGraphSnapshotCreationData struct { + ID int64 `json:"id"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Message *string `json:"message,omitempty"` + // Represents the snapshot creation result. + // Can have the following values: + // - "SUCCESS": indicates that the snapshot was successfully created and the repository's dependencies were updated. + // - "ACCEPTED": indicates that the snapshot was successfully created, but the repository's dependencies were not updated. + // - "INVALID": indicates that the snapshot was malformed. + Result *string `json:"result,omitempty"` +} + +// CreateSnapshot creates a new snapshot of a repository's dependencies. +// +// GitHub API docs: https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/dependency-graph/snapshots +func (s *DependencyGraphService) CreateSnapshot(ctx context.Context, owner, repo string, body *DependencyGraphSnapshot) (*DependencyGraphSnapshotCreationData, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/dependency-graph/snapshots", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", url, body) + if err != nil { + return nil, nil, err + } + + var snapshotCreationData *DependencyGraphSnapshotCreationData + resp, err := s.client.Do(req, &snapshotCreationData) + if err != nil { + return nil, resp, err + } + + return snapshotCreationData, resp, nil +} diff --git a/github/dependency_graph_snapshots_test.go b/github/dependency_graph_snapshots_test.go new file mode 100644 index 00000000000..ef8f360bada --- /dev/null +++ b/github/dependency_graph_snapshots_test.go @@ -0,0 +1,103 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDependencyGraphService_CreateSnapshot(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + snapshot := &DependencyGraphSnapshot{ + Version: 0, + Sha: Ptr("ce587453ced02b1526dfb4cb910479d431683101"), + Ref: Ptr("refs/heads/main"), + Job: &DependencyGraphSnapshotJob{ + Correlator: Ptr("yourworkflowname_youractionname"), + ID: Ptr("yourrunid"), + HTMLURL: Ptr("https://example.com"), + }, + Detector: &DependencyGraphSnapshotDetector{ + Name: Ptr("octo-detector"), + Version: Ptr("0.0.1"), + URL: Ptr("https://github.com/octo-org/octo-repo"), + }, + Scanned: &referenceTimestamp, + Metadata: map[string]any{ + "key1": "value1", + "key2": "value2", + }, + Manifests: map[string]*DependencyGraphSnapshotManifest{ + "package-lock.json": { + Name: Ptr("package-lock.json"), + File: &DependencyGraphSnapshotManifestFile{SourceLocation: Ptr("src/package-lock.json")}, + Metadata: map[string]any{ + "key1": "value1", + "key2": "value2", + }, + Resolved: map[string]*DependencyGraphSnapshotResolvedDependency{ + "@actions/core": { + PackageURL: Ptr("pkg:/npm/%40actions/core@1.1.9"), + Relationship: Ptr("direct"), + Scope: Ptr("runtime"), + Metadata: map[string]any{ + "licenses": "MIT", + }, + Dependencies: []string{"@actions/http-client"}, + }, + "@actions/http-client": { + PackageURL: Ptr("pkg:/npm/%40actions/http-client@1.0.7"), + Relationship: Ptr("indirect"), + Scope: Ptr("runtime"), + Dependencies: []string{"tunnel"}, + }, + "tunnel": { + PackageURL: Ptr("pkg:/npm/tunnel@0.0.6"), + Relationship: Ptr("indirect"), + Scope: Ptr("runtime"), + }, + }, + }, + }, + } + + mux.HandleFunc("/repos/o/r/dependency-graph/snapshots", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, snapshot) + fmt.Fprint(w, `{"id":12345,"created_at":`+referenceTimeStr+`,"message":"Dependency results for the repo have been successfully updated.","result":"SUCCESS"}`) + }) + + ctx := t.Context() + snapshotCreationData, _, err := client.DependencyGraph.CreateSnapshot(ctx, "o", "r", snapshot) + if err != nil { + t.Errorf("DependencyGraph.CreateSnapshot returned error: %v", err) + } + + want := &DependencyGraphSnapshotCreationData{ + ID: 12345, + CreatedAt: &referenceTimestamp, + Message: Ptr("Dependency results for the repo have been successfully updated."), + Result: Ptr("SUCCESS"), + } + if !cmp.Equal(snapshotCreationData, want) { + t.Errorf("DependencyGraph.CreateSnapshot returned %+v, want %+v", snapshotCreationData, want) + } + + const methodName = "CreateSnapshot" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.DependencyGraph.CreateSnapshot(ctx, "o", "r", snapshot) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/dependency_graph_test.go b/github/dependency_graph_test.go new file mode 100644 index 00000000000..994c87fa18a --- /dev/null +++ b/github/dependency_graph_test.go @@ -0,0 +1,76 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDependencyGraphService_GetSBOM(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/dependency-graph/sbom", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "sbom":{ + "creationInfo":{ + "created":`+referenceTimeStr+` + }, + "name":"owner/repo", + "packages":[ + { + "name":"rubygems:rails", + "versionInfo":"1.0.0" + } + ] + } + }`) + }) + + ctx := t.Context() + sbom, _, err := client.DependencyGraph.GetSBOM(ctx, "owner", "repo") + if err != nil { + t.Errorf("DependencyGraph.GetSBOM returned error: %v", err) + } + + want := &SBOM{ + &SBOMInfo{ + CreationInfo: &CreationInfo{ + Created: &referenceTimestamp, + }, + Name: Ptr("owner/repo"), + Packages: []*RepoDependencies{ + { + Name: Ptr("rubygems:rails"), + VersionInfo: Ptr("1.0.0"), + }, + }, + }, + } + + if !cmp.Equal(sbom, want) { + t.Errorf("DependencyGraph.GetSBOM returned %+v, want %+v", sbom, want) + } + + const methodName = "GetSBOM" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.DependencyGraph.GetSBOM(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.DependencyGraph.GetSBOM(ctx, "owner", "repo") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/doc.go b/github/doc.go index 8adfdc1d49b..5c6a907d21d 100644 --- a/github/doc.go +++ b/github/doc.go @@ -8,20 +8,25 @@ Package github provides a client for using the GitHub API. Usage: - import "github.com/google/go-github/v28/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) - import "github.com/google/go-github/github" // with go modules disabled + import "github.com/google/go-github/v90/github" -Construct a new GitHub client, then use the various services on the client to +Construct a new GitHub client using [NewClient], then use the various services on the client to access different parts of the GitHub API. For example: - client := github.NewClient(nil) + client, err := github.NewClient() + if err != nil { + // Handle error. + } // list all organizations for user "willnorris" orgs, _, err := client.Organizations.List(ctx, "willnorris", nil) Some API methods have optional parameters that can be passed. For example: - client := github.NewClient(nil) + client, err := github.NewClient() + if err != nil { + // Handle error. + } // list public repositories for org "github" opt := &github.RepositoryListByOrgOptions{Type: "public"} @@ -29,50 +34,39 @@ Some API methods have optional parameters that can be passed. For example: The services of a client divide the API into logical chunks and correspond to the structure of the GitHub API documentation at -https://developer.github.com/v3/. +https://docs.github.com/rest?apiVersion=2022-11-28. -NOTE: Using the https://godoc.org/context package, one can easily -pass cancelation signals and deadlines to various services of the client for -handling a request. In case there is no context available, then context.Background() +NOTE: Using the [context] package, one can easily +pass cancellation signals and deadlines to various services of the client for +handling a request. In case there is no context available, then [context.Background] can be used as a starting point. For more sample code snippets, head over to the https://github.com/google/go-github/tree/master/example directory. -Authentication - -The go-github library does not directly handle authentication. Instead, when -creating a new client, pass an http.Client that can handle authentication for -you. The easiest and recommended way to do this is using the golang.org/x/oauth2 -library, but you can always use any other library that provides an http.Client. -If you have an OAuth2 access token (for example, a personal API token), you can -use it with the oauth2 library using: - - import "golang.org/x/oauth2" +# Authentication - func main() { - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: "... your access token ..."}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) +Use [WithAuthToken] to configure your client to authenticate using an OAuth token +(for example, a personal access token). This is what is needed for a majority of use cases +aside from GitHub Apps. - // list all repositories for the authenticated user - repos, _, err := client.Repositories.List(ctx, "", nil) + client, err := github.NewClient(github.WithAuthToken("... your access token ...")) + if err != nil { + // Handle error. } -Note that when using an authenticated Client, all calls made by the client will +Note that when using an authenticated [Client], all calls made by the client will include the specified OAuth token. Therefore, authenticated clients should almost never be shared between different users. -See the oauth2 docs for complete instructions on using that library. - For API methods that require HTTP Basic Authentication, use the -BasicAuthTransport. +[BasicAuthTransport]. GitHub Apps authentication can be provided by the https://github.com/bradleyfalzon/ghinstallation package. +It supports both authentication as an installation, using an installation access token, +and as an app, using a JWT. Use the [WithTransport] option to configure your client to use the appropriate transport. + +To authenticate as an installation: import "github.com/bradleyfalzon/ghinstallation" @@ -84,12 +78,35 @@ https://github.com/bradleyfalzon/ghinstallation package. } // Use installation transport with client - client := github.NewClient(&http.Client{Transport: itr}) + client, err := github.NewClient(github.WithTransport(itr)) + if err != nil { + // Handle error. + } + + // Use client... + } + +To authenticate as an app, using a JWT: + + import "github.com/bradleyfalzon/ghinstallation" + + func main() { + // Wrap the shared transport for use with the application ID 1. + atr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem") + if err != nil { + // Handle error. + } + + // Use app transport with client + client, err := github.NewClient(github.WithTransport(atr)) + if err != nil { + // Handle error. + } // Use client... } -Rate Limiting +# Rate Limiting GitHub imposes a rate limit on all API clients. Unauthenticated clients are limited to 60 requests per hour, while authenticated clients can make up to @@ -97,24 +114,28 @@ limited to 60 requests per hour, while authenticated clients can make up to clients are limited to 10 requests per minute, while authenticated clients can make up to 30 requests per minute. To receive the higher rate limit when making calls that are not issued on behalf of a user, -use UnauthenticatedRateLimitedTransport. +use [UnauthenticatedRateLimitedTransport]. -The returned Response.Rate value contains the rate limit information +The returned [Response].[Rate] value contains the rate limit information from the most recent API call. If a recent enough response isn't available, you can use RateLimits to fetch the most up-to-date rate limit data for the client. -To detect an API rate limit error, you can check if its type is *github.RateLimitError: +To detect an API rate limit error, you can check if its type is *[RateLimitError]. +For secondary rate limits, you can check if its type is *[AbuseRateLimitError]: repos, _, err := client.Repositories.List(ctx, "", nil) - if _, ok := err.(*github.RateLimitError); ok { + if errors.As(err, new(*github.RateLimitError)) { log.Println("hit rate limit") } + if errors.As(err, new(*github.AbuseRateLimitError)) { + log.Println("hit secondary rate limit") + } Learn more about GitHub rate limiting at -https://developer.github.com/v3/#rate-limiting. +https://docs.github.com/rest/rate-limit?apiVersion=2022-11-28. -Accepted Status +# Accepted Status Some endpoints may return a 202 Accepted status code, meaning that the information required is not yet ready and was scheduled to be gathered on @@ -122,50 +143,59 @@ the GitHub side. Methods known to behave like this are documented specifying this behavior. To detect this condition of error, you can check if its type is -*github.AcceptedError: +*[AcceptedError]: stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo) - if _, ok := err.(*github.AcceptedError); ok { + if errors.As(err, new(*github.AcceptedError)) { log.Println("scheduled on GitHub side") } -Conditional Requests +# Conditional Requests -The GitHub API has good support for conditional requests which will help -prevent you from burning through your rate limit, as well as help speed up your -application. go-github does not handle conditional requests directly, but is -instead designed to work with a caching http.Transport. We recommend using -https://github.com/gregjones/httpcache for that. +The GitHub REST API has good support for conditional HTTP requests +via the ETag header which will help prevent you from burning through your +rate limit, as well as help speed up your application. go-github does not +handle conditional requests directly, but is instead designed to work with a +caching [http.Transport]. + +Typically, an RFC 9111 compliant HTTP cache such as https://github.com/bartventer/httpcache +is recommended. Alternatively, the https://github.com/bored-engineer/github-conditional-http-transport +package relies on (undocumented) GitHub specific cache logic and is +recommended when making requests using short-lived credentials such as a +GitHub App installation token. Learn more about GitHub conditional requests at -https://developer.github.com/v3/#conditional-requests. +https://docs.github.com/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#use-conditional-requests-if-appropriate. -Creating and Updating Resources +# Creating and Updating Resources All structs for GitHub resources use pointer values for all non-repeated fields. This allows distinguishing between unset fields and those set to a zero-value. -Helper functions have been provided to easily create these pointers for string, +A helper function, [Ptr], has been provided to easily create these pointers for string, bool, and int values. For example: // create a new private repository named "foo" repo := &github.Repository{ - Name: github.String("foo"), - Private: github.Bool(true), + Name: github.Ptr("foo"), + Private: github.Ptr(true), } client.Repositories.Create(ctx, "", repo) Users who have worked with protocol buffers should find this pattern familiar. -Pagination +# Pagination All requests for resource collections (repos, pull requests, issues, etc.) support pagination. Pagination options are described in the -github.ListOptions struct and passed to the list methods directly or as an +[ListOptions] struct and passed to the list methods directly or as an embedded type of a more specific list options struct (for example -github.PullRequestListOptions). Pages information is available via the -github.Response struct. +[PullRequestListOptions]). Pages information is available via the +[Response] struct. - client := github.NewClient(nil) + client, err := github.NewClient() + if err != nil { + // Handle error. + } opt := &github.RepositoryListByOrgOptions{ ListOptions: github.ListOptions{PerPage: 10}, @@ -183,6 +213,5 @@ github.Response struct. } opt.Page = resp.NextPage } - */ package github diff --git a/github/emojis.go b/github/emojis.go new file mode 100644 index 00000000000..0bf9ed1430c --- /dev/null +++ b/github/emojis.go @@ -0,0 +1,40 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// EmojisService provides access to emoji-related functions in the GitHub API. +type EmojisService service + +// List returns the emojis available to use on GitHub. +// +// GitHub API docs: https://docs.github.com/rest/emojis/emojis?apiVersion=2022-11-28#get-emojis +// +//meta:operation GET /emojis +func (s *EmojisService) List(ctx context.Context) (map[string]string, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "emojis", nil) + if err != nil { + return nil, nil, err + } + + var emoji map[string]string + resp, err := s.client.Do(req, &emoji) + if err != nil { + return nil, resp, err + } + + return emoji, resp, nil +} + +// ListEmojis returns the emojis available to use on GitHub. +// +// Deprecated: Use EmojisService.List instead. +func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { + return c.Emojis.List(ctx) +} diff --git a/github/emojis_test.go b/github/emojis_test.go new file mode 100644 index 00000000000..d20ac181626 --- /dev/null +++ b/github/emojis_test.go @@ -0,0 +1,44 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEmojisService_List(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"+1": "+1.png"}`) + }) + + ctx := t.Context() + emoji, _, err := client.ListEmojis(ctx) + if err != nil { + t.Errorf("List returned error: %v", err) + } + + want := map[string]string{"+1": "+1.png"} + if !cmp.Equal(want, emoji) { + t.Errorf("List returned %+v, want %+v", emoji, want) + } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Emojis.List(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise.go b/github/enterprise.go new file mode 100644 index 00000000000..f808a292656 --- /dev/null +++ b/github/enterprise.go @@ -0,0 +1,12 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +// EnterpriseService provides access to the enterprise related functions +// in the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-admin?apiVersion=2022-11-28 +type EnterpriseService service diff --git a/github/enterprise_actions_hosted_runners.go b/github/enterprise_actions_hosted_runners.go new file mode 100644 index 00000000000..7dbcc64d607 --- /dev/null +++ b/github/enterprise_actions_hosted_runners.go @@ -0,0 +1,344 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListHostedRunners lists all the GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#list-github-hosted-runners-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners +func (s *EnterpriseService) ListHostedRunners(ctx context.Context, enterprise string, opts *ListOptions) (*HostedRunners, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *HostedRunners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// CreateHostedRunner creates a GitHub-hosted runner for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#create-a-github-hosted-runner-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/actions/hosted-runners +func (s *EnterpriseService) CreateHostedRunner(ctx context.Context, enterprise string, body CreateHostedRunnerRequest) (*HostedRunner, *Response, error) { + if err := validateCreateHostedRunnerRequest(&body); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners", enterprise) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// GetHostedRunnerGitHubOwnedImages gets the list of GitHub-owned images available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-github-owned-images-for-github-hosted-runners-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/github-owned +func (s *EnterpriseService) GetHostedRunnerGitHubOwnedImages(ctx context.Context, enterprise string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/github-owned", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunnerImages *HostedRunnerImages + resp, err := s.client.Do(req, &hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// GetHostedRunnerPartnerImages gets the list of partner images available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-partner-images-for-github-hosted-runners-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/partner +func (s *EnterpriseService) GetHostedRunnerPartnerImages(ctx context.Context, enterprise string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/partner", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunnerImages *HostedRunnerImages + resp, err := s.client.Do(req, &hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// GetHostedRunnerLimits gets the GitHub-hosted runners Static public IP Limits for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-limits-on-github-hosted-runners-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/limits +func (s *EnterpriseService) GetHostedRunnerLimits(ctx context.Context, enterprise string) (*HostedRunnerPublicIPLimits, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/limits", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var publicIPLimits *HostedRunnerPublicIPLimits + resp, err := s.client.Do(req, &publicIPLimits) + if err != nil { + return nil, resp, err + } + + return publicIPLimits, resp, nil +} + +// GetHostedRunnerMachineSpecs gets the list of machine specs available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-github-hosted-runners-machine-specs-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/machine-sizes +func (s *EnterpriseService) GetHostedRunnerMachineSpecs(ctx context.Context, enterprise string) (*HostedRunnerMachineSpecs, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/machine-sizes", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var machineSpecs *HostedRunnerMachineSpecs + resp, err := s.client.Do(req, &machineSpecs) + if err != nil { + return nil, resp, err + } + + return machineSpecs, resp, nil +} + +// GetHostedRunnerPlatforms gets list of platforms available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-platforms-for-github-hosted-runners-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/platforms +func (s *EnterpriseService) GetHostedRunnerPlatforms(ctx context.Context, enterprise string) (*HostedRunnerPlatforms, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/platforms", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var platforms *HostedRunnerPlatforms + resp, err := s.client.Do(req, &platforms) + if err != nil { + return nil, resp, err + } + + return platforms, resp, nil +} + +// GetHostedRunner gets a GitHub-hosted runner in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-a-github-hosted-runner-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} +func (s *EnterpriseService) GetHostedRunner(ctx context.Context, enterprise string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// UpdateHostedRunner updates a GitHub-hosted runner for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#update-a-github-hosted-runner-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} +func (s *EnterpriseService) UpdateHostedRunner(ctx context.Context, enterprise string, runnerID int64, body UpdateHostedRunnerRequest) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// DeleteHostedRunner deletes GitHub-hosted runner from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#delete-a-github-hosted-runner-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} +func (s *EnterpriseService) DeleteHostedRunner(ctx context.Context, enterprise string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + var hostedRunner *HostedRunner + resp, err := s.client.Do(req, &hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// ListHostedRunnerCustomImages lists custom images for GitHub-hosted runners in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#list-custom-images-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/custom +func (s *EnterpriseService) ListHostedRunnerCustomImages(ctx context.Context, enterprise string) (*HostedRunnerCustomImages, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/custom", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var images *HostedRunnerCustomImages + resp, err := s.client.Do(req, &images) + if err != nil { + return nil, resp, err + } + + return images, resp, nil +} + +// GetHostedRunnerCustomImage gets a custom image definition for GitHub-hosted runners in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-an-enterprise-custom-image-definition-for-github-actions-hosted-runners +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id} +func (s *EnterpriseService) GetHostedRunnerCustomImage(ctx context.Context, enterprise string, imageDefinitionID int64) (*HostedRunnerCustomImage, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/custom/%v", enterprise, imageDefinitionID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var image *HostedRunnerCustomImage + resp, err := s.client.Do(req, &image) + if err != nil { + return nil, resp, err + } + + return image, resp, nil +} + +// DeleteHostedRunnerCustomImage deletes a custom image from the enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#delete-a-custom-image-from-the-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id} +func (s *EnterpriseService) DeleteHostedRunnerCustomImage(ctx context.Context, enterprise string, imageDefinitionID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/custom/%v", enterprise, imageDefinitionID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListHostedRunnerCustomImageVersions lists image versions of a custom image for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#list-image-versions-of-a-custom-image-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id}/versions +func (s *EnterpriseService) ListHostedRunnerCustomImageVersions(ctx context.Context, enterprise string, imageDefinitionID int64) (*HostedRunnerCustomImageVersions, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/custom/%v/versions", enterprise, imageDefinitionID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var versions *HostedRunnerCustomImageVersions + resp, err := s.client.Do(req, &versions) + if err != nil { + return nil, resp, err + } + + return versions, resp, nil +} + +// GetHostedRunnerCustomImageVersion gets an image version of a custom image for GitHub-hosted runners in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#get-an-image-version-of-an-enterprise-custom-image-for-github-actions-hosted-runners +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} +func (s *EnterpriseService) GetHostedRunnerCustomImageVersion(ctx context.Context, enterprise string, imageDefinitionID int64, version string) (*HostedRunnerCustomImageVersion, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/custom/%v/versions/%v", enterprise, imageDefinitionID, version) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var imageVersion *HostedRunnerCustomImageVersion + resp, err := s.client.Do(req, &imageVersion) + if err != nil { + return nil, resp, err + } + + return imageVersion, resp, nil +} + +// DeleteHostedRunnerCustomImageVersion deletes an image version of a custom image from the enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners?apiVersion=2022-11-28#delete-an-image-version-of-custom-image-from-the-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} +func (s *EnterpriseService) DeleteHostedRunnerCustomImageVersion(ctx context.Context, enterprise string, imageDefinitionID int64, version string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/custom/%v/versions/%v", enterprise, imageDefinitionID, version) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_actions_hosted_runners_test.go b/github/enterprise_actions_hosted_runners_test.go new file mode 100644 index 00000000000..abb6df8db6b --- /dev/null +++ b/github/enterprise_actions_hosted_runners_test.go @@ -0,0 +1,1160 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListHostedRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 2, + "runners": [ + { + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }, + { + "id": 7, + "name": "My hosted Windows runner", + "runner_group_id": 2, + "platform": "win-x64", + "image_details": { + "id": "windows-latest", + "size_gb": 256 + }, + "machine_size_details": { + "id": "8-core", + "cpu_cores": 8, + "memory_gb": 32, + "storage_gb": 300 + }, + "status": "Ready", + "maximum_runners": 20, + "public_ip_enabled": false, + "public_ips": [], + "last_active_on": `+referenceTimeStr+` + } + ] + }`) + }) + opts := &ListOptions{Page: 1, PerPage: 1} + ctx := t.Context() + hostedRunners, _, err := client.Enterprise.ListHostedRunners(ctx, "o", opts) + if err != nil { + t.Errorf("Enterprise.ListHostedRunners returned error: %v", err) + } + + want := &HostedRunners{ + TotalCount: 2, + Runners: []*HostedRunner{ + { + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + }, + { + ID: Ptr(int64(7)), + Name: Ptr("My hosted Windows runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("win-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("windows-latest"), + SizeGB: Ptr(int64(256)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "8-core", + CPUCores: 8, + MemoryGB: 32, + StorageGB: 300, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(20)), + PublicIPEnabled: Ptr(false), + PublicIPs: []*HostedRunnerPublicIP{}, + LastActiveOn: &referenceTimestamp, + }, + }, + } + if !cmp.Equal(hostedRunners, want) { + t.Errorf("Enterprise.ListHostedRunners returned %+v, want %+v", hostedRunners, want) + } + + const methodName = "ListHostedRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListHostedRunners(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListHostedRunners(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + req := CreateHostedRunnerRequest{ + Name: "My Hosted runner", + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + RunnerGroupID: 1, + Size: "4-core", + MaximumRunners: Ptr(int64(50)), + EnableStaticIP: Ptr(false), + ImageGen: Ptr(true), + } + hostedRunner, _, err := client.Enterprise.CreateHostedRunner(ctx, "o", req) + if err != nil { + t.Errorf("Enterprise.CreateHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Enterprise.CreateHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + // Validation tests + testCases := []struct { + name string + request CreateHostedRunnerRequest + expectedError string + }{ + { + name: "Missing Size", + request: CreateHostedRunnerRequest{ + Name: "My Hosted runner", + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + RunnerGroupID: 1, + }, + expectedError: "validation failed: size is required for creating a hosted runner", + }, + { + name: "Missing Image", + request: CreateHostedRunnerRequest{ + Name: "My Hosted runner", + RunnerGroupID: 1, + Size: "4-core", + }, + expectedError: "validation failed: image is required for creating a hosted runner", + }, + { + name: "Missing Name", + request: CreateHostedRunnerRequest{ + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + RunnerGroupID: 1, + Size: "4-core", + }, + expectedError: "validation failed: name is required for creating a hosted runner", + }, + { + name: "Missing RunnerGroupID", + request: CreateHostedRunnerRequest{ + Name: "My Hosted runner", + Image: HostedRunnerImage{ + ID: "ubuntu-latest", + Source: "github", + Version: Ptr("latest"), + }, + Size: "4-core", + }, + expectedError: "validation failed: runner group ID is required for creating a hosted runner", + }, + } + + for _, tt := range testCases { + _, _, err := client.Enterprise.CreateHostedRunner(ctx, "o", tt.request) + if err == nil || err.Error() != tt.expectedError { + t.Errorf("expected error: %v, got: %v", tt.expectedError, err) + } + } + + const methodName = "CreateHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateHostedRunner(ctx, "\n", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateHostedRunner(ctx, "o", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerGitHubOwnedImages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/images/github-owned", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "images": [ + { + "id": "ubuntu-20.04", + "platform": "linux-x64", + "size_gb": 86, + "display_name": "20.04", + "source": "github" + } + ] + }`) + }) + + ctx := t.Context() + hostedRunnerImages, _, err := client.Enterprise.GetHostedRunnerGitHubOwnedImages(ctx, "o") + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerGitHubOwnedImages returned error: %v", err) + } + + want := &HostedRunnerImages{ + TotalCount: 1, + Images: []*HostedRunnerImageSpecs{ + { + ID: "ubuntu-20.04", + Platform: "linux-x64", + SizeGB: 86, + DisplayName: "20.04", + Source: "github", + }, + }, + } + + if !cmp.Equal(hostedRunnerImages, want) { + t.Errorf("Enterprise.GetHostedRunnerGitHubOwnedImages returned %+v, want %+v", hostedRunnerImages, want) + } + + const methodName = "GetHostedRunnerGitHubOwnedImages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerGitHubOwnedImages(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerGitHubOwnedImages(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerPartnerImages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/images/partner", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "images": [ + { + "id": "ubuntu-20.04", + "platform": "linux-x64", + "size_gb": 86, + "display_name": "20.04", + "source": "partner" + } + ] + }`) + }) + + ctx := t.Context() + hostedRunnerImages, _, err := client.Enterprise.GetHostedRunnerPartnerImages(ctx, "o") + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerPartnerImages returned error: %v", err) + } + + want := &HostedRunnerImages{ + TotalCount: 1, + Images: []*HostedRunnerImageSpecs{ + { + ID: "ubuntu-20.04", + Platform: "linux-x64", + SizeGB: 86, + DisplayName: "20.04", + Source: "partner", + }, + }, + } + + if !cmp.Equal(hostedRunnerImages, want) { + t.Errorf("Enterprise.GetHostedRunnerPartnerImages returned %+v, want %+v", hostedRunnerImages, want) + } + + const methodName = "GetHostedRunnerPartnerImages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerPartnerImages(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerPartnerImages(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerLimits(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/enterprises/o/actions/hosted-runners/limits", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "public_ips": { + "current_usage": 17, + "maximum": 50 + } + }`) + }) + + ctx := t.Context() + publicIPLimits, _, err := client.Enterprise.GetHostedRunnerLimits(ctx, "o") + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerLimits returned error: %v", err) + } + + want := &HostedRunnerPublicIPLimits{ + PublicIPs: &PublicIPUsage{ + CurrentUsage: 17, + Maximum: 50, + }, + } + + if !cmp.Equal(publicIPLimits, want) { + t.Errorf("Enterprise.GetHostedRunnerLimits returned %+v, want %+v", publicIPLimits, want) + } + + const methodName = "GetHostedRunnerLimits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerLimits(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerLimits(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerMachineSpecs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/machine-sizes", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "machine_specs": [ + { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + } + ] + }`) + }) + + ctx := t.Context() + machineSpecs, _, err := client.Enterprise.GetHostedRunnerMachineSpecs(ctx, "o") + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerMachineSpecs returned error: %v", err) + } + want := &HostedRunnerMachineSpecs{ + TotalCount: 1, + MachineSpecs: []*HostedRunnerMachineSpec{ + { + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + }, + } + + if !cmp.Equal(machineSpecs, want) { + t.Errorf("Enterprise.GetHostedRunnerMachineSpecs returned %+v, want %+v", machineSpecs, want) + } + + const methodName = "GetHostedRunnerMachineSpecs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerMachineSpecs(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerMachineSpecs(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerPlatforms(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/platforms", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 1, + "platforms": [ + "linux-x64", + "win-x64" + ] + }`) + }) + + ctx := t.Context() + platforms, _, err := client.Enterprise.GetHostedRunnerPlatforms(ctx, "o") + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerPlatforms returned error: %v", err) + } + want := &HostedRunnerPlatforms{ + TotalCount: 1, + Platforms: []string{ + "linux-x64", + "win-x64", + }, + } + + if !cmp.Equal(platforms, want) { + t.Errorf("Enterprise.GetHostedRunnerPlatforms returned %+v, want %+v", platforms, want) + } + + const methodName = "GetHostedRunnerPlatforms" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerPlatforms(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerPlatforms(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + hostedRunner, _, err := client.Enterprise.GetHostedRunner(ctx, "o", 23) + if err != nil { + t.Errorf("Enterprise.GetHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Enterprise.GetHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + const methodName = "GetHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunner(ctx, "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunner(ctx, "o", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + // Test for a valid update without `Size` + ctx := t.Context() + validReq := UpdateHostedRunnerRequest{ + Name: Ptr("My larger runner"), + RunnerGroupID: Ptr(int64(1)), + MaximumRunners: Ptr(int64(50)), + EnableStaticIP: Ptr(false), + ImageVersion: Ptr("1.0.0"), + } + hostedRunner, _, err := client.Enterprise.UpdateHostedRunner(ctx, "o", 23, validReq) + if err != nil { + t.Errorf("Enterprise.UpdateHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Enterprise.UpdateHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + const methodName = "UpdateHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateHostedRunner(ctx, "\n", 23, validReq) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateHostedRunner(ctx, "o", 23, validReq) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteHostedRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/hosted-runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "id": 5, + "name": "My hosted ubuntu runner", + "runner_group_id": 2, + "platform": "linux-x64", + "image_details": { + "id": "ubuntu-20.04", + "size_gb": 86 + }, + "machine_size_details": { + "id": "4-core", + "cpu_cores": 4, + "memory_gb": 16, + "storage_gb": 150 + }, + "status": "Ready", + "maximum_runners": 10, + "public_ip_enabled": true, + "public_ips": [ + { + "enabled": true, + "prefix": "20.80.208.150", + "length": 31 + } + ], + "last_active_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + hostedRunner, _, err := client.Enterprise.DeleteHostedRunner(ctx, "o", 23) + if err != nil { + t.Errorf("Enterprise.DeleteHostedRunner returned error: %v", err) + } + + want := &HostedRunner{ + ID: Ptr(int64(5)), + Name: Ptr("My hosted ubuntu runner"), + RunnerGroupID: Ptr(int64(2)), + Platform: Ptr("linux-x64"), + ImageDetails: &HostedRunnerImageDetail{ + ID: Ptr("ubuntu-20.04"), + SizeGB: Ptr(int64(86)), + }, + MachineSizeDetails: &HostedRunnerMachineSpec{ + ID: "4-core", + CPUCores: 4, + MemoryGB: 16, + StorageGB: 150, + }, + Status: Ptr("Ready"), + MaximumRunners: Ptr(int64(10)), + PublicIPEnabled: Ptr(true), + PublicIPs: []*HostedRunnerPublicIP{ + { + Enabled: true, + Prefix: "20.80.208.150", + Length: 31, + }, + }, + LastActiveOn: &referenceTimestamp, + } + + if !cmp.Equal(hostedRunner, want) { + t.Errorf("Enterprise.DeleteHostedRunner returned %+v, want %+v", hostedRunner, want) + } + + const methodName = "DeleteHostedRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.DeleteHostedRunner(ctx, "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.DeleteHostedRunner(ctx, "o", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListHostedRunnerCustomImages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/hosted-runners/images/custom", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 2, + "images": [ + { + "id": 1, + "platform": "linux-x64", + "name": "CustomImage1", + "source": "custom", + "versions_count": 4, + "total_versions_size": 200, + "latest_version": "1.3.0", + "state": "Ready" + }, + { + "id": 2, + "platform": "linux-x64", + "name": "CustomImage2", + "source": "custom", + "versions_count": 2, + "total_versions_size": 150, + "latest_version": "1.0.0", + "state": "Ready" + } + ] + }`) + }) + + ctx := t.Context() + images, _, err := client.Enterprise.ListHostedRunnerCustomImages(ctx, "e") + if err != nil { + t.Errorf("Enterprise.ListHostedRunnerCustomImages returned error: %v", err) + } + + want := &HostedRunnerCustomImages{ + TotalCount: 2, + Images: []*HostedRunnerCustomImage{ + { + ID: 1, + Platform: "linux-x64", + Name: "CustomImage1", + Source: "custom", + VersionsCount: 4, + TotalVersionsSize: 200, + LatestVersion: "1.3.0", + State: "Ready", + }, + { + ID: 2, + Platform: "linux-x64", + Name: "CustomImage2", + Source: "custom", + VersionsCount: 2, + TotalVersionsSize: 150, + LatestVersion: "1.0.0", + State: "Ready", + }, + }, + } + + if !cmp.Equal(images, want) { + t.Errorf("Enterprise.ListHostedRunnerCustomImages returned %+v, want %+v", images, want) + } + + const methodName = "ListHostedRunnerCustomImages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListHostedRunnerCustomImages(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListHostedRunnerCustomImages(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerCustomImage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/hosted-runners/images/custom/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 1, + "platform": "linux-x64", + "name": "CustomImage", + "source": "custom", + "versions_count": 4, + "total_versions_size": 200, + "latest_version": "1.3.0", + "state": "Ready" + }`) + }) + + ctx := t.Context() + image, _, err := client.Enterprise.GetHostedRunnerCustomImage(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerCustomImage returned error: %v", err) + } + + want := &HostedRunnerCustomImage{ + ID: 1, + Platform: "linux-x64", + Name: "CustomImage", + Source: "custom", + VersionsCount: 4, + TotalVersionsSize: 200, + LatestVersion: "1.3.0", + State: "Ready", + } + + if !cmp.Equal(image, want) { + t.Errorf("Enterprise.GetHostedRunnerCustomImage returned %+v, want %+v", image, want) + } + + const methodName = "GetHostedRunnerCustomImage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerCustomImage(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerCustomImage(ctx, "e", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteHostedRunnerCustomImage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/hosted-runners/images/custom/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteHostedRunnerCustomImage(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.DeleteHostedRunnerCustomImage returned error: %v", err) + } + + const methodName = "DeleteHostedRunnerCustomImage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteHostedRunnerCustomImage(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteHostedRunnerCustomImage(ctx, "e", 1) + }) +} + +func TestEnterpriseService_ListHostedRunnerCustomImageVersions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/hosted-runners/images/custom/1/versions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "total_count": 2, + "image_versions": [ + { + "version": "1.1.0", + "size_gb": 75, + "state": "Ready", + "state_details": "None", + "created_on": `+referenceTimeStr+` + }, + { + "version": "1.0.0", + "size_gb": 75, + "state": "Ready", + "state_details": "None", + "created_on": `+referenceTimeStr+` + } + ] + }`) + }) + + ctx := t.Context() + versions, _, err := client.Enterprise.ListHostedRunnerCustomImageVersions(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.ListHostedRunnerCustomImageVersions returned error: %v", err) + } + + want := &HostedRunnerCustomImageVersions{ + TotalCount: 2, + ImageVersions: []*HostedRunnerCustomImageVersion{ + { + Version: "1.1.0", + SizeGB: 75, + State: "Ready", + StateDetails: "None", + CreatedOn: referenceTimestamp, + }, + { + Version: "1.0.0", + SizeGB: 75, + State: "Ready", + StateDetails: "None", + CreatedOn: referenceTimestamp, + }, + }, + } + + if !cmp.Equal(versions, want) { + t.Errorf("Enterprise.ListHostedRunnerCustomImageVersions returned %+v, want %+v", versions, want) + } + + const methodName = "ListHostedRunnerCustomImageVersions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListHostedRunnerCustomImageVersions(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListHostedRunnerCustomImageVersions(ctx, "e", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetHostedRunnerCustomImageVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/hosted-runners/images/custom/1/versions/1.0.0", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "version": "1.0.0", + "size_gb": 75, + "state": "Ready", + "state_details": "None", + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + version, _, err := client.Enterprise.GetHostedRunnerCustomImageVersion(ctx, "e", 1, "1.0.0") + if err != nil { + t.Errorf("Enterprise.GetHostedRunnerCustomImageVersion returned error: %v", err) + } + + want := &HostedRunnerCustomImageVersion{ + Version: "1.0.0", + SizeGB: 75, + State: "Ready", + StateDetails: "None", + CreatedOn: referenceTimestamp, + } + + if !cmp.Equal(version, want) { + t.Errorf("Enterprise.GetHostedRunnerCustomImageVersion returned %+v, want %+v", version, want) + } + + const methodName = "GetHostedRunnerCustomImageVersion" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetHostedRunnerCustomImageVersion(ctx, "\n", 1, "1.0.0") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetHostedRunnerCustomImageVersion(ctx, "e", 1, "1.0.0") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteHostedRunnerCustomImageVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/hosted-runners/images/custom/1/versions/1.0.0", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteHostedRunnerCustomImageVersion(ctx, "e", 1, "1.0.0") + if err != nil { + t.Errorf("Enterprise.DeleteHostedRunnerCustomImageVersion returned error: %v", err) + } + + const methodName = "DeleteHostedRunnerCustomImageVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteHostedRunnerCustomImageVersion(ctx, "\n", 1, "1.0.0") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteHostedRunnerCustomImageVersion(ctx, "e", 1, "1.0.0") + }) +} diff --git a/github/enterprise_actions_runner_groups.go b/github/enterprise_actions_runner_groups.go new file mode 100644 index 00000000000..7cf75c33ae3 --- /dev/null +++ b/github/enterprise_actions_runner_groups.go @@ -0,0 +1,341 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListOrganizations represents the response from the list orgs endpoints. +type ListOrganizations struct { + TotalCount *int `json:"total_count,omitempty"` + Organizations []*Organization `json:"organizations"` +} + +// EnterpriseRunnerGroup represents a self-hosted runner group configured in an enterprise. +type EnterpriseRunnerGroup struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Visibility *string `json:"visibility,omitempty"` + Default *bool `json:"default,omitempty"` + SelectedOrganizationsURL *string `json:"selected_organizations_url,omitempty"` + RunnersURL *string `json:"runners_url,omitempty"` + HostedRunnersURL *string `json:"hosted_runners_url,omitempty"` + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` + Inherited *bool `json:"inherited,omitempty"` + AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` + RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"` + SelectedWorkflows []string `json:"selected_workflows,omitempty"` + WorkflowRestrictionsReadOnly *bool `json:"workflow_restrictions_read_only,omitempty"` +} + +// EnterpriseRunnerGroups represents a collection of self-hosted runner groups configured for an enterprise. +type EnterpriseRunnerGroups struct { + TotalCount *int `json:"total_count,omitempty"` + RunnerGroups []*EnterpriseRunnerGroup `json:"runner_groups"` +} + +// CreateEnterpriseRunnerGroupRequest represents a request to create a Runner group for an enterprise. +type CreateEnterpriseRunnerGroupRequest struct { + Name *string `json:"name,omitempty"` + Visibility *string `json:"visibility,omitempty"` + // List of organization IDs that can access the runner group. + SelectedOrganizationIDs []int64 `json:"selected_organization_ids,omitempty"` + // Runners represent a list of runner IDs to add to the runner group. + Runners []int64 `json:"runners,omitempty"` + // If set to True, public repos can use this runner group + AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` + // If true, the runner group will be restricted to running only the workflows specified in the SelectedWorkflows slice. + RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"` + // List of workflows the runner group should be allowed to run. This setting will be ignored unless RestrictedToWorkflows is set to true. + SelectedWorkflows []string `json:"selected_workflows,omitempty"` + // The identifier of a hosted compute network configuration. + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` +} + +// UpdateEnterpriseRunnerGroupRequest represents a request to update a Runner group for an enterprise. +type UpdateEnterpriseRunnerGroupRequest struct { + Name *string `json:"name,omitempty"` + Visibility *string `json:"visibility,omitempty"` + AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` + RestrictedToWorkflows *bool `json:"restricted_to_workflows,omitempty"` + SelectedWorkflows []string `json:"selected_workflows,omitempty"` + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` +} + +// SetOrgAccessRunnerGroupRequest represents a request to replace the list of organizations +// that can access a self-hosted runner group configured in an enterprise. +type SetOrgAccessRunnerGroupRequest struct { + // Updated list of organization IDs that should be given access to the runner group. + SelectedOrganizationIDs []int64 `json:"selected_organization_ids"` +} + +// ListEnterpriseRunnerGroupOptions extend ListOptions to have the optional parameters VisibleToOrganization. +type ListEnterpriseRunnerGroupOptions struct { + ListOptions + + // Only return runner groups that are allowed to be used by this organization. + VisibleToOrganization string `url:"visible_to_organization,omitempty"` +} + +// ListRunnerGroups lists all self-hosted runner groups configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-self-hosted-runner-groups-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runner-groups +func (s *EnterpriseService) ListRunnerGroups(ctx context.Context, enterprise string, opts *ListEnterpriseRunnerGroupOptions) (*EnterpriseRunnerGroups, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var groups *EnterpriseRunnerGroups + resp, err := s.client.Do(req, &groups) + if err != nil { + return nil, resp, err + } + + return groups, resp, nil +} + +// GetEnterpriseRunnerGroup gets a specific self-hosted runner group for an enterprise using its RunnerGroup ID. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#get-a-self-hosted-runner-group-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id} +func (s *EnterpriseService) GetEnterpriseRunnerGroup(ctx context.Context, enterprise string, groupID int64) (*EnterpriseRunnerGroup, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v", enterprise, groupID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runnerGroup *EnterpriseRunnerGroup + resp, err := s.client.Do(req, &runnerGroup) + if err != nil { + return nil, resp, err + } + + return runnerGroup, resp, nil +} + +// DeleteEnterpriseRunnerGroup deletes a self-hosted runner group from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#delete-a-self-hosted-runner-group-from-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/runner-groups/{runner_group_id} +func (s *EnterpriseService) DeleteEnterpriseRunnerGroup(ctx context.Context, enterprise string, groupID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v", enterprise, groupID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// CreateEnterpriseRunnerGroup creates a new self-hosted runner group for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#create-a-self-hosted-runner-group-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/actions/runner-groups +func (s *EnterpriseService) CreateEnterpriseRunnerGroup(ctx context.Context, enterprise string, body CreateEnterpriseRunnerGroupRequest) (*EnterpriseRunnerGroup, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups", enterprise) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var runnerGroup *EnterpriseRunnerGroup + resp, err := s.client.Do(req, &runnerGroup) + if err != nil { + return nil, resp, err + } + + return runnerGroup, resp, nil +} + +// UpdateEnterpriseRunnerGroup updates a self-hosted runner group for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#update-a-self-hosted-runner-group-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/actions/runner-groups/{runner_group_id} +func (s *EnterpriseService) UpdateEnterpriseRunnerGroup(ctx context.Context, enterprise string, groupID int64, body UpdateEnterpriseRunnerGroupRequest) (*EnterpriseRunnerGroup, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v", enterprise, groupID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var runnerGroup *EnterpriseRunnerGroup + resp, err := s.client.Do(req, &runnerGroup) + if err != nil { + return nil, resp, err + } + + return runnerGroup, resp, nil +} + +// ListOrganizationAccessRunnerGroup lists the organizations with access to a self-hosted runner group configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-organization-access-to-a-self-hosted-runner-group-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations +func (s *EnterpriseService) ListOrganizationAccessRunnerGroup(ctx context.Context, enterprise string, groupID int64, opts *ListOptions) (*ListOrganizations, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/organizations", enterprise, groupID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var orgs *ListOrganizations + resp, err := s.client.Do(req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// SetOrganizationAccessRunnerGroup replaces the list of organizations that have access to a self-hosted runner group configured in an enterprise +// with a new List of organizations. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#set-organization-access-for-a-self-hosted-runner-group-in-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations +func (s *EnterpriseService) SetOrganizationAccessRunnerGroup(ctx context.Context, enterprise string, groupID int64, body SetOrgAccessRunnerGroupRequest) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/organizations", enterprise, groupID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddOrganizationAccessRunnerGroup adds an organization to the list of selected organizations that can access a self-hosted runner group. +// The runner group must have visibility set to 'selected'. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#add-organization-access-to-a-self-hosted-runner-group-in-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations/{org_id} +func (s *EnterpriseService) AddOrganizationAccessRunnerGroup(ctx context.Context, enterprise string, groupID, orgID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/organizations/%v", enterprise, groupID, orgID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveOrganizationAccessRunnerGroup removes an organization from the list of selected organizations that can access a self-hosted runner group. +// The runner group must have visibility set to 'selected'. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#remove-organization-access-to-a-self-hosted-runner-group-in-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations/{org_id} +func (s *EnterpriseService) RemoveOrganizationAccessRunnerGroup(ctx context.Context, enterprise string, groupID, orgID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/organizations/%v", enterprise, groupID, orgID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListRunnerGroupRunners lists self-hosted runners that are in a specific enterprise group. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#list-self-hosted-runners-in-a-group-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners +func (s *EnterpriseService) ListRunnerGroupRunners(ctx context.Context, enterprise string, groupID int64, opts *ListOptions) (*Runners, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/runners", enterprise, groupID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *Runners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// SetRunnerGroupRunners replaces the list of self-hosted runners that are part of an enterprise runner group +// with a new list of runners. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#set-self-hosted-runners-in-a-group-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners +func (s *EnterpriseService) SetRunnerGroupRunners(ctx context.Context, enterprise string, groupID int64, body SetRunnerGroupRunnersRequest) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/runners", enterprise, groupID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddRunnerGroupRunners adds a self-hosted runner to a runner group configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#add-a-self-hosted-runner-to-a-group-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners/{runner_id} +func (s *EnterpriseService) AddRunnerGroupRunners(ctx context.Context, enterprise string, groupID, runnerID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/runners/%v", enterprise, groupID, runnerID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveRunnerGroupRunners removes a self-hosted runner from a group configured in an enterprise. +// The runner is then returned to the default group. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups?apiVersion=2022-11-28#remove-a-self-hosted-runner-from-a-group-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners/{runner_id} +func (s *EnterpriseService) RemoveRunnerGroupRunners(ctx context.Context, enterprise string, groupID, runnerID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runner-groups/%v/runners/%v", enterprise, groupID, runnerID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_actions_runner_groups_test.go b/github/enterprise_actions_runner_groups_test.go new file mode 100644 index 00000000000..f77a4ff0450 --- /dev/null +++ b/github/enterprise_actions_runner_groups_test.go @@ -0,0 +1,531 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListRunnerGroups(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"runner_groups":[{"id":1,"name":"Default","visibility":"all","default":true,"runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/1/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":true,"selected_workflows":["a","b"]},{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_organizations_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations","runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners","inherited":true,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]},{"id":3,"name":"expensive-hardware","visibility":"private","default":false,"runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/3/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}]}`) + }) + + opts := &ListEnterpriseRunnerGroupOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + groups, _, err := client.Enterprise.ListRunnerGroups(ctx, "o", opts) + if err != nil { + t.Errorf("Enterprise.ListRunnerGroups returned error: %v", err) + } + + want := &EnterpriseRunnerGroups{ + TotalCount: Ptr(3), + RunnerGroups: []*EnterpriseRunnerGroup{ + {ID: Ptr(int64(1)), Name: Ptr("Default"), Visibility: Ptr("all"), Default: Ptr(true), RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/1/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(true), SelectedWorkflows: []string{"a", "b"}}, + {ID: Ptr(int64(2)), Name: Ptr("octo-runner-group"), Visibility: Ptr("selected"), Default: Ptr(false), SelectedOrganizationsURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations"), RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners"), Inherited: Ptr(true), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + {ID: Ptr(int64(3)), Name: Ptr("expensive-hardware"), Visibility: Ptr("private"), Default: Ptr(false), RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/3/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Enterprise.ListRunnerGroups returned %+v, want %+v", groups, want) + } + + const methodName = "ListRunnerGroups" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListRunnerGroups(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListRunnerGroups(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListRunnerGroupsVisibleToOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2", "visible_to_organization": "github"}) + fmt.Fprint(w, `{"total_count":3,"runner_groups":[{"id":1,"name":"Default","visibility":"all","default":true,"runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/1/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]},{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_organizations_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations","runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners","inherited":true,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]},{"id":3,"name":"expensive-hardware","visibility":"private","default":false,"runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/3/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}]}`) + }) + + opts := &ListEnterpriseRunnerGroupOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}, VisibleToOrganization: "github"} + ctx := t.Context() + groups, _, err := client.Enterprise.ListRunnerGroups(ctx, "o", opts) + if err != nil { + t.Errorf("Enterprise.ListRunnerGroups returned error: %v", err) + } + + want := &EnterpriseRunnerGroups{ + TotalCount: Ptr(3), + RunnerGroups: []*EnterpriseRunnerGroup{ + {ID: Ptr(int64(1)), Name: Ptr("Default"), Visibility: Ptr("all"), Default: Ptr(true), RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/1/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + {ID: Ptr(int64(2)), Name: Ptr("octo-runner-group"), Visibility: Ptr("selected"), Default: Ptr(false), SelectedOrganizationsURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations"), RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners"), Inherited: Ptr(true), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + {ID: Ptr(int64(3)), Name: Ptr("expensive-hardware"), Visibility: Ptr("private"), Default: Ptr(false), RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/3/runners"), Inherited: Ptr(false), AllowsPublicRepositories: Ptr(true), RestrictedToWorkflows: Ptr(false), SelectedWorkflows: []string{}}, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Enterprise.ListRunnerGroups returned %+v, want %+v", groups, want) + } + + const methodName = "ListRunnerGroups" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListRunnerGroups(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListRunnerGroups(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetEnterpriseRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_organizations_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations","runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}`) + }) + + ctx := t.Context() + group, _, err := client.Enterprise.GetEnterpriseRunnerGroup(ctx, "o", 2) + if err != nil { + t.Errorf("Enterprise.GetEnterpriseRunnerGroup returned error: %v", err) + } + + want := &EnterpriseRunnerGroup{ + ID: Ptr(int64(2)), + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + Default: Ptr(false), + SelectedOrganizationsURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations"), + RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners"), + Inherited: Ptr(false), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + + if !cmp.Equal(group, want) { + t.Errorf("Enterprise.GetEnterpriseRunnerGroup returned %+v, want %+v", group, want) + } + + const methodName = "GetEnterpriseRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetEnterpriseRunnerGroup(ctx, "\n", 2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetEnterpriseRunnerGroup(ctx, "o", 2) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteEnterpriseRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteEnterpriseRunnerGroup(ctx, "o", 2) + if err != nil { + t.Errorf("Enterprise.DeleteEnterpriseRunnerGroup returned error: %v", err) + } + + const methodName = "DeleteEnterpriseRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteEnterpriseRunnerGroup(ctx, "\n", 2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteEnterpriseRunnerGroup(ctx, "o", 2) + }) +} + +func TestEnterpriseService_CreateEnterpriseRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_organizations_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations","runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}`) + }) + + ctx := t.Context() + req := CreateEnterpriseRunnerGroupRequest{ + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + group, _, err := client.Enterprise.CreateEnterpriseRunnerGroup(ctx, "o", req) + if err != nil { + t.Errorf("Enterprise.CreateEnterpriseRunnerGroup returned error: %v", err) + } + + want := &EnterpriseRunnerGroup{ + ID: Ptr(int64(2)), + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + Default: Ptr(false), + SelectedOrganizationsURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations"), + RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners"), + Inherited: Ptr(false), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + + if !cmp.Equal(group, want) { + t.Errorf("Enterprise.CreateEnterpriseRunnerGroup returned %+v, want %+v", group, want) + } + + const methodName = "CreateEnterpriseRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateEnterpriseRunnerGroup(ctx, "\n", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateEnterpriseRunnerGroup(ctx, "o", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateEnterpriseRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"id":2,"name":"octo-runner-group","visibility":"selected","default":false,"selected_organizations_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations","runners_url":"https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners","inherited":false,"allows_public_repositories":true,"restricted_to_workflows":false,"selected_workflows":[]}`) + }) + + ctx := t.Context() + req := UpdateEnterpriseRunnerGroupRequest{ + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + group, _, err := client.Enterprise.UpdateEnterpriseRunnerGroup(ctx, "o", 2, req) + if err != nil { + t.Errorf("Enterprise.UpdateEnterpriseRunnerGroup returned error: %v", err) + } + + want := &EnterpriseRunnerGroup{ + ID: Ptr(int64(2)), + Name: Ptr("octo-runner-group"), + Visibility: Ptr("selected"), + Default: Ptr(false), + SelectedOrganizationsURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/organizations"), + RunnersURL: Ptr("https://api.github.com/enterprises/octo-enterprise/actions/runner_groups/2/runners"), + Inherited: Ptr(false), + AllowsPublicRepositories: Ptr(true), + RestrictedToWorkflows: Ptr(false), + SelectedWorkflows: []string{}, + } + + if !cmp.Equal(group, want) { + t.Errorf("Enterprise.UpdateEnterpriseRunnerGroup returned %+v, want %+v", group, want) + } + + const methodName = "UpdateEnterpriseRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateEnterpriseRunnerGroup(ctx, "\n", 2, req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateEnterpriseRunnerGroup(ctx, "o", 2, req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListOrganizationAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/organizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "1", "page": "1"}) + fmt.Fprint(w, `{"total_count": 1, "organizations": [{"id": 43, "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", "name": "Hello-World", "login": "octocat"}]}`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 1} + groups, _, err := client.Enterprise.ListOrganizationAccessRunnerGroup(ctx, "o", 2, opts) + if err != nil { + t.Errorf("Enterprise.ListOrganizationAccessRunnerGroup returned error: %v", err) + } + + want := &ListOrganizations{ + TotalCount: Ptr(1), + Organizations: []*Organization{ + {ID: Ptr(int64(43)), NodeID: Ptr("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"), Name: Ptr("Hello-World"), Login: Ptr("octocat")}, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Enterprise.ListOrganizationAccessRunnerGroup returned %+v, want %+v", groups, want) + } + + const methodName = "ListOrganizationAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListOrganizationAccessRunnerGroup(ctx, "\n", 2, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListOrganizationAccessRunnerGroup(ctx, "o", 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_SetOrganizationAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/organizations", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + req := SetOrgAccessRunnerGroupRequest{ + SelectedOrganizationIDs: []int64{ + 1, + 2, + }, + } + + ctx := t.Context() + _, err := client.Enterprise.SetOrganizationAccessRunnerGroup(ctx, "o", 2, req) + if err != nil { + t.Errorf("Enterprise.SetOrganizationAccessRunnerGroup returned error: %v", err) + } + + const methodName = "SetRepositoryAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.SetOrganizationAccessRunnerGroup(ctx, "\n", 2, req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.SetOrganizationAccessRunnerGroup(ctx, "o", 2, req) + }) +} + +func TestEnterpriseService_AddOrganizationAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/organizations/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + ctx := t.Context() + _, err := client.Enterprise.AddOrganizationAccessRunnerGroup(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Enterprise.AddOrganizationAccessRunnerGroup returned error: %v", err) + } + + const methodName = "AddOrganizationAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.AddOrganizationAccessRunnerGroup(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.AddOrganizationAccessRunnerGroup(ctx, "o", 2, 42) + }) +} + +func TestEnterpriseService_RemoveOrganizationAccessRunnerGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/organizations/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.RemoveOrganizationAccessRunnerGroup(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Enterprise.RemoveOrganizationAccessRunnerGroup returned error: %v", err) + } + + const methodName = "RemoveOrganizationAccessRunnerGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.RemoveOrganizationAccessRunnerGroup(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.RemoveOrganizationAccessRunnerGroup(ctx, "o", 2, 42) + }) +} + +func TestEnterpriseService_ListRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":2,"runners":[{"id":23,"name":"MBP","os":"macos","status":"online"},{"id":24,"name":"iMac","os":"macos","status":"offline"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + runners, _, err := client.Enterprise.ListRunnerGroupRunners(ctx, "o", 2, opts) + if err != nil { + t.Errorf("Enterprise.ListRunnerGroupRunners returned error: %v", err) + } + + want := &Runners{ + TotalCount: 2, + Runners: []*Runner{ + {ID: Ptr(int64(23)), Name: Ptr("MBP"), OS: Ptr("macos"), Status: Ptr("online")}, + {ID: Ptr(int64(24)), Name: Ptr("iMac"), OS: Ptr("macos"), Status: Ptr("offline")}, + }, + } + if !cmp.Equal(runners, want) { + t.Errorf("Enterprise.ListRunnerGroupRunners returned %+v, want %+v", runners, want) + } + + const methodName = "ListRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListRunnerGroupRunners(ctx, "\n", 2, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListRunnerGroupRunners(ctx, "o", 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_SetRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/runners", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + req := SetRunnerGroupRunnersRequest{ + Runners: []int64{ + 1, + 2, + }, + } + + ctx := t.Context() + _, err := client.Enterprise.SetRunnerGroupRunners(ctx, "o", 2, req) + if err != nil { + t.Errorf("Enterprise.SetRunnerGroupRunners returned error: %v", err) + } + + const methodName = "SetRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.SetRunnerGroupRunners(ctx, "\n", 2, req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.SetRunnerGroupRunners(ctx, "o", 2, req) + }) +} + +func TestEnterpriseService_AddRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/runners/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + ctx := t.Context() + _, err := client.Enterprise.AddRunnerGroupRunners(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Enterprise.AddRunnerGroupRunners returned error: %v", err) + } + + const methodName = "AddRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.AddRunnerGroupRunners(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.AddRunnerGroupRunners(ctx, "o", 2, 42) + }) +} + +func TestEnterpriseService_RemoveRunnerGroupRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runner-groups/2/runners/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.RemoveRunnerGroupRunners(ctx, "o", 2, 42) + if err != nil { + t.Errorf("Enterprise.RemoveRunnerGroupRunners returned error: %v", err) + } + + const methodName = "RemoveRunnerGroupRunners" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.RemoveRunnerGroupRunners(ctx, "\n", 2, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.RemoveRunnerGroupRunners(ctx, "o", 2, 42) + }) +} diff --git a/github/enterprise_actions_runners.go b/github/enterprise_actions_runners.go new file mode 100644 index 00000000000..732dbcf5aec --- /dev/null +++ b/github/enterprise_actions_runners.go @@ -0,0 +1,139 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListRunnerApplicationDownloads lists self-hosted runner application binaries that can be downloaded and run. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-runner-applications-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runners/downloads +func (s *EnterpriseService) ListRunnerApplicationDownloads(ctx context.Context, enterprise string) ([]*RunnerApplicationDownload, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runners/downloads", enterprise) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rads []*RunnerApplicationDownload + resp, err := s.client.Do(req, &rads) + if err != nil { + return nil, resp, err + } + + return rads, resp, nil +} + +// CreateJITConfig creates a just-in-time configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-configuration-for-a-just-in-time-runner-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/actions/runners/generate-jitconfig +func (s *EnterpriseService) CreateJITConfig(ctx context.Context, enterprise string, body CreateJITConfigRequest) (*JITRunnerConfig, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runners/generate-jitconfig", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var jitConfig *JITRunnerConfig + resp, err := s.client.Do(req, &jitConfig) + if err != nil { + return nil, resp, err + } + + return jitConfig, resp, nil +} + +// CreateRegistrationToken creates a token that can be used to add a self-hosted runner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/actions/runners/registration-token +func (s *EnterpriseService) CreateRegistrationToken(ctx context.Context, enterprise string) (*RegistrationToken, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runners/registration-token", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var registrationToken *RegistrationToken + resp, err := s.client.Do(req, ®istrationToken) + if err != nil { + return nil, resp, err + } + + return registrationToken, resp, nil +} + +// ListRunners lists all the self-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#list-self-hosted-runners-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runners +func (s *EnterpriseService) ListRunners(ctx context.Context, enterprise string, opts *ListRunnersOptions) (*Runners, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runners", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runners *Runners + resp, err := s.client.Do(req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// GetRunner gets a specific self-hosted runner configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#get-a-self-hosted-runner-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/runners/{runner_id} +func (s *EnterpriseService) GetRunner(ctx context.Context, enterprise string, runnerID int64) (*Runner, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var runner *Runner + resp, err := s.client.Do(req, &runner) + if err != nil { + return nil, resp, err + } + + return runner, resp, nil +} + +// RemoveRunner forces the removal of a self-hosted runner from an enterprise using the runner id. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#delete-a-self-hosted-runner-from-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/runners/{runner_id} +func (s *EnterpriseService) RemoveRunner(ctx context.Context, enterprise string, runnerID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/runners/%v", enterprise, runnerID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_actions_runners_test.go b/github/enterprise_actions_runners_test.go new file mode 100644 index 00000000000..8edbc966e80 --- /dev/null +++ b/github/enterprise_actions_runners_test.go @@ -0,0 +1,241 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_CreateJITConfig(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateJITConfigRequest{Name: "test", RunnerGroupID: 1, Labels: []string{"one", "two"}} + + mux.HandleFunc("/enterprises/o/actions/runners/generate-jitconfig", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"encoded_jit_config":"foo"}`) + }) + + ctx := t.Context() + jitConfig, _, err := client.Enterprise.CreateJITConfig(ctx, "o", input) + if err != nil { + t.Errorf("Enterprise.CreateJITConfig returned error: %v", err) + } + + want := &JITRunnerConfig{EncodedJITConfig: Ptr("foo")} + if !cmp.Equal(jitConfig, want) { + t.Errorf("Enterprise.CreateJITConfig returned %+v, want %+v", jitConfig, want) + } + + const methodName = "CreateJITConfig" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateJITConfig(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateJITConfig(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateRegistrationToken(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/runners/registration-token", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"token":"LLBF3JGZDX3P5PMEXLND6TS6FCWO6","expires_at":`+referenceTimeStr+`}`) + }) + + ctx := t.Context() + token, _, err := client.Enterprise.CreateRegistrationToken(ctx, "e") + if err != nil { + t.Errorf("Enterprise.CreateRegistrationToken returned error: %v", err) + } + + want := &RegistrationToken{ + Token: Ptr("LLBF3JGZDX3P5PMEXLND6TS6FCWO6"), + ExpiresAt: &referenceTimestamp, + } + if !cmp.Equal(token, want) { + t.Errorf("Enterprise.CreateRegistrationToken returned %+v, want %+v", token, want) + } + + const methodName = "CreateRegistrationToken" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateRegistrationToken(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateRegistrationToken(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListRunners(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/runners", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"name": "MBP", "per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":1,"runners":[{"id":23,"name":"MBP","os":"macos","status":"online"}]}`) + }) + + opts := &ListRunnersOptions{ + Name: Ptr("MBP"), + ListOptions: ListOptions{Page: 2, PerPage: 2}, + } + ctx := t.Context() + runners, _, err := client.Enterprise.ListRunners(ctx, "e", opts) + if err != nil { + t.Errorf("Enterprise.ListRunners returned error: %v", err) + } + + want := &Runners{ + TotalCount: 1, + Runners: []*Runner{ + {ID: Ptr(int64(23)), Name: Ptr("MBP"), OS: Ptr("macos"), Status: Ptr("online")}, + }, + } + if !cmp.Equal(runners, want) { + t.Errorf("Actions.ListRunners returned %+v, want %+v", runners, want) + } + + const methodName = "ListRunners" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListRunners(ctx, "\n", &ListRunnersOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListRunners(ctx, "e", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/actions/runners/23", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":23,"name":"MBP","os":"macos","status":"online"}`) + }) + + ctx := t.Context() + runner, _, err := client.Enterprise.GetRunner(ctx, "e", 23) + if err != nil { + t.Errorf("Enterprise.GetRunner returned error: %v", err) + } + + want := &Runner{ + ID: Ptr(int64(23)), + Name: Ptr("MBP"), + OS: Ptr("macos"), + Status: Ptr("online"), + } + if !cmp.Equal(runner, want) { + t.Errorf("Enterprise.GetRunner returned %+v, want %+v", runner, want) + } + + const methodName = "GetRunner" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetRunner(ctx, "\n", 23) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetRunner(ctx, "e", 23) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveRunner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runners/21", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.RemoveRunner(ctx, "o", 21) + if err != nil { + t.Errorf("Actions.RemoveRunner returned error: %v", err) + } + + const methodName = "RemoveRunner" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.RemoveRunner(ctx, "\n", 21) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.RemoveRunner(ctx, "o", 21) + }) +} + +func TestEnterpriseService_ListRunnerApplicationDownloads(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/o/actions/runners/downloads", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"os":"osx","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-osx-x64-2.164.0.tar.gz","filename":"actions-runner-osx-x64-2.164.0.tar.gz"},{"os":"linux","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz","filename":"actions-runner-linux-x64-2.164.0.tar.gz"},{"os": "linux","architecture":"arm","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm-2.164.0.tar.gz","filename":"actions-runner-linux-arm-2.164.0.tar.gz"},{"os":"win","architecture":"x64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-win-x64-2.164.0.zip","filename":"actions-runner-win-x64-2.164.0.zip"},{"os":"linux","architecture":"arm64","download_url":"https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm64-2.164.0.tar.gz","filename":"actions-runner-linux-arm64-2.164.0.tar.gz"}]`) + }) + + ctx := t.Context() + downloads, _, err := client.Enterprise.ListRunnerApplicationDownloads(ctx, "o") + if err != nil { + t.Errorf("Enterprise.ListRunnerApplicationDownloads returned error: %v", err) + } + + want := []*RunnerApplicationDownload{ + {OS: Ptr("osx"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-osx-x64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-osx-x64-2.164.0.tar.gz")}, + {OS: Ptr("linux"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-x64-2.164.0.tar.gz")}, + {OS: Ptr("linux"), Architecture: Ptr("arm"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-arm-2.164.0.tar.gz")}, + {OS: Ptr("win"), Architecture: Ptr("x64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-win-x64-2.164.0.zip"), Filename: Ptr("actions-runner-win-x64-2.164.0.zip")}, + {OS: Ptr("linux"), Architecture: Ptr("arm64"), DownloadURL: Ptr("https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-arm64-2.164.0.tar.gz"), Filename: Ptr("actions-runner-linux-arm64-2.164.0.tar.gz")}, + } + if !cmp.Equal(downloads, want) { + t.Errorf("Enterprise.ListRunnerApplicationDownloads returned %+v, want %+v", downloads, want) + } + + const methodName = "ListRunnerApplicationDownloads" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListRunnerApplicationDownloads(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListRunnerApplicationDownloads(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_app_installation.go b/github/enterprise_app_installation.go new file mode 100644 index 00000000000..9f710b85319 --- /dev/null +++ b/github/enterprise_app_installation.go @@ -0,0 +1,268 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// InstallableOrganization represents an organization in an enterprise in which a GitHub app can be installed. +type InstallableOrganization struct { + ID int64 `json:"id"` + Login string `json:"login"` + AccessibleRepositoriesURL *string `json:"accessible_repositories_url,omitempty"` +} + +// AccessibleRepository represents a repository that can be made accessible to a GitHub app. +type AccessibleRepository struct { + ID int64 `json:"id"` + Name string `json:"name"` + FullName string `json:"full_name"` +} + +// InstallAppRequest represents the request to install a GitHub app on an enterprise-owned organization. +type InstallAppRequest struct { + // The Client ID of the GitHub App to install. + ClientID string `json:"client_id"` + // The selection of repositories that the GitHub app can access. + // Can be one of: all, selected, none + RepositorySelection string `json:"repository_selection"` + // A list of repository names that the GitHub App can access, if the repository_selection is set to selected. + Repositories []string `json:"repositories,omitempty"` +} + +// ListAppInstallableOrganizations lists the organizations in an enterprise that are installable for an app. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#get-enterprise-owned-organizations-that-can-have-github-apps-installed +// +//meta:operation GET /enterprises/{enterprise}/apps/installable_organizations +func (s *EnterpriseService) ListAppInstallableOrganizations(ctx context.Context, enterprise string, opts *ListOptions) ([]*InstallableOrganization, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/installable_organizations", enterprise) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var orgs []*InstallableOrganization + resp, err := s.client.Do(req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// ListAppAccessibleOrganizationRepositories lists the repositories accessible to an app in an enterprise-owned organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#get-repositories-belonging-to-an-enterprise-owned-organization +// +//meta:operation GET /enterprises/{enterprise}/apps/installable_organizations/{org}/accessible_repositories +func (s *EnterpriseService) ListAppAccessibleOrganizationRepositories(ctx context.Context, enterprise, org string, opts *ListOptions) ([]*AccessibleRepository, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/installable_organizations/%v/accessible_repositories", enterprise, org) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repos []*AccessibleRepository + resp, err := s.client.Do(req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// ListAppInstallations lists the GitHub app installations associated with the given enterprise-owned organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#list-github-apps-installed-on-an-enterprise-owned-organization +// +//meta:operation GET /enterprises/{enterprise}/apps/organizations/{org}/installations +func (s *EnterpriseService) ListAppInstallations(ctx context.Context, enterprise, org string, opts *ListOptions) ([]*Installation, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations", enterprise, org) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var installation []*Installation + resp, err := s.client.Do(req, &installation) + if err != nil { + return nil, resp, err + } + + return installation, resp, nil +} + +// InstallApp installs any valid GitHub app on the specified organization owned by the enterprise. +// If the app is already installed on the organization, and is suspended, it will be unsuspended. If the app has a pending installation request, they will all be approved. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#install-a-github-app-on-an-enterprise-owned-organization +// +//meta:operation POST /enterprises/{enterprise}/apps/organizations/{org}/installations +func (s *EnterpriseService) InstallApp(ctx context.Context, enterprise, org string, body InstallAppRequest) (*Installation, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations", enterprise, org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var installation *Installation + resp, err := s.client.Do(req, &installation) + if err != nil { + return nil, resp, err + } + + return installation, resp, nil +} + +// UninstallApp uninstalls a GitHub app from an organization. Any app installed on the organization can be removed. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#uninstall-a-github-app-from-an-enterprise-owned-organization +// +//meta:operation DELETE /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id} +func (s *EnterpriseService) UninstallApp(ctx context.Context, enterprise, org string, installationID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations/%v", enterprise, org, installationID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AppInstallationRepositoriesRequest specifies the parameters for +// EnterpriseService.AddRepositoriesToAppInstallation and +// EnterpriseService.RemoveRepositoriesFromAppInstallation. +type AppInstallationRepositoriesRequest struct { + // Repository names to add to or remove from the installation. + Repositories []string `json:"repositories"` +} + +// UpdateAppInstallationRepositoriesRequest specifies the parameters for +// EnterpriseService.UpdateAppInstallationRepositories. +type UpdateAppInstallationRepositoriesRequest struct { + // Can be "all" or "selected". + RepositorySelection *string `json:"repository_selection,omitempty"` + // Repository names to grant the installation access to. Only required + // when RepositorySelection is "selected". + Repositories []string `json:"repositories,omitempty"` +} + +// ListRepositoriesForOrgAppInstallation lists the repositories that an enterprise app installation +// has access to on an organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#get-the-repositories-accessible-to-a-given-github-app-installation +// +//meta:operation GET /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories +func (s *EnterpriseService) ListRepositoriesForOrgAppInstallation(ctx context.Context, enterprise, org string, installationID int64, opts *ListOptions) ([]*AccessibleRepository, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations/%v/repositories", enterprise, org, installationID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var r []*AccessibleRepository + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// UpdateAppInstallationRepositories changes a GitHub App installation's repository access +// between all repositories and a selected set. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#toggle-installation-repository-access-between-selected-and-all-repositories +// +//meta:operation PATCH /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories +func (s *EnterpriseService) UpdateAppInstallationRepositories(ctx context.Context, enterprise, org string, installationID int64, body UpdateAppInstallationRepositoriesRequest) (*Installation, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations/%v/repositories", enterprise, org, installationID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var r *Installation + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// AddRepositoriesToAppInstallation grants repository access for a GitHub App installation. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#grant-repository-access-to-an-organization-installation +// +//meta:operation PATCH /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories/add +func (s *EnterpriseService) AddRepositoriesToAppInstallation(ctx context.Context, enterprise, org string, installationID int64, body AppInstallationRepositoriesRequest) ([]*AccessibleRepository, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations/%v/repositories/add", enterprise, org, installationID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var r []*AccessibleRepository + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// RemoveRepositoriesFromAppInstallation revokes repository access from a GitHub App installation. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations?apiVersion=2022-11-28#remove-repository-access-from-an-organization-installation +// +//meta:operation PATCH /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories/remove +func (s *EnterpriseService) RemoveRepositoriesFromAppInstallation(ctx context.Context, enterprise, org string, installationID int64, body AppInstallationRepositoriesRequest) ([]*AccessibleRepository, *Response, error) { + u := fmt.Sprintf("enterprises/%v/apps/organizations/%v/installations/%v/repositories/remove", enterprise, org, installationID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var r []*AccessibleRepository + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} diff --git a/github/enterprise_app_installation_test.go b/github/enterprise_app_installation_test.go new file mode 100644 index 00000000000..b42318f70a7 --- /dev/null +++ b/github/enterprise_app_installation_test.go @@ -0,0 +1,338 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListAppInstallableOrganizations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/apps/installable_organizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1, "login":"org1"}]`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + got, _, err := client.Enterprise.ListAppInstallableOrganizations(ctx, "e", opts) + if err != nil { + t.Fatalf("Enterprise.ListAppInstallableOrganizations returned error: %v", err) + } + + want := []*InstallableOrganization{ + {ID: int64(1), Login: "org1"}, + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.ListAppInstallableOrganizations = %+v, want %+v", got, want) + } + + const methodName = "ListAppInstallableOrganizations" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.ListAppInstallableOrganizations(ctx, "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListAppInstallableOrganizations(ctx, "e", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListAppAccessibleOrganizationRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/apps/installable_organizations/org1/accessible_repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":10, "name":"repo1", "full_name":"org1/repo1"}]`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + repos, _, err := client.Enterprise.ListAppAccessibleOrganizationRepositories(ctx, "e", "org1", opts) + if err != nil { + t.Errorf("Enterprise.ListAppAccessibleOrganizationRepositories returned error: %v", err) + } + + want := []*AccessibleRepository{ + {ID: int64(10), Name: "repo1", FullName: "org1/repo1"}, + } + + if !cmp.Equal(repos, want) { + t.Errorf("Enterprise.ListAppAccessibleOrganizationRepositories returned %+v, want %+v", repos, want) + } + + const methodName = "ListAppAccessibleOrganizationRepositories" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListAppAccessibleOrganizationRepositories(ctx, "\n", "org1", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListAppAccessibleOrganizationRepositories(ctx, "e", "org1", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListAppInstallations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/apps/organizations/org1/installations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `[{"id":99}]`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + installations, _, err := client.Enterprise.ListAppInstallations(ctx, "e", "org1", opts) + if err != nil { + t.Errorf("ListAppInstallations returned error: %v", err) + } + want := []*Installation{ + {ID: Ptr(int64(99))}, + } + + if !cmp.Equal(installations, want) { + t.Errorf("ListAppInstallations returned %+v, want %+v", installations, want) + } + + const methodName = "ListAppInstallations" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListAppInstallations(ctx, "\n", "org1", &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListAppInstallations(ctx, "e", "org1", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_InstallApp(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := InstallAppRequest{ + ClientID: "cid", + RepositorySelection: "selected", + Repositories: []string{"r1", "r2"}, + } + + mux.HandleFunc("/enterprises/e/apps/organizations/org1/installations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, req) + fmt.Fprint(w, `{"id":555}`) + }) + + ctx := t.Context() + installation, _, err := client.Enterprise.InstallApp(ctx, "e", "org1", req) + if err != nil { + t.Errorf("InstallApp returned error: %v", err) + } + + want := &Installation{ID: Ptr(int64(555))} + + if !cmp.Equal(installation, want) { + t.Errorf("InstallApp returned %+v, want %+v", installation, want) + } + + const methodName = "InstallApp" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.InstallApp(ctx, "e", "org1", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UninstallApp(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/apps/organizations/org1/installations/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Enterprise.UninstallApp(ctx, "e", "org1", 123) + if err != nil { + t.Errorf("UninstallApp returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("UninstallApp returned status %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UninstallApp" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.UninstallApp(ctx, "e", "org1", 123) + }) +} + +func TestEnterpriseService_ListRepositoriesForOrgAppInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/apps/organizations/o/installations/1/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + ctx := t.Context() + repos, _, err := client.Enterprise.ListRepositoriesForOrgAppInstallation(ctx, "e", "o", 1, &ListOptions{Page: 1}) + if err != nil { + t.Errorf("Enterprise.ListRepositoriesForOrgAppInstallation returned error: %v", err) + } + + want := []*AccessibleRepository{{ID: 1}} + if diff := cmp.Diff(repos, want); diff != "" { + t.Errorf("Enterprise.ListRepositoriesForOrgAppInstallation returned diff (-want +got):\n%v", diff) + } + + const methodName = "ListRepositoriesForOrgAppInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListRepositoriesForOrgAppInstallation(ctx, "\n", "\n", -1, &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.Enterprise.ListRepositoriesForOrgAppInstallation(ctx, "e", "o", 1, &ListOptions{}) + return resp, err + }) +} + +func TestEnterpriseService_UpdateAppInstallationRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := UpdateAppInstallationRepositoriesRequest{ + RepositorySelection: Ptr("selected"), + Repositories: []string{"hello-world", "hello-world-2"}, + } + + mux.HandleFunc("/enterprises/e/apps/organizations/o/installations/1/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1, "repository_selection":"selected"}`) + }) + + ctx := t.Context() + inst, _, err := client.Enterprise.UpdateAppInstallationRepositories(ctx, "e", "o", 1, input) + if err != nil { + t.Errorf("Enterprise.UpdateAppInstallationRepositories returned error: %v", err) + } + + want := &Installation{ID: Ptr(int64(1)), RepositorySelection: Ptr("selected")} + if diff := cmp.Diff(inst, want); diff != "" { + t.Errorf("Enterprise.UpdateAppInstallationRepositories returned diff (-want +got):\n%v", diff) + } + + const methodName = "UpdateAppInstallationRepositories" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateAppInstallationRepositories(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.Enterprise.UpdateAppInstallationRepositories(ctx, "e", "o", 1, input) + return resp, err + }) +} + +func TestEnterpriseService_AddRepositoriesToAppInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := AppInstallationRepositoriesRequest{Repositories: []string{"hello-world", "hello-world-2"}} + + mux.HandleFunc("/enterprises/e/apps/organizations/o/installations/1/repositories/add", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + ctx := t.Context() + repos, _, err := client.Enterprise.AddRepositoriesToAppInstallation(ctx, "e", "o", 1, input) + if err != nil { + t.Errorf("Enterprise.AddRepositoriesToAppInstallation returned error: %v", err) + } + + want := []*AccessibleRepository{{ID: 1}, {ID: 2}} + if diff := cmp.Diff(repos, want); diff != "" { + t.Errorf("Enterprise.AddRepositoriesToAppInstallation returned diff (-want +got):\n%v", diff) + } + + const methodName = "AddRepositoriesToAppInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.AddRepositoriesToAppInstallation(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.Enterprise.AddRepositoriesToAppInstallation(ctx, "e", "o", 1, input) + return resp, err + }) +} + +func TestEnterpriseService_RemoveRepositoriesFromAppInstallation(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := AppInstallationRepositoriesRequest{Repositories: []string{"hello-world", "hello-world-2"}} + + mux.HandleFunc("/enterprises/e/apps/organizations/o/installations/1/repositories/remove", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + ctx := t.Context() + repos, _, err := client.Enterprise.RemoveRepositoriesFromAppInstallation(ctx, "e", "o", 1, input) + if err != nil { + t.Errorf("Enterprise.RemoveRepositoriesFromAppInstallation returned error: %v", err) + } + + want := []*AccessibleRepository{{ID: 1}, {ID: 2}} + if diff := cmp.Diff(repos, want); diff != "" { + t.Errorf("Enterprise.RemoveRepositoriesFromAppInstallation returned diff (-want +got):\n%v", diff) + } + + const methodName = "RemoveRepositoriesFromAppInstallation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.RemoveRepositoriesFromAppInstallation(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.Enterprise.RemoveRepositoriesFromAppInstallation(ctx, "e", "o", 1, input) + return resp, err + }) +} diff --git a/github/enterprise_audit_log.go b/github/enterprise_audit_log.go new file mode 100644 index 00000000000..afac46235f4 --- /dev/null +++ b/github/enterprise_audit_log.go @@ -0,0 +1,37 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetAuditLog gets the audit-log entries for an organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#get-the-audit-log-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/audit-log +func (s *EnterpriseService) GetAuditLog(ctx context.Context, enterprise string, opts *GetAuditLogOptions) ([]*AuditEntry, *Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var auditEntries []*AuditEntry + resp, err := s.client.Do(req, &auditEntries) + if err != nil { + return nil, resp, err + } + + return auditEntries, resp, nil +} diff --git a/github/enterprise_audit_log_stream.go b/github/enterprise_audit_log_stream.go new file mode 100644 index 00000000000..72e377c9d6f --- /dev/null +++ b/github/enterprise_audit_log_stream.go @@ -0,0 +1,287 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// AuditLogStream represents an audit log stream configuration for an enterprise. +type AuditLogStream struct { + ID int64 `json:"id"` + StreamType string `json:"stream_type"` + StreamDetails string `json:"stream_details"` + Enabled bool `json:"enabled"` + CreatedAt Timestamp `json:"created_at"` + UpdatedAt Timestamp `json:"updated_at"` + PausedAt *Timestamp `json:"paused_at,omitempty"` +} + +// AuditLogStreamConfig represents a configuration for creating or updating an audit log stream. +type AuditLogStreamConfig struct { + Enabled bool `json:"enabled"` + StreamType string `json:"stream_type"` + VendorSpecific AuditLogStreamVendorConfig `json:"vendor_specific"` +} + +// AuditLogStreamVendorConfig is a sealed marker interface for vendor-specific audit log +// stream configurations. Only this package can define implementations. +type AuditLogStreamVendorConfig interface { + isAuditLogStreamVendorConfig() +} + +// AuditLogStreamKey represents the public key used to encrypt secrets for audit log streaming. +type AuditLogStreamKey struct { + KeyID string `json:"key_id"` + Key string `json:"key"` +} + +// AzureBlobConfig represents vendor-specific config for Azure Blob Storage. +type AzureBlobConfig struct { + KeyID string `json:"key_id"` + EncryptedSASURL string `json:"encrypted_sas_url"` + Container string `json:"container"` +} + +// AzureHubConfig represents vendor-specific config for Azure Event Hubs. +type AzureHubConfig struct { + Name string `json:"name"` + EncryptedConnstring string `json:"encrypted_connstring"` + KeyID string `json:"key_id"` +} + +// AmazonS3OIDCConfig represents vendor-specific config for Amazon S3 with OIDC authentication. +type AmazonS3OIDCConfig struct { + Bucket string `json:"bucket"` + Region string `json:"region"` + KeyID string `json:"key_id"` + AuthenticationType string `json:"authentication_type"` // Value: "oidc" + ArnRole string `json:"arn_role"` +} + +// AmazonS3AccessKeysConfig represents vendor-specific config for Amazon S3 with access key authentication. +type AmazonS3AccessKeysConfig struct { + Bucket string `json:"bucket"` + Region string `json:"region"` + KeyID string `json:"key_id"` + AuthenticationType string `json:"authentication_type"` // Value: "access_keys" + EncryptedSecretKey string `json:"encrypted_secret_key"` + EncryptedAccessKeyID string `json:"encrypted_access_key_id"` +} + +// SplunkConfig represents vendor-specific config for Splunk. +type SplunkConfig struct { + Domain string `json:"domain"` + Port uint16 `json:"port"` + KeyID string `json:"key_id"` + EncryptedToken string `json:"encrypted_token"` + SSLVerify bool `json:"ssl_verify"` +} + +// HecConfig represents vendor-specific config for an HTTPS Event Collector (HEC) endpoint. +type HecConfig struct { + Domain string `json:"domain"` + Port uint16 `json:"port"` + KeyID string `json:"key_id"` + EncryptedToken string `json:"encrypted_token"` + Path string `json:"path"` + SSLVerify bool `json:"ssl_verify"` +} + +// GoogleCloudConfig represents vendor-specific config for Google Cloud Storage. +type GoogleCloudConfig struct { + Bucket string `json:"bucket"` + KeyID string `json:"key_id"` + EncryptedJSONCredentials string `json:"encrypted_json_credentials"` +} + +// DatadogConfig represents vendor-specific config for Datadog. +type DatadogConfig struct { + EncryptedToken string `json:"encrypted_token"` + Site string `json:"site"` // One of: US, US3, US5, EU1, US1-FED, AP1 + KeyID string `json:"key_id"` +} + +// Implement the sealed marker interface for all vendor config types. +func (*AzureBlobConfig) isAuditLogStreamVendorConfig() {} +func (*AzureHubConfig) isAuditLogStreamVendorConfig() {} +func (*AmazonS3OIDCConfig) isAuditLogStreamVendorConfig() {} +func (*AmazonS3AccessKeysConfig) isAuditLogStreamVendorConfig() {} +func (*SplunkConfig) isAuditLogStreamVendorConfig() {} +func (*HecConfig) isAuditLogStreamVendorConfig() {} +func (*GoogleCloudConfig) isAuditLogStreamVendorConfig() {} +func (*DatadogConfig) isAuditLogStreamVendorConfig() {} + +// NewAzureBlobStreamConfig returns an AuditLogStreamConfig for Azure Blob Storage. +func NewAzureBlobStreamConfig(enabled bool, cfg *AzureBlobConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Azure Blob Storage", VendorSpecific: cfg} +} + +// NewAzureHubStreamConfig returns an AuditLogStreamConfig for Azure Event Hubs. +func NewAzureHubStreamConfig(enabled bool, cfg *AzureHubConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Azure Event Hubs", VendorSpecific: cfg} +} + +// NewAmazonS3OIDCStreamConfig returns an AuditLogStreamConfig for Amazon S3 with OIDC auth. +func NewAmazonS3OIDCStreamConfig(enabled bool, cfg *AmazonS3OIDCConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Amazon S3", VendorSpecific: cfg} +} + +// NewAmazonS3AccessKeysStreamConfig returns an AuditLogStreamConfig for Amazon S3 with access key auth. +func NewAmazonS3AccessKeysStreamConfig(enabled bool, cfg *AmazonS3AccessKeysConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Amazon S3", VendorSpecific: cfg} +} + +// NewSplunkStreamConfig returns an AuditLogStreamConfig for Splunk. +func NewSplunkStreamConfig(enabled bool, cfg *SplunkConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Splunk", VendorSpecific: cfg} +} + +// NewHecStreamConfig returns an AuditLogStreamConfig for an HTTPS Event Collector endpoint. +func NewHecStreamConfig(enabled bool, cfg *HecConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "HTTPS Event Collector", VendorSpecific: cfg} +} + +// NewGoogleCloudStreamConfig returns an AuditLogStreamConfig for Google Cloud Storage. +func NewGoogleCloudStreamConfig(enabled bool, cfg *GoogleCloudConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Google Cloud Storage", VendorSpecific: cfg} +} + +// NewDatadogStreamConfig returns an AuditLogStreamConfig for Datadog. +func NewDatadogStreamConfig(enabled bool, cfg *DatadogConfig) *AuditLogStreamConfig { + return &AuditLogStreamConfig{Enabled: enabled, StreamType: "Datadog", VendorSpecific: cfg} +} + +// GetAuditLogStreamKey retrieves the public key used to encrypt secrets for audit log streaming. +// Credentials must be encrypted with this key before being submitted via CreateAuditLogStream +// or UpdateAuditLogStream. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#get-the-audit-log-stream-key-for-encrypting-secrets +// +//meta:operation GET /enterprises/{enterprise}/audit-log/stream-key +func (s *EnterpriseService) GetAuditLogStreamKey(ctx context.Context, enterprise string) (*AuditLogStreamKey, *Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log/stream-key", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var key *AuditLogStreamKey + resp, err := s.client.Do(req, &key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// ListAuditLogStreams lists the audit log stream configurations for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#list-audit-log-stream-configurations-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/audit-log/streams +func (s *EnterpriseService) ListAuditLogStreams(ctx context.Context, enterprise string) ([]*AuditLogStream, *Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log/streams", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var streams []*AuditLogStream + resp, err := s.client.Do(req, &streams) + if err != nil { + return nil, resp, err + } + + return streams, resp, nil +} + +// GetAuditLogStream gets a single audit log stream configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#list-one-audit-log-streaming-configuration-via-a-stream-id +// +//meta:operation GET /enterprises/{enterprise}/audit-log/streams/{stream_id} +func (s *EnterpriseService) GetAuditLogStream(ctx context.Context, enterprise string, streamID int64) (*AuditLogStream, *Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log/streams/%v", enterprise, streamID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var stream *AuditLogStream + resp, err := s.client.Do(req, &stream) + if err != nil { + return nil, resp, err + } + + return stream, resp, nil +} + +// CreateAuditLogStream creates an audit log streaming configuration for an enterprise. +// Credentials in the config must be encrypted using the key returned by GetAuditLogStreamKey. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#create-an-audit-log-streaming-configuration-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/audit-log/streams +func (s *EnterpriseService) CreateAuditLogStream(ctx context.Context, enterprise string, body AuditLogStreamConfig) (*AuditLogStream, *Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log/streams", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var stream *AuditLogStream + resp, err := s.client.Do(req, &stream) + if err != nil { + return nil, resp, err + } + + return stream, resp, nil +} + +// UpdateAuditLogStream updates an existing audit log stream configuration for an enterprise. +// Credentials in the config must be encrypted using the key returned by GetAuditLogStreamKey. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#update-an-existing-audit-log-stream-configuration +// +//meta:operation PUT /enterprises/{enterprise}/audit-log/streams/{stream_id} +func (s *EnterpriseService) UpdateAuditLogStream(ctx context.Context, enterprise string, streamID int64, body AuditLogStreamConfig) (*AuditLogStream, *Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log/streams/%v", enterprise, streamID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var stream *AuditLogStream + resp, err := s.client.Do(req, &stream) + if err != nil { + return nil, resp, err + } + + return stream, resp, nil +} + +// DeleteAuditLogStream deletes an audit log stream configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log?apiVersion=2022-11-28#delete-an-audit-log-streaming-configuration-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/audit-log/streams/{stream_id} +func (s *EnterpriseService) DeleteAuditLogStream(ctx context.Context, enterprise string, streamID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/audit-log/streams/%v", enterprise, streamID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_audit_log_stream_test.go b/github/enterprise_audit_log_stream_test.go new file mode 100644 index 00000000000..b715bc9cc96 --- /dev/null +++ b/github/enterprise_audit_log_stream_test.go @@ -0,0 +1,417 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetAuditLogStreamKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log/stream-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"key_id":"1234","key":"2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234"}`) + }) + + ctx := t.Context() + key, _, err := client.Enterprise.GetAuditLogStreamKey(ctx, "e") + if err != nil { + t.Errorf("Enterprise.GetAuditLogStreamKey returned error: %v", err) + } + + want := &AuditLogStreamKey{ + KeyID: "1234", + Key: "2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvv1234", + } + if !cmp.Equal(key, want) { + t.Errorf("Enterprise.GetAuditLogStreamKey returned %+v, want %+v", key, want) + } + + const methodName = "GetAuditLogStreamKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetAuditLogStreamKey(ctx, "\n") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetAuditLogStreamKey(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListAuditLogStreams(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log/streams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1,"stream_type":"Splunk","stream_details":"US","enabled":true}]`) + }) + + ctx := t.Context() + streams, _, err := client.Enterprise.ListAuditLogStreams(ctx, "e") + if err != nil { + t.Errorf("Enterprise.ListAuditLogStreams returned error: %v", err) + } + + want := []*AuditLogStream{ + { + ID: 1, + StreamType: "Splunk", + StreamDetails: "US", + Enabled: true, + }, + } + if !cmp.Equal(streams, want) { + t.Errorf("Enterprise.ListAuditLogStreams returned %+v, want %+v", streams, want) + } + + const methodName = "ListAuditLogStreams" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListAuditLogStreams(ctx, "\n") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListAuditLogStreams(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetAuditLogStream(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log/streams/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1,"stream_type":"Datadog","stream_details":"US","enabled":true}`) + }) + + ctx := t.Context() + stream, _, err := client.Enterprise.GetAuditLogStream(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.GetAuditLogStream returned error: %v", err) + } + + want := &AuditLogStream{ + ID: 1, + StreamType: "Datadog", + StreamDetails: "US", + Enabled: true, + } + if !cmp.Equal(stream, want) { + t.Errorf("Enterprise.GetAuditLogStream returned %+v, want %+v", stream, want) + } + + const methodName = "GetAuditLogStream" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetAuditLogStream(ctx, "\n", 1) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetAuditLogStream(ctx, "e", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateAuditLogStream(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log/streams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":2,"stream_type":"Datadog","stream_details":"US3","enabled":false}`) + }) + + input := NewDatadogStreamConfig(false, &DatadogConfig{ + EncryptedToken: "ENCRYPTED", + Site: "US3", + KeyID: "v1", + }) + + ctx := t.Context() + stream, _, err := client.Enterprise.CreateAuditLogStream(ctx, "e", *input) + if err != nil { + t.Errorf("Enterprise.CreateAuditLogStream returned error: %v", err) + } + + want := &AuditLogStream{ + ID: 2, + StreamType: "Datadog", + StreamDetails: "US3", + Enabled: false, + } + if !cmp.Equal(stream, want) { + t.Errorf("Enterprise.CreateAuditLogStream returned %+v, want %+v", stream, want) + } + + const methodName = "CreateAuditLogStream" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateAuditLogStream(ctx, "\n", *input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateAuditLogStream(ctx, "e", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateAuditLogStream(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log/streams/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{"id":1,"stream_type":"Splunk","stream_details":"splunk.example.com","enabled":true}`) + }) + + input := NewSplunkStreamConfig(true, &SplunkConfig{ + Domain: "splunk.example.com", + Port: 8089, + KeyID: "v1", + EncryptedToken: "ENCRYPTED", + SSLVerify: true, + }) + + ctx := t.Context() + stream, _, err := client.Enterprise.UpdateAuditLogStream(ctx, "e", 1, *input) + if err != nil { + t.Errorf("Enterprise.UpdateAuditLogStream returned error: %v", err) + } + + want := &AuditLogStream{ + ID: 1, + StreamType: "Splunk", + StreamDetails: "splunk.example.com", + Enabled: true, + } + if !cmp.Equal(stream, want) { + t.Errorf("Enterprise.UpdateAuditLogStream returned %+v, want %+v", stream, want) + } + + const methodName = "UpdateAuditLogStream" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateAuditLogStream(ctx, "\n", 1, *input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateAuditLogStream(ctx, "e", 1, *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteAuditLogStream(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log/streams/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteAuditLogStream(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.DeleteAuditLogStream returned error: %v", err) + } + + const methodName = "DeleteAuditLogStream" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteAuditLogStream(ctx, "\n", 1) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteAuditLogStream(ctx, "e", 1) + }) +} + +func TestNewAzureBlobStreamConfig(t *testing.T) { + t.Parallel() + cfg := &AzureBlobConfig{ + KeyID: "v1", + EncryptedSASURL: "ENCRYPTED", + Container: "my-container", + } + + got := NewAzureBlobStreamConfig(true, cfg) + + want := &AuditLogStreamConfig{ + Enabled: true, + StreamType: "Azure Blob Storage", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewAzureBlobStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewAzureHubStreamConfig(t *testing.T) { + t.Parallel() + cfg := &AzureHubConfig{ + Name: "my-hub", + EncryptedConnstring: "ENCRYPTED", + KeyID: "v1", + } + + got := NewAzureHubStreamConfig(true, cfg) + + want := &AuditLogStreamConfig{ + Enabled: true, + StreamType: "Azure Event Hubs", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewAzureHubStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewAmazonS3OIDCStreamConfig(t *testing.T) { + t.Parallel() + cfg := &AmazonS3OIDCConfig{ + Bucket: "my-bucket", + Region: "us-east-1", + KeyID: "v1", + AuthenticationType: "oidc", + ArnRole: "arn:aws:iam::role/my-role", + } + + got := NewAmazonS3OIDCStreamConfig(true, cfg) + + want := &AuditLogStreamConfig{ + Enabled: true, + StreamType: "Amazon S3", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewAmazonS3OIDCStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewAmazonS3AccessKeysStreamConfig(t *testing.T) { + t.Parallel() + cfg := &AmazonS3AccessKeysConfig{ + Bucket: "my-bucket", + Region: "us-west-2", + KeyID: "v1", + AuthenticationType: "access_keys", + EncryptedSecretKey: "ENCRYPTED_SECRET", + EncryptedAccessKeyID: "ENCRYPTED_KEY_ID", + } + + got := NewAmazonS3AccessKeysStreamConfig(false, cfg) + + want := &AuditLogStreamConfig{ + Enabled: false, + StreamType: "Amazon S3", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewAmazonS3AccessKeysStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewSplunkStreamConfig(t *testing.T) { + t.Parallel() + cfg := &SplunkConfig{ + Domain: "splunk.example.com", + Port: 8089, + KeyID: "v1", + EncryptedToken: "ENCRYPTED", + SSLVerify: true, + } + + got := NewSplunkStreamConfig(true, cfg) + + want := &AuditLogStreamConfig{ + Enabled: true, + StreamType: "Splunk", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewSplunkStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewHecStreamConfig(t *testing.T) { + t.Parallel() + cfg := &HecConfig{ + Domain: "hec.example.com", + Port: 443, + KeyID: "v1", + EncryptedToken: "ENCRYPTED", + Path: "/services/collector", + SSLVerify: true, + } + + got := NewHecStreamConfig(false, cfg) + + want := &AuditLogStreamConfig{ + Enabled: false, + StreamType: "HTTPS Event Collector", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewHecStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewGoogleCloudStreamConfig(t *testing.T) { + t.Parallel() + cfg := &GoogleCloudConfig{ + Bucket: "my-gcs-bucket", + KeyID: "v1", + EncryptedJSONCredentials: "ENCRYPTED", + } + + got := NewGoogleCloudStreamConfig(true, cfg) + + want := &AuditLogStreamConfig{ + Enabled: true, + StreamType: "Google Cloud Storage", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewGoogleCloudStreamConfig = %+v, want %+v", got, want) + } +} + +func TestNewDatadogStreamConfig(t *testing.T) { + t.Parallel() + cfg := &DatadogConfig{ + EncryptedToken: "ENCRYPTED", + Site: "US", + KeyID: "v1", + } + + got := NewDatadogStreamConfig(false, cfg) + + want := &AuditLogStreamConfig{ + Enabled: false, + StreamType: "Datadog", + VendorSpecific: cfg, + } + if !cmp.Equal(got, want) { + t.Errorf("NewDatadogStreamConfig = %+v, want %+v", got, want) + } +} diff --git a/github/enterprise_audit_log_test.go b/github/enterprise_audit_log_test.go new file mode 100644 index 00000000000..b4cc8ee2fce --- /dev/null +++ b/github/enterprise_audit_log_test.go @@ -0,0 +1,93 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + "time" +) + +func TestEnterpriseService_GetAuditLog(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/audit-log", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `[ + { + "workflow_id": 123456, + "head_branch": "master", + "org": "o", + "trigger_id": null, + "repo": "o/blue-crayon-1", + "created_at": 1615077308538, + "head_sha": "5acdeadbeef64d1a62388e901e5cdc9358644b37", + "conclusion": "success", + "actor": "testactor", + "completed_at": "2021-03-07T00:35:08.000Z", + "@timestamp": 1615077308538, + "name": "Code scanning - action", + "action": "workflows.completed_workflow_run", + "started_at": "2021-03-07T00:33:04.000Z", + "event": "schedule", + "workflow_run_id": 628312345, + "_document_id": "beeZYapIUe-wKg5-beadb33" + } + ]`) + }) + getOpts := GetAuditLogOptions{ + Include: Ptr("all"), + Phrase: Ptr("action:workflows"), + Order: Ptr("asc"), + } + ctx := t.Context() + auditEntries, _, err := client.Enterprise.GetAuditLog(ctx, "e", &getOpts) + if err != nil { + t.Errorf("Enterprise.GetAuditLog returned error: %v", err) + } + timestamp := time.Unix(0, 1615077308538*1e6) + want := []*AuditEntry{ + { + Timestamp: &Timestamp{timestamp}, + DocumentID: Ptr("beeZYapIUe-wKg5-beadb33"), + Action: Ptr("workflows.completed_workflow_run"), + Actor: Ptr("testactor"), + CreatedAt: &Timestamp{timestamp}, + Org: Ptr("o"), + AdditionalFields: map[string]any{ + "completed_at": "2021-03-07T00:35:08.000Z", + "conclusion": "success", + "event": "schedule", + "head_branch": "master", + "head_sha": "5acdeadbeef64d1a62388e901e5cdc9358644b37", + "name": "Code scanning - action", + "repo": "o/blue-crayon-1", + "started_at": "2021-03-07T00:33:04.000Z", + "workflow_id": float64(123456), + "workflow_run_id": float64(628312345), + }, + }, + } + + assertNoDiff(t, want, auditEntries) + + const methodName = "GetAuditLog" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetAuditLog(ctx, "\n", &getOpts) + return err + }) + + testNewRequestAndDoFailureCategory(t, methodName, client, AuditLogCategory, func() (*Response, error) { + got, resp, err := client.Enterprise.GetAuditLog(ctx, "o", &GetAuditLogOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_billing.go b/github/enterprise_billing.go new file mode 100644 index 00000000000..4f16fd8862f --- /dev/null +++ b/github/enterprise_billing.go @@ -0,0 +1,227 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnterpriseUsageReportOptions specifies optional parameters for the EnterpriseService.GetUsageReport method. +type EnterpriseUsageReportOptions struct { + Year int `url:"year,omitempty"` + Month int `url:"month,omitempty"` + Day int `url:"day,omitempty"` + CostCenterID string `url:"cost_center_id,omitempty"` +} + +// EnterprisePremiumRequestUsageReportOptions specifies optional parameters for the EnterpriseService.GetPremiumRequestUsageReport and EnterpriseService.GetAICreditUsage methods. +type EnterprisePremiumRequestUsageReportOptions struct { + EnterpriseUsageReportOptions + Organization string `url:"organization,omitempty"` + User string `url:"user,omitempty"` + Model string `url:"model,omitempty"` + Product string `url:"product,omitempty"` +} + +// EnterpriseUsageSummaryOptions specifies optional parameters for the EnterpriseService.GetUsageSummary method. +type EnterpriseUsageSummaryOptions struct { + EnterpriseUsageReportOptions + Organization string `url:"organization,omitempty"` + Repository string `url:"repository,omitempty"` + Product string `url:"product,omitempty"` + SKU string `url:"sku,omitempty"` +} + +// EnterpriseUsageTimePeriod represents a time period used in aggregated enterprise billing reports. +type EnterpriseUsageTimePeriod struct { + Year int `json:"year"` + Month *int `json:"month,omitempty"` + Day *int `json:"day,omitempty"` +} + +// EnterpriseAggregatedUsageItem represents a single usage line item in an enterprise billing premium request or AI credit report. +type EnterpriseAggregatedUsageItem struct { + Product string `json:"product"` + SKU string `json:"sku"` + Model string `json:"model"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossQuantity float64 `json:"grossQuantity"` + GrossAmount float64 `json:"grossAmount"` + DiscountQuantity float64 `json:"discountQuantity"` + DiscountAmount float64 `json:"discountAmount"` + NetQuantity float64 `json:"netQuantity"` + NetAmount float64 `json:"netAmount"` +} + +// EnterpriseAggregatedUsageReport represents the aggregated billing usage report response for the premium request and AI credit endpoints. +type EnterpriseAggregatedUsageReport struct { + TimePeriod EnterpriseUsageTimePeriod `json:"timePeriod"` + Enterprise string `json:"enterprise"` + Organization *string `json:"organization,omitempty"` + User *string `json:"user,omitempty"` + Product *string `json:"product,omitempty"` + Model *string `json:"model,omitempty"` + CostCenter *BillingCostCenter `json:"costCenter,omitempty"` + UsageItems []*EnterpriseAggregatedUsageItem `json:"usageItems"` +} + +// EnterpriseUsageSummaryItem represents a single usage line item in an enterprise billing usage summary report. +type EnterpriseUsageSummaryItem struct { + Product string `json:"product"` + SKU string `json:"sku"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossQuantity float64 `json:"grossQuantity"` + GrossAmount float64 `json:"grossAmount"` + DiscountQuantity float64 `json:"discountQuantity"` + DiscountAmount float64 `json:"discountAmount"` + NetQuantity float64 `json:"netQuantity"` + NetAmount float64 `json:"netAmount"` +} + +// EnterpriseUsageSummaryReport represents the billing usage summary report response for the EnterpriseService.GetUsageSummary endpoint. +type EnterpriseUsageSummaryReport struct { + TimePeriod EnterpriseUsageTimePeriod `json:"timePeriod"` + Enterprise string `json:"enterprise"` + Organization *string `json:"organization,omitempty"` + Repository *string `json:"repository,omitempty"` + Product *string `json:"product,omitempty"` + SKU *string `json:"sku,omitempty"` + CostCenter *BillingCostCenter `json:"costCenter,omitempty"` + UsageItems []*EnterpriseUsageSummaryItem `json:"usageItems"` +} + +// BillingCostCenter represents a cost center reference embedded in enterprise billing usage reports. +type BillingCostCenter struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// EnterpriseUsageItem represents a single usage line item in an enterprise billing platform report. +type EnterpriseUsageItem struct { + Date string `json:"date"` + Product string `json:"product"` + SKU string `json:"sku"` + Quantity float64 `json:"quantity"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossAmount float64 `json:"grossAmount"` + DiscountAmount float64 `json:"discountAmount"` + NetAmount float64 `json:"netAmount"` + OrganizationName string `json:"organizationName"` + RepositoryName *string `json:"repositoryName,omitempty"` +} + +// EnterpriseUsageReport represents the enterprise billing usage report response. +type EnterpriseUsageReport struct { + UsageItems []*EnterpriseUsageItem `json:"usageItems,omitempty"` +} + +// GetUsageReport returns a report of the total usage for an enterprise using the enhanced billing platform. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage?apiVersion=2022-11-28#get-billing-usage-report-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/usage +func (s *EnterpriseService) GetUsageReport(ctx context.Context, enterprise string, opts *EnterpriseUsageReportOptions) (*EnterpriseUsageReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/usage", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *EnterpriseUsageReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetUsageSummary returns a summary report of usage for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage?apiVersion=2022-11-28#get-billing-usage-summary-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/usage/summary +func (s *EnterpriseService) GetUsageSummary(ctx context.Context, enterprise string, opts *EnterpriseUsageSummaryOptions) (*EnterpriseUsageSummaryReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/usage/summary", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *EnterpriseUsageSummaryReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetPremiumRequestUsageReport returns a report of the premium request usage for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage?apiVersion=2022-11-28#get-billing-premium-request-usage-report-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/premium_request/usage +func (s *EnterpriseService) GetPremiumRequestUsageReport(ctx context.Context, enterprise string, opts *EnterprisePremiumRequestUsageReportOptions) (*EnterpriseAggregatedUsageReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/premium_request/usage", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *EnterpriseAggregatedUsageReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} + +// GetAICreditUsage returns a report of the AI credit usage for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage?apiVersion=2022-11-28#get-billing-ai-credit-usage-report-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/ai_credit/usage +func (s *EnterpriseService) GetAICreditUsage(ctx context.Context, enterprise string, opts *EnterprisePremiumRequestUsageReportOptions) (*EnterpriseAggregatedUsageReport, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/ai_credit/usage", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var report *EnterpriseAggregatedUsageReport + resp, err := s.client.Do(req, &report) + if err != nil { + return nil, resp, err + } + + return report, resp, nil +} diff --git a/github/enterprise_billing_cost_centers.go b/github/enterprise_billing_cost_centers.go new file mode 100644 index 00000000000..824902604ef --- /dev/null +++ b/github/enterprise_billing_cost_centers.go @@ -0,0 +1,233 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CostCenter represents an enterprise cost center. +type CostCenter struct { + ID string `json:"id"` + Name string `json:"name"` + Resources []*CostCenterResource `json:"resources"` + State *string `json:"state,omitempty"` + AzureSubscription *string `json:"azure_subscription,omitempty"` +} + +// CostCenterResource represents a resource assigned to a cost center. +type CostCenterResource struct { + Type string `json:"type"` + Name string `json:"name"` +} + +// CostCenters represents a list of cost centers. +type CostCenters struct { + CostCenters []*CostCenter `json:"costCenters,omitempty"` +} + +// ListCostCenterOptions specifies optional parameters to the EnterpriseService.ListCostCenters method. +type ListCostCenterOptions struct { + // Set to `active` or `deleted` to only list cost centers in a specific state. + State *string `url:"state,omitempty"` +} + +// CostCenterRequest represents a request to create or update a cost center. +type CostCenterRequest struct { + Name string `json:"name"` +} + +// CostCenterResourceRequest represents a request to add or remove resources from a cost center. +type CostCenterResourceRequest struct { + Users []string `json:"users,omitempty"` + Organizations []string `json:"organizations,omitempty"` + Repositories []string `json:"repositories,omitempty"` +} + +// AddResourcesToCostCenterResponse represents a response from adding resources to a cost center. +type AddResourcesToCostCenterResponse struct { + Message *string `json:"message,omitempty"` + ReassignedResources []*ReassignedResource `json:"reassigned_resources,omitempty"` +} + +// ReassignedResource represents a resource that was reassigned from another cost center. +type ReassignedResource struct { + ResourceType *string `json:"resource_type,omitempty"` + Name *string `json:"name,omitempty"` + PreviousCostCenter *string `json:"previous_cost_center,omitempty"` +} + +// RemoveResourcesFromCostCenterResponse represents a response from removing resources from a cost center. +type RemoveResourcesFromCostCenterResponse struct { + Message *string `json:"message,omitempty"` +} + +// DeleteCostCenterResponse represents a response from deleting a cost center. +type DeleteCostCenterResponse struct { + Message string `json:"message"` + ID string `json:"id"` + Name string `json:"name"` + CostCenterState string `json:"costCenterState"` +} + +// ListCostCenters lists all cost centers for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#get-all-cost-centers-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/cost-centers +func (s *EnterpriseService) ListCostCenters(ctx context.Context, enterprise string, opts *ListCostCenterOptions) (*CostCenters, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var costCenters *CostCenters + resp, err := s.client.Do(req, &costCenters) + if err != nil { + return nil, resp, err + } + + return costCenters, resp, nil +} + +// CreateCostCenter creates a new cost center for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#create-a-new-cost-center +// +//meta:operation POST /enterprises/{enterprise}/settings/billing/cost-centers +func (s *EnterpriseService) CreateCostCenter(ctx context.Context, enterprise string, body CostCenterRequest) (*CostCenter, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var result *CostCenter + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// GetCostCenter gets a cost center by ID for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#get-a-cost-center-by-id +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} +func (s *EnterpriseService) GetCostCenter(ctx context.Context, enterprise, costCenterID string) (*CostCenter, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v", enterprise, costCenterID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var costCenter *CostCenter + resp, err := s.client.Do(req, &costCenter) + if err != nil { + return nil, resp, err + } + + return costCenter, resp, nil +} + +// UpdateCostCenter updates the name of a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#update-a-cost-center +// +//meta:operation PATCH /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} +func (s *EnterpriseService) UpdateCostCenter(ctx context.Context, enterprise, costCenterID string, body CostCenterRequest) (*CostCenter, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v", enterprise, costCenterID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var result *CostCenter + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// DeleteCostCenter deletes a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#delete-a-cost-center +// +//meta:operation DELETE /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} +func (s *EnterpriseService) DeleteCostCenter(ctx context.Context, enterprise, costCenterID string) (*DeleteCostCenterResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v", enterprise, costCenterID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + var result *DeleteCostCenterResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// AddResourcesToCostCenter adds resources to a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#add-resources-to-a-cost-center +// +//meta:operation POST /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id}/resource +func (s *EnterpriseService) AddResourcesToCostCenter(ctx context.Context, enterprise, costCenterID string, body CostCenterResourceRequest) (*AddResourcesToCostCenterResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v/resource", enterprise, costCenterID) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var result *AddResourcesToCostCenterResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// RemoveResourcesFromCostCenter removes resources from a cost center. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers?apiVersion=2022-11-28#remove-resources-from-a-cost-center +// +//meta:operation DELETE /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id}/resource +func (s *EnterpriseService) RemoveResourcesFromCostCenter(ctx context.Context, enterprise, costCenterID string, resources CostCenterResourceRequest) (*RemoveResourcesFromCostCenterResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/cost-centers/%v/resource", enterprise, costCenterID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, resources) + if err != nil { + return nil, nil, err + } + + var result *RemoveResourcesFromCostCenterResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} diff --git a/github/enterprise_billing_cost_centers_test.go b/github/enterprise_billing_cost_centers_test.go new file mode 100644 index 00000000000..3994c06dae5 --- /dev/null +++ b/github/enterprise_billing_cost_centers_test.go @@ -0,0 +1,528 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListCostCenters(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "active", + }) + fmt.Fprint(w, `{ + "costCenters": [ + { + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Cost Center Name", + "state": "active", + "azure_subscription": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "resources": [ + { + "type": "User", + "name": "Monalisa" + }, + { + "type": "Repo", + "name": "octocat/hello-world" + } + ] + }, + { + "id": "3ffb9ffe-6903-11ee-8c99-0242ac120003", + "name": "Another Cost Center", + "state": "active", + "resources": [ + { + "type": "User", + "name": "Octocat" + } + ] + } + ] + }`) + }) + + ctx := t.Context() + opts := &ListCostCenterOptions{ + State: Ptr("active"), + } + costCenters, _, err := client.Enterprise.ListCostCenters(ctx, "e", opts) + if err != nil { + t.Errorf("Enterprise.ListCostCenters returned error: %v", err) + } + + want := &CostCenters{ + CostCenters: []*CostCenter{ + { + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Cost Center Name", + State: Ptr("active"), + AzureSubscription: Ptr("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Monalisa", + }, + { + Type: "Repo", + Name: "octocat/hello-world", + }, + }, + }, + { + ID: "3ffb9ffe-6903-11ee-8c99-0242ac120003", + Name: "Another Cost Center", + State: Ptr("active"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Octocat", + }, + }, + }, + }, + } + if !cmp.Equal(costCenters, want) { + t.Errorf("Enterprise.ListCostCenters returned %+v, want %+v", costCenters, want) + } + + const methodName = "ListCostCenters" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListCostCenters(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListCostCenters(ctx, "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListCostCenters_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.ListCostCenters(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestEnterpriseService_CreateCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := CostCenterRequest{ + Name: "Engineering Team", + } + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, req) + fmt.Fprint(w, `{ + "id": "abc123", + "name": "Engineering Team", + "resources": [] + }`) + }) + + ctx := t.Context() + costCenter, _, err := client.Enterprise.CreateCostCenter(ctx, "e", req) + if err != nil { + t.Errorf("Enterprise.CreateCostCenter returned error: %v", err) + } + + want := &CostCenter{ + ID: "abc123", + Name: "Engineering Team", + Resources: []*CostCenterResource{}, + } + if !cmp.Equal(costCenter, want) { + t.Errorf("Enterprise.CreateCostCenter returned %+v, want %+v", costCenter, want) + } + + const methodName = "CreateCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateCostCenter(ctx, "\n", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateCostCenter(ctx, "e", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.CreateCostCenter(ctx, "%", CostCenterRequest{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Cost Center Name", + "state": "active", + "azure_subscription": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "resources": [ + { + "type": "User", + "name": "Monalisa" + }, + { + "type": "Repo", + "name": "octocat/hello-world" + } + ] + }`) + }) + + ctx := t.Context() + costCenter, _, err := client.Enterprise.GetCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if err != nil { + t.Errorf("Enterprise.GetCostCenter returned error: %v", err) + } + + want := &CostCenter{ + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Cost Center Name", + State: Ptr("active"), + AzureSubscription: Ptr("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Monalisa", + }, + { + Type: "Repo", + Name: "octocat/hello-world", + }, + }, + } + if !cmp.Equal(costCenter, want) { + t.Errorf("Enterprise.GetCostCenter returned %+v, want %+v", costCenter, want) + } + + const methodName = "GetCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + testURLParseError(t, err) +} + +func TestEnterpriseService_UpdateCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := CostCenterRequest{ + Name: "Updated Cost Center Name", + } + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, req) + fmt.Fprint(w, `{ + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Updated Cost Center Name", + "state": "active", + "azure_subscription": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", + "resources": [ + { + "type": "User", + "name": "Monalisa" + }, + { + "type": "Repo", + "name": "octocat/hello-world" + } + ] + }`) + }) + + ctx := t.Context() + costCenter, _, err := client.Enterprise.UpdateCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if err != nil { + t.Errorf("Enterprise.UpdateCostCenter returned error: %v", err) + } + + want := &CostCenter{ + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Updated Cost Center Name", + State: Ptr("active"), + AzureSubscription: Ptr("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), + Resources: []*CostCenterResource{ + { + Type: "User", + Name: "Monalisa", + }, + { + Type: "Repo", + Name: "octocat/hello-world", + }, + }, + } + if !cmp.Equal(costCenter, want) { + t.Errorf("Enterprise.UpdateCostCenter returned %+v, want %+v", costCenter, want) + } + + const methodName = "UpdateCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.UpdateCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002", CostCenterRequest{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_DeleteCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "message": "Cost center successfully deleted.", + "id": "2eeb8ffe-6903-11ee-8c99-0242ac120002", + "name": "Engineering Team", + "costCenterState": "CostCenterArchived" + }`) + }) + + ctx := t.Context() + result, _, err := client.Enterprise.DeleteCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if err != nil { + t.Errorf("Enterprise.DeleteCostCenter returned error: %v", err) + } + + want := &DeleteCostCenterResponse{ + Message: "Cost center successfully deleted.", + ID: "2eeb8ffe-6903-11ee-8c99-0242ac120002", + Name: "Engineering Team", + CostCenterState: "CostCenterArchived", + } + if !cmp.Equal(result, want) { + t.Errorf("Enterprise.DeleteCostCenter returned %+v, want %+v", result, want) + } + + const methodName = "DeleteCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.DeleteCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.DeleteCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.DeleteCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002") + testURLParseError(t, err) +} + +func TestEnterpriseService_AddResourcesToCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := CostCenterResourceRequest{ + Users: []string{"monalisa"}, + } + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002/resource", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, req) + fmt.Fprint(w, `{ + "message": "Resources successfully added to the cost center.", + "reassigned_resources": [ + { + "resource_type": "user", + "name": "monalisa", + "previous_cost_center": "old-cost-center" + }, + { + "resource_type": "organization", + "name": "octo-org", + "previous_cost_center": "another-cost-center" + }, + { + "resource_type": "repository", + "name": "octo-repo", + "previous_cost_center": "yet-another-cost-center" + } + ] + }`) + }) + + ctx := t.Context() + result, _, err := client.Enterprise.AddResourcesToCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if err != nil { + t.Errorf("Enterprise.AddResourcesToCostCenter returned error: %v", err) + } + + want := &AddResourcesToCostCenterResponse{ + Message: Ptr("Resources successfully added to the cost center."), + ReassignedResources: []*ReassignedResource{ + { + ResourceType: Ptr("user"), + Name: Ptr("monalisa"), + PreviousCostCenter: Ptr("old-cost-center"), + }, + { + ResourceType: Ptr("organization"), + Name: Ptr("octo-org"), + PreviousCostCenter: Ptr("another-cost-center"), + }, + { + ResourceType: Ptr("repository"), + Name: Ptr("octo-repo"), + PreviousCostCenter: Ptr("yet-another-cost-center"), + }, + }, + } + if !cmp.Equal(result, want) { + t.Errorf("Enterprise.AddResourcesToCostCenter returned %+v, want %+v", result, want) + } + + const methodName = "AddResourcesToCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.AddResourcesToCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.AddResourcesToCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_AddResourcesToCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.AddResourcesToCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002", CostCenterResourceRequest{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_RemoveResourcesFromCostCenter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := CostCenterResourceRequest{ + Users: []string{"monalisa"}, + } + + mux.HandleFunc("/enterprises/e/settings/billing/cost-centers/2eeb8ffe-6903-11ee-8c99-0242ac120002/resource", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, req) + fmt.Fprint(w, `{ + "message": "Resources successfully removed from the cost center." + }`) + }) + + ctx := t.Context() + result, _, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if err != nil { + t.Errorf("Enterprise.RemoveResourcesFromCostCenter returned error: %v", err) + } + + want := &RemoveResourcesFromCostCenterResponse{ + Message: Ptr("Resources successfully removed from the cost center."), + } + if !cmp.Equal(result, want) { + t.Errorf("Enterprise.RemoveResourcesFromCostCenter returned %+v, want %+v", result, want) + } + + const methodName = "RemoveResourcesFromCostCenter" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.RemoveResourcesFromCostCenter(ctx, "\n", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, "e", "2eeb8ffe-6903-11ee-8c99-0242ac120002", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveResourcesFromCostCenter_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.RemoveResourcesFromCostCenter(ctx, "%", "2eeb8ffe-6903-11ee-8c99-0242ac120002", CostCenterResourceRequest{}) + testURLParseError(t, err) +} diff --git a/github/enterprise_billing_test.go b/github/enterprise_billing_test.go new file mode 100644 index 00000000000..f180d90e7f9 --- /dev/null +++ b/github/enterprise_billing_test.go @@ -0,0 +1,492 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2023", + "month": "8", + "cost_center_id": "cc-123", + }) + fmt.Fprint(w, `{ + "usageItems": [ + { + "date": "2023-08-01", + "product": "Actions", + "sku": "Actions Linux", + "quantity": 100, + "unitType": "minutes", + "pricePerUnit": 0.008, + "grossAmount": 0.8, + "discountAmount": 0, + "netAmount": 0.8, + "organizationName": "GitHub", + "repositoryName": "github/example" + } + ] + }`) + }) + + ctx := t.Context() + opts := &EnterpriseUsageReportOptions{ + Year: 2023, + Month: 8, + CostCenterID: "cc-123", + } + report, resp, err := client.Enterprise.GetUsageReport(ctx, "test-enterprise", opts) + if err != nil { + t.Errorf("GetUsageReport returned error: %v", err) + } + + if resp == nil { + t.Error("GetUsageReport returned nil response") + } + + want := &EnterpriseUsageReport{ + UsageItems: []*EnterpriseUsageItem{ + { + Date: "2023-08-01", + Product: "Actions", + SKU: "Actions Linux", + Quantity: 100.0, + UnitType: "minutes", + PricePerUnit: 0.008, + GrossAmount: 0.8, + DiscountAmount: 0.0, + NetAmount: 0.8, + OrganizationName: "GitHub", + RepositoryName: Ptr("github/example"), + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("GetUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetUsageReport(ctx, "test-enterprise", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetUsageReport_InvalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetUsageReport_APIError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusForbidden) + fmt.Fprint(w, `{"message": "Forbidden"}`) + }) + + ctx := t.Context() + _, resp, err := client.Enterprise.GetUsageReport(ctx, "test-enterprise", nil) + if err == nil { + t.Error("GetUsageReport should return error on 403 response") + } + if resp == nil { + t.Error("GetUsageReport should return response even on error") + } +} + +func TestEnterpriseService_GetUsageSummary(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/usage/summary", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "product": "Actions", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025 + }, + "enterprise": "GitHub", + "usageItems": [ + { + "product": "Actions", + "sku": "actions_linux", + "unitType": "minutes", + "pricePerUnit": 0.008, + "grossQuantity": 1000, + "grossAmount": 8, + "discountQuantity": 0, + "discountAmount": 0, + "netQuantity": 1000, + "netAmount": 8 + } + ] + }`) + }) + + ctx := t.Context() + opts := &EnterpriseUsageSummaryOptions{ + EnterpriseUsageReportOptions: EnterpriseUsageReportOptions{Year: 2025}, + Product: "Actions", + } + report, resp, err := client.Enterprise.GetUsageSummary(ctx, "test-enterprise", opts) + if err != nil { + t.Errorf("GetUsageSummary returned error: %v", err) + } + + if resp == nil { + t.Error("GetUsageSummary returned nil response") + } + + want := &EnterpriseUsageSummaryReport{ + TimePeriod: EnterpriseUsageTimePeriod{ + Year: 2025, + }, + Enterprise: "GitHub", + UsageItems: []*EnterpriseUsageSummaryItem{ + { + Product: "Actions", + SKU: "actions_linux", + UnitType: "minutes", + PricePerUnit: 0.008, + GrossQuantity: 1000.0, + GrossAmount: 8.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 1000.0, + NetAmount: 8.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("GetUsageSummary returned %+v, want %+v", report, want) + } + + const methodName = "GetUsageSummary" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetUsageSummary(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetUsageSummary(ctx, "test-enterprise", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetUsageSummary_InvalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetUsageSummary(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetUsageSummary_APIError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/usage/summary", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + fmt.Fprint(w, `{"message": "Not Found"}`) + }) + + ctx := t.Context() + _, resp, err := client.Enterprise.GetUsageSummary(ctx, "test-enterprise", nil) + if err == nil { + t.Error("GetUsageSummary should return error on 404 response") + } + if resp == nil { + t.Error("GetUsageSummary should return response even on error") + } +} + +func TestEnterpriseService_GetPremiumRequestUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "month": "10", + "user": "testuser", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 10 + }, + "enterprise": "GitHub", + "organization": "GitHub", + "user": "testuser", + "product": "Copilot", + "model": "GPT-5", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot Premium Request", + "model": "GPT-5", + "unitType": "requests", + "pricePerUnit": 0.04, + "grossQuantity": 100, + "grossAmount": 4.0, + "discountQuantity": 0, + "discountAmount": 0.0, + "netQuantity": 100, + "netAmount": 4.0 + } + ] + }`) + }) + + ctx := t.Context() + opts := &EnterprisePremiumRequestUsageReportOptions{ + EnterpriseUsageReportOptions: EnterpriseUsageReportOptions{Year: 2025, Month: 10}, + User: "testuser", + } + report, resp, err := client.Enterprise.GetPremiumRequestUsageReport(ctx, "test-enterprise", opts) + if err != nil { + t.Errorf("GetPremiumRequestUsageReport returned error: %v", err) + } + + if resp == nil { + t.Error("GetPremiumRequestUsageReport returned nil response") + } + + want := &EnterpriseAggregatedUsageReport{ + TimePeriod: EnterpriseUsageTimePeriod{ + Year: 2025, + Month: Ptr(10), + }, + Enterprise: "GitHub", + Organization: Ptr("GitHub"), + User: Ptr("testuser"), + Product: Ptr("Copilot"), + Model: Ptr("GPT-5"), + UsageItems: []*EnterpriseAggregatedUsageItem{ + { + Product: "Copilot", + SKU: "Copilot Premium Request", + Model: "GPT-5", + UnitType: "requests", + PricePerUnit: 0.04, + GrossQuantity: 100.0, + GrossAmount: 4.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 100.0, + NetAmount: 4.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("GetPremiumRequestUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetPremiumRequestUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetPremiumRequestUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetPremiumRequestUsageReport(ctx, "test-enterprise", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetPremiumRequestUsageReport_InvalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetPremiumRequestUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetPremiumRequestUsageReport_APIError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/premium_request/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusUnauthorized) + fmt.Fprint(w, `{"message": "Unauthorized"}`) + }) + + ctx := t.Context() + _, resp, err := client.Enterprise.GetPremiumRequestUsageReport(ctx, "test-enterprise", nil) + if err == nil { + t.Error("GetPremiumRequestUsageReport should return error on 401 response") + } + if resp == nil { + t.Error("GetPremiumRequestUsageReport should return response even on error") + } +} + +func TestEnterpriseService_GetAICreditUsage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2025", + "month": "6", + "user": "testuser", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2025, + "month": 6 + }, + "enterprise": "GitHub", + "organization": "GitHub", + "user": "testuser", + "product": "Copilot", + "model": "GPT-5", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "GPT-5", + "unitType": "credits", + "pricePerUnit": 0.01, + "grossQuantity": 100, + "grossAmount": 1.0, + "discountQuantity": 0, + "discountAmount": 0.0, + "netQuantity": 100, + "netAmount": 1.0 + } + ] + }`) + }) + + ctx := t.Context() + opts := &EnterprisePremiumRequestUsageReportOptions{ + EnterpriseUsageReportOptions: EnterpriseUsageReportOptions{Year: 2025, Month: 6}, + User: "testuser", + } + report, resp, err := client.Enterprise.GetAICreditUsage(ctx, "test-enterprise", opts) + if err != nil { + t.Errorf("GetAICreditUsage returned error: %v", err) + } + + if resp == nil { + t.Error("GetAICreditUsage returned nil response") + } + + want := &EnterpriseAggregatedUsageReport{ + TimePeriod: EnterpriseUsageTimePeriod{ + Year: 2025, + Month: Ptr(6), + }, + Enterprise: "GitHub", + Organization: Ptr("GitHub"), + User: Ptr("testuser"), + Product: Ptr("Copilot"), + Model: Ptr("GPT-5"), + UsageItems: []*EnterpriseAggregatedUsageItem{ + { + Product: "Copilot", + SKU: "Copilot AI Credits", + Model: "GPT-5", + UnitType: "credits", + PricePerUnit: 0.01, + GrossQuantity: 100.0, + GrossAmount: 1.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 100.0, + NetAmount: 1.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("GetAICreditUsage returned %+v, want %+v", report, want) + } + + const methodName = "GetAICreditUsage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetAICreditUsage(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetAICreditUsage(ctx, "test-enterprise", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetAICreditUsage_InvalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetAICreditUsage(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetAICreditUsage_APIError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/test-enterprise/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, `{"message": "Bad Request"}`) + }) + + ctx := t.Context() + _, resp, err := client.Enterprise.GetAICreditUsage(ctx, "test-enterprise", nil) + if err == nil { + t.Error("GetAICreditUsage should return error on 400 response") + } + if resp == nil { + t.Error("GetAICreditUsage should return response even on error") + } +} diff --git a/github/enterprise_budgets.go b/github/enterprise_budgets.go new file mode 100644 index 00000000000..c0a783d8609 --- /dev/null +++ b/github/enterprise_budgets.go @@ -0,0 +1,198 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// BudgetScope constants represent the scope of the budget. +const ( + BudgetScopeEnterprise = "enterprise" + BudgetScopeOrganization = "organization" + BudgetScopeRepository = "repository" + BudgetScopeCostCenter = "cost_center" +) + +// BudgetType constants represent the type of pricing for the budget. +const ( + BudgetTypeProductPricing = "ProductPricing" + BudgetTypeSkuPricing = "SkuPricing" +) + +// EnterpriseBudgetAlerting represents alerting settings for a GitHub enterprise budget. +type EnterpriseBudgetAlerting struct { + WillAlert *bool `json:"will_alert,omitempty"` + AlertRecipients []string `json:"alert_recipients,omitempty"` +} + +// EnterpriseBudget represents a GitHub enterprise budget. +type EnterpriseBudget struct { + ID *string `json:"id,omitempty"` + BudgetType *string `json:"budget_type,omitempty"` + BudgetProductSKU *string `json:"budget_product_sku,omitempty"` + BudgetScope *string `json:"budget_scope,omitempty"` + BudgetEntityName *string `json:"budget_entity_name,omitempty"` + BudgetAmount *int `json:"budget_amount,omitempty"` + PreventFurtherUsage *bool `json:"prevent_further_usage,omitempty"` + BudgetAlerting *EnterpriseBudgetAlerting `json:"budget_alerting,omitempty"` +} + +func (b EnterpriseBudget) String() string { + return Stringify(b) +} + +// EnterpriseListBudgets represents a collection of GitHub enterprise budgets. +type EnterpriseListBudgets struct { + Budgets []*EnterpriseBudget `json:"budgets"` + HasNextPage *bool `json:"has_next_page,omitempty"` + TotalCount *int `json:"total_count,omitempty"` +} + +// EnterpriseCreateBudget represents the payload to create a GitHub enterprise budget. +type EnterpriseCreateBudget struct { + BudgetAmount int `json:"budget_amount"` + PreventFurtherUsage bool `json:"prevent_further_usage"` + BudgetAlerting *EnterpriseBudgetAlerting `json:"budget_alerting"` + BudgetScope string `json:"budget_scope"` + BudgetEntityName *string `json:"budget_entity_name,omitempty"` + BudgetType string `json:"budget_type"` + BudgetProductSKU *string `json:"budget_product_sku,omitempty"` +} + +// EnterpriseUpdateBudget represents the payload to update a GitHub enterprise budget. +type EnterpriseUpdateBudget struct { + BudgetAmount *int `json:"budget_amount,omitempty"` + PreventFurtherUsage *bool `json:"prevent_further_usage,omitempty"` + BudgetAlerting *EnterpriseBudgetAlerting `json:"budget_alerting,omitempty"` + BudgetScope *string `json:"budget_scope,omitempty"` + BudgetEntityName *string `json:"budget_entity_name,omitempty"` + BudgetType *string `json:"budget_type,omitempty"` + BudgetProductSKU *string `json:"budget_product_sku,omitempty"` +} + +// EnterpriseCreateOrUpdateBudgetResponse represents the response when creating or updating a budget. +type EnterpriseCreateOrUpdateBudgetResponse struct { + Message string `json:"message"` + Budget *EnterpriseBudget `json:"budget"` +} + +// EnterpriseDeleteBudgetResponse represents the response when deleting a budget. +type EnterpriseDeleteBudgetResponse struct { + Message string `json:"message"` + ID string `json:"id"` +} + +// ListBudgets gets all budgets for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets?apiVersion=2022-11-28#get-all-budgets +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/budgets +func (s *EnterpriseService) ListBudgets(ctx context.Context, enterprise string) (*EnterpriseListBudgets, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/budgets", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var budgets *EnterpriseListBudgets + resp, err := s.client.Do(req, &budgets) + if err != nil { + return nil, resp, err + } + + return budgets, resp, nil +} + +// CreateBudget creates a new budget for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets?apiVersion=2022-11-28#create-a-budget +// +//meta:operation POST /enterprises/{enterprise}/settings/billing/budgets +func (s *EnterpriseService) CreateBudget(ctx context.Context, enterprise string, body EnterpriseCreateBudget) (*EnterpriseCreateOrUpdateBudgetResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/budgets", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var createBudgetResponse *EnterpriseCreateOrUpdateBudgetResponse + resp, err := s.client.Do(req, &createBudgetResponse) + if err != nil { + return nil, resp, err + } + + return createBudgetResponse, resp, nil +} + +// GetBudget gets a budget by ID for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets?apiVersion=2022-11-28#get-a-budget-by-id +// +//meta:operation GET /enterprises/{enterprise}/settings/billing/budgets/{budget_id} +func (s *EnterpriseService) GetBudget(ctx context.Context, enterprise, budgetID string) (*EnterpriseBudget, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/budgets/%v", enterprise, budgetID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var budget *EnterpriseBudget + resp, err := s.client.Do(req, &budget) + if err != nil { + return nil, resp, err + } + + return budget, resp, nil +} + +// UpdateBudget updates an existing budget for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets?apiVersion=2022-11-28#update-a-budget +// +//meta:operation PATCH /enterprises/{enterprise}/settings/billing/budgets/{budget_id} +func (s *EnterpriseService) UpdateBudget(ctx context.Context, enterprise, budgetID string, body EnterpriseUpdateBudget) (*EnterpriseCreateOrUpdateBudgetResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/budgets/%v", enterprise, budgetID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var updateBudgetResponse *EnterpriseCreateOrUpdateBudgetResponse + resp, err := s.client.Do(req, &updateBudgetResponse) + if err != nil { + return nil, resp, err + } + + return updateBudgetResponse, resp, nil +} + +// DeleteBudget deletes a budget by ID for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets?apiVersion=2022-11-28#delete-a-budget +// +//meta:operation DELETE /enterprises/{enterprise}/settings/billing/budgets/{budget_id} +func (s *EnterpriseService) DeleteBudget(ctx context.Context, enterprise, budgetID string) (*EnterpriseDeleteBudgetResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/settings/billing/budgets/%v", enterprise, budgetID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + var deleteBudgetResponse *EnterpriseDeleteBudgetResponse + resp, err := s.client.Do(req, &deleteBudgetResponse) + if err != nil { + return nil, resp, err + } + + return deleteBudgetResponse, resp, nil +} diff --git a/github/enterprise_budgets_test.go b/github/enterprise_budgets_test.go new file mode 100644 index 00000000000..d8a5d7af704 --- /dev/null +++ b/github/enterprise_budgets_test.go @@ -0,0 +1,332 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListBudgets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/budgets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "budgets": [ + { + "id": "2066deda-923f-43f9-88d2-62395a28c0cdd", + "budget_type": "ProductPricing", + "budget_product_sku": "actions", + "budget_scope": "enterprise", + "budget_amount": 1000, + "prevent_further_usage": true, + "budget_alerting": { + "will_alert": true, + "alert_recipients": ["enterprise-admin"] + } + } + ], + "has_next_page": true, + "total_count": 1 + }`) + }) + + ctx := t.Context() + budgets, _, err := client.Enterprise.ListBudgets(ctx, "e") + if err != nil { + t.Errorf("Enterprise.ListBudgets returned error: %v", err) + } + + want := &EnterpriseListBudgets{ + Budgets: []*EnterpriseBudget{ + { + ID: Ptr("2066deda-923f-43f9-88d2-62395a28c0cdd"), + BudgetType: Ptr(BudgetTypeProductPricing), + BudgetProductSKU: Ptr("actions"), + BudgetScope: Ptr(BudgetScopeEnterprise), + BudgetAmount: Ptr(1000), + PreventFurtherUsage: Ptr(true), + BudgetAlerting: &EnterpriseBudgetAlerting{ + WillAlert: Ptr(true), + AlertRecipients: []string{"enterprise-admin"}, + }, + }, + }, + HasNextPage: Ptr(true), + TotalCount: Ptr(1), + } + if !cmp.Equal(budgets, want) { + t.Errorf("Enterprise.ListBudgets returned %+v, want %+v", budgets, want) + } + + const methodName = "ListBudgets" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListBudgets(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListBudgets(ctx, "\n") + return err + }) +} + +func TestEnterpriseService_ListBudgets_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.ListBudgets(ctx, "%") + testURLParseError(t, err) +} + +func TestEnterpriseService_CreateBudget(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := EnterpriseCreateBudget{ + BudgetAmount: 200, + PreventFurtherUsage: true, + BudgetScope: BudgetScopeEnterprise, + BudgetType: BudgetTypeProductPricing, + BudgetProductSKU: Ptr("actions"), + BudgetAlerting: &EnterpriseBudgetAlerting{}, + } + + mux.HandleFunc("/enterprises/e/settings/billing/budgets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, req) + fmt.Fprint(w, `{ + "message": "Budget successfully created.", + "budget": { + "id": "b-123", + "budget_amount": 200, + "prevent_further_usage": true + } + }`) + }) + + ctx := t.Context() + resp, _, err := client.Enterprise.CreateBudget(ctx, "e", req) + if err != nil { + t.Errorf("Enterprise.CreateBudget returned error: %v", err) + } + + want := &EnterpriseCreateOrUpdateBudgetResponse{ + Message: "Budget successfully created.", + Budget: &EnterpriseBudget{ + ID: Ptr("b-123"), + BudgetAmount: Ptr(200), + PreventFurtherUsage: Ptr(true), + }, + } + if !cmp.Equal(resp, want) { + t.Errorf("Enterprise.CreateBudget returned %+v, want %+v", resp, want) + } + + const methodName = "CreateBudget" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateBudget(ctx, "e", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateBudget(ctx, "\n", req) + return err + }) +} + +func TestEnterpriseService_CreateBudget_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.CreateBudget(ctx, "%", EnterpriseCreateBudget{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_GetBudget(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/budgets/2066deda-923f-43f9-88d2-62395a28c0cdd", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "2066deda-923f-43f9-88d2-62395a28c0cdd", + "budget_type": "ProductPricing", + "budget_product_sku": "actions_linux", + "budget_scope": "repository", + "budget_amount": 0, + "prevent_further_usage": true + }`) + }) + + ctx := t.Context() + budget, _, err := client.Enterprise.GetBudget(ctx, "e", "2066deda-923f-43f9-88d2-62395a28c0cdd") + if err != nil { + t.Errorf("Enterprise.GetBudget returned error: %v", err) + } + + want := &EnterpriseBudget{ + ID: Ptr("2066deda-923f-43f9-88d2-62395a28c0cdd"), + BudgetType: Ptr(BudgetTypeProductPricing), + BudgetProductSKU: Ptr("actions_linux"), + BudgetScope: Ptr(BudgetScopeRepository), + BudgetAmount: Ptr(0), + PreventFurtherUsage: Ptr(true), + } + if !cmp.Equal(budget, want) { + t.Errorf("Enterprise.GetBudget returned %+v, want %+v", budget, want) + } + + const methodName = "GetBudget" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetBudget(ctx, "e", "2066deda-923f-43f9-88d2-62395a28c0cdd") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetBudget(ctx, "\n", "\n") + return err + }) +} + +func TestEnterpriseService_GetBudget_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.GetBudget(ctx, "%", "b-123") + testURLParseError(t, err) +} + +func TestEnterpriseService_UpdateBudget(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + req := EnterpriseUpdateBudget{ + BudgetAmount: Ptr(10), + PreventFurtherUsage: Ptr(false), + } + + mux.HandleFunc("/enterprises/e/settings/billing/budgets/b-123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, req) + fmt.Fprint(w, `{ + "message": "Budget successfully updated.", + "budget": { + "id": "b-123", + "budget_amount": 10, + "prevent_further_usage": false + } + }`) + }) + + ctx := t.Context() + resp, _, err := client.Enterprise.UpdateBudget(ctx, "e", "b-123", req) + if err != nil { + t.Errorf("Enterprise.UpdateBudget returned error: %v", err) + } + + want := &EnterpriseCreateOrUpdateBudgetResponse{ + Message: "Budget successfully updated.", + Budget: &EnterpriseBudget{ + ID: Ptr("b-123"), + BudgetAmount: Ptr(10), + PreventFurtherUsage: Ptr(false), + }, + } + if !cmp.Equal(resp, want) { + t.Errorf("Enterprise.UpdateBudget returned %+v, want %+v", resp, want) + } + + const methodName = "UpdateBudget" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateBudget(ctx, "e", "b-123", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateBudget(ctx, "\n", "\n", req) + return err + }) +} + +func TestEnterpriseService_UpdateBudget_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.UpdateBudget(ctx, "%", "b-123", EnterpriseUpdateBudget{}) + testURLParseError(t, err) +} + +func TestEnterpriseService_DeleteBudget(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/settings/billing/budgets/b-123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{ + "message": "Budget successfully deleted.", + "id": "b-123" + }`) + }) + + ctx := t.Context() + resp, _, err := client.Enterprise.DeleteBudget(ctx, "e", "b-123") + if err != nil { + t.Errorf("Enterprise.DeleteBudget returned error: %v", err) + } + + want := &EnterpriseDeleteBudgetResponse{ + Message: "Budget successfully deleted.", + ID: "b-123", + } + if !cmp.Equal(resp, want) { + t.Errorf("Enterprise.DeleteBudget returned %+v, want %+v", resp, want) + } + + const methodName = "DeleteBudget" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.DeleteBudget(ctx, "e", "b-123") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.DeleteBudget(ctx, "\n", "\n") + return err + }) +} + +func TestEnterpriseService_DeleteBudget_invalidEnterprise(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Enterprise.DeleteBudget(ctx, "%", "b-123") + testURLParseError(t, err) +} diff --git a/github/enterprise_code_security_and_analysis.go b/github/enterprise_code_security_and_analysis.go new file mode 100644 index 00000000000..af6f5122502 --- /dev/null +++ b/github/enterprise_code_security_and_analysis.go @@ -0,0 +1,91 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnterpriseSecurityAnalysisSettings represents security analysis settings for an enterprise. +type EnterpriseSecurityAnalysisSettings struct { + AdvancedSecurityEnabledForNewRepositories *bool `json:"advanced_security_enabled_for_new_repositories,omitempty"` + SecretScanningEnabledForNewRepositories *bool `json:"secret_scanning_enabled_for_new_repositories,omitempty"` + SecretScanningPushProtectionEnabledForNewRepositories *bool `json:"secret_scanning_push_protection_enabled_for_new_repositories,omitempty"` + SecretScanningPushProtectionCustomLink *string `json:"secret_scanning_push_protection_custom_link,omitempty"` + SecretScanningValidityChecksEnabled *bool `json:"secret_scanning_validity_checks_enabled,omitempty"` +} + +// GetCodeSecurityAndAnalysis gets code security and analysis features for an enterprise. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis?apiVersion=2022-11-28#get-code-security-and-analysis-features-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/code_security_and_analysis +func (s *EnterpriseService) GetCodeSecurityAndAnalysis(ctx context.Context, enterprise string) (*EnterpriseSecurityAnalysisSettings, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code_security_and_analysis", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var settings *EnterpriseSecurityAnalysisSettings + resp, err := s.client.Do(req, &settings) + if err != nil { + return nil, resp, err + } + + return settings, resp, nil +} + +// UpdateCodeSecurityAndAnalysis updates code security and analysis features for new repositories in an enterprise. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis?apiVersion=2022-11-28#update-code-security-and-analysis-features-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/code_security_and_analysis +func (s *EnterpriseService) UpdateCodeSecurityAndAnalysis(ctx context.Context, enterprise string, body *EnterpriseSecurityAnalysisSettings) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/code_security_and_analysis", enterprise) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// EnableDisableSecurityFeature enables or disables a security feature for all repositories in an enterprise. +// +// Valid values for securityProduct: "advanced_security", "secret_scanning", "secret_scanning_push_protection". +// Valid values for enablement: "enable_all", "disable_all". +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis?apiVersion=2022-11-28#enable-or-disable-a-security-feature +// +//meta:operation POST /enterprises/{enterprise}/{security_product}/{enablement} +func (s *EnterpriseService) EnableDisableSecurityFeature(ctx context.Context, enterprise, securityProduct, enablement string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/%v/%v", enterprise, securityProduct, enablement) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/enterprise_code_security_and_analysis_test.go b/github/enterprise_code_security_and_analysis_test.go new file mode 100644 index 00000000000..603ce809929 --- /dev/null +++ b/github/enterprise_code_security_and_analysis_test.go @@ -0,0 +1,128 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetCodeSecurityAndAnalysis(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/code_security_and_analysis", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, ` + { + "advanced_security_enabled_for_new_repositories": true, + "secret_scanning_enabled_for_new_repositories": true, + "secret_scanning_push_protection_enabled_for_new_repositories": true, + "secret_scanning_push_protection_custom_link": "https://github.com/test-org/test-repo/blob/main/README.md", + "secret_scanning_validity_checks_enabled": true + }`) + }) + + ctx := t.Context() + + const methodName = "GetCodeSecurityAndAnalysis" + + settings, _, err := client.Enterprise.GetCodeSecurityAndAnalysis(ctx, "e") + if err != nil { + t.Errorf("Enterprise.%v returned error: %v", methodName, err) + } + want := &EnterpriseSecurityAnalysisSettings{ + AdvancedSecurityEnabledForNewRepositories: Ptr(true), + SecretScanningEnabledForNewRepositories: Ptr(true), + SecretScanningPushProtectionEnabledForNewRepositories: Ptr(true), + SecretScanningPushProtectionCustomLink: Ptr("https://github.com/test-org/test-repo/blob/main/README.md"), + SecretScanningValidityChecksEnabled: Ptr(true), + } + + if !cmp.Equal(settings, want) { + t.Errorf("Enterprise.%v return \ngot: %+v,\nwant:%+v", methodName, settings, want) + } + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetCodeSecurityAndAnalysis(ctx, "o") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetCodeSecurityAndAnalysis(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateCodeSecurityAndAnalysis(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &EnterpriseSecurityAnalysisSettings{ + AdvancedSecurityEnabledForNewRepositories: Ptr(true), + SecretScanningEnabledForNewRepositories: Ptr(true), + SecretScanningPushProtectionEnabledForNewRepositories: Ptr(true), + SecretScanningPushProtectionCustomLink: Ptr("https://github.com/test-org/test-repo/blob/main/README.md"), + SecretScanningValidityChecksEnabled: Ptr(true), + } + + mux.HandleFunc("/enterprises/e/code_security_and_analysis", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + + const methodName = "UpdateCodeSecurityAndAnalysis" + + _, err := client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, "e", input) + if err != nil { + t.Errorf("Enterprise.%v returned error: %v", methodName, err) + } + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, "o", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, "e", input) + }) +} + +func TestEnterpriseService_EnableAdvancedSecurity(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/advanced_security/enable_all", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + + const methodName = "EnableDisableSecurityFeature" + + _, err := client.Enterprise.EnableDisableSecurityFeature(ctx, "e", "advanced_security", "enable_all") + if err != nil { + t.Errorf("Enterprise.%v returned error: %v", methodName, err) + } + + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.EnableDisableSecurityFeature(ctx, "o", "advanced_security", "enable_all") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.EnableDisableSecurityFeature(ctx, "e", "advanced_security", "enable_all") + }) +} diff --git a/github/enterprise_codesecurity_configurations.go b/github/enterprise_codesecurity_configurations.go new file mode 100644 index 00000000000..85619daad42 --- /dev/null +++ b/github/enterprise_codesecurity_configurations.go @@ -0,0 +1,232 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" +) + +// ListEnterpriseCodeSecurityConfigurationOptions specifies optional parameters to get security configurations for enterprises. +// +// Note: Pagination is powered by before/after cursor-style pagination. After the initial call, +// inspect the returned *Response. Use resp.After as the opts.After value to request +// the next page, and resp.Before as the opts.Before value to request the previous +// page. Set either Before or After for a request; if both are +// supplied GitHub API will return an error. PerPage controls the number of items +// per page (max 100 per GitHub API docs). +type ListEnterpriseCodeSecurityConfigurationOptions struct { + // A cursor, as given in the Link header. If specified, the query only searches for security configurations before this cursor. + Before string `url:"before,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for security configurations after this cursor. + After string `url:"after,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty"` +} + +// ListCodeSecurityConfigurations lists all code security configurations available in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-code-security-configurations-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/code-security/configurations +func (s *EnterpriseService) ListCodeSecurityConfigurations(ctx context.Context, enterprise string, opts *ListEnterpriseCodeSecurityConfigurationOptions) ([]*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configurations []*CodeSecurityConfiguration + resp, err := s.client.Do(req, &configurations) + if err != nil { + return nil, resp, err + } + return configurations, resp, nil +} + +// CreateCodeSecurityConfiguration creates a code security configuration in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#create-a-code-security-configuration-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/code-security/configurations +func (s *EnterpriseService) CreateCodeSecurityConfiguration(ctx context.Context, enterprise string, body CodeSecurityConfiguration) (*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var configuration *CodeSecurityConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// ListDefaultCodeSecurityConfigurations lists the default code security configurations for an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-default-code-security-configurations-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/code-security/configurations/defaults +func (s *EnterpriseService) ListDefaultCodeSecurityConfigurations(ctx context.Context, enterprise string) ([]*CodeSecurityConfigurationWithDefaultForNewRepos, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/defaults", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configurations []*CodeSecurityConfigurationWithDefaultForNewRepos + resp, err := s.client.Do(req, &configurations) + if err != nil { + return nil, resp, err + } + return configurations, resp, nil +} + +// GetCodeSecurityConfiguration gets a code security configuration available in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#retrieve-a-code-security-configuration-of-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/code-security/configurations/{configuration_id} +func (s *EnterpriseService) GetCodeSecurityConfiguration(ctx context.Context, enterprise string, configurationID int64) (*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/%v", enterprise, configurationID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configuration *CodeSecurityConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// UpdateCodeSecurityConfiguration updates a code security configuration in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#update-a-custom-code-security-configuration-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/code-security/configurations/{configuration_id} +func (s *EnterpriseService) UpdateCodeSecurityConfiguration(ctx context.Context, enterprise string, configurationID int64, body CodeSecurityConfiguration) (*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/%v", enterprise, configurationID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var configuration *CodeSecurityConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// DeleteCodeSecurityConfiguration deletes a code security configuration from an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#delete-a-code-security-configuration-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/code-security/configurations/{configuration_id} +func (s *EnterpriseService) DeleteCodeSecurityConfiguration(ctx context.Context, enterprise string, configurationID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/%v", enterprise, configurationID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// AttachCodeSecurityConfigurationToRepositories attaches an enterprise code security configuration to repositories. +// `scope` is the type of repositories to attach the configuration to. +// Can be one of: `all`, `all_without_configurations`. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#attach-an-enterprise-configuration-to-repositories +// +//meta:operation POST /enterprises/{enterprise}/code-security/configurations/{configuration_id}/attach +func (s *EnterpriseService) AttachCodeSecurityConfigurationToRepositories(ctx context.Context, enterprise string, configurationID int64, scope string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/%v/attach", enterprise, configurationID) + type scopeType struct { + Scope string `json:"scope"` + } + + req, err := s.client.NewRequest(ctx, "POST", u, scopeType{Scope: scope}) + if err != nil { + return nil, err + } + resp, err := s.client.Do(req, nil) + if err != nil && resp.StatusCode != http.StatusAccepted { // StatusAccepted(202) is the expected status code as job is queued for processing + return resp, err + } + return resp, nil +} + +// SetDefaultCodeSecurityConfiguration sets a code security configuration as a default for an enterprise. +// `defaultForNewRepos` specifies which types of repository this security configuration should be applied to by default. +// Can be one of: `all`, `none`, `private_and_internal`, `public`. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#set-a-code-security-configuration-as-a-default-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/code-security/configurations/{configuration_id}/defaults +func (s *EnterpriseService) SetDefaultCodeSecurityConfiguration(ctx context.Context, enterprise string, configurationID int64, defaultForNewRepos string) (*CodeSecurityConfigurationWithDefaultForNewRepos, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/%v/defaults", enterprise, configurationID) + type configParam struct { + DefaultForNewRepos string `json:"default_for_new_repos"` + } + + req, err := s.client.NewRequest(ctx, "PUT", u, configParam{DefaultForNewRepos: defaultForNewRepos}) + if err != nil { + return nil, nil, err + } + var config *CodeSecurityConfigurationWithDefaultForNewRepos + resp, err := s.client.Do(req, &config) + if err != nil { + return nil, resp, err + } + return config, resp, nil +} + +// ListCodeSecurityConfigurationRepositories lists the repositories associated with an enterprise code security configuration. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-repositories-associated-with-an-enterprise-code-security-configuration +// +//meta:operation GET /enterprises/{enterprise}/code-security/configurations/{configuration_id}/repositories +func (s *EnterpriseService) ListCodeSecurityConfigurationRepositories(ctx context.Context, enterprise string, configurationID int64, opts *ListCodeSecurityConfigurationRepositoriesOptions) ([]*RepositoryAttachment, *Response, error) { + u := fmt.Sprintf("enterprises/%v/code-security/configurations/%v/repositories", enterprise, configurationID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + var attachments []*RepositoryAttachment + resp, err := s.client.Do(req, &attachments) + if err != nil { + return nil, resp, err + } + return attachments, resp, nil +} diff --git a/github/enterprise_codesecurity_configurations_test.go b/github/enterprise_codesecurity_configurations_test.go new file mode 100644 index 00000000000..2d53cf5a195 --- /dev/null +++ b/github/enterprise_codesecurity_configurations_test.go @@ -0,0 +1,430 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListCodeSecurityConfigurations(t *testing.T) { + t.Parallel() + opts := &ListEnterpriseCodeSecurityConfigurationOptions{Before: "1", After: "2", PerPage: 30} + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/code-security/configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"before": "1", "after": "2", "per_page": "30"}) + fmt.Fprint(w, `[ + { + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }, + { + "id":2, + "name":"config2", + "description":"desc2", + "private_vulnerability_reporting": "enabled" + }]`) + }) + + configurations, _, err := client.Enterprise.ListCodeSecurityConfigurations(ctx, "e", opts) + if err != nil { + t.Errorf("Enterprise.ListCodeSecurityConfigurations returned error: %v", err) + } + + want := []*CodeSecurityConfiguration{ + {ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")}, + {ID: Ptr(int64(2)), Name: "config2", Description: "desc2", PrivateVulnerabilityReporting: Ptr("enabled")}, + } + if !cmp.Equal(configurations, want) { + t.Errorf("Enterprise.ListCodeSecurityConfigurations returned %+v, want %+v", configurations, want) + } + const methodName = "ListCodeSecurityConfigurations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListCodeSecurityConfigurations(ctx, "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListCodeSecurityConfigurations(ctx, "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + mux.HandleFunc("/enterprises/e/code-security/configurations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }`) + }) + + configuration, _, err := client.Enterprise.GetCodeSecurityConfiguration(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.GetCodeSecurityConfiguration returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + if !cmp.Equal(configuration, want) { + t.Errorf("Enterprise.GetCodeSecurityConfiguration returned %+v, want %+v", configuration, want) + } + + const methodName = "GetCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetCodeSecurityConfiguration(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetCodeSecurityConfiguration(ctx, "e", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + input := CodeSecurityConfiguration{ + Name: "config1", + Description: "desc1", + CodeScanningDefaultSetup: Ptr("enabled"), + } + + mux.HandleFunc("/enterprises/e/code-security/configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }`) + }) + + configuration, _, err := client.Enterprise.CreateCodeSecurityConfiguration(ctx, "e", input) + if err != nil { + t.Errorf("Enterprise.CreateCodeSecurityConfiguration returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + if !cmp.Equal(configuration, want) { + t.Errorf("Enterprise.CreateCodeSecurityConfiguration returned %+v, want %+v", configuration, want) + } + + const methodName = "CreateCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.CreateCodeSecurityConfiguration(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateCodeSecurityConfiguration(ctx, "e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListDefaultCodeSecurityConfigurations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + mux.HandleFunc("/enterprises/e/code-security/configurations/defaults", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "default_for_new_repos": "public", + "configuration": { + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + } + }, + { + "default_for_new_repos": "private_and_internal", + "configuration": { + "id":2, + "name":"config2", + "description":"desc2", + "private_vulnerability_reporting": "enabled" + } + } + ]`) + }) + + configurations, _, err := client.Enterprise.ListDefaultCodeSecurityConfigurations(ctx, "e") + if err != nil { + t.Errorf("Enterprise.ListDefaultCodeSecurityConfigurations returned error: %v", err) + } + + want := []*CodeSecurityConfigurationWithDefaultForNewRepos{ + {DefaultForNewRepos: Ptr("public"), Configuration: &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")}}, + {DefaultForNewRepos: Ptr("private_and_internal"), Configuration: &CodeSecurityConfiguration{ID: Ptr(int64(2)), Name: "config2", Description: "desc2", PrivateVulnerabilityReporting: Ptr("enabled")}}, + } + if !cmp.Equal(configurations, want) { + t.Errorf("Enterprise.ListDefaultCodeSecurityConfigurations returned %+v, want %+v", configurations, want) + } + + const methodName = "ListDefaultCodeSecurityConfigurations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListDefaultCodeSecurityConfigurations(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListDefaultCodeSecurityConfigurations(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + input := CodeSecurityConfiguration{ + Name: "config1", + Description: "desc1", + CodeScanningDefaultSetup: Ptr("enabled"), + } + + mux.HandleFunc("/enterprises/e/code-security/configurations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }`) + }) + + configuration, _, err := client.Enterprise.UpdateCodeSecurityConfiguration(ctx, "e", 1, input) + if err != nil { + t.Errorf("Enterprise.UpdateCodeSecurityConfiguration returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + if !cmp.Equal(configuration, want) { + t.Errorf("Enterprise.UpdateCodeSecurityConfiguration returned %+v, want %+v", configuration, want) + } + + const methodName = "UpdateCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateCodeSecurityConfiguration(ctx, "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateCodeSecurityConfiguration(ctx, "e", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/code-security/configurations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + resp, err := client.Enterprise.DeleteCodeSecurityConfiguration(ctx, "e", 1) + if err != nil { + t.Errorf("Enterprise.DeleteCodeSecurityConfiguration returned error: %v", err) + } + + want := http.StatusNoContent + if resp.StatusCode != want { + t.Errorf("Enterprise.DeleteCodeSecurityConfiguration returned status %v, want %v", resp.StatusCode, want) + } + + const methodName = "DeleteCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteCodeSecurityConfiguration(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Enterprise.DeleteCodeSecurityConfiguration(ctx, "e", 1) + return resp, err + }) +} + +func TestEnterpriseService_AttachCodeSecurityConfigurationToRepositories(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + input := struct { + Scope string `json:"scope"` + }{ + Scope: "all_without_configurations", + } + mux.HandleFunc("/enterprises/e/code-security/configurations/1/attach", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusAccepted) + }) + + resp, err := client.Enterprise.AttachCodeSecurityConfigurationToRepositories(ctx, "e", int64(1), input.Scope) + if err != nil { + t.Errorf("Enterprise.AttachCodeSecurityConfigurationToRepositories returned error: %v", err) + } + + want := http.StatusAccepted + if resp.StatusCode != want { + t.Errorf("Enterprise.AttachCodeSecurityConfigurationToRepositories returned status %v, want %v", resp.StatusCode, want) + } + + const methodName = "AttachCodeSecurityConfigurationToRepositories" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.AttachCodeSecurityConfigurationToRepositories(ctx, "\n", -1, "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Enterprise.AttachCodeSecurityConfigurationToRepositories(ctx, "e", 1, input.Scope) + return resp, err + }) +} + +func TestEnterpriseService_SetDefaultCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/code-security/configurations/1/defaults", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, ` + { + "default_for_new_repos": "all", + "configuration": + { + "id": 1, + "name": "config1", + "description": "desc1", + "code_scanning_default_setup": "enabled" + } + }`) + }) + got, resp, err := client.Enterprise.SetDefaultCodeSecurityConfiguration(ctx, "e", 1, "all") + if err != nil { + t.Errorf("Enterprise.SetDefaultCodeSecurityConfiguration returned error: %v", err) + } + wantStatus := http.StatusOK + if resp.StatusCode != wantStatus { + t.Errorf("Enterprise.SetDefaultCodeSecurityConfiguration returned status %v, want %v", resp.StatusCode, wantStatus) + } + want := &CodeSecurityConfigurationWithDefaultForNewRepos{ + DefaultForNewRepos: Ptr("all"), + Configuration: &CodeSecurityConfiguration{ + ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled"), + }, + } + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.SetDefaultCodeSecurityConfiguration returned %+v, want %+v", got, want) + } + + const methodName = "SetDefaultCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.SetDefaultCodeSecurityConfiguration(ctx, "\n", -1, "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.SetDefaultCodeSecurityConfiguration(ctx, "e", 1, "all") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListCodeSecurityConfigurationRepositories(t *testing.T) { + t.Parallel() + opts := &ListCodeSecurityConfigurationRepositoriesOptions{Before: "1", After: "2", PerPage: 30, Status: "attached"} + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/code-security/configurations/1/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"before": "1", "after": "2", "per_page": "30", "status": "attached"}) + fmt.Fprint(w, `[ + { + "status": "attached", + "repository": { + "id":8, + "name":"repo8" + } + }, + { + "status": "attached", + "repository": { + "id":42, + "name":"repo42" + } + } + ]`) + }) + + attachments, _, err := client.Enterprise.ListCodeSecurityConfigurationRepositories(ctx, "e", 1, opts) + if err != nil { + t.Errorf("Enterprise.ListCodeSecurityConfigurationRepositories returned error: %v", err) + } + want := []*RepositoryAttachment{ + {Status: Ptr("attached"), Repository: &Repository{ID: Ptr(int64(8)), Name: Ptr("repo8")}}, + {Status: Ptr("attached"), Repository: &Repository{ID: Ptr(int64(42)), Name: Ptr("repo42")}}, + } + if !cmp.Equal(attachments, want) { + t.Errorf("Enterprise.ListCodeSecurityConfigurationRepositories returned %+v, want %+v", attachments, want) + } + + const methodName = "ListCodeSecurityConfigurationRepositories" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListCodeSecurityConfigurationRepositories(ctx, "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListCodeSecurityConfigurationRepositories(ctx, "e", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_licenses.go b/github/enterprise_licenses.go new file mode 100644 index 00000000000..73da6c5e66f --- /dev/null +++ b/github/enterprise_licenses.go @@ -0,0 +1,138 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnterpriseConsumedLicenses represents information about users with consumed enterprise licenses. +type EnterpriseConsumedLicenses struct { + TotalSeatsConsumed int `json:"total_seats_consumed"` + TotalSeatsPurchased int `json:"total_seats_purchased"` + Users []*EnterpriseLicensedUsers `json:"users,omitempty"` +} + +// EnterpriseLicensedUsers represents a user with license information in an enterprise. +type EnterpriseLicensedUsers struct { + GithubComLogin string `json:"github_com_login"` + GithubComName *string `json:"github_com_name"` + EnterpriseServerUserIDs []string `json:"enterprise_server_user_ids,omitempty"` + GithubComUser bool `json:"github_com_user"` + EnterpriseServerUser *bool `json:"enterprise_server_user"` + VisualStudioSubscriptionUser bool `json:"visual_studio_subscription_user"` + LicenseType string `json:"license_type"` + GithubComProfile *string `json:"github_com_profile"` + GithubComMemberRoles []string `json:"github_com_member_roles,omitempty"` + GithubComEnterpriseRoles []string `json:"github_com_enterprise_roles,omitempty"` + GithubComVerifiedDomainEmails []string `json:"github_com_verified_domain_emails,omitempty"` + GithubComSamlNameID *string `json:"github_com_saml_name_id"` + GithubComOrgsWithPendingInvites []string `json:"github_com_orgs_with_pending_invites,omitempty"` + GithubComTwoFactorAuth *bool `json:"github_com_two_factor_auth"` + EnterpriseServerEmails []string `json:"enterprise_server_emails,omitempty"` + VisualStudioLicenseStatus *string `json:"visual_studio_license_status"` + VisualStudioSubscriptionEmail *string `json:"visual_studio_subscription_email"` + TotalUserAccounts int `json:"total_user_accounts"` +} + +// EnterpriseLicenseSyncStatus represents the synchronization status of +// GitHub Enterprise Server instances with an enterprise account. +type EnterpriseLicenseSyncStatus struct { + Title string `json:"title"` + Description string `json:"description"` + Properties *ServerInstanceProperties `json:"properties,omitempty"` +} + +// ServerInstanceProperties contains the collection of server instances. +type ServerInstanceProperties struct { + ServerInstances *ServerInstances `json:"server_instances,omitempty"` +} + +// ServerInstances represents a collection of GitHub Enterprise Server instances +// and their synchronization status. +type ServerInstances struct { + Type string `json:"type"` + Items *ServiceInstanceItems `json:"items,omitempty"` +} + +// ServiceInstanceItems defines the structure and properties of individual server instances +// in the collection. +type ServiceInstanceItems struct { + Type string `json:"type"` + Properties *ServerItemProperties `json:"properties,omitempty"` +} + +// ServerItemProperties represents the properties of a GitHub Enterprise Server instance, +// including its identifier, hostname, and last synchronization status. +type ServerItemProperties struct { + ServerID string `json:"server_id"` + Hostname string `json:"hostname"` + LastSync *LastLicenseSync `json:"last_sync,omitempty"` +} + +// LastLicenseSync contains information about the most recent license synchronization +// attempt for a server instance. +type LastLicenseSync struct { + Type string `json:"type"` + Properties *LastLicenseSyncProperties `json:"properties,omitempty"` +} + +// LastLicenseSyncProperties represents the details of the last synchronization attempt, +// including the date, status, and any error that occurred. +type LastLicenseSyncProperties struct { + Date *Timestamp `json:"date,omitempty"` + Status string `json:"status"` + Error string `json:"error"` +} + +// ListConsumedLicenses collect information about the number of consumed licenses and a collection with all the users with consumed enterprise licenses. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing?apiVersion=2022-11-28#list-enterprise-consumed-licenses +// +//meta:operation GET /enterprises/{enterprise}/consumed-licenses +func (s *EnterpriseService) ListConsumedLicenses(ctx context.Context, enterprise string, opts *ListOptions) (*EnterpriseConsumedLicenses, *Response, error) { + u := fmt.Sprintf("enterprises/%v/consumed-licenses", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var consumedLicenses *EnterpriseConsumedLicenses + resp, err := s.client.Do(req, &consumedLicenses) + if err != nil { + return nil, resp, err + } + + return consumedLicenses, resp, nil +} + +// GetLicenseSyncStatus collects information about the status of a license sync job for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing?apiVersion=2022-11-28#get-a-license-sync-status +// +//meta:operation GET /enterprises/{enterprise}/license-sync-status +func (s *EnterpriseService) GetLicenseSyncStatus(ctx context.Context, enterprise string) (*EnterpriseLicenseSyncStatus, *Response, error) { + u := fmt.Sprintf("enterprises/%v/license-sync-status", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var syncStatus *EnterpriseLicenseSyncStatus + resp, err := s.client.Do(req, &syncStatus) + if err != nil { + return nil, resp, err + } + + return syncStatus, resp, nil +} diff --git a/github/enterprise_licenses_test.go b/github/enterprise_licenses_test.go new file mode 100644 index 00000000000..288e8572e88 --- /dev/null +++ b/github/enterprise_licenses_test.go @@ -0,0 +1,182 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListConsumedLicenses(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/consumed-licenses", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "10"}) + fmt.Fprint(w, `{ + "total_seats_consumed": 20, + "total_seats_purchased": 25, + "users": [{ + "github_com_login": "user1", + "github_com_name": "User One", + "enterprise_server_user_ids": ["123", "456"], + "github_com_user": true, + "enterprise_server_user": false, + "visual_studio_subscription_user": false, + "license_type": "Enterprise", + "github_com_profile": "https://github.com/user1", + "github_com_member_roles": ["member"], + "github_com_enterprise_roles": ["member"], + "github_com_verified_domain_emails": ["user1@example.com"], + "github_com_saml_name_id": "saml123", + "github_com_orgs_with_pending_invites": [], + "github_com_two_factor_auth": true, + "enterprise_server_emails": ["user1@example.com"], + "visual_studio_license_status": "active", + "visual_studio_subscription_email": "user1@example.com", + "total_user_accounts": 1 + }] + }`) + }) + + opt := &ListOptions{Page: 2, PerPage: 10} + ctx := t.Context() + licenses, _, err := client.Enterprise.ListConsumedLicenses(ctx, "e", opt) + if err != nil { + t.Errorf("Enterprise.ListConsumedLicenses returned error: %v", err) + } + + want := &EnterpriseConsumedLicenses{ + TotalSeatsConsumed: 20, + TotalSeatsPurchased: 25, + Users: []*EnterpriseLicensedUsers{ + { + GithubComLogin: "user1", + GithubComName: Ptr("User One"), + EnterpriseServerUserIDs: []string{"123", "456"}, + GithubComUser: true, + EnterpriseServerUser: Ptr(false), + VisualStudioSubscriptionUser: false, + LicenseType: "Enterprise", + GithubComProfile: Ptr("https://github.com/user1"), + GithubComMemberRoles: []string{"member"}, + GithubComEnterpriseRoles: []string{"member"}, + GithubComVerifiedDomainEmails: []string{"user1@example.com"}, + GithubComSamlNameID: Ptr("saml123"), + GithubComOrgsWithPendingInvites: []string{}, + GithubComTwoFactorAuth: Ptr(true), + EnterpriseServerEmails: []string{"user1@example.com"}, + VisualStudioLicenseStatus: Ptr("active"), + VisualStudioSubscriptionEmail: Ptr("user1@example.com"), + TotalUserAccounts: 1, + }, + }, + } + + if !cmp.Equal(licenses, want) { + t.Errorf("Enterprise.ListConsumedLicenses returned %+v, want %+v", licenses, want) + } + + const methodName = "ListConsumedLicenses" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListConsumedLicenses(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListConsumedLicenses(ctx, "e", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetLicenseSyncStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/license-sync-status", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "title": "Enterprise License Sync Status", + "description": "Status of license synchronization", + "properties": { + "server_instances": { + "type": "array", + "items": { + "type": "object", + "properties": { + "server_id": "ghes-1", + "hostname": "github.enterprise.local", + "last_sync": { + "type": "object", + "properties": { + "date": `+referenceTimeStr+`, + "status": "success", + "error": "" + } + } + } + } + } + } + }`) + }) + + ctx := t.Context() + syncStatus, _, err := client.Enterprise.GetLicenseSyncStatus(ctx, "e") + if err != nil { + t.Errorf("Enterprise.GetLicenseSyncStatus returned error: %v", err) + } + + want := &EnterpriseLicenseSyncStatus{ + Title: "Enterprise License Sync Status", + Description: "Status of license synchronization", + Properties: &ServerInstanceProperties{ + ServerInstances: &ServerInstances{ + Type: "array", + Items: &ServiceInstanceItems{ + Type: "object", + Properties: &ServerItemProperties{ + ServerID: "ghes-1", + Hostname: "github.enterprise.local", + LastSync: &LastLicenseSync{ + Type: "object", + Properties: &LastLicenseSyncProperties{ + Date: &referenceTimestamp, + Status: "success", + Error: "", + }, + }, + }, + }, + }, + }, + } + + if !cmp.Equal(syncStatus, want) { + t.Errorf("Enterprise.GetLicenseSyncStatus returned %+v, want %+v", syncStatus, want) + } + + const methodName = "GetLicenseSyncStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetLicenseSyncStatus(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetLicenseSyncStatus(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_manage_ghes.go b/github/enterprise_manage_ghes.go new file mode 100644 index 00000000000..ad10ad8c5cb --- /dev/null +++ b/github/enterprise_manage_ghes.go @@ -0,0 +1,163 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// NodeQueryOptions specifies the optional parameters to the EnterpriseService +// Node management APIs. +type NodeQueryOptions struct { + // UUID filters issues based on the node UUID. + UUID *string `url:"uuid,omitempty"` + + // ClusterRoles filters the cluster roles from the cluster configuration file. + ClusterRoles *string `url:"cluster_roles,omitempty"` +} + +// ClusterStatus represents a response from the ClusterStatus and ReplicationStatus methods. +type ClusterStatus struct { + Status *string `json:"status,omitempty"` + Nodes []*ClusterStatusNode `json:"nodes"` +} + +// ClusterStatusNode represents the status of a cluster node. +type ClusterStatusNode struct { + Hostname *string `json:"hostname,omitempty"` + Status *string `json:"status,omitempty"` + Services []*ClusterStatusNodeServiceItem `json:"services"` +} + +// ClusterStatusNodeServiceItem represents the status of a service running on a cluster node. +type ClusterStatusNodeServiceItem struct { + Status *string `json:"status,omitempty"` + Name *string `json:"name,omitempty"` + Details *string `json:"details,omitempty"` +} + +// SystemRequirements represents a response from the CheckSystemRequirements method. +type SystemRequirements struct { + Status *string `json:"status,omitempty"` + Nodes []*SystemRequirementsNode `json:"nodes"` +} + +// SystemRequirementsNode represents the status of a system node. +type SystemRequirementsNode struct { + Hostname *string `json:"hostname,omitempty"` + Status *string `json:"status,omitempty"` + RolesStatus []*SystemRequirementsNodeRoleStatus `json:"roles_status"` +} + +// SystemRequirementsNodeRoleStatus represents the status of a role on a system node. +type SystemRequirementsNodeRoleStatus struct { + Status *string `json:"status,omitempty"` + Role *string `json:"role,omitempty"` +} + +// NodeReleaseVersion represents a response from the GetNodeReleaseVersions method. +type NodeReleaseVersion struct { + Hostname *string `json:"hostname,omitempty"` + Version *ReleaseVersion `json:"version"` +} + +// ReleaseVersion holds the release version information of the node. +type ReleaseVersion struct { + Version *string `json:"version,omitempty"` + Platform *string `json:"platform,omitempty"` + BuildID *string `json:"build_id,omitempty"` + BuildDate *string `json:"build_date,omitempty"` +} + +// CheckSystemRequirements checks if GHES system nodes meet the system requirements. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-system-requirement-check-results-for-configured-cluster-nodes +// +//meta:operation GET /manage/v1/checks/system-requirements +func (s *EnterpriseService) CheckSystemRequirements(ctx context.Context) (*SystemRequirements, *Response, error) { + u := "manage/v1/checks/system-requirements" + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var systemRequirements *SystemRequirements + resp, err := s.client.Do(req, &systemRequirements) + if err != nil { + return nil, resp, err + } + + return systemRequirements, resp, nil +} + +// ClusterStatus gets the status of all services running on each cluster node. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-services-running-on-all-cluster-nodes +// +//meta:operation GET /manage/v1/cluster/status +func (s *EnterpriseService) ClusterStatus(ctx context.Context) (*ClusterStatus, *Response, error) { + u := "manage/v1/cluster/status" + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var clusterStatus *ClusterStatus + resp, err := s.client.Do(req, &clusterStatus) + if err != nil { + return nil, resp, err + } + + return clusterStatus, resp, nil +} + +// ReplicationStatus gets the status of all services running on each replica node. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-services-running-on-all-replica-nodes +// +//meta:operation GET /manage/v1/replication/status +func (s *EnterpriseService) ReplicationStatus(ctx context.Context, opts *NodeQueryOptions) (*ClusterStatus, *Response, error) { + u, err := addOptions("manage/v1/replication/status", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var status *ClusterStatus + resp, err := s.client.Do(req, &status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} + +// GetNodeReleaseVersions gets the version information deployed to each node. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-all-ghes-release-versions-for-all-nodes +// +//meta:operation GET /manage/v1/version +func (s *EnterpriseService) GetNodeReleaseVersions(ctx context.Context, opts *NodeQueryOptions) ([]*NodeReleaseVersion, *Response, error) { + u, err := addOptions("manage/v1/version", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var releaseVersions []*NodeReleaseVersion + resp, err := s.client.Do(req, &releaseVersions) + if err != nil { + return nil, resp, err + } + + return releaseVersions, resp, nil +} diff --git a/github/enterprise_manage_ghes_config.go b/github/enterprise_manage_ghes_config.go new file mode 100644 index 00000000000..fe06035056f --- /dev/null +++ b/github/enterprise_manage_ghes_config.go @@ -0,0 +1,522 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" +) + +// ConfigApplyOptions is a struct to hold the options for the ConfigApply API and the response. +type ConfigApplyOptions struct { + // RunID is the ID of the run to get the status of. If empty a random one will be generated. + RunID *string `json:"run_id,omitempty"` +} + +// ConfigApplyStatus is a struct to hold the response from the ConfigApply API. +type ConfigApplyStatus struct { + Running *bool `json:"running,omitempty"` + Successful *bool `json:"successful,omitempty"` + Nodes []*ConfigApplyStatusNode `json:"nodes"` +} + +// ConfigApplyStatusNode is a struct to hold the response from the ConfigApply API. +type ConfigApplyStatusNode struct { + Hostname *string `json:"hostname,omitempty"` + Running *bool `json:"running,omitempty"` + Successful *bool `json:"successful,omitempty"` + RunID *string `json:"run_id,omitempty"` +} + +// ConfigApplyEventsOptions is used to enable pagination. +type ConfigApplyEventsOptions struct { + LastRequestID *string `url:"last_request_id,omitempty"` +} + +// ConfigApplyEvents is a struct to hold the response from the ConfigApplyEvents API. +type ConfigApplyEvents struct { + Nodes []*ConfigApplyEventsNode `json:"nodes"` +} + +// ConfigApplyEventsNode is a struct to hold the response from the ConfigApplyEvents API. +type ConfigApplyEventsNode struct { + Node *string `json:"node,omitempty"` + LastRequestID *string `json:"last_request_id,omitempty"` + Events []*ConfigApplyEventsNodeEvent `json:"events"` +} + +// ConfigApplyEventsNodeEvent is a struct to hold the response from the ConfigApplyEvents API. +type ConfigApplyEventsNodeEvent struct { + Timestamp *Timestamp `json:"timestamp,omitempty"` + SeverityText *string `json:"severity_text,omitempty"` + Body *string `json:"body,omitempty"` + EventName *string `json:"event_name,omitempty"` + Topology *string `json:"topology,omitempty"` + Hostname *string `json:"hostname,omitempty"` + ConfigRunID *string `json:"config_run_id,omitempty"` + TraceID *string `json:"trace_id,omitempty"` + SpanID *string `json:"span_id,omitempty"` + SpanParentID *int64 `json:"span_parent_id,omitempty"` + SpanDepth *int `json:"span_depth,omitempty"` +} + +// InitialConfigOptions represents the payload for initializing instance configuration. +type InitialConfigOptions struct { + License string `json:"license"` + Password string `json:"password"` +} + +// LicenseStatus is a struct to hold the response from the License API. +type LicenseStatus struct { + AdvancedSecurityEnabled *bool `json:"advancedSecurityEnabled,omitempty"` + AdvancedSecuritySeats *int `json:"advancedSecuritySeats,omitempty"` + ClusterSupport *bool `json:"clusterSupport,omitempty"` + Company *string `json:"company,omitempty"` + CroquetSupport *bool `json:"croquetSupport,omitempty"` + CustomTerms *bool `json:"customTerms,omitempty"` + Evaluation *bool `json:"evaluation,omitempty"` + ExpireAt *Timestamp `json:"expireAt,omitempty"` + InsightsEnabled *bool `json:"insightsEnabled,omitempty"` + InsightsExpireAt *Timestamp `json:"insightsExpireAt,omitempty"` + LearningLabEvaluationExpires *Timestamp `json:"learningLabEvaluationExpires,omitempty"` + LearningLabSeats *int `json:"learningLabSeats,omitempty"` + Perpetual *bool `json:"perpetual,omitempty"` + ReferenceNumber *string `json:"referenceNumber,omitempty"` + Seats *int `json:"seats,omitempty"` + SSHAllowed *bool `json:"sshAllowed,omitempty"` + // SupportKey is documented as a string, but the actual response is a bool. + // TODO: Remove this note once GitHub corrects the schema documentation. + SupportKey *bool `json:"supportKey,omitempty"` + UnlimitedSeating *bool `json:"unlimitedSeating,omitempty"` +} + +// UploadLicenseOptions is a struct to hold the options for the UploadLicense API. +type UploadLicenseOptions struct { + License string `url:"license"` +} + +// LicenseCheck is a struct to hold the response from the LicenseStatus API. +type LicenseCheck struct { + Status *string `json:"status,omitempty"` +} + +// ConfigSettings is a struct to hold the response from the Settings API. +// There are many fields that link to other structs. +type ConfigSettings struct { + PrivateMode *bool `json:"private_mode,omitempty"` + PublicPages *bool `json:"public_pages,omitempty"` + SubdomainIsolation *bool `json:"subdomain_isolation,omitempty"` + SignupEnabled *bool `json:"signup_enabled,omitempty"` + GithubHostname *string `json:"github_hostname,omitempty"` + IdenticonsHost *string `json:"identicons_host,omitempty"` + HTTPProxy *string `json:"http_proxy,omitempty"` + AuthMode *string `json:"auth_mode,omitempty"` + ExpireSessions *bool `json:"expire_sessions,omitempty"` + AdminPassword *string `json:"admin_password,omitempty"` + ConfigurationID *int64 `json:"configuration_id,omitempty"` + ConfigurationRunCount *int `json:"configuration_run_count,omitempty"` + Avatar *ConfigSettingsAvatar `json:"avatar,omitempty"` + Customer *ConfigSettingsCustomer `json:"customer,omitempty"` + License *ConfigSettingsLicenseSettings `json:"license,omitempty"` + GithubSSL *ConfigSettingsGithubSSL `json:"github_ssl,omitempty"` + LDAP *ConfigSettingsLDAP `json:"ldap,omitempty"` + CAS *ConfigSettingsCAS `json:"cas,omitempty"` + SAML *ConfigSettingsSAML `json:"saml,omitempty"` + GithubOAuth *ConfigSettingsGithubOAuth `json:"github_oauth,omitempty"` + SMTP *ConfigSettingsSMTP `json:"smtp,omitempty"` + NTP *ConfigSettingsNTP `json:"ntp,omitempty"` + Timezone *string `json:"timezone,omitempty"` + SNMP *ConfigSettingsSNMP `json:"snmp,omitempty"` + Syslog *ConfigSettingsSyslog `json:"syslog,omitempty"` + Assets *string `json:"assets,omitempty"` + Pages *ConfigSettingsPagesSettings `json:"pages,omitempty"` + Collectd *ConfigSettingsCollectd `json:"collectd,omitempty"` + Mapping *ConfigSettingsMapping `json:"mapping,omitempty"` + LoadBalancer *string `json:"load_balancer,omitempty"` +} + +// ConfigSettingsAvatar is a struct to hold the response from the Settings API. +type ConfigSettingsAvatar struct { + Enabled *bool `json:"enabled,omitempty"` + URI *string `json:"uri,omitempty"` +} + +// ConfigSettingsCustomer is a struct to hold the response from the Settings API. +type ConfigSettingsCustomer struct { + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` + UUID *string `json:"uuid,omitempty"` + Secret *string `json:"secret,omitempty"` + PublicKeyData *string `json:"public_key_data,omitempty"` +} + +// ConfigSettingsLicenseSettings is a struct to hold the response from the Settings API. +type ConfigSettingsLicenseSettings struct { + Seats *int `json:"seats,omitempty"` + Evaluation *bool `json:"evaluation,omitempty"` + Perpetual *bool `json:"perpetual,omitempty"` + UnlimitedSeating *bool `json:"unlimited_seating,omitempty"` + SupportKey *string `json:"support_key,omitempty"` + SSHAllowed *bool `json:"ssh_allowed,omitempty"` + ClusterSupport *bool `json:"cluster_support,omitempty"` + ExpireAt *Timestamp `json:"expire_at,omitempty"` +} + +// ConfigSettingsGithubSSL is a struct to hold the response from the Settings API. +type ConfigSettingsGithubSSL struct { + Enabled *bool `json:"enabled,omitempty"` + Cert *string `json:"cert,omitempty"` + Key *string `json:"key,omitempty"` +} + +// ConfigSettingsLDAP is a struct to hold the response from the Settings API. +type ConfigSettingsLDAP struct { + Host *string `json:"host,omitempty"` + Port *int `json:"port,omitempty"` + Base []string `json:"base,omitempty"` + UID *string `json:"uid,omitempty"` + BindDN *string `json:"bind_dn,omitempty"` + Password *string `json:"password,omitempty"` + Method *string `json:"method,omitempty"` + SearchStrategy *string `json:"search_strategy,omitempty"` + UserGroups []string `json:"user_groups,omitempty"` + AdminGroup *string `json:"admin_group,omitempty"` + VirtualAttributeEnabled *bool `json:"virtual_attribute_enabled,omitempty"` + RecursiveGroupSearch *bool `json:"recursive_group_search,omitempty"` + PosixSupport *bool `json:"posix_support,omitempty"` + UserSyncEmails *bool `json:"user_sync_emails,omitempty"` + UserSyncKeys *bool `json:"user_sync_keys,omitempty"` + UserSyncInterval *int `json:"user_sync_interval,omitempty"` + TeamSyncInterval *int `json:"team_sync_interval,omitempty"` + SyncEnabled *bool `json:"sync_enabled,omitempty"` + Reconciliation *ConfigSettingsLDAPReconciliation `json:"reconciliation,omitempty"` + Profile *ConfigSettingsLDAPProfile `json:"profile,omitempty"` +} + +// ConfigSettingsLDAPReconciliation is part of the ConfigSettingsLDAP struct. +type ConfigSettingsLDAPReconciliation struct { + User *string `json:"user,omitempty"` + Org *string `json:"org,omitempty"` +} + +// ConfigSettingsLDAPProfile is part of the ConfigSettingsLDAP struct. +type ConfigSettingsLDAPProfile struct { + UID *string `json:"uid,omitempty"` + Name *string `json:"name,omitempty"` + Mail *string `json:"mail,omitempty"` + Key *string `json:"key,omitempty"` +} + +// ConfigSettingsCAS is a struct to hold the response from the Settings API. +type ConfigSettingsCAS struct { + URL *string `json:"url,omitempty"` +} + +// ConfigSettingsSAML is a struct to hold the response from the Settings API. +type ConfigSettingsSAML struct { + SSOURL *string `json:"sso_url,omitempty"` + Certificate *string `json:"certificate,omitempty"` + CertificatePath *string `json:"certificate_path,omitempty"` + Issuer *string `json:"issuer,omitempty"` + IDPInitiatedSSO *bool `json:"idp_initiated_sso,omitempty"` + DisableAdminDemote *bool `json:"disable_admin_demote,omitempty"` +} + +// ConfigSettingsGithubOAuth is a struct to hold the response from the Settings API. +type ConfigSettingsGithubOAuth struct { + ClientID *string `json:"client_id,omitempty"` + ClientSecret *string `json:"client_secret,omitempty"` + OrganizationName *string `json:"organization_name,omitempty"` + OrganizationTeam *string `json:"organization_team,omitempty"` +} + +// ConfigSettingsSMTP is a struct to hold the response from the Settings API. +type ConfigSettingsSMTP struct { + Enabled *bool `json:"enabled,omitempty"` + Address *string `json:"address,omitempty"` + Authentication *string `json:"authentication,omitempty"` + Port *string `json:"port,omitempty"` + Domain *string `json:"domain,omitempty"` + Username *string `json:"username,omitempty"` + UserName *string `json:"user_name,omitempty"` + EnableStarttlsAuto *bool `json:"enable_starttls_auto,omitempty"` + Password *string `json:"password,omitempty"` + DiscardToNoreplyAddress *bool `json:"discard-to-noreply-address,omitempty"` + SupportAddress *string `json:"support_address,omitempty"` + SupportAddressType *string `json:"support_address_type,omitempty"` + NoreplyAddress *string `json:"noreply_address,omitempty"` +} + +// ConfigSettingsNTP is a struct to hold the response from the Settings API. +type ConfigSettingsNTP struct { + PrimaryServer *string `json:"primary_server,omitempty"` + SecondaryServer *string `json:"secondary_server,omitempty"` +} + +// ConfigSettingsSNMP is a struct to hold the response from the Settings API. +type ConfigSettingsSNMP struct { + Enabled *bool `json:"enabled,omitempty"` + Community *string `json:"community,omitempty"` +} + +// ConfigSettingsSyslog is a struct to hold the response from the Settings API. +type ConfigSettingsSyslog struct { + Enabled *bool `json:"enabled,omitempty"` + Server *string `json:"server,omitempty"` + ProtocolName *string `json:"protocol_name,omitempty"` +} + +// ConfigSettingsPagesSettings is a struct to hold the response from the Settings API. +type ConfigSettingsPagesSettings struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// ConfigSettingsCollectd is a struct to hold the response from the Settings API. +type ConfigSettingsCollectd struct { + Enabled *bool `json:"enabled,omitempty"` + Server *string `json:"server,omitempty"` + Port *int `json:"port,omitempty"` + Encryption *string `json:"encryption,omitempty"` + Username *string `json:"username,omitempty"` + Password *string `json:"password,omitempty"` +} + +// ConfigSettingsMapping is a struct to hold the response from the Settings API. +type ConfigSettingsMapping struct { + Enabled *bool `json:"enabled,omitempty"` + Tileserver *string `json:"tileserver,omitempty"` + Basemap *string `json:"basemap,omitempty"` + Token *string `json:"token,omitempty"` +} + +// NodeMetadataStatus is a struct to hold the response from the NodeMetadata API. +type NodeMetadataStatus struct { + Topology *string `json:"topology,omitempty"` + Nodes []*NodeDetails `json:"nodes"` +} + +// NodeDetails is a struct to hold the response from the NodeMetadata API. +type NodeDetails struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + ClusterRoles []string `json:"cluster_roles,omitempty"` +} + +// ConfigApplyEvents gets events from the command ghe-config-apply. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#list-events-from-ghe-config-apply +// +//meta:operation GET /manage/v1/config/apply/events +func (s *EnterpriseService) ConfigApplyEvents(ctx context.Context, opts *ConfigApplyEventsOptions) (*ConfigApplyEvents, *Response, error) { + u, err := addOptions("manage/v1/config/apply/events", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configApplyEvents *ConfigApplyEvents + resp, err := s.client.Do(req, &configApplyEvents) + if err != nil { + return nil, resp, err + } + + return configApplyEvents, resp, nil +} + +// InitialConfig initializes the GitHub Enterprise instance with a license and password. +// After initializing the instance, you need to run an apply to apply the configuration. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#initialize-instance-configuration-with-license-and-password +// +//meta:operation POST /manage/v1/config/init +func (s *EnterpriseService) InitialConfig(ctx context.Context, license, password string) (*Response, error) { + u := "manage/v1/config/init" + + payload := &InitialConfigOptions{ + License: license, + Password: password, + } + + req, err := s.client.NewRequest(ctx, "POST", u, payload) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// License gets the current license information for the GitHub Enterprise instance. +// +// NOTE: The GitHub documentation incorrectly shows the return type as a list ([{...}]), +// but the actual response is a single object ({...}). +// TODO: Remove this note once GitHub corrects the schema documentation. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-enterprise-license-information +// +//meta:operation GET /manage/v1/config/license +func (s *EnterpriseService) License(ctx context.Context) (*LicenseStatus, *Response, error) { + u := "manage/v1/config/license" + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var licenseStatus *LicenseStatus + resp, err := s.client.Do(req, &licenseStatus) + if err != nil { + return nil, resp, err + } + + return licenseStatus, resp, nil +} + +// UploadLicense uploads a new license to the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#upload-an-enterprise-license +// +//meta:operation PUT /manage/v1/config/license +func (s *EnterpriseService) UploadLicense(ctx context.Context, license string) (*Response, error) { + u := "manage/v1/config/license" + opts := &UploadLicenseOptions{ + License: license, + } + req, err := s.client.NewRequest(ctx, "PUT", u, opts) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// LicenseStatus gets the current license status for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#check-a-license +// +//meta:operation GET /manage/v1/config/license/check +func (s *EnterpriseService) LicenseStatus(ctx context.Context) ([]*LicenseCheck, *Response, error) { + u := "manage/v1/config/license/check" + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var checks []*LicenseCheck + resp, err := s.client.Do(req, &checks) + if err != nil { + return nil, resp, err + } + + return checks, resp, nil +} + +// NodeMetadata gets the metadata for all nodes in the GitHub Enterprise instance. +// This is required for clustered setups. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-ghes-node-metadata-for-all-nodes +// +//meta:operation GET /manage/v1/config/nodes +func (s *EnterpriseService) NodeMetadata(ctx context.Context, opts *NodeQueryOptions) (*NodeMetadataStatus, *Response, error) { + u, err := addOptions("manage/v1/config/nodes", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var status *NodeMetadataStatus + resp, err := s.client.Do(req, &status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} + +// Settings gets the current configuration settings for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-ghes-settings +// +//meta:operation GET /manage/v1/config/settings +func (s *EnterpriseService) Settings(ctx context.Context) (*ConfigSettings, *Response, error) { + u := "manage/v1/config/settings" + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configSettings *ConfigSettings + resp, err := s.client.Do(req, &configSettings) + if err != nil { + return nil, resp, err + } + + return configSettings, resp, nil +} + +// UpdateSettings updates the configuration settings for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#set-settings +// +//meta:operation PUT /manage/v1/config/settings +func (s *EnterpriseService) UpdateSettings(ctx context.Context, body *ConfigSettings) (*Response, error) { + u := "manage/v1/config/settings" + + if body == nil { + return nil, errors.New("opts should not be nil") + } + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ConfigApply triggers a configuration apply run on the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#trigger-a-ghe-config-apply-run +// +//meta:operation POST /manage/v1/config/apply +func (s *EnterpriseService) ConfigApply(ctx context.Context, body *ConfigApplyOptions) (*ConfigApplyOptions, *Response, error) { + u := "manage/v1/config/apply" + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var configApplyOptions *ConfigApplyOptions + resp, err := s.client.Do(req, &configApplyOptions) + if err != nil { + return nil, resp, err + } + return configApplyOptions, resp, nil +} + +// ConfigApplyStatus gets the status of a ghe-config-apply run on the GitHub Enterprise instance. +// You can request lat one or specific id one. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-a-ghe-config-apply-run +// +//meta:operation GET /manage/v1/config/apply +func (s *EnterpriseService) ConfigApplyStatus(ctx context.Context, opts *ConfigApplyOptions) (*ConfigApplyStatus, *Response, error) { + u := "manage/v1/config/apply" + req, err := s.client.NewRequest(ctx, "GET", u, opts) + if err != nil { + return nil, nil, err + } + + var status *ConfigApplyStatus + resp, err := s.client.Do(req, &status) + if err != nil { + return nil, resp, err + } + return status, resp, nil +} diff --git a/github/enterprise_manage_ghes_config_test.go b/github/enterprise_manage_ghes_config_test.go new file mode 100644 index 00000000000..cc43ead90b9 --- /dev/null +++ b/github/enterprise_manage_ghes_config_test.go @@ -0,0 +1,707 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_Settings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/settings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "private_mode": false, + "public_pages": false, + "subdomain_isolation": true, + "signup_enabled": false, + "github_hostname": "ghe.local", + "identicons_host": "dotcom", + "http_proxy": null, + "auth_mode": "default", + "expire_sessions": false, + "admin_password": null, + "configuration_id": 1401777404, + "configuration_run_count": 4, + "avatar": { + "enabled": false, + "uri": "" + }, + "customer": { + "name": "GitHub", + "email": "stannis", + "uuid": "af6cac80-e4e1-012e-d822-1231380e52e9", + "secret_key_data": "-", + "public_key_data": "-" + }, + "license": { + "seats": 0, + "evaluation": false, + "perpetual": false, + "unlimited_seating": true, + "support_key": "ssh-rsa AAAAB3N....", + "ssh_allowed": true, + "cluster_support": false, + "expire_at": `+referenceTimeStr+` + }, + "github_ssl": { + "enabled": false, + "cert": null, + "key": null + }, + "ldap": { + "host": null, + "port": 0, + "base": [], + "uid": null, + "bind_dn": null, + "password": null, + "method": "Plain", + "search_strategy": "detect", + "user_groups": [], + "admin_group": null, + "virtual_attribute_enabled": false, + "recursive_group_search": false, + "posix_support": true, + "user_sync_emails": false, + "user_sync_keys": false, + "user_sync_interval": 4, + "team_sync_interval": 4, + "sync_enabled": false, + "reconciliation": { + "user": null, + "org": null + }, + "profile": { + "uid": "uid", + "name": null, + "mail": null, + "key": null + } + }, + "cas": { + "url": null + }, + "saml": { + "sso_url": null, + "certificate": null, + "certificate_path": null, + "issuer": null, + "idp_initiated_sso": false, + "disable_admin_demote": false + }, + "github_oauth": { + "client_id": "12313412", + "client_secret": "kj123131132", + "organization_name": "Homestar Runners", + "organization_team": "homestarrunners/characters" + }, + "smtp": { + "enabled": true, + "address": "smtp.example.com", + "authentication": "plain", + "port": "1234", + "domain": "blah", + "username": "foo", + "user_name": "mr_foo", + "enable_starttls_auto": true, + "password": "bar", + "discard-to-noreply-address": true, + "support_address": "enterprise@github.com", + "support_address_type": "email", + "noreply_address": "noreply@github.com" + }, + "ntp": { + "primary_server": "example.com/primary", + "secondary_server": "example.com/secondary" + }, + "timezone": null, + "snmp": { + "enabled": false, + "community": "" + }, + "syslog": { + "enabled": false, + "server": null, + "protocol_name": "udp" + }, + "assets": null, + "pages": { + "enabled": true + }, + "collectd": { + "enabled": false, + "server": null, + "port": 0, + "encryption": null, + "username": null, + "password": null + }, + "mapping": { + "enabled": true, + "tileserver": null, + "basemap": "company.map-qsz2zrvs", + "token": null + }, + "load_balancer": null + }`) + }) + + ctx := t.Context() + configSettings, _, err := client.Enterprise.Settings(ctx) + if err != nil { + t.Errorf("Enterprise.Settings returned error: %v", err) + } + + want := &ConfigSettings{ + PrivateMode: Ptr(false), + PublicPages: Ptr(false), + SubdomainIsolation: Ptr(true), + SignupEnabled: Ptr(false), + GithubHostname: Ptr("ghe.local"), + IdenticonsHost: Ptr("dotcom"), + HTTPProxy: nil, + AuthMode: Ptr("default"), + ExpireSessions: Ptr(false), + AdminPassword: nil, + ConfigurationID: Ptr(int64(1401777404)), + ConfigurationRunCount: Ptr(4), + Avatar: &ConfigSettingsAvatar{ + Enabled: Ptr(false), + URI: Ptr(""), + }, + Customer: &ConfigSettingsCustomer{ + Name: Ptr("GitHub"), + Email: Ptr("stannis"), + UUID: Ptr("af6cac80-e4e1-012e-d822-1231380e52e9"), + Secret: nil, + PublicKeyData: Ptr("-"), + }, + License: &ConfigSettingsLicenseSettings{ + Seats: Ptr(0), + Evaluation: Ptr(false), + Perpetual: Ptr(false), + UnlimitedSeating: Ptr(true), + SupportKey: Ptr("ssh-rsa AAAAB3N...."), + SSHAllowed: Ptr(true), + ClusterSupport: Ptr(false), + ExpireAt: &referenceTimestamp, + }, + GithubSSL: &ConfigSettingsGithubSSL{ + Enabled: Ptr(false), + Cert: nil, + Key: nil, + }, + LDAP: &ConfigSettingsLDAP{ + Host: nil, + Port: Ptr(0), + Base: []string{}, + UID: nil, + BindDN: nil, + Password: nil, + Method: Ptr("Plain"), + SearchStrategy: Ptr("detect"), + UserGroups: []string{}, + AdminGroup: nil, + VirtualAttributeEnabled: Ptr(false), + RecursiveGroupSearch: Ptr(false), + PosixSupport: Ptr(true), + UserSyncEmails: Ptr(false), + UserSyncKeys: Ptr(false), + UserSyncInterval: Ptr(4), + TeamSyncInterval: Ptr(4), + SyncEnabled: Ptr(false), + Reconciliation: &ConfigSettingsLDAPReconciliation{ + User: nil, + Org: nil, + }, + Profile: &ConfigSettingsLDAPProfile{ + UID: Ptr("uid"), + Name: nil, + Mail: nil, + Key: nil, + }, + }, + CAS: &ConfigSettingsCAS{ + URL: nil, + }, + SAML: &ConfigSettingsSAML{ + SSOURL: nil, + Certificate: nil, + CertificatePath: nil, + Issuer: nil, + IDPInitiatedSSO: Ptr(false), + DisableAdminDemote: Ptr(false), + }, + GithubOAuth: &ConfigSettingsGithubOAuth{ + ClientID: Ptr("12313412"), + ClientSecret: Ptr("kj123131132"), + OrganizationName: Ptr("Homestar Runners"), + OrganizationTeam: Ptr("homestarrunners/characters"), + }, + SMTP: &ConfigSettingsSMTP{ + Enabled: Ptr(true), + Address: Ptr("smtp.example.com"), + Authentication: Ptr("plain"), + Port: Ptr("1234"), + Domain: Ptr("blah"), + Username: Ptr("foo"), + UserName: Ptr("mr_foo"), + Password: Ptr("bar"), + DiscardToNoreplyAddress: Ptr(true), + SupportAddress: Ptr("enterprise@github.com"), + SupportAddressType: Ptr("email"), + NoreplyAddress: Ptr("noreply@github.com"), + EnableStarttlsAuto: Ptr(true), + }, + NTP: &ConfigSettingsNTP{ + PrimaryServer: Ptr("example.com/primary"), + SecondaryServer: Ptr("example.com/secondary"), + }, + Timezone: nil, + SNMP: &ConfigSettingsSNMP{ + Enabled: Ptr(false), + Community: Ptr(""), + }, + Syslog: &ConfigSettingsSyslog{ + Enabled: Ptr(false), + Server: nil, + ProtocolName: Ptr("udp"), + }, + Assets: nil, + Pages: &ConfigSettingsPagesSettings{ + Enabled: Ptr(true), + }, + Collectd: &ConfigSettingsCollectd{ + Enabled: Ptr(false), + Server: nil, + Port: Ptr(0), + Encryption: nil, + Username: nil, + Password: nil, + }, + Mapping: &ConfigSettingsMapping{ + Enabled: Ptr(true), + Tileserver: nil, + Basemap: Ptr("company.map-qsz2zrvs"), + Token: nil, + }, + LoadBalancer: nil, + } + if diff := cmp.Diff(want, configSettings); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "Settings" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.Settings(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_NodeMetadata(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/nodes", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "uuid": "1234-1234", + "cluster_roles": "primary", + }) + fmt.Fprint(w, `{ + "topology": "Cluster", + "nodes": [{ + "hostname": "data1", + "uuid": "1b6cf518-f97c-11ed-8544-061d81f7eedb", + "cluster_roles": [ + "ConsulServer" + ] + }] + }`) + }) + + opt := &NodeQueryOptions{ + UUID: Ptr("1234-1234"), ClusterRoles: Ptr("primary"), + } + ctx := t.Context() + configNodes, _, err := client.Enterprise.NodeMetadata(ctx, opt) + if err != nil { + t.Errorf("Enterprise.NodeMetadata returned error: %v", err) + } + + want := &NodeMetadataStatus{ + Topology: Ptr("Cluster"), + Nodes: []*NodeDetails{{ + Hostname: Ptr("data1"), + UUID: Ptr("1b6cf518-f97c-11ed-8544-061d81f7eedb"), + ClusterRoles: []string{ + "ConsulServer", + }, + }}, + } + if !cmp.Equal(configNodes, want) { + t.Errorf("Enterprise.NodeMetadata returned %+v, want %+v", configNodes, want) + } + + const methodName = "NodeMetadata" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.NodeMetadata(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_LicenseStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/license/check", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "status": "valid" + }]`) + }) + + ctx := t.Context() + licenseCheck, _, err := client.Enterprise.LicenseStatus(ctx) + if err != nil { + t.Errorf("Enterprise.LicenseStatus returned error: %v", err) + } + + want := []*LicenseCheck{{ + Status: Ptr("valid"), + }} + if !cmp.Equal(licenseCheck, want) { + t.Errorf("Enterprise.LicenseStatus returned %+v, want %+v", licenseCheck, want) + } + + const methodName = "LicenseStatus" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.LicenseStatus(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_License(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/license", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "advancedSecurityEnabled": true, + "advancedSecuritySeats": 0, + "clusterSupport": false, + "company": "GitHub", + "croquetSupport": true, + "customTerms": true, + "evaluation": false, + "expireAt": `+refTimeStr(1136178000)+`, + "insightsEnabled": true, + "insightsExpireAt": `+refTimeStr(1136178001)+`, + "learningLabEvaluationExpires": `+refTimeStr(1136178002)+`, + "learningLabSeats": 100, + "perpetual": false, + "referenceNumber": "32a145", + "seats": 0, + "sshAllowed": true, + "supportKey": true, + "unlimitedSeating": true + }`) + }) + + ctx := t.Context() + license, _, err := client.Enterprise.License(ctx) + if err != nil { + t.Errorf("Enterprise.License returned error: %v", err) + } + + want := &LicenseStatus{ + AdvancedSecurityEnabled: Ptr(true), + AdvancedSecuritySeats: Ptr(0), + ClusterSupport: Ptr(false), + Company: Ptr("GitHub"), + CroquetSupport: Ptr(true), + CustomTerms: Ptr(true), + Evaluation: Ptr(false), + ExpireAt: refTimestamp(1136178000), + InsightsEnabled: Ptr(true), + InsightsExpireAt: refTimestamp(1136178001), + LearningLabEvaluationExpires: refTimestamp(1136178002), + LearningLabSeats: Ptr(100), + Perpetual: Ptr(false), + ReferenceNumber: Ptr("32a145"), + Seats: Ptr(0), + SSHAllowed: Ptr(true), + SupportKey: Ptr(true), + UnlimitedSeating: Ptr(true), + } + if diff := cmp.Diff(want, license); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } + const methodName = "License" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.License(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ConfigApplyEvents(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/apply/events", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "nodes": [{ + "node": "ghes-01.lan", + "last_request_id": "387cd628c06d606700e79be368e5e574:0cde553750689c76:0000000000000000", + "events": [{ + "timestamp": `+referenceTimeStr+`, + "severity_text": "INFO", + "body": "Validating services", + "event_name": "Enterprise::ConfigApply::PhaseValidation#config_phase_validation", + "topology": "multinode", + "hostname": "ghes-01.lan", + "config_run_id": "d34db33f", + "trace_id": "387cd628c06d606700e79be368e5e574", + "span_id": "0cde553750689c76", + "span_parent_id": 0, + "span_depth": 0 + }] + }] + }`) + }) + + input := &ConfigApplyEventsOptions{ + LastRequestID: Ptr("387cd628c06d606700e79be368e5e574:0cde553750689"), + } + + ctx := t.Context() + configEvents, _, err := client.Enterprise.ConfigApplyEvents(ctx, input) + if err != nil { + t.Errorf("Enterprise.ConfigApplyEvents returned error: %v", err) + } + + want := &ConfigApplyEvents{ + Nodes: []*ConfigApplyEventsNode{{ + Node: Ptr("ghes-01.lan"), + LastRequestID: Ptr("387cd628c06d606700e79be368e5e574:0cde553750689c76:0000000000000000"), + Events: []*ConfigApplyEventsNodeEvent{{ + Timestamp: &referenceTimestamp, + SeverityText: Ptr("INFO"), + Body: Ptr("Validating services"), + EventName: Ptr("Enterprise::ConfigApply::PhaseValidation#config_phase_validation"), + Topology: Ptr("multinode"), + Hostname: Ptr("ghes-01.lan"), + ConfigRunID: Ptr("d34db33f"), + TraceID: Ptr("387cd628c06d606700e79be368e5e574"), + SpanID: Ptr("0cde553750689c76"), + SpanParentID: Ptr(int64(0)), + SpanDepth: Ptr(0), + }}, + }}, + } + if diff := cmp.Diff(want, configEvents); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } + if !cmp.Equal(configEvents, want) { + t.Errorf("Enterprise.ConfigApplyEvents returned %+v, want %+v", configEvents, want) + } + + const methodName = "ConfigApplyEvents" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ConfigApplyEvents(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateSettings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/settings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + + w.WriteHeader(http.StatusNoContent) + }) + + input := &ConfigSettings{ + PrivateMode: Ptr(false), + } + + ctx := t.Context() + if _, err := client.Enterprise.UpdateSettings(ctx, input); err != nil { + t.Errorf("Enterprise.UpdateSettings returned error: %v", err) + } + + const methodName = "UpdateSettings" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.UpdateSettings(ctx, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.UpdateSettings(ctx, input) + }) +} + +func TestEnterpriseService_UploadLicense(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/config/license", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Enterprise.UploadLicense(ctx, "abc"); err != nil { + t.Errorf("Enterprise.UploadLicense returned error: %v", err) + } + + const methodName = "UploadLicense" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.UploadLicense(ctx, "") + }) +} + +func TestEnterpriseService_InitialConfig(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &InitialConfigOptions{ + License: "1234-1234", + Password: "password", + } + + mux.HandleFunc("/manage/v1/config/init", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + if _, err := client.Enterprise.InitialConfig(ctx, "1234-1234", "password"); err != nil { + t.Errorf("Enterprise.InitialConfig returned error: %v", err) + } + + const methodName = "InitialConfig" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.InitialConfig(ctx, "", "") + }) +} + +func TestEnterpriseService_ConfigApply(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ConfigApplyOptions{ + RunID: Ptr("1234"), + } + + mux.HandleFunc("/manage/v1/config/apply", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ "run_id": "1234" }`) + }) + + ctx := t.Context() + configApply, _, err := client.Enterprise.ConfigApply(ctx, input) + if err != nil { + t.Errorf("Enterprise.ConfigApply returned error: %v", err) + } + want := &ConfigApplyOptions{ + RunID: Ptr("1234"), + } + if !cmp.Equal(configApply, want) { + t.Errorf("Enterprise.ConfigApply returned %+v, want %+v", configApply, want) + } + const methodName = "ConfigApply" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ConfigApply(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ConfigApplyStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ConfigApplyOptions{ + RunID: Ptr("1234"), + } + + mux.HandleFunc("/manage/v1/config/apply", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "running": true, + "successful": false, + "nodes": [ + { + "run_id": "d34db33f", + "hostname": "ghes-01.lan", + "running": true, + "successful": false + } + ] + }`) + }) + + ctx := t.Context() + configApplyStatus, _, err := client.Enterprise.ConfigApplyStatus(ctx, input) + if err != nil { + t.Errorf("Enterprise.ConfigApplyStatus returned error: %v", err) + } + want := &ConfigApplyStatus{ + Running: Ptr(true), + Successful: Ptr(false), + Nodes: []*ConfigApplyStatusNode{{ + RunID: Ptr("d34db33f"), + Hostname: Ptr("ghes-01.lan"), + Running: Ptr(true), + Successful: Ptr(false), + }}, + } + if !cmp.Equal(configApplyStatus, want) { + t.Errorf("Enterprise.ConfigApplyStatus returned %+v, want %+v", configApplyStatus, want) + } + const methodName = "ConfigApplyStatus" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ConfigApplyStatus(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_manage_ghes_maintenance.go b/github/enterprise_manage_ghes_maintenance.go new file mode 100644 index 00000000000..59a815c3615 --- /dev/null +++ b/github/enterprise_manage_ghes_maintenance.go @@ -0,0 +1,94 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// MaintenanceOperationStatus represents the message to be displayed when the instance gets a maintenance operation request. +type MaintenanceOperationStatus struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + Message *string `json:"message,omitempty"` +} + +// MaintenanceStatus represents the status of maintenance mode for all nodes. +type MaintenanceStatus struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + Status *string `json:"status,omitempty"` + ScheduledTime *Timestamp `json:"scheduled_time,omitempty"` + ConnectionServices []*ConnectionServiceItem `json:"connection_services,omitempty"` + CanUnsetMaintenance *bool `json:"can_unset_maintenance,omitempty"` + IPExceptionList []string `json:"ip_exception_list,omitempty"` + MaintenanceModeMessage *string `json:"maintenance_mode_message,omitempty"` +} + +// ConnectionServiceItem represents the connection services for the maintenance status. +type ConnectionServiceItem struct { + Name *string `json:"name,omitempty"` + Number *int `json:"number,omitempty"` +} + +// MaintenanceOptions represents the options for setting the maintenance mode for the instance. +// When can be a string, so we can't use a Timestamp type. +type MaintenanceOptions struct { + Enabled bool `json:"enabled"` + UUID *string `json:"uuid,omitempty"` + When *string `json:"when,omitempty"` + IPExceptionList []string `json:"ip_exception_list,omitempty"` + MaintenanceModeMessage *string `json:"maintenance_mode_message,omitempty"` +} + +// GetMaintenanceStatus gets the status of maintenance mode for all nodes. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-maintenance-mode +// +//meta:operation GET /manage/v1/maintenance +func (s *EnterpriseService) GetMaintenanceStatus(ctx context.Context, opts *NodeQueryOptions) ([]*MaintenanceStatus, *Response, error) { + u, err := addOptions("manage/v1/maintenance", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var status []*MaintenanceStatus + resp, err := s.client.Do(req, &status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} + +// CreateMaintenance sets the maintenance mode for the instance. +// With the enable parameter we can control to put instance into maintenance mode or not. With false we can disable the maintenance mode. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#set-the-status-of-maintenance-mode +// +//meta:operation POST /manage/v1/maintenance +func (s *EnterpriseService) CreateMaintenance(ctx context.Context, enable bool, body *MaintenanceOptions) ([]*MaintenanceOperationStatus, *Response, error) { + u := "manage/v1/maintenance" + + body.Enabled = enable + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var i []*MaintenanceOperationStatus + resp, err := s.client.Do(req, &i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} diff --git a/github/enterprise_manage_ghes_maintenance_test.go b/github/enterprise_manage_ghes_maintenance_test.go new file mode 100644 index 00000000000..0a3f3cf559d --- /dev/null +++ b/github/enterprise_manage_ghes_maintenance_test.go @@ -0,0 +1,120 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetMaintenanceStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/maintenance", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "uuid": "1234-1234", + "cluster_roles": "primary", + }) + fmt.Fprint(w, `[{ + "hostname": "primary", + "uuid": "1b6cf518-f97c-11ed-8544-061d81f7eedb", + "status": "scheduled", + "scheduled_time": `+referenceTimeStr+`, + "connection_services": [ + { + "name": "git operations", + "number": 15 + } + ], + "can_unset_maintenance": true, + "ip_exception_list": [ + "1.1.1.1" + ], + "maintenance_mode_message": "Scheduled maintenance for upgrading." + }]`) + }) + + opt := &NodeQueryOptions{ + UUID: Ptr("1234-1234"), ClusterRoles: Ptr("primary"), + } + ctx := t.Context() + maintenanceStatus, _, err := client.Enterprise.GetMaintenanceStatus(ctx, opt) + if err != nil { + t.Errorf("Enterprise.GetMaintenanceStatus returned error: %v", err) + } + + want := []*MaintenanceStatus{{ + Hostname: Ptr("primary"), + UUID: Ptr("1b6cf518-f97c-11ed-8544-061d81f7eedb"), + Status: Ptr("scheduled"), + ScheduledTime: &referenceTimestamp, + ConnectionServices: []*ConnectionServiceItem{{ + Name: Ptr("git operations"), + Number: Ptr(15), + }}, + CanUnsetMaintenance: Ptr(true), + IPExceptionList: []string{"1.1.1.1"}, + MaintenanceModeMessage: Ptr("Scheduled maintenance for upgrading."), + }} + if !cmp.Equal(maintenanceStatus, want) { + t.Errorf("Enterprise.GetMaintenanceStatus returned %+v, want %+v", maintenanceStatus, want) + } + + const methodName = "GetMaintenanceStatus" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetMaintenanceStatus(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateMaintenance(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &MaintenanceOptions{ + Enabled: true, + UUID: Ptr("1234-1234"), + When: Ptr("now"), + IPExceptionList: []string{ + "1.1.1.1", + }, + MaintenanceModeMessage: Ptr("Scheduled maintenance for upgrading."), + } + + mux.HandleFunc("/manage/v1/maintenance", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `[ { "hostname": "primary", "uuid": "1b6cf518-f97c-11ed-8544-061d81f7eedb", "message": "Scheduled maintenance for upgrading." } ]`) + }) + + ctx := t.Context() + maintenanceStatus, _, err := client.Enterprise.CreateMaintenance(ctx, true, input) + if err != nil { + t.Errorf("Enterprise.CreateMaintenance returned error: %v", err) + } + + want := []*MaintenanceOperationStatus{{Hostname: Ptr("primary"), UUID: Ptr("1b6cf518-f97c-11ed-8544-061d81f7eedb"), Message: Ptr("Scheduled maintenance for upgrading.")}} + if diff := cmp.Diff(want, maintenanceStatus); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "CreateMaintenance" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateMaintenance(ctx, true, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_manage_ghes_ssh.go b/github/enterprise_manage_ghes_ssh.go new file mode 100644 index 00000000000..37c8089f387 --- /dev/null +++ b/github/enterprise_manage_ghes_ssh.go @@ -0,0 +1,99 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// SSHKeyStatus represents the status of a SSH key operation. +type SSHKeyStatus struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + Message *string `json:"message,omitempty"` + Modified *bool `json:"modified,omitempty"` +} + +// SSHKeyOptions specifies the parameters to the SSH create and delete functions. +type SSHKeyOptions struct { + // Key is the SSH key to add to the instance. + Key string `json:"key"` +} + +// ClusterSSHKey represents the SSH keys configured for the instance. +type ClusterSSHKey struct { + Key *string `json:"key,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +// DeleteSSHKey deletes the SSH key from the instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#delete-a-ssh-key +// +//meta:operation DELETE /manage/v1/access/ssh +func (s *EnterpriseService) DeleteSSHKey(ctx context.Context, key string) ([]*SSHKeyStatus, *Response, error) { + u := "manage/v1/access/ssh" + opts := &SSHKeyOptions{ + Key: key, + } + req, err := s.client.NewRequest(ctx, "DELETE", u, opts) + if err != nil { + return nil, nil, err + } + + var sshStatus []*SSHKeyStatus + resp, err := s.client.Do(req, &sshStatus) + if err != nil { + return nil, resp, err + } + + return sshStatus, resp, nil +} + +// GetSSHKey gets the SSH keys configured for the instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-configured-ssh-keys +// +//meta:operation GET /manage/v1/access/ssh +func (s *EnterpriseService) GetSSHKey(ctx context.Context) ([]*ClusterSSHKey, *Response, error) { + u := "manage/v1/access/ssh" + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var sshKeys []*ClusterSSHKey + resp, err := s.client.Do(req, &sshKeys) + if err != nil { + return nil, resp, err + } + + return sshKeys, resp, nil +} + +// CreateSSHKey adds a new SSH key to the instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#set-a-new-ssh-key +// +//meta:operation POST /manage/v1/access/ssh +func (s *EnterpriseService) CreateSSHKey(ctx context.Context, key string) ([]*SSHKeyStatus, *Response, error) { + u := "manage/v1/access/ssh" + opts := &SSHKeyOptions{ + Key: key, + } + req, err := s.client.NewRequest(ctx, "POST", u, opts) + if err != nil { + return nil, nil, err + } + + var sshKeyResponse []*SSHKeyStatus + resp, err := s.client.Do(req, &sshKeyResponse) + if err != nil { + return nil, resp, err + } + + return sshKeyResponse, resp, nil +} diff --git a/github/enterprise_manage_ghes_ssh_test.go b/github/enterprise_manage_ghes_ssh_test.go new file mode 100644 index 00000000000..30cfdc23bf3 --- /dev/null +++ b/github/enterprise_manage_ghes_ssh_test.go @@ -0,0 +1,120 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetSSHKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/access/ssh", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "key": "ssh-rsa 1234", + "fingerprint": "bd" + }]`) + }) + + ctx := t.Context() + accessSSH, _, err := client.Enterprise.GetSSHKey(ctx) + if err != nil { + t.Errorf("Enterprise.GetSSHKey returned error: %v", err) + } + + want := []*ClusterSSHKey{{ + Key: Ptr("ssh-rsa 1234"), + Fingerprint: Ptr("bd"), + }} + if !cmp.Equal(accessSSH, want) { + t.Errorf("Enterprise.GetSSHKey returned %+v, want %+v", accessSSH, want) + } + + const methodName = "GetSSHKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetSSHKey(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteSSHKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SSHKeyOptions{ + Key: "ssh-rsa 1234", + } + + mux.HandleFunc("/manage/v1/access/ssh", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + fmt.Fprint(w, `[ { "hostname": "primary", "uuid": "1b6cf518-f97c-11ed-8544-061d81f7eedb", "message": "SSH key removed successfully" } ]`) + }) + + ctx := t.Context() + sshStatus, _, err := client.Enterprise.DeleteSSHKey(ctx, "ssh-rsa 1234") + if err != nil { + t.Errorf("Enterprise.DeleteSSHKey returned error: %v", err) + } + + want := []*SSHKeyStatus{{Hostname: Ptr("primary"), UUID: Ptr("1b6cf518-f97c-11ed-8544-061d81f7eedb"), Message: Ptr("SSH key removed successfully")}} + if diff := cmp.Diff(want, sshStatus); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "DeleteSSHKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.DeleteSSHKey(ctx, "ssh-rsa 1234") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateSSHKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SSHKeyOptions{ + Key: "ssh-rsa 1234", + } + + mux.HandleFunc("/manage/v1/access/ssh", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `[ { "hostname": "primary", "uuid": "1b6cf518-f97c-11ed-8544-061d81f7eedb", "message": "SSH key added successfully", "modified": true } ]`) + }) + + ctx := t.Context() + sshStatus, _, err := client.Enterprise.CreateSSHKey(ctx, "ssh-rsa 1234") + if err != nil { + t.Errorf("Enterprise.CreateSSHKey returned error: %v", err) + } + + want := []*SSHKeyStatus{{Hostname: Ptr("primary"), UUID: Ptr("1b6cf518-f97c-11ed-8544-061d81f7eedb"), Message: Ptr("SSH key added successfully"), Modified: Ptr(true)}} + if diff := cmp.Diff(want, sshStatus); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "CreateSSHKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateSSHKey(ctx, "ssh-rsa 1234") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_manage_ghes_test.go b/github/enterprise_manage_ghes_test.go new file mode 100644 index 00000000000..1240b0cc5a6 --- /dev/null +++ b/github/enterprise_manage_ghes_test.go @@ -0,0 +1,212 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_CheckSystemRequirements(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/checks/system-requirements", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "status": "OK", + "nodes": [{ + "hostname": "primary", + "status": "OK", + "roles_status": [{ + "status": "OK", + "role": "ConsulServer" + } + ]} + ]}`) + }) + + ctx := t.Context() + systemRequirements, _, err := client.Enterprise.CheckSystemRequirements(ctx) + if err != nil { + t.Errorf("Enterprise.CheckSystemRequirements returned error: %v", err) + } + + want := &SystemRequirements{ + Status: Ptr("OK"), + Nodes: []*SystemRequirementsNode{{ + Hostname: Ptr("primary"), + Status: Ptr("OK"), + RolesStatus: []*SystemRequirementsNodeRoleStatus{{ + Status: Ptr("OK"), + Role: Ptr("ConsulServer"), + }}, + }}, + } + if !cmp.Equal(systemRequirements, want) { + t.Errorf("Enterprise.CheckSystemRequirements returned %+v, want %+v", systemRequirements, want) + } + + const methodName = "CheckSystemRequirements" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CheckSystemRequirements(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ClusterStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/cluster/status", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "status": "OK", + "nodes": [{ + "hostname": "primary", + "status": "OK", + "services": [] + }] + }`) + }) + + ctx := t.Context() + clusterStatus, _, err := client.Enterprise.ClusterStatus(ctx) + if err != nil { + t.Errorf("Enterprise.ClusterStatus returned error: %v", err) + } + + want := &ClusterStatus{ + Status: Ptr("OK"), + Nodes: []*ClusterStatusNode{{ + Hostname: Ptr("primary"), + Status: Ptr("OK"), + Services: []*ClusterStatusNodeServiceItem{}, + }}, + } + if !cmp.Equal(clusterStatus, want) { + t.Errorf("Enterprise.ClusterStatus returned %+v, want %+v", clusterStatus, want) + } + + const methodName = "ClusterStatus" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ClusterStatus(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ReplicationStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/replication/status", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "uuid": "1234-1234", + "cluster_roles": "primary", + }) + fmt.Fprint(w, `{ + "status": "OK", + "nodes": [{ + "hostname": "primary", + "status": "OK", + "services": [] + }] + }`) + }) + + opt := &NodeQueryOptions{ + UUID: Ptr("1234-1234"), ClusterRoles: Ptr("primary"), + } + ctx := t.Context() + replicationStatus, _, err := client.Enterprise.ReplicationStatus(ctx, opt) + if err != nil { + t.Errorf("Enterprise.ReplicationStatus returned error: %v", err) + } + + want := &ClusterStatus{ + Status: Ptr("OK"), + Nodes: []*ClusterStatusNode{{ + Hostname: Ptr("primary"), + Status: Ptr("OK"), + Services: []*ClusterStatusNodeServiceItem{}, + }}, + } + if !cmp.Equal(replicationStatus, want) { + t.Errorf("Enterprise.ReplicationStatus returned %+v, want %+v", replicationStatus, want) + } + + const methodName = "ReplicationStatus" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ReplicationStatus(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_Versions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/manage/v1/version", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "uuid": "1234-1234", + "cluster_roles": "primary", + }) + fmt.Fprint(w, `[{ + "hostname": "primary", + "version": { + "version": "3.9.0", + "platform": "azure", + "build_id": "fc542058b5", + "build_date": "2023-05-02" + } + }]`) + }) + + opt := &NodeQueryOptions{ + UUID: Ptr("1234-1234"), ClusterRoles: Ptr("primary"), + } + ctx := t.Context() + releaseVersions, _, err := client.Enterprise.GetNodeReleaseVersions(ctx, opt) + if err != nil { + t.Errorf("Enterprise.GetNodeReleaseVersions returned error: %v", err) + } + + want := []*NodeReleaseVersion{{ + Hostname: Ptr("primary"), + Version: &ReleaseVersion{ + Version: Ptr("3.9.0"), + Platform: Ptr("azure"), + BuildID: Ptr("fc542058b5"), + BuildDate: Ptr("2023-05-02"), + }, + }} + if !cmp.Equal(releaseVersions, want) { + t.Errorf("Enterprise.GetNodeReleaseVersions returned %+v, want %+v", releaseVersions, want) + } + + const methodName = "GetNodeReleaseVersions" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetNodeReleaseVersions(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_network_configurations.go b/github/enterprise_network_configurations.go new file mode 100644 index 00000000000..5a87efdb69b --- /dev/null +++ b/github/enterprise_network_configurations.go @@ -0,0 +1,144 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListEnterpriseNetworkConfigurations lists all hosted compute network configurations configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations?apiVersion=2022-11-28#list-hosted-compute-network-configurations-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/network-configurations +func (s *EnterpriseService) ListEnterpriseNetworkConfigurations(ctx context.Context, enterprise string, opts *ListOptions) (*NetworkConfigurations, *Response, error) { + u := fmt.Sprintf("enterprises/%v/network-configurations", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var networks *NetworkConfigurations + resp, err := s.client.Do(req, &networks) + if err != nil { + return nil, resp, err + } + + return networks, resp, nil +} + +// CreateEnterpriseNetworkConfiguration creates a hosted compute network configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations?apiVersion=2022-11-28#create-a-hosted-compute-network-configuration-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/network-configurations +func (s *EnterpriseService) CreateEnterpriseNetworkConfiguration(ctx context.Context, enterprise string, body NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(body); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/network-configurations", enterprise) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var network *NetworkConfiguration + resp, err := s.client.Do(req, &network) + if err != nil { + return nil, resp, err + } + + return network, resp, nil +} + +// GetEnterpriseNetworkConfiguration gets a hosted compute network configuration configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations?apiVersion=2022-11-28#get-a-hosted-compute-network-configuration-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/network-configurations/{network_configuration_id} +func (s *EnterpriseService) GetEnterpriseNetworkConfiguration(ctx context.Context, enterprise, networkID string) (*NetworkConfiguration, *Response, error) { + u := fmt.Sprintf("enterprises/%v/network-configurations/%v", enterprise, networkID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var network *NetworkConfiguration + resp, err := s.client.Do(req, &network) + if err != nil { + return nil, resp, err + } + + return network, resp, nil +} + +// UpdateEnterpriseNetworkConfiguration updates a hosted compute network configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations?apiVersion=2022-11-28#update-a-hosted-compute-network-configuration-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/network-configurations/{network_configuration_id} +func (s *EnterpriseService) UpdateEnterpriseNetworkConfiguration(ctx context.Context, enterprise, networkID string, body NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(body); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/network-configurations/%v", enterprise, networkID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var network *NetworkConfiguration + resp, err := s.client.Do(req, &network) + if err != nil { + return nil, resp, err + } + + return network, resp, nil +} + +// DeleteEnterpriseNetworkConfiguration deletes a hosted compute network configuration from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations?apiVersion=2022-11-28#delete-a-hosted-compute-network-configuration-from-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/network-configurations/{network_configuration_id} +func (s *EnterpriseService) DeleteEnterpriseNetworkConfiguration(ctx context.Context, enterprise, networkID string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/network-configurations/%v", enterprise, networkID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetEnterpriseNetworkSettingsResource gets a hosted compute network settings resource configured for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations?apiVersion=2022-11-28#get-a-hosted-compute-network-settings-resource-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/network-settings/{network_settings_id} +func (s *EnterpriseService) GetEnterpriseNetworkSettingsResource(ctx context.Context, enterprise, networkID string) (*NetworkSettingsResource, *Response, error) { + u := fmt.Sprintf("enterprises/%v/network-settings/%v", enterprise, networkID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var resource *NetworkSettingsResource + resp, err := s.client.Do(req, &resource) + if err != nil { + return nil, resp, err + } + + return resource, resp, err +} diff --git a/github/enterprise_network_configurations_test.go b/github/enterprise_network_configurations_test.go new file mode 100644 index 00000000000..c4154b5f654 --- /dev/null +++ b/github/enterprise_network_configurations_test.go @@ -0,0 +1,403 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListEnterpriseNetworkConfigurations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(" /enterprises/e/network-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "3", "per_page": "2"}) + fmt.Fprint(w, `{ + "total_count": 2, + "network_configurations": [ + { + "id": "123456789ABCDEF", + "name": "configuration one", + "compute_service": "actions", + "network_settings_ids": [ + "23456789ABDCEF1", + "3456789ABDCEF12" + ], + "created_on": `+referenceTimeStr+` + }, + { + "id": "456789ABDCEF123", + "name": "configuration two", + "compute_service": "none", + "network_settings_ids": [ + "56789ABDCEF1234", + "6789ABDCEF12345" + ], + "created_on": `+referenceTimeStr+` + } + ] + }`) + }) + + ctx := t.Context() + + opts := &ListOptions{Page: 3, PerPage: 2} + configurations, _, err := client.Enterprise.ListEnterpriseNetworkConfigurations(ctx, "e", opts) + if err != nil { + t.Errorf("Enterprise.ListEnterpriseNetworkConfigurations returned error: %v", err) + } + + want := &NetworkConfigurations{ + TotalCount: Ptr(int64(2)), + NetworkConfigurations: []*NetworkConfiguration{ + { + ID: Ptr("123456789ABCDEF"), + Name: Ptr("configuration one"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{"23456789ABDCEF1", "3456789ABDCEF12"}, + CreatedOn: &referenceTimestamp, + }, + { + ID: Ptr("456789ABDCEF123"), + Name: Ptr("configuration two"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{"56789ABDCEF1234", "6789ABDCEF12345"}, + CreatedOn: &referenceTimestamp, + }, + }, + } + if !cmp.Equal(configurations, want) { + t.Errorf("Enterprise.ListEnterpriseNetworkConfigurations mismatch (-want +got):\n%v", cmp.Diff(want, configurations)) + } + + const methodName = "ListEnterpriseNetworkConfigurations" + testBadOptions(t, methodName, func() error { + _, _, err = client.Enterprise.ListEnterpriseNetworkConfigurations(ctx, "\ne", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListEnterpriseNetworkConfigurations(ctx, "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateEnterpriseNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/network-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": "123456789ABCDEF", + "name": "configuration one", + "compute_service": "actions", + "network_settings_ids": [ + "23456789ABDCEF1" + ], + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + + req := NetworkConfigurationRequest{ + Name: Ptr("configuration-one"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{ + "23456789ABDCEF1", + }, + } + configuration, _, err := client.Enterprise.CreateEnterpriseNetworkConfiguration(ctx, "e", req) + if err != nil { + t.Errorf("Enterprise.CreateEnterpriseNetworkConfiguration returned error: %v", err) + } + + want := &NetworkConfiguration{ + ID: Ptr("123456789ABCDEF"), + Name: Ptr("configuration one"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{"23456789ABDCEF1"}, + CreatedOn: &referenceTimestamp, + } + if !cmp.Equal(configuration, want) { + t.Errorf("Enterprise.CreateEnterpriseNetworkConfiguration mismatch (-want +got):\n%v", cmp.Diff(want, configuration)) + } + + validationTest := []struct { + name string + request NetworkConfigurationRequest + want string + }{ + { + name: "invalid network settings id", + request: NetworkConfigurationRequest{ + Name: Ptr(""), + NetworkSettingsIDs: []string{"56789ABDCEF1234"}, + }, + want: "validation failed: must be between 1 and 100 characters", + }, + { + name: "invalid network settings id", + request: NetworkConfigurationRequest{ + Name: Ptr("updated-configuration-one"), + }, + want: "validation failed: exactly one network settings id must be specified", + }, + { + name: "invalid compute service", + request: NetworkConfigurationRequest{ + Name: Ptr("updated-configuration-one"), + ComputeService: Ptr(ComputeService("")), + NetworkSettingsIDs: []string{"56789ABDCEF1234"}, + }, + want: "validation failed: compute service can only be one of: none, actions", + }, + } + for _, tc := range validationTest { + _, _, err = client.Enterprise.CreateEnterpriseNetworkConfiguration(ctx, "e", tc.request) + if err == nil || err.Error() != tc.want { + t.Errorf("expected error to be %v, got %v", tc.want, err) + } + } + + const methodName = "CreateEnterpriseNetworkConfiguration" + testBadOptions(t, methodName, func() error { + _, _, err = client.Enterprise.CreateEnterpriseNetworkConfiguration(ctx, "\ne", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateEnterpriseNetworkConfiguration(ctx, "e", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetEnterpriseNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/network-configurations/123456789ABCDEF", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "123456789ABCDEF", + "name": "configuration one", + "compute_service": "actions", + "network_settings_ids": [ + "23456789ABDCEF1", + "3456789ABDCEF12" + ], + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + configuration, _, err := client.Enterprise.GetEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF") + if err != nil { + t.Errorf("Enterprise.GetEnterpriseNetworkConfiguration returned err: %v", err) + } + + want := &NetworkConfiguration{ + ID: Ptr("123456789ABCDEF"), + Name: Ptr("configuration one"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{"23456789ABDCEF1", "3456789ABDCEF12"}, + CreatedOn: &referenceTimestamp, + } + if !cmp.Equal(configuration, want) { + t.Errorf("Enterprise.GetEnterpriseNetworkConfiguration mismatch (-want +got):\n%v", cmp.Diff(want, configuration)) + } + + const methodName = "GetEnterpriseNetworkConfiguration" + testBadOptions(t, methodName, func() error { + _, _, err = client.Enterprise.GetEnterpriseNetworkConfiguration(ctx, "\ne", "123456789ABCDEF") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateEnterpriseNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/network-configurations/123456789ABCDEF", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{ + "id": "123456789ABCDEF", + "name": "updated configuration one", + "compute_service": "none", + "network_settings_ids": [ + "456789ABDCEF123" + ], + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + req := NetworkConfigurationRequest{ + Name: Ptr("updated-configuration-one"), + NetworkSettingsIDs: []string{ + "456789ABDCEF123", + }, + ComputeService: Ptr(ComputeService("none")), + } + configuration, _, err := client.Enterprise.UpdateEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF", req) + if err != nil { + t.Errorf("Enterprise.UpdateEnterpriseNetworkConfiguration returned error %v", err) + } + + want := &NetworkConfiguration{ + ID: Ptr("123456789ABCDEF"), + Name: Ptr("updated configuration one"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{"456789ABDCEF123"}, + CreatedOn: &referenceTimestamp, + } + if !cmp.Equal(configuration, want) { + t.Errorf("Enterprise.UpdateEnterpriseNetworkConfiguration mismatch (-want +get)\n%v", cmp.Diff(want, configuration)) + } + + validationTest := []struct { + name string + request NetworkConfigurationRequest + want string + }{ + { + name: "invalid network settings id", + request: NetworkConfigurationRequest{ + Name: Ptr(""), + NetworkSettingsIDs: []string{"56789ABDCEF1234"}, + }, + want: "validation failed: must be between 1 and 100 characters", + }, + { + name: "invalid network settings id", + request: NetworkConfigurationRequest{ + Name: Ptr("updated-configuration-one"), + }, + want: "validation failed: exactly one network settings id must be specified", + }, + { + name: "invalid compute service", + request: NetworkConfigurationRequest{ + Name: Ptr("updated-configuration-one"), + ComputeService: Ptr(ComputeService("something")), + NetworkSettingsIDs: []string{"56789ABDCEF1234"}, + }, + want: "validation failed: compute service can only be one of: none, actions", + }, + } + for _, tc := range validationTest { + _, _, err = client.Enterprise.UpdateEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF", tc.request) + if err == nil || err.Error() != tc.want { + t.Errorf("expected error to be %v, got %v", tc.want, err) + } + } + + const methodName = "UpdateEnterpriseNetworkConfiguration" + testBadOptions(t, methodName, func() error { + _, _, err = client.Enterprise.UpdateEnterpriseNetworkConfiguration(ctx, "\ne", "123456789ABCDEF", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteEnterpriseNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/network-configurations/123456789ABCDEF", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF") + if err != nil { + t.Errorf("Enterprise.DeleteEnterpriseNetworkConfiguration returned error %v", err) + } + + const methodName = "DeleteEnterpriseNetworkConfiguration" + testBadOptions(t, methodName, func() error { + _, err = client.Enterprise.DeleteEnterpriseNetworkConfiguration(ctx, "\ne", "123456789ABCDEF") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteEnterpriseNetworkConfiguration(ctx, "e", "123456789ABCDEF") + }) +} + +func TestEnterpriseService_GetEnterpriseNetworkSettingsResource(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/network-settings/123456789ABCDEF", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "220F78DACB92BBFBC5E6F22DE1CCF52309D", + "network_configuration_id": "934E208B3EE0BD60CF5F752C426BFB53562", + "name": "my_network_settings", + "subnet_id": "/subscriptions/14839728-3ad9-43ab-bd2b-fa6ad0f75e2a/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet", + "region": "germanywestcentral" + }`) + }) + + ctx := t.Context() + resource, _, err := client.Enterprise.GetEnterpriseNetworkSettingsResource(ctx, "e", "123456789ABCDEF") + if err != nil { + t.Errorf("Enterprise.GetEnterpriseNetworkSettingsResource returned error %v", err) + } + + want := &NetworkSettingsResource{ + ID: Ptr("220F78DACB92BBFBC5E6F22DE1CCF52309D"), + NetworkConfigurationID: Ptr("934E208B3EE0BD60CF5F752C426BFB53562"), + Name: Ptr("my_network_settings"), + SubnetID: Ptr("/subscriptions/14839728-3ad9-43ab-bd2b-fa6ad0f75e2a/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet"), + Region: Ptr("germanywestcentral"), + } + if !cmp.Equal(resource, want) { + t.Errorf("Enterprise.GetEnterpriseNetworkSettingsResource mismatch (-want +got):\n%v", cmp.Diff(want, resource)) + } + + const methodName = "GetEnterpriseNetworkSettingsResource" + testBadOptions(t, methodName, func() error { + _, _, err = client.Enterprise.GetEnterpriseNetworkSettingsResource(ctx, "\ne", "123456789ABCDEF") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetEnterpriseNetworkSettingsResource(ctx, "e", "123456789ABCDEF") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/enterprise_organization_properties.go b/github/enterprise_organization_properties.go new file mode 100644 index 00000000000..8468b151034 --- /dev/null +++ b/github/enterprise_organization_properties.go @@ -0,0 +1,189 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnterpriseCustomPropertySchema represents the schema response for GetEnterpriseCustomPropertiesSchema. +type EnterpriseCustomPropertySchema struct { + // An ordered list of the custom property defined in the enterprise. + Properties []*CustomProperty `json:"properties,omitempty"` +} + +// EnterpriseCustomPropertiesValues represents the custom properties values for an organization within an enterprise. +type EnterpriseCustomPropertiesValues struct { + // The Organization ID that the custom property values will be applied to. + OrganizationID *int64 `json:"organization_id,omitempty"` + // The names of organizations that the custom property values will be applied to. + OrganizationLogin *string `json:"organization_login,omitempty"` + // List of custom property names and associated values to apply to the organizations. + Properties []*CustomPropertyValue `json:"properties,omitempty"` +} + +// EnterpriseCustomPropertyValuesRequest represents the request to update custom property values for organizations within an enterprise. +type EnterpriseCustomPropertyValuesRequest struct { + // The names of organizations that the custom property values will be applied to. + // OrganizationLogin specifies the organization name when updating multiple organizations. + OrganizationLogin []string `json:"organization_login"` + // List of custom property names and associated values to apply to the organizations. + Properties []*CustomPropertyValue `json:"properties"` +} + +// GetOrganizationCustomPropertySchema gets all organization custom property definitions that are defined on an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#get-organization-custom-properties-schema-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/org-properties/schema +func (s *EnterpriseService) GetOrganizationCustomPropertySchema(ctx context.Context, enterprise string) (*EnterpriseCustomPropertySchema, *Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/schema", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var schema *EnterpriseCustomPropertySchema + resp, err := s.client.Do(req, &schema) + if err != nil { + return nil, resp, err + } + + return schema, resp, nil +} + +// CreateOrUpdateOrganizationCustomPropertySchema creates new or updates existing organization custom properties defined on an enterprise in a batch. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#create-or-update-organization-custom-property-definitions-on-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/org-properties/schema +func (s *EnterpriseService) CreateOrUpdateOrganizationCustomPropertySchema(ctx context.Context, enterprise string, body EnterpriseCustomPropertySchema) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/schema", enterprise) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// GetOrganizationCustomProperty retrieves a specific organization custom property definition from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#get-an-organization-custom-property-definition-from-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/org-properties/schema/{custom_property_name} +func (s *EnterpriseService) GetOrganizationCustomProperty(ctx context.Context, enterprise, customPropertyName string) (*CustomProperty, *Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/schema/%v", enterprise, customPropertyName) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var property *CustomProperty + resp, err := s.client.Do(req, &property) + if err != nil { + return nil, resp, err + } + + return property, resp, nil +} + +// CreateOrUpdateOrganizationCustomProperty creates a new or updates an existing organization custom property definition that is defined on an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#create-or-update-an-organization-custom-property-definition-on-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/org-properties/schema/{custom_property_name} +func (s *EnterpriseService) CreateOrUpdateOrganizationCustomProperty(ctx context.Context, enterprise, customPropertyName string, body CustomProperty) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/schema/%v", enterprise, customPropertyName) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DeleteOrganizationCustomProperty removes an organization custom property definition that is defined on an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#remove-an-organization-custom-property-definition-from-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/org-properties/schema/{custom_property_name} +func (s *EnterpriseService) DeleteOrganizationCustomProperty(ctx context.Context, enterprise, customPropertyName string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/schema/%v", enterprise, customPropertyName) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListOrganizationCustomPropertyValues lists enterprise organizations with all of their custom property values. +// Returns a list of organizations and their custom property values defined in the enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#list-custom-property-values-for-organizations-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/org-properties/values +func (s *EnterpriseService) ListOrganizationCustomPropertyValues(ctx context.Context, enterprise string, opts *ListOptions) ([]*EnterpriseCustomPropertiesValues, *Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/values", enterprise) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var values []*EnterpriseCustomPropertiesValues + resp, err := s.client.Do(req, &values) + if err != nil { + return nil, resp, err + } + + return values, resp, nil +} + +// CreateOrUpdateOrganizationCustomPropertyValues creates or updates custom property values for organizations in an enterprise. +// To remove a custom property value from an organization, set the property value to null. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs?apiVersion=2022-11-28#create-or-update-custom-property-values-for-organizations-in-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/org-properties/values +func (s *EnterpriseService) CreateOrUpdateOrganizationCustomPropertyValues(ctx context.Context, enterprise string, body EnterpriseCustomPropertyValuesRequest) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/org-properties/values", enterprise) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/enterprise_organization_properties_test.go b/github/enterprise_organization_properties_test.go new file mode 100644 index 00000000000..567676eafb3 --- /dev/null +++ b/github/enterprise_organization_properties_test.go @@ -0,0 +1,267 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetOrganizationCustomPropertySchema(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/schema", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "properties": [{ + "property_name": "team", + "value_type": "string", + "description": "Team name" + }] + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.GetOrganizationCustomPropertySchema(ctx, "e") + if err != nil { + t.Fatalf("Enterprise.GetOrganizationCustomPropertySchema returned error: %v", err) + } + + want := &EnterpriseCustomPropertySchema{ + Properties: []*CustomProperty{ + { + PropertyName: Ptr("team"), + ValueType: PropertyValueTypeString, + Description: Ptr("Team name"), + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.GetOrganizationCustomPropertySchema = %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationCustomPropertySchema" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.GetOrganizationCustomPropertySchema(ctx, "\n") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetOrganizationCustomPropertySchema(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateOrUpdateOrganizationCustomPropertySchema(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/schema", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{}`) + }) + + ctx := t.Context() + schema := EnterpriseCustomPropertySchema{ + Properties: []*CustomProperty{{PropertyName: Ptr("team")}}, + } + _, err := client.Enterprise.CreateOrUpdateOrganizationCustomPropertySchema(ctx, "e", schema) + if err != nil { + t.Errorf("Enterprise.CreateOrUpdateOrganizationCustomPropertySchema returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrganizationCustomPropertySchema" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.CreateOrUpdateOrganizationCustomPropertySchema(ctx, "\n", schema) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.CreateOrUpdateOrganizationCustomPropertySchema(ctx, "e", schema) + }) +} + +func TestEnterpriseService_GetOrganizationCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/schema/prop", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "property_name": "team", + "value_type": "string", + "description": "Team name" + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.GetOrganizationCustomProperty(ctx, "e", "prop") + if err != nil { + t.Fatalf("Enterprise.GetOrganizationCustomProperty returned error: %v", err) + } + + want := &CustomProperty{ + PropertyName: Ptr("team"), + ValueType: PropertyValueTypeString, + Description: Ptr("Team name"), + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.GetOrganizationCustomProperty = %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationCustomProperty" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.GetOrganizationCustomProperty(ctx, "\n", "prop") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetOrganizationCustomProperty(ctx, "e", "prop") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateOrUpdateOrganizationCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/schema/prop", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{}`) + }) + + ctx := t.Context() + property := CustomProperty{PropertyName: Ptr("team")} + _, err := client.Enterprise.CreateOrUpdateOrganizationCustomProperty(ctx, "e", "prop", property) + if err != nil { + t.Errorf("Enterprise.CreateOrUpdateOrganizationCustomProperty returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrganizationCustomProperty" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.CreateOrUpdateOrganizationCustomProperty(ctx, "\n", "prop", property) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.CreateOrUpdateOrganizationCustomProperty(ctx, "e", "prop", property) + }) +} + +func TestEnterpriseService_DeleteOrganizationCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/schema/prop", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteOrganizationCustomProperty(ctx, "e", "prop") + if err != nil { + t.Errorf("Enterprise.DeleteOrganizationCustomProperty returned error: %v", err) + } + + const methodName = "DeleteOrganizationCustomProperty" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.DeleteOrganizationCustomProperty(ctx, "\n", "prop") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteOrganizationCustomProperty(ctx, "e", "prop") + }) +} + +func TestEnterpriseService_ListOrganizationCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "organization_id": 1, + "organization_login": "org1", + "properties": [ + {"property_name": "team", "value": "core"} + ] + }]`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + got, _, err := client.Enterprise.ListOrganizationCustomPropertyValues(ctx, "e", opts) + if err != nil { + t.Fatalf("Enterprise.ListOrganizationCustomPropertyValues returned error: %v", err) + } + + want := []*EnterpriseCustomPropertiesValues{ + { + OrganizationID: Ptr(int64(1)), + OrganizationLogin: Ptr("org1"), + Properties: []*CustomPropertyValue{ + {PropertyName: "team", Value: "core"}, + }, + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.ListOrganizationCustomPropertyValues = %+v, want %+v", got, want) + } + + const methodName = "ListEnterpriseCustomPropertyValues" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.ListOrganizationCustomPropertyValues(ctx, "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListOrganizationCustomPropertyValues(ctx, "e", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateOrUpdateOrganizationCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/org-properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{}`) + }) + + ctx := t.Context() + values := []*CustomPropertyValue{{PropertyName: "team", Value: Ptr("core")}} + orgs := []string{"org1"} + + opts := EnterpriseCustomPropertyValuesRequest{ + OrganizationLogin: orgs, + Properties: values, + } + _, err := client.Enterprise.CreateOrUpdateOrganizationCustomPropertyValues(ctx, "e", opts) + if err != nil { + t.Errorf("Enterprise.CreateOrUpdateOrganizationCustomPropertyValues returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrganizationCustomPropertyValues" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.CreateOrUpdateOrganizationCustomPropertyValues(ctx, "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.CreateOrUpdateOrganizationCustomPropertyValues(ctx, "e", opts) + }) +} diff --git a/github/enterprise_properties.go b/github/enterprise_properties.go new file mode 100644 index 00000000000..f8c53b6ea49 --- /dev/null +++ b/github/enterprise_properties.go @@ -0,0 +1,121 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetAllCustomProperties gets all custom properties that are defined for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties?apiVersion=2022-11-28#get-custom-properties-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/properties/schema +func (s *EnterpriseService) GetAllCustomProperties(ctx context.Context, enterprise string) ([]*CustomProperty, *Response, error) { + u := fmt.Sprintf("enterprises/%v/properties/schema", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customProperties []*CustomProperty + resp, err := s.client.Do(req, &customProperties) + if err != nil { + return nil, resp, err + } + + return customProperties, resp, nil +} + +// CreateOrUpdateCustomProperties creates new or updates existing custom properties that are defined for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties?apiVersion=2022-11-28#create-or-update-custom-properties-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/properties/schema +func (s *EnterpriseService) CreateOrUpdateCustomProperties(ctx context.Context, enterprise string, properties []*CustomProperty) ([]*CustomProperty, *Response, error) { + u := fmt.Sprintf("enterprises/%v/properties/schema", enterprise) + + params := struct { + Properties []*CustomProperty `json:"properties"` + }{ + Properties: properties, + } + + req, err := s.client.NewRequest(ctx, "PATCH", u, params) + if err != nil { + return nil, nil, err + } + + var customProperties []*CustomProperty + resp, err := s.client.Do(req, &customProperties) + if err != nil { + return nil, resp, err + } + + return customProperties, resp, nil +} + +// GetCustomProperty gets a custom property that is defined for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties?apiVersion=2022-11-28#get-a-custom-property-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/properties/schema/{custom_property_name} +func (s *EnterpriseService) GetCustomProperty(ctx context.Context, enterprise, customPropertyName string) (*CustomProperty, *Response, error) { + u := fmt.Sprintf("enterprises/%v/properties/schema/%v", enterprise, customPropertyName) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customProperty *CustomProperty + resp, err := s.client.Do(req, &customProperty) + if err != nil { + return nil, resp, err + } + + return customProperty, resp, nil +} + +// CreateOrUpdateCustomProperty creates a new or updates an existing custom property that is defined for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties?apiVersion=2022-11-28#create-or-update-a-custom-property-for-an-enterprise +// +//meta:operation PUT /enterprises/{enterprise}/properties/schema/{custom_property_name} +func (s *EnterpriseService) CreateOrUpdateCustomProperty(ctx context.Context, enterprise, customPropertyName string, body *CustomProperty) (*CustomProperty, *Response, error) { + u := fmt.Sprintf("enterprises/%v/properties/schema/%v", enterprise, customPropertyName) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var customProperty *CustomProperty + resp, err := s.client.Do(req, &customProperty) + if err != nil { + return nil, resp, err + } + + return customProperty, resp, nil +} + +// RemoveCustomProperty removes a custom property that is defined for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties?apiVersion=2022-11-28#remove-a-custom-property-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/properties/schema/{custom_property_name} +func (s *EnterpriseService) RemoveCustomProperty(ctx context.Context, enterprise, customPropertyName string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/properties/schema/%v", enterprise, customPropertyName) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_properties_test.go b/github/enterprise_properties_test.go new file mode 100644 index 00000000000..e6f62c4224a --- /dev/null +++ b/github/enterprise_properties_test.go @@ -0,0 +1,283 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_GetAllCustomProperties(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/properties/schema", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "property_name": "name", + "value_type": "single_select", + "required": true, + "default_value": "production", + "description": "Prod or dev environment", + "allowed_values":[ + "production", + "development" + ], + "values_editable_by": "org_actors" + }, + { + "property_name": "service", + "value_type": "string" + }, + { + "property_name": "team", + "value_type": "string", + "description": "Team owning the repository" + } + ]`) + }) + + ctx := t.Context() + properties, _, err := client.Enterprise.GetAllCustomProperties(ctx, "e") + if err != nil { + t.Errorf("Enterprise.GetAllCustomProperties returned error: %v", err) + } + + want := []*CustomProperty{ + { + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + }, + { + PropertyName: Ptr("service"), + ValueType: PropertyValueTypeString, + }, + { + PropertyName: Ptr("team"), + ValueType: PropertyValueTypeString, + Description: Ptr("Team owning the repository"), + }, + } + if !cmp.Equal(properties, want) { + t.Errorf("Enterprise.GetAllCustomProperties returned %+v, want %+v", properties, want) + } + + const methodName = "GetAllCustomProperties" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetAllCustomProperties(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateOrUpdateCustomProperties(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + properties := []*CustomProperty{ + { + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + }, + { + PropertyName: Ptr("service"), + ValueType: PropertyValueTypeString, + }, + } + + mux.HandleFunc("/enterprises/e/properties/schema", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, struct { + Properties []*CustomProperty `json:"properties"` + }{ + Properties: properties, + }) + fmt.Fprint(w, `[ + { + "property_name": "name", + "value_type": "single_select", + "required": true + }, + { + "property_name": "service", + "value_type": "string" + } + ]`) + }) + + ctx := t.Context() + properties, _, err := client.Enterprise.CreateOrUpdateCustomProperties(ctx, "e", properties) + if err != nil { + t.Errorf("Enterprise.CreateOrUpdateCustomProperties returned error: %v", err) + } + + want := []*CustomProperty{ + { + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + }, + { + PropertyName: Ptr("service"), + ValueType: PropertyValueTypeString, + }, + } + + if !cmp.Equal(properties, want) { + t.Errorf("Enterprise.CreateOrUpdateCustomProperties returned %+v, want %+v", properties, want) + } + + const methodName = "CreateOrUpdateCustomProperties" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateOrUpdateCustomProperties(ctx, "e", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/properties/schema/name", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "property_name": "name", + "value_type": "single_select", + "required": true, + "default_value": "production", + "description": "Prod or dev environment", + "allowed_values":[ + "production", + "development" + ], + "values_editable_by": "org_actors" + }`) + }) + + ctx := t.Context() + property, _, err := client.Enterprise.GetCustomProperty(ctx, "e", "name") + if err != nil { + t.Errorf("Enterprise.GetCustomProperty returned error: %v", err) + } + + want := &CustomProperty{ + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + } + if !cmp.Equal(property, want) { + t.Errorf("Enterprise.GetCustomProperty returned %+v, want %+v", property, want) + } + + const methodName = "GetCustomProperty" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetCustomProperty(ctx, "e", "name") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateOrUpdateCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/properties/schema/name", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "property_name": "name", + "value_type": "single_select", + "required": true, + "default_value": "production", + "description": "Prod or dev environment", + "allowed_values":[ + "production", + "development" + ], + "values_editable_by": "org_actors" + }`) + }) + + ctx := t.Context() + property, _, err := client.Enterprise.CreateOrUpdateCustomProperty(ctx, "e", "name", &CustomProperty{ + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + }) + if err != nil { + t.Errorf("Enterprise.CreateOrUpdateCustomProperty returned error: %v", err) + } + + want := &CustomProperty{ + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + } + if !cmp.Equal(property, want) { + t.Errorf("Enterprise.CreateOrUpdateCustomProperty returned %+v, want %+v", property, want) + } + + const methodName = "CreateOrUpdateCustomProperty" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateOrUpdateCustomProperty(ctx, "e", "name", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/properties/schema/name", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.RemoveCustomProperty(ctx, "e", "name") + if err != nil { + t.Errorf("Enterprise.RemoveCustomProperty returned error: %v", err) + } + + const methodName = "RemoveCustomProperty" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.RemoveCustomProperty(ctx, "e", "name") + }) +} diff --git a/github/enterprise_rules.go b/github/enterprise_rules.go new file mode 100644 index 00000000000..b89567a344a --- /dev/null +++ b/github/enterprise_rules.go @@ -0,0 +1,93 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CreateRepositoryRuleset creates a repository ruleset for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules?apiVersion=2022-11-28#create-an-enterprise-repository-ruleset +// +//meta:operation POST /enterprises/{enterprise}/rulesets +func (s *EnterpriseService) CreateRepositoryRuleset(ctx context.Context, enterprise string, body RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// GetRepositoryRuleset gets a repository ruleset for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules?apiVersion=2022-11-28#get-an-enterprise-repository-ruleset +// +//meta:operation GET /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) GetRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *RepositoryRuleset + resp, err := s.client.Do(req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRepositoryRuleset updates a repository ruleset for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules?apiVersion=2022-11-28#update-an-enterprise-repository-ruleset +// +//meta:operation PUT /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) UpdateRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64, body RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// DeleteRepositoryRuleset deletes a repository ruleset from the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules?apiVersion=2022-11-28#delete-an-enterprise-repository-ruleset +// +//meta:operation DELETE /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) DeleteRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_rules_test.go b/github/enterprise_rules_test.go new file mode 100644 index 00000000000..73065a8e7d0 --- /dev/null +++ b/github/enterprise_rules_test.go @@ -0,0 +1,1835 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_CreateRepositoryRuleset_OrgNameRepoName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 84, + "name": "ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "organization_name": { + "include": [ + "important_organization", + "another_important_organization" + ], + "exclude": [ + "unimportant_organization" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationName: &RepositoryRulesetOrganizationNamesConditionParameters{ + Include: []string{"important_organization", "another_important_organization"}, + Exclude: []string{"unimportant_organization"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Enterprise.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(84)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeEnterprise), + Source: "e", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationName: &RepositoryRulesetOrganizationNamesConditionParameters{ + Include: []string{"important_organization", "another_important_organization"}, + Exclude: []string{"unimportant_organization"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Enterprise.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateRepositoryRuleset_OmitZero_Nil(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepositoryRuleset{ + Name: "test ruleset", + BypassActors: nil, + } + + mux.HandleFunc("/enterprises/e/rulesets/84", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id": 84, "name": "test ruleset"}`) + }) + + ctx := t.Context() + _, _, err := client.Enterprise.UpdateRepositoryRuleset(ctx, "e", 84, input) + if err != nil { + t.Errorf("Enterprise.UpdateRepositoryRuleset returned error: %v", err) + } +} + +func TestEnterpriseService_UpdateRepositoryRuleset_OmitZero_EmptySlice(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepositoryRuleset{ + Name: "test ruleset", + BypassActors: []*BypassActor{}, + } + + mux.HandleFunc("/enterprises/e/rulesets/84", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id": 84, "name": "test ruleset", "bypass_actors": []}`) + }) + + ctx := t.Context() + _, _, err := client.Enterprise.UpdateRepositoryRuleset(ctx, "e", 84, input) + if err != nil { + t.Errorf("Enterprise.UpdateRepositoryRuleset returned error: %v", err) + } +} + +func TestEnterpriseService_CreateRepositoryRuleset_OrgNameRepoProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 84, + "name": "ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "organization_name": { + "include": [ + "important_organization", + "another_important_organization" + ], + "exclude": [ + "unimportant_organization" + ] + }, + "repository_property": { + "include": [ + { + "name": "testIncludeProp", + "source": "custom", + "property_values": [ + "true" + ] + } + ], + "exclude": [ + { + "name": "testExcludeProp", + "property_values": [ + "false" + ] + } + ] + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationName: &RepositoryRulesetOrganizationNamesConditionParameters{ + Include: []string{"important_organization", "another_important_organization"}, + Exclude: []string{"unimportant_organization"}, + }, + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testExcludeProp", + PropertyValues: []string{"false"}, + }, + }, + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Enterprise.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(84)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeEnterprise), + Source: "e", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationName: &RepositoryRulesetOrganizationNamesConditionParameters{ + Include: []string{"important_organization", "another_important_organization"}, + Exclude: []string{"unimportant_organization"}, + }, + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testExcludeProp", + PropertyValues: []string{"false"}, + }, + }, + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Enterprise.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateRepositoryRuleset_OrgIdRepoName(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 84, + "name": "ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "organization_id": { + "organization_ids": [1001, 1002] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationID: &RepositoryRulesetOrganizationIDsConditionParameters{ + OrganizationIDs: []int64{1001, 1002}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Enterprise.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(84)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeEnterprise), + Source: "e", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationID: &RepositoryRulesetOrganizationIDsConditionParameters{ + OrganizationIDs: []int64{1001, 1002}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Enterprise.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateRepositoryRuleset_OrgIdRepoProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 84, + "name": "ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "organization_id": { + "organization_ids": [1001, 1002] + }, + "repository_property": { + "include": [ + { + "name": "testIncludeProp", + "source": "custom", + "property_values": [ + "true" + ] + } + ], + "exclude": [ + { + "name": "testExcludeProp", + "property_values": [ + "false" + ] + } + ] + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationID: &RepositoryRulesetOrganizationIDsConditionParameters{ + OrganizationIDs: []int64{1001, 1002}, + }, + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testExcludeProp", + PropertyValues: []string{"false"}, + }, + }, + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Enterprise.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(84)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeEnterprise), + Source: "e", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationID: &RepositoryRulesetOrganizationIDsConditionParameters{ + OrganizationIDs: []int64{1001, 1002}, + }, + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testExcludeProp", + PropertyValues: []string{"false"}, + }, + }, + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Enterprise.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateRepositoryRuleset(ctx, "e", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetRepositoryRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets/84", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 84, + "name": "test ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/enterprises/e/rulesets/84" + } + }, + "conditions": { + "organization_name": { + "include": [ + "important_organization", + "another_important_organization" + ], + "exclude": [ + "unimportant_organization" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := t.Context() + rulesets, _, err := client.Enterprise.GetRepositoryRuleset(ctx, "e", 84) + if err != nil { + t.Errorf("Enterprise.GetRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(84)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeEnterprise), + Source: "e", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/enterprises/e/rulesets/84")}, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationName: &RepositoryRulesetOrganizationNamesConditionParameters{ + Include: []string{"important_organization", "another_important_organization"}, + Exclude: []string{"unimportant_organization"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Enterprise.GetRepositoryRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "GetRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetRepositoryRuleset(ctx, "e", 84) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateRepositoryRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets/84", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "id": 84, + "name": "test ruleset", + "target": "branch", + "source_type": "Enterprise", + "source": "e", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/enterprises/e/rulesets/84" + } + }, + "conditions": { + "organization_name": { + "include": [ + "important_organization", + "another_important_organization" + ], + "exclude": [ + "unimportant_organization" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + }, + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := t.Context() + rulesets, _, err := client.Enterprise.UpdateRepositoryRuleset(ctx, "e", 84, RepositoryRuleset{ + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + }) + if err != nil { + t.Errorf("Enterprise.UpdateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(84)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeEnterprise), + Source: "e", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/enterprises/e/rulesets/84")}, + }, + Conditions: &RepositoryRulesetConditions{ + OrganizationName: &RepositoryRulesetOrganizationNamesConditionParameters{ + Include: []string{"important_organization", "another_important_organization"}, + Exclude: []string{"unimportant_organization"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Enterprise.UpdateRepositoryRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "UpdateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateRepositoryRuleset(ctx, "e", 84, RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteRepositoryRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/rulesets/84", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteRepositoryRuleset(ctx, "e", 84) + if err != nil { + t.Errorf("Enterprise.DeleteRepositoryRuleset returned error: %v", err) + } + + const methodName = "DeleteRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteRepositoryRuleset(ctx, "e", 84) + }) +} diff --git a/github/enterprise_scim.go b/github/enterprise_scim.go new file mode 100644 index 00000000000..674c9c33f61 --- /dev/null +++ b/github/enterprise_scim.go @@ -0,0 +1,481 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SCIMSchemasURINamespacesGroups is the SCIM schema URI namespace for group resources. +// This constant represents the standard SCIM core schema for group objects as defined by RFC 7643. +const SCIMSchemasURINamespacesGroups = "urn:ietf:params:scim:schemas:core:2.0:Group" + +// SCIMSchemasURINamespacesUser is the SCIM schema URI namespace for user resources. +// This constant represents the standard SCIM core schema for user objects as defined by RFC 7643. +const SCIMSchemasURINamespacesUser = "urn:ietf:params:scim:schemas:core:2.0:User" + +// SCIMSchemasURINamespacesListResponse is the SCIM schema URI namespace for list response resources. +// This constant represents the standard SCIM namespace for list responses used in paginated queries, as defined by RFC 7644. +const SCIMSchemasURINamespacesListResponse = "urn:ietf:params:scim:api:messages:2.0:ListResponse" + +// SCIMSchemasURINamespacesPatchOp is the SCIM schema URI namespace for patch operations. +// This constant represents the standard SCIM namespace for patch operations as defined by RFC 7644. +const SCIMSchemasURINamespacesPatchOp = "urn:ietf:params:scim:api:messages:2.0:PatchOp" + +// SCIMEnterpriseGroupAttributes represents supported SCIM Enterprise group attributes, and represents the result of calling UpdateSCIMGroupAttribute. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#supported-scim-group-attributes +type SCIMEnterpriseGroupAttributes struct { + DisplayName *string `json:"displayName,omitempty"` // Human-readable name for a group. + Members []*SCIMEnterpriseDisplayReference `json:"members,omitempty"` // List of members who are assigned to the group in SCIM provider + ExternalID *string `json:"externalId,omitempty"` // This identifier is generated by a SCIM provider. Must be unique per group. + Schemas []string `json:"schemas,omitempty"` // The URIs that are used to indicate the namespaces of the SCIM schemas. + // Bellow: Only populated as a result of calling UpdateSCIMGroupAttribute: + ID *string `json:"id,omitempty"` // The internally generated id for the group object. + Meta *SCIMEnterpriseMeta `json:"meta,omitempty"` // The metadata associated with the creation/updates to the group. +} + +// SCIMEnterpriseDisplayReference represents a JSON SCIM (System for Cross-domain Identity Management) resource reference. +type SCIMEnterpriseDisplayReference struct { + Value string `json:"value"` // The local unique identifier for the member (e.g., user ID or group ID). + Ref *string `json:"$ref,omitempty"` // The URI reference to the Members or Groups resource (e.g., /scim/v2/enterprises/{enterprise}/Users/{scim_user_id}). + Display *string `json:"display,omitempty"` // The display name associated with the member (e.g., user name or group name). +} + +// SCIMEnterpriseMeta represents metadata about the SCIM resource. +type SCIMEnterpriseMeta struct { + ResourceType string `json:"resourceType"` // A type of a resource (`User` or `Group`). + Created *Timestamp `json:"created,omitempty"` // A date and time when the user was created. + LastModified *Timestamp `json:"lastModified,omitempty"` // A date and time when the user was last modified. + Location *string `json:"location,omitempty"` // A URL location of an object +} + +// SCIMEnterpriseGroups represents the result of calling ListProvisionedSCIMGroups. +type SCIMEnterpriseGroups struct { + Schemas []string `json:"schemas,omitempty"` + TotalResults *int `json:"totalResults,omitempty"` + Resources []*SCIMEnterpriseGroupAttributes `json:"Resources,omitempty"` + StartIndex *int `json:"startIndex,omitempty"` + ItemsPerPage *int `json:"itemsPerPage,omitempty"` +} + +// ListProvisionedSCIMGroupsEnterpriseOptions represents query parameters for ListProvisionedSCIMGroups. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#list-provisioned-scim-groups-for-an-enterprise--parameters +type ListProvisionedSCIMGroupsEnterpriseOptions struct { + // If specified, only results that match the specified filter will be returned. + // Possible filters are `externalId`, `id`, and `displayName`. For example, `externalId eq "a123"`. + Filter *string `url:"filter,omitempty"` + // Excludes the specified attributes from being returned in the results. + ExcludedAttributes *string `url:"excludedAttributes,omitempty"` + // Used for pagination: the starting index of the first result to return when paginating through values. + // Default: 1. + StartIndex *int `url:"startIndex,omitempty"` + // Used for pagination: the number of results to return per page. + // Default: 30. + Count *int `url:"count,omitempty"` +} + +// GetProvisionedSCIMGroupEnterpriseOptions represents query parameters for GetProvisionedSCIMGroup. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#get-scim-provisioning-information-for-an-enterprise-group +type GetProvisionedSCIMGroupEnterpriseOptions struct { + // Excludes the specified attributes from being returned in the results. + ExcludedAttributes *string `url:"excludedAttributes,omitempty"` +} + +// SCIMEnterpriseUserAttributes represents supported SCIM enterprise user attributes, and represents the result of calling UpdateSCIMUserAttribute. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#supported-scim-user-attributes +type SCIMEnterpriseUserAttributes struct { + DisplayName string `json:"displayName"` // Human-readable name for a user + Name *SCIMEnterpriseUserName `json:"name,omitempty"` // The user's full name + UserName string `json:"userName"` // The username for the user (GitHub Account after normalized), generated by the SCIM provider. Must be unique per user. + Emails []*SCIMEnterpriseUserEmail `json:"emails"` // List of the user's emails. They all must be unique per user. + Roles []*SCIMEnterpriseUserRole `json:"roles,omitempty"` // List of the user's roles. + ExternalID string `json:"externalId"` // This identifier is generated by a SCIM provider. Must be unique per user. + Active bool `json:"active"` // Indicates whether the identity is active (true) or should be suspended (false). + Schemas []string `json:"schemas"` // The URIs that are used to indicate the namespaces of the SCIM schemas. + // Bellow: Only populated as a result of calling UpdateSCIMUserAttribute: + ID *string `json:"id,omitempty"` // Identifier generated by the GitHub's SCIM endpoint. + Groups []*SCIMEnterpriseDisplayReference `json:"groups,omitempty"` // List of groups who are assigned to the user in SCIM provider + Meta *SCIMEnterpriseMeta `json:"meta,omitempty"` // The metadata associated with the creation/updates to the user. +} + +// SCIMEnterpriseUserName represents SCIM enterprise user's name information. +type SCIMEnterpriseUserName struct { + GivenName string `json:"givenName"` // The first name of the user. + FamilyName string `json:"familyName"` // The last name of the user. + Formatted *string `json:"formatted,omitempty"` // The user's full name, including all middle names, titles, and suffixes, formatted for display. + MiddleName *string `json:"middleName,omitempty"` // The middle name(s) of the user. +} + +// SCIMEnterpriseUserEmail represents SCIM enterprise user's emails. +type SCIMEnterpriseUserEmail struct { + Value string `json:"value"` // The email address. + Primary bool `json:"primary"` // Whether this email address is the primary address. + Type string `json:"type"` // The type of email address +} + +// SCIMEnterpriseUserRole is an enterprise-wide role granted to the user. +type SCIMEnterpriseUserRole struct { + Value string `json:"value"` // The role value representing a user role in GitHub. + Display *string `json:"display,omitempty"` + Type *string `json:"type,omitempty"` + Primary *bool `json:"primary,omitempty"` // Is the role a primary role for the user? +} + +// SCIMEnterpriseUsers represents the result of calling ListProvisionedSCIMUsers. +type SCIMEnterpriseUsers struct { + Schemas []string `json:"schemas,omitempty"` + TotalResults *int `json:"totalResults,omitempty"` + ItemsPerPage *int `json:"itemsPerPage,omitempty"` + StartIndex *int `json:"startIndex,omitempty"` + Resources []*SCIMEnterpriseUserAttributes `json:"Resources,omitempty"` +} + +// ListProvisionedSCIMUsersEnterpriseOptions represents query parameters for ListProvisionedSCIMUsers. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#list-scim-provisioned-identities-for-an-enterprise +type ListProvisionedSCIMUsersEnterpriseOptions struct { + // If specified, only results that match the specified filter will be returned. + // Possible filters are `userName`, `externalId`, `id`, and `displayName`. For example, `externalId eq "a123"`. + Filter *string `url:"filter,omitempty"` + // Used for pagination: the starting index of the first result to return when paginating through values. + // Default: 1. + StartIndex *int `url:"startIndex,omitempty"` + // Used for pagination: the number of results to return per page. + // Default: 30. + Count *int `url:"count,omitempty"` +} + +// SCIMEnterpriseAttribute represents attribute operations for UpdateSCIMGroupAttribute or UpdateSCIMUserAttribute. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#update-an-attribute-for-a-scim-enterprise-group +type SCIMEnterpriseAttribute struct { + Schemas []string `json:"schemas"` // The URIs that are used to indicate the namespaces for a SCIM patch operation. + Operations []*SCIMEnterpriseAttributeOperation `json:"Operations"` // Set of operations to be performed. +} + +// SCIMEnterpriseAttributeOperation represents an operation for UpdateSCIMGroupAttribute or UpdateSCIMUserAttribute. +type SCIMEnterpriseAttributeOperation struct { + Op string `json:"op"` // Can be one of: `add`, `replace`, `remove`. + Path *string `json:"path,omitempty"` // Path to the attribute being modified (Filters are not supported). + Value any `json:"value,omitempty"` // New value for the attribute being modified. +} + +// ListProvisionedSCIMGroups lists provisioned SCIM groups in an enterprise. +// +// You can improve query search time by using the `excludedAttributes` and +// exclude the specified attributes, e.g. `members` to exclude members from the +// response. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#list-provisioned-scim-groups-for-an-enterprise +// +//meta:operation GET /scim/v2/enterprises/{enterprise}/Groups +func (s *EnterpriseService) ListProvisionedSCIMGroups(ctx context.Context, enterprise string, opts *ListProvisionedSCIMGroupsEnterpriseOptions) (*SCIMEnterpriseGroups, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var groups *SCIMEnterpriseGroups + resp, err := s.client.Do(req, &groups) + if err != nil { + return nil, resp, err + } + + return groups, resp, nil +} + +// ListProvisionedSCIMUsers lists provisioned SCIM enterprise users. +// +// When members are part of the group provisioning payload, they're designated +// as external group members. Providers are responsible for maintaining a +// mapping between the `externalId` and `id` for each user. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#list-scim-provisioned-identities-for-an-enterprise +// +//meta:operation GET /scim/v2/enterprises/{enterprise}/Users +func (s *EnterpriseService) ListProvisionedSCIMUsers(ctx context.Context, enterprise string, opts *ListProvisionedSCIMUsersEnterpriseOptions) (*SCIMEnterpriseUsers, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Users", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var users *SCIMEnterpriseUsers + resp, err := s.client.Do(req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// SetProvisionedSCIMGroup replaces an existing provisioned group’s information. +// +// You must provide all the information required for the group as if you were provisioning it for the first time. Any +// existing group information that you don't provide will be removed, including group membership. To update only +// specific attributes, refer to the `Enterprise.UpdateSCIMGroupAttribute()` method. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#set-scim-information-for-a-provisioned-enterprise-group +// +//meta:operation PUT /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} +func (s *EnterpriseService) SetProvisionedSCIMGroup(ctx context.Context, enterprise, scimGroupID string, body SCIMEnterpriseGroupAttributes) (*SCIMEnterpriseGroupAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups/%v", enterprise, scimGroupID) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var groupNew *SCIMEnterpriseGroupAttributes + resp, err := s.client.Do(req, &groupNew) + if err != nil { + return nil, resp, err + } + + return groupNew, resp, nil +} + +// SetProvisionedSCIMUser replaces an existing provisioned user's information. +// +// You must supply complete user information, just as you would when provisioning them initially. Any previously +// existing data not provided will be deleted. To update specific attributes only, refer to the +// `Enterprise.UpdateSCIMUserAttribute()` method. +// +// **Warning**: Setting `active: false` will suspend a user, and their handle and email will be obfuscated. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#set-scim-information-for-a-provisioned-enterprise-user +// +//meta:operation PUT /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} +func (s *EnterpriseService) SetProvisionedSCIMUser(ctx context.Context, enterprise, scimUserID string, body SCIMEnterpriseUserAttributes) (*SCIMEnterpriseUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Users/%v", enterprise, scimUserID) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var userNew *SCIMEnterpriseUserAttributes + resp, err := s.client.Do(req, &userNew) + if err != nil { + return nil, resp, err + } + + return userNew, resp, nil +} + +// UpdateSCIMGroupAttribute updates a provisioned group’s individual attributes. +// +// The `attribute` parameter must include at least one of the following +// Operations: `add`, `remove`, or `replace`. +// +// The update function can also be used to add group memberships. +// +// You can submit group memberships individually or in batches for improved +// efficiency. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#update-an-attribute-for-a-scim-enterprise-group +// +//meta:operation PATCH /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} +func (s *EnterpriseService) UpdateSCIMGroupAttribute(ctx context.Context, enterprise, scimGroupID string, body SCIMEnterpriseAttribute) (*SCIMEnterpriseGroupAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups/%v", enterprise, scimGroupID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var group *SCIMEnterpriseGroupAttributes + resp, err := s.client.Do(req, &group) + if err != nil { + return nil, resp, err + } + + return group, resp, nil +} + +// UpdateSCIMUserAttribute updates a provisioned user's individual attributes. +// +// The `attribute` parameter must include at least one of the following +// Operations: `add`, `remove`, or `replace`. +// +// Note: Complex SCIM path selectors that include filters are not supported. +// For example, a path selector defined as `"path": "emails[type eq \"work\"]"` +// will be ineffective. +// +// Warning: Setting `active: false` will suspend a user, and their handle and +// email will be obfuscated. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#update-an-attribute-for-a-scim-enterprise-user +// +//meta:operation PATCH /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} +func (s *EnterpriseService) UpdateSCIMUserAttribute(ctx context.Context, enterprise, scimUserID string, body SCIMEnterpriseAttribute) (*SCIMEnterpriseUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Users/%v", enterprise, scimUserID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var user *SCIMEnterpriseUserAttributes + resp, err := s.client.Do(req, &user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// ProvisionSCIMGroup creates a SCIM group for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#provision-a-scim-enterprise-group +// +//meta:operation POST /scim/v2/enterprises/{enterprise}/Groups +func (s *EnterpriseService) ProvisionSCIMGroup(ctx context.Context, enterprise string, body SCIMEnterpriseGroupAttributes) (*SCIMEnterpriseGroupAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups", enterprise) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var groupProvisioned *SCIMEnterpriseGroupAttributes + resp, err := s.client.Do(req, &groupProvisioned) + if err != nil { + return nil, resp, err + } + + return groupProvisioned, resp, nil +} + +// ProvisionSCIMUser creates an external identity for a new SCIM enterprise user. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#provision-a-scim-enterprise-user +// +//meta:operation POST /scim/v2/enterprises/{enterprise}/Users +func (s *EnterpriseService) ProvisionSCIMUser(ctx context.Context, enterprise string, body SCIMEnterpriseUserAttributes) (*SCIMEnterpriseUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Users", enterprise) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var userProvisioned *SCIMEnterpriseUserAttributes + resp, err := s.client.Do(req, &userProvisioned) + if err != nil { + return nil, resp, err + } + + return userProvisioned, resp, nil +} + +// GetProvisionedSCIMGroup gets information about a SCIM group. +// +// You can use the `excludedAttributes` from `opts` and exclude the specified +// attributes from being returned in the results. Using this parameter can +// speed up response time. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#get-scim-provisioning-information-for-an-enterprise-group +// +//meta:operation GET /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} +func (s *EnterpriseService) GetProvisionedSCIMGroup(ctx context.Context, enterprise, scimGroupID string, opts *GetProvisionedSCIMGroupEnterpriseOptions) (*SCIMEnterpriseGroupAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups/%v", enterprise, scimGroupID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var group *SCIMEnterpriseGroupAttributes + resp, err := s.client.Do(req, &group) + if err != nil { + return nil, resp, err + } + + return group, resp, nil +} + +// GetProvisionedSCIMUser gets information about a SCIM user. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#get-scim-provisioning-information-for-an-enterprise-user +// +//meta:operation GET /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} +func (s *EnterpriseService) GetProvisionedSCIMUser(ctx context.Context, enterprise, scimUserID string) (*SCIMEnterpriseUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Users/%v", enterprise, scimUserID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + req.Header.Set("Accept", mediaTypeSCIM) + + var user *SCIMEnterpriseUserAttributes + resp, err := s.client.Do(req, &user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// DeleteSCIMGroup deletes a SCIM group from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#delete-a-scim-group-from-an-enterprise +// +//meta:operation DELETE /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} +func (s *EnterpriseService) DeleteSCIMGroup(ctx context.Context, enterprise, scimGroupID string) (*Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups/%v", enterprise, scimGroupID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteSCIMUser deletes a SCIM user from an enterprise. +// +// Suspends a SCIM user permanently from an enterprise. This action will: +// remove all the user's data, anonymize their login, email, and display name, +// erase all external identity SCIM attributes, delete the user's emails, +// avatar, PATs, SSH keys, OAuth authorizations, GPG keys, and SAML mappings. +// This action is irreversible. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#delete-a-scim-user-from-an-enterprise +// +//meta:operation DELETE /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} +func (s *EnterpriseService) DeleteSCIMUser(ctx context.Context, enterprise, scimUserID string) (*Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Users/%v", enterprise, scimUserID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/enterprise_scim_test.go b/github/enterprise_scim_test.go new file mode 100644 index 00000000000..c0759fff5f2 --- /dev/null +++ b/github/enterprise_scim_test.go @@ -0,0 +1,967 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListProvisionedSCIMGroups(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/enterprises/ee/Groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeSCIM) + testFormValues(t, r, values{ + "startIndex": "1", + "excludedAttributes": "members,meta", + "count": "3", + "filter": `externalId eq "914a"`, + }) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesListResponse+`"], + "totalResults": 1, + "itemsPerPage": 1, + "startIndex": 1, + "Resources": [{ + "schemas": ["`+SCIMSchemasURINamespacesGroups+`"], + "id": "914a", + "externalId": "de88", + "displayName": "gn1", + "meta": { + "resourceType": "Group", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.com/scim/v2/enterprises/ee/Groups/914a" + }, + "members": [{ + "value": "e7f9", + "$ref": "https://api.github.com/scim/v2/enterprises/ee/Users/e7f9", + "display": "d1" + }] + }] + }`) + }) + + ctx := t.Context() + opts := &ListProvisionedSCIMGroupsEnterpriseOptions{ + StartIndex: Ptr(1), + ExcludedAttributes: Ptr("members,meta"), + Count: Ptr(3), + Filter: Ptr(`externalId eq "914a"`), + } + got, _, err := client.Enterprise.ListProvisionedSCIMGroups(ctx, "ee", opts) + if err != nil { + t.Fatalf("Enterprise.ListProvisionedSCIMGroups returned unexpected error: %v", err) + } + + want := &SCIMEnterpriseGroups{ + Schemas: []string{SCIMSchemasURINamespacesListResponse}, + TotalResults: Ptr(1), + ItemsPerPage: Ptr(1), + StartIndex: Ptr(1), + Resources: []*SCIMEnterpriseGroupAttributes{{ + ID: Ptr("914a"), + Meta: &SCIMEnterpriseMeta{ + ResourceType: "Group", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.com/scim/v2/enterprises/ee/Groups/914a"), + }, + DisplayName: Ptr("gn1"), + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ExternalID: Ptr("de88"), + Members: []*SCIMEnterpriseDisplayReference{{ + Value: "e7f9", + Ref: Ptr("https://api.github.com/scim/v2/enterprises/ee/Users/e7f9"), + Display: Ptr("d1"), + }}, + }}, + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.ListProvisionedSCIMGroups diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "ListProvisionedSCIMGroups" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListProvisionedSCIMGroups(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListProvisionedSCIMGroups(ctx, "ee", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ListProvisionedSCIMUsers(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/enterprises/ee/Users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeSCIM) + testFormValues(t, r, values{ + "startIndex": "1", + "count": "3", + "filter": `userName eq "octocat@github.com"`, + }) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesListResponse+`"], + "totalResults": 1, + "itemsPerPage": 1, + "startIndex": 1, + "Resources": [ + { + "schemas": ["`+SCIMSchemasURINamespacesUser+`"], + "id": "5fc0", + "externalId": "00u1", + "userName": "octocat@github.com", + "displayName": "Mona Octocat", + "name": { + "givenName": "Mona", + "familyName": "Octocat", + "formatted": "Mona Octocat" + }, + "emails": [ + { + "value": "octocat@github.com", + "primary": true + } + ], + "active": true, + "meta": { + "resourceType": "User", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.com/scim/v2/enterprises/ee/Users/5fc0" + } + } + ] + }`) + }) + + ctx := t.Context() + opts := &ListProvisionedSCIMUsersEnterpriseOptions{ + StartIndex: Ptr(1), + Count: Ptr(3), + Filter: Ptr(`userName eq "octocat@github.com"`), + } + got, _, err := client.Enterprise.ListProvisionedSCIMUsers(ctx, "ee", opts) + if err != nil { + t.Fatalf("Enterprise.ListProvisionedSCIMUsers returned unexpected error: %v", err) + } + + want := &SCIMEnterpriseUsers{ + Schemas: []string{SCIMSchemasURINamespacesListResponse}, + TotalResults: Ptr(1), + ItemsPerPage: Ptr(1), + StartIndex: Ptr(1), + Resources: []*SCIMEnterpriseUserAttributes{{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ID: Ptr("5fc0"), + ExternalID: "00u1", + UserName: "octocat@github.com", + DisplayName: "Mona Octocat", + Name: &SCIMEnterpriseUserName{ + GivenName: "Mona", + FamilyName: "Octocat", + Formatted: Ptr("Mona Octocat"), + }, + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "octocat@github.com", + Primary: true, + }}, + Active: true, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "User", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.com/scim/v2/enterprises/ee/Users/5fc0"), + }, + }}, + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.ListProvisionedSCIMUsers diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "ListProvisionedSCIMUsers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ListProvisionedSCIMUsers(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListProvisionedSCIMUsers(ctx, "ee", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_SetProvisionedSCIMGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SCIMEnterpriseGroupAttributes{ + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ExternalID: Ptr("8aa1"), + DisplayName: Ptr("dn"), + } + + mux.HandleFunc("/scim/v2/enterprises/ee/Groups/abcd", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeSCIM) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesGroups+`"], + "id": "abcd", + "externalId": "8aa1", + "displayName": "dn", + "meta": { + "resourceType": "Group", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.localhost/scim/v2/enterprises/ee/Groups/abcd" + } + }`) + }) + want := &SCIMEnterpriseGroupAttributes{ + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ID: Ptr("abcd"), + ExternalID: Ptr("8aa1"), + DisplayName: Ptr("dn"), + Meta: &SCIMEnterpriseMeta{ + ResourceType: "Group", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Groups/abcd"), + }, + } + + ctx := t.Context() + got, _, err := client.Enterprise.SetProvisionedSCIMGroup(ctx, "ee", "abcd", input) + if err != nil { + t.Fatalf("Enterprise.SetProvisionedSCIMGroup returned unexpected error: %v", err) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.SetProvisionedSCIMGroup diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "SetProvisionedSCIMGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.SetProvisionedSCIMGroup(ctx, "\n", "\n", SCIMEnterpriseGroupAttributes{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.SetProvisionedSCIMGroup(ctx, "ee", "abcd", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_SetProvisionedSCIMUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SCIMEnterpriseUserAttributes{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ExternalID: "e123", + Active: true, + UserName: "e123", + DisplayName: "John Doe", + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "john@example.com", + Type: "work", + Primary: true, + }}, + } + + mux.HandleFunc("/scim/v2/enterprises/ee/Users/7fce", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeSCIM) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesUser+`"], + "id": "7fce", + "externalId": "e123", + "active": true, + "userName": "e123", + "displayName": "John Doe", + "emails": [{ + "value": "john@example.com", + "type": "work", + "primary": true + }], + "meta": { + "resourceType": "User", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.localhost/scim/v2/enterprises/ee/Users/7fce" + } + }`) + }) + want := &SCIMEnterpriseUserAttributes{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ID: Ptr("7fce"), + ExternalID: "e123", + Active: true, + UserName: "e123", + DisplayName: "John Doe", + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "john@example.com", + Type: "work", + Primary: true, + }}, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "User", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Users/7fce"), + }, + } + + ctx := t.Context() + got, _, err := client.Enterprise.SetProvisionedSCIMUser(ctx, "ee", "7fce", input) + if err != nil { + t.Fatalf("Enterprise.SetProvisionedSCIMUser returned unexpected error: %v", err) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.SetProvisionedSCIMUser diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "SetProvisionedSCIMUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.SetProvisionedSCIMUser(ctx, "\n", "\n", SCIMEnterpriseUserAttributes{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.SetProvisionedSCIMUser(ctx, "ee", "7fce", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateSCIMGroupAttribute(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SCIMEnterpriseAttribute{ + Schemas: []string{SCIMSchemasURINamespacesPatchOp}, + Operations: []*SCIMEnterpriseAttributeOperation{{ + Op: "replace", + Path: Ptr("displayName"), + Value: "Employees", + }}, + } + + mux.HandleFunc("/scim/v2/enterprises/ee/Groups/abcd", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeSCIM) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesGroups+`"], + "id": "abcd", + "externalId": "8aa1", + "displayName": "Employees", + "members": [{ + "value": "879d", + "$ref": "https://api.github.localhost/scim/v2/enterprises/ee/Users/879d", + "display": "User 1" + }], + "meta": { + "resourceType": "Group", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.localhost/scim/v2/enterprises/ee/Groups/abcd" + } + }`) + }) + want := &SCIMEnterpriseGroupAttributes{ + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ID: Ptr("abcd"), + ExternalID: Ptr("8aa1"), + DisplayName: Ptr("Employees"), + Members: []*SCIMEnterpriseDisplayReference{{ + Value: "879d", + Ref: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Users/879d"), + Display: Ptr("User 1"), + }}, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "Group", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Groups/abcd"), + }, + } + + ctx := t.Context() + got, _, err := client.Enterprise.UpdateSCIMGroupAttribute(ctx, "ee", "abcd", input) + if err != nil { + t.Fatalf("Enterprise.UpdateSCIMGroupAttribute returned unexpected error: %v", err) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.UpdateSCIMGroupAttribute diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "UpdateSCIMGroupAttribute" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateSCIMGroupAttribute(ctx, "\n", "\n", SCIMEnterpriseAttribute{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateSCIMGroupAttribute(ctx, "ee", "abcd", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateSCIMUserAttribute(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SCIMEnterpriseAttribute{ + Schemas: []string{SCIMSchemasURINamespacesPatchOp}, + Operations: []*SCIMEnterpriseAttributeOperation{{ + Op: "replace", + Path: Ptr("emails[type eq 'work'].value"), + Value: "updatedEmail@example.com", + }, { + Op: "replace", + Path: Ptr("name.familyName"), + Value: "updatedFamilyName", + }}, + } + + mux.HandleFunc("/scim/v2/enterprises/ee/Users/7fce", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeSCIM) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesUser+`"], + "id": "7fce", + "externalId": "e123", + "active": true, + "userName": "e123", + "name": { + "formatted": "John Doe X", + "familyName": "updatedFamilyName", + "givenName": "John", + "middleName": "X" + }, + "displayName": "John Doe", + "emails": [{ + "value": "john@example.com", + "type": "work", + "primary": true + }], + "roles": [{ + "value": "User", + "primary": false + }], + "meta": { + "resourceType": "User", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.localhost/scim/v2/enterprises/ee/Users/7fce" + } + }`) + }) + want := &SCIMEnterpriseUserAttributes{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ID: Ptr("7fce"), + ExternalID: "e123", + Active: true, + UserName: "e123", + DisplayName: "John Doe", + Name: &SCIMEnterpriseUserName{ + Formatted: Ptr("John Doe X"), + FamilyName: "updatedFamilyName", + GivenName: "John", + MiddleName: Ptr("X"), + }, + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "john@example.com", + Type: "work", + Primary: true, + }}, + Roles: []*SCIMEnterpriseUserRole{{ + Value: "User", + Primary: Ptr(false), + }}, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "User", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Users/7fce"), + }, + } + + ctx := t.Context() + got, _, err := client.Enterprise.UpdateSCIMUserAttribute(ctx, "ee", "7fce", input) + if err != nil { + t.Fatalf("Enterprise.UpdateSCIMUserAttribute returned unexpected error: %v", err) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.UpdateSCIMUserAttribute diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "UpdateSCIMUserAttribute" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.UpdateSCIMUserAttribute(ctx, "\n", "\n", SCIMEnterpriseAttribute{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateSCIMUserAttribute(ctx, "ee", "7fce", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ProvisionSCIMGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SCIMEnterpriseGroupAttributes{ + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ExternalID: Ptr("8aa1"), + DisplayName: Ptr("dn"), + Members: []*SCIMEnterpriseDisplayReference{{ + Value: "879d", + Display: Ptr("d1"), + }, { + Value: "0db5", + Display: Ptr("d2"), + }}, + } + + mux.HandleFunc("/scim/v2/enterprises/ee/Groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeSCIM) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesGroups+`"], + "id": "abcd", + "externalId": "8aa1", + "displayName": "dn", + "members": [ + { + "value": "879d", + "$ref": "https://api.github.localhost/scim/v2/enterprises/ee/Users/879d", + "display": "d1" + }, + { + "value": "0db5", + "$ref": "https://api.github.localhost/scim/v2/enterprises/ee/Users/0db5", + "display": "d2" + } + ], + "meta": { + "resourceType": "Group", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.localhost/scim/v2/enterprises/ee/Groups/abcd" + } + }`) + }) + want := &SCIMEnterpriseGroupAttributes{ + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ID: Ptr("abcd"), + ExternalID: Ptr("8aa1"), + DisplayName: Ptr("dn"), + Members: []*SCIMEnterpriseDisplayReference{{ + Value: "879d", + Ref: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Users/879d"), + Display: Ptr("d1"), + }, { + Value: "0db5", + Ref: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Users/0db5"), + Display: Ptr("d2"), + }}, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "Group", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Groups/abcd"), + }, + } + + ctx := t.Context() + got, _, err := client.Enterprise.ProvisionSCIMGroup(ctx, "ee", input) + if err != nil { + t.Fatalf("Enterprise.ProvisionSCIMGroup returned unexpected error: %v", err) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.ProvisionSCIMGroup diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "ProvisionSCIMGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ProvisionSCIMGroup(ctx, "\n", SCIMEnterpriseGroupAttributes{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ProvisionSCIMGroup(ctx, "ee", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_ProvisionSCIMUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SCIMEnterpriseUserAttributes{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ExternalID: "e123", + Active: true, + UserName: "e123", + Name: &SCIMEnterpriseUserName{ + Formatted: Ptr("John Doe"), + FamilyName: "Doe", + GivenName: "John", + }, + DisplayName: "DOE John", + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "john@example.com", + Type: "work", + Primary: true, + }}, + Roles: []*SCIMEnterpriseUserRole{{ + Value: "User", + Primary: Ptr(false), + }}, + } + + mux.HandleFunc("/scim/v2/enterprises/ee/Users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeSCIM) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesUser+`"], + "id": "7fce", + "externalId": "e123", + "active": true, + "userName": "e123", + "name": { + "formatted": "John Doe", + "familyName": "Doe", + "givenName": "John" + }, + "displayName": "DOE John", + "emails": [{ + "value": "john@example.com", + "type": "work", + "primary": true + }], + "roles": [{ + "value": "User", + "primary": false + }], + "meta": { + "resourceType": "User", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.localhost/scim/v2/enterprises/ee/Users/7fce" + } + }`) + }) + want := &SCIMEnterpriseUserAttributes{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ID: Ptr("7fce"), + ExternalID: "e123", + Active: true, + UserName: "e123", + DisplayName: "DOE John", + Name: &SCIMEnterpriseUserName{ + Formatted: Ptr("John Doe"), + FamilyName: "Doe", + GivenName: "John", + }, + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "john@example.com", + Type: "work", + Primary: true, + }}, + Roles: []*SCIMEnterpriseUserRole{{ + Value: "User", + Primary: Ptr(false), + }}, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "User", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.localhost/scim/v2/enterprises/ee/Users/7fce"), + }, + } + + ctx := t.Context() + got, _, err := client.Enterprise.ProvisionSCIMUser(ctx, "ee", input) + if err != nil { + t.Fatalf("Enterprise.ProvisionSCIMUser returned unexpected error: %v", err) + } + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.ProvisionSCIMUser diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "ProvisionSCIMUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.ProvisionSCIMUser(ctx, "\n", SCIMEnterpriseUserAttributes{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ProvisionSCIMUser(ctx, "ee", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetProvisionedSCIMGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/enterprises/ee/Groups/914a", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeSCIM) + testFormValues(t, r, values{"excludedAttributes": "members,meta"}) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesGroups+`"], + "id": "914a", + "externalId": "de88", + "displayName": "gn1", + "meta": { + "resourceType": "Group", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.com/scim/v2/enterprises/ee/Groups/914a" + }, + "members": [{ + "value": "e7f9", + "$ref": "https://api.github.com/scim/v2/enterprises/ee/Users/e7f9", + "display": "d1" + }] + }`) + }) + + ctx := t.Context() + opts := &GetProvisionedSCIMGroupEnterpriseOptions{ExcludedAttributes: Ptr("members,meta")} + got, _, err := client.Enterprise.GetProvisionedSCIMGroup(ctx, "ee", "914a", opts) + if err != nil { + t.Fatalf("Enterprise.GetProvisionedSCIMGroup returned unexpected error: %v", err) + } + + want := &SCIMEnterpriseGroupAttributes{ + ID: Ptr("914a"), + Meta: &SCIMEnterpriseMeta{ + ResourceType: "Group", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.com/scim/v2/enterprises/ee/Groups/914a"), + }, + DisplayName: Ptr("gn1"), + Schemas: []string{SCIMSchemasURINamespacesGroups}, + ExternalID: Ptr("de88"), + Members: []*SCIMEnterpriseDisplayReference{{ + Value: "e7f9", + Ref: Ptr("https://api.github.com/scim/v2/enterprises/ee/Users/e7f9"), + Display: Ptr("d1"), + }}, + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.GetProvisionedSCIMGroup diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "GetProvisionedSCIMGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetProvisionedSCIMGroup(ctx, "ee", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetProvisionedSCIMGroup(ctx, "ee", "914a", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetProvisionedSCIMUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/enterprises/ee/Users/5fc0", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeSCIM) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{ + "schemas": ["`+SCIMSchemasURINamespacesUser+`"], + "id": "5fc0", + "externalId": "00u1", + "userName": "octocat@github.com", + "displayName": "Mona Octocat", + "name": { + "givenName": "Mona", + "familyName": "Octocat", + "formatted": "Mona Octocat" + }, + "emails": [ + { + "value": "octocat@github.com", + "primary": true + } + ], + "active": true, + "meta": { + "resourceType": "User", + "created": `+referenceTimeStr+`, + "lastModified": `+referenceTimeStr+`, + "location": "https://api.github.com/scim/v2/enterprises/ee/Users/5fc0" + } + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.GetProvisionedSCIMUser(ctx, "ee", "5fc0") + if err != nil { + t.Fatalf("Enterprise.GetProvisionedSCIMUser returned unexpected error: %v", err) + } + + want := &SCIMEnterpriseUserAttributes{ + Schemas: []string{SCIMSchemasURINamespacesUser}, + ID: Ptr("5fc0"), + ExternalID: "00u1", + UserName: "octocat@github.com", + DisplayName: "Mona Octocat", + Name: &SCIMEnterpriseUserName{ + GivenName: "Mona", + FamilyName: "Octocat", + Formatted: Ptr("Mona Octocat"), + }, + Emails: []*SCIMEnterpriseUserEmail{{ + Value: "octocat@github.com", + Primary: true, + }}, + Active: true, + Meta: &SCIMEnterpriseMeta{ + ResourceType: "User", + Created: &Timestamp{referenceTime}, + LastModified: &Timestamp{referenceTime}, + Location: Ptr("https://api.github.com/scim/v2/enterprises/ee/Users/5fc0"), + }, + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Fatalf("Enterprise.GetProvisionedSCIMUser diff mismatch (-want +got):\n%v", diff) + } + + const methodName = "GetProvisionedSCIMUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Enterprise.GetProvisionedSCIMUser(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetProvisionedSCIMUser(ctx, "ee", "5fc0") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteSCIMGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/enterprises/ee/Groups/abcd", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeV3) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteSCIMGroup(ctx, "ee", "abcd") + if err != nil { + t.Fatalf("Enterprise.DeleteSCIMGroup returned unexpected error: %v", err) + } + + const methodName = "DeleteSCIMGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteSCIMGroup(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteSCIMGroup(ctx, "ee", "abcd") + }) +} + +func TestEnterpriseService_DeleteSCIMUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/enterprises/ee/Users/7fce", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeV3) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteSCIMUser(ctx, "ee", "7fce") + if err != nil { + t.Fatalf("Enterprise.DeleteSCIMUser returned unexpected error: %v", err) + } + + const methodName = "DeleteSCIMUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Enterprise.DeleteSCIMUser(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteSCIMUser(ctx, "ee", "7fce") + }) +} diff --git a/github/enterprise_team.go b/github/enterprise_team.go new file mode 100644 index 00000000000..fde014d7a39 --- /dev/null +++ b/github/enterprise_team.go @@ -0,0 +1,420 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnterpriseTeam represent a team in a GitHub Enterprise. +type EnterpriseTeam struct { + ID int64 `json:"id"` + URL string `json:"url"` + MemberURL string `json:"member_url"` + Name string `json:"name"` + Description *string `json:"description,omitempty"` + HTMLURL string `json:"html_url"` + Slug string `json:"slug"` + CreatedAt Timestamp `json:"created_at"` + UpdatedAt Timestamp `json:"updated_at"` + GroupID string `json:"group_id"` + OrganizationSelectionType *string `json:"organization_selection_type,omitempty"` +} + +// EnterpriseTeamCreateOrUpdateRequest is used to create or update an enterprise team. +type EnterpriseTeamCreateOrUpdateRequest struct { + // The name of the team. + Name string `json:"name"` + // A description of the team. + Description *string `json:"description,omitempty"` + // Specifies which organizations in the enterprise should have access to this team. + // Possible values are "disabled" , "all" and "selected". If not specified, the default is "disabled". + OrganizationSelectionType *string `json:"organization_selection_type,omitempty"` + // The ID of the IdP group to assign team membership with. + GroupID *string `json:"group_id,omitempty"` +} + +// ListTeams lists all teams in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-teams?apiVersion=2022-11-28#list-enterprise-teams +// +//meta:operation GET /enterprises/{enterprise}/teams +func (s *EnterpriseService) ListTeams(ctx context.Context, enterprise string, opts *ListOptions) ([]*EnterpriseTeam, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var teams []*EnterpriseTeam + resp, err := s.client.Do(req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// CreateTeam creates a new team in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-teams?apiVersion=2022-11-28#create-an-enterprise-team +// +//meta:operation POST /enterprises/{enterprise}/teams +func (s *EnterpriseService) CreateTeam(ctx context.Context, enterprise string, body EnterpriseTeamCreateOrUpdateRequest) (*EnterpriseTeam, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams", enterprise) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var createdTeam *EnterpriseTeam + resp, err := s.client.Do(req, &createdTeam) + if err != nil { + return nil, resp, err + } + + return createdTeam, resp, nil +} + +// GetTeam retrieves a team in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-teams?apiVersion=2022-11-28#get-an-enterprise-team +// +//meta:operation GET /enterprises/{enterprise}/teams/{team_slug} +func (s *EnterpriseService) GetTeam(ctx context.Context, enterprise, teamSlug string) (*EnterpriseTeam, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v", enterprise, teamSlug) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var team *EnterpriseTeam + resp, err := s.client.Do(req, &team) + if err != nil { + return nil, resp, err + } + + return team, resp, nil +} + +// UpdateTeam updates a team in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-teams?apiVersion=2022-11-28#update-an-enterprise-team +// +//meta:operation PATCH /enterprises/{enterprise}/teams/{team_slug} +func (s *EnterpriseService) UpdateTeam(ctx context.Context, enterprise, teamSlug string, body EnterpriseTeamCreateOrUpdateRequest) (*EnterpriseTeam, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v", enterprise, teamSlug) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var updatedTeam *EnterpriseTeam + resp, err := s.client.Do(req, &updatedTeam) + if err != nil { + return nil, resp, err + } + + return updatedTeam, resp, nil +} + +// DeleteTeam deletes a team in an enterprise. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-teams?apiVersion=2022-11-28#delete-an-enterprise-team +// +//meta:operation DELETE /enterprises/{enterprise}/teams/{team_slug} +func (s *EnterpriseService) DeleteTeam(ctx context.Context, enterprise, teamSlug string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v", enterprise, teamSlug) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListTeamMembers lists all members of an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-members?apiVersion=2022-11-28#list-members-in-an-enterprise-team +// +//meta:operation GET /enterprises/{enterprise}/teams/{enterprise-team}/memberships +func (s *EnterpriseService) ListTeamMembers(ctx context.Context, enterprise, enterpriseTeam string, opts *ListOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/memberships", enterprise, enterpriseTeam) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var members []*User + resp, err := s.client.Do(req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil +} + +// BulkAddTeamMembers adds multiple members to an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-members?apiVersion=2022-11-28#bulk-add-team-members +// +//meta:operation POST /enterprises/{enterprise}/teams/{enterprise-team}/memberships/add +func (s *EnterpriseService) BulkAddTeamMembers(ctx context.Context, enterprise, enterpriseTeam string, username []string) ([]*User, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/memberships/add", enterprise, enterpriseTeam) + req, err := s.client.NewRequest(ctx, "POST", u, map[string][]string{"usernames": username}) + if err != nil { + return nil, nil, err + } + + var members []*User + resp, err := s.client.Do(req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil +} + +// BulkRemoveTeamMembers removes multiple members from an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-members?apiVersion=2022-11-28#bulk-remove-team-members +// +//meta:operation POST /enterprises/{enterprise}/teams/{enterprise-team}/memberships/remove +func (s *EnterpriseService) BulkRemoveTeamMembers(ctx context.Context, enterprise, enterpriseTeam string, username []string) ([]*User, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/memberships/remove", enterprise, enterpriseTeam) + req, err := s.client.NewRequest(ctx, "POST", u, map[string][]string{"usernames": username}) + if err != nil { + return nil, nil, err + } + + var members []*User + resp, err := s.client.Do(req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil +} + +// GetTeamMembership retrieves a team membership for a user in an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-members?apiVersion=2022-11-28#get-enterprise-team-membership +// +//meta:operation GET /enterprises/{enterprise}/teams/{enterprise-team}/memberships/{username} +func (s *EnterpriseService) GetTeamMembership(ctx context.Context, enterprise, enterpriseTeam, username string) (*User, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/memberships/%v", enterprise, enterpriseTeam, username) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var membership *User + resp, err := s.client.Do(req, &membership) + if err != nil { + return nil, resp, err + } + + return membership, resp, nil +} + +// AddTeamMember adds a member to an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-members?apiVersion=2022-11-28#add-team-member +// +//meta:operation PUT /enterprises/{enterprise}/teams/{enterprise-team}/memberships/{username} +func (s *EnterpriseService) AddTeamMember(ctx context.Context, enterprise, enterpriseTeam, username string) (*User, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/memberships/%v", enterprise, enterpriseTeam, username) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, nil, err + } + + var member *User + resp, err := s.client.Do(req, &member) + if err != nil { + return nil, resp, err + } + + return member, resp, nil +} + +// RemoveTeamMember removes a member from an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-members?apiVersion=2022-11-28#remove-team-membership +// +//meta:operation DELETE /enterprises/{enterprise}/teams/{enterprise-team}/memberships/{username} +func (s *EnterpriseService) RemoveTeamMember(ctx context.Context, enterprise, enterpriseTeam, username string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/memberships/%v", enterprise, enterpriseTeam, username) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListAssignments gets all organizations assigned to an enterprise team. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations?apiVersion=2022-11-28#get-organization-assignments +// +//meta:operation GET /enterprises/{enterprise}/teams/{enterprise-team}/organizations +func (s *EnterpriseService) ListAssignments(ctx context.Context, enterprise, enterpriseTeam string, opts *ListOptions) ([]*Organization, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/organizations", enterprise, enterpriseTeam) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var orgs []*Organization + resp, err := s.client.Do(req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// AddMultipleAssignments assigns an enterprise team to multiple organizations. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations?apiVersion=2022-11-28#add-organization-assignments +// +//meta:operation POST /enterprises/{enterprise}/teams/{enterprise-team}/organizations/add +func (s *EnterpriseService) AddMultipleAssignments(ctx context.Context, enterprise, enterpriseTeam string, organizationSlugs []string) ([]*Organization, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/organizations/add", enterprise, enterpriseTeam) + + req, err := s.client.NewRequest(ctx, "POST", u, map[string][]string{"organization_slugs": organizationSlugs}) + if err != nil { + return nil, nil, err + } + + var orgs []*Organization + resp, err := s.client.Do(req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// RemoveMultipleAssignments unassigns an enterprise team from multiple organizations. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations?apiVersion=2022-11-28#remove-organization-assignments +// +//meta:operation POST /enterprises/{enterprise}/teams/{enterprise-team}/organizations/remove +func (s *EnterpriseService) RemoveMultipleAssignments(ctx context.Context, enterprise, enterpriseTeam string, organizationSlugs []string) ([]*Organization, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/organizations/remove", enterprise, enterpriseTeam) + + req, err := s.client.NewRequest(ctx, "POST", u, map[string][]string{"organization_slugs": organizationSlugs}) + if err != nil { + return nil, nil, err + } + + var orgs []*Organization + resp, err := s.client.Do(req, &orgs) + if err != nil { + return nil, resp, err + } + + return orgs, resp, nil +} + +// GetAssignment checks if an enterprise team is assigned to an organization. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations?apiVersion=2022-11-28#get-organization-assignment +// +//meta:operation GET /enterprises/{enterprise}/teams/{enterprise-team}/organizations/{org} +func (s *EnterpriseService) GetAssignment(ctx context.Context, enterprise, enterpriseTeam, org string) (*Organization, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/organizations/%v", enterprise, enterpriseTeam, org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var organization *Organization + resp, err := s.client.Do(req, &organization) + if err != nil { + return nil, resp, err + } + + return organization, resp, nil +} + +// AddAssignment assigns an enterprise team to an organizations. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations?apiVersion=2022-11-28#add-an-organization-assignment +// +//meta:operation PUT /enterprises/{enterprise}/teams/{enterprise-team}/organizations/{org} +func (s *EnterpriseService) AddAssignment(ctx context.Context, enterprise, enterpriseTeam, org string) (*Organization, *Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/organizations/%v", enterprise, enterpriseTeam, org) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, nil, err + } + + var organization *Organization + resp, err := s.client.Do(req, &organization) + if err != nil { + return nil, resp, err + } + + return organization, resp, nil +} + +// RemoveAssignment unassigns an enterprise team from an organizations. +// +// GitHub API docs: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations?apiVersion=2022-11-28#delete-an-organization-assignment +// +//meta:operation DELETE /enterprises/{enterprise}/teams/{enterprise-team}/organizations/{org} +func (s *EnterpriseService) RemoveAssignment(ctx context.Context, enterprise, enterpriseTeam, org string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/teams/%v/organizations/%v", enterprise, enterpriseTeam, org) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/enterprise_team_test.go b/github/enterprise_team_test.go new file mode 100644 index 00000000000..e8a9562fcae --- /dev/null +++ b/github/enterprise_team_test.go @@ -0,0 +1,747 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestEnterpriseService_ListTeams(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "id": 1, + "url": "https://example.com/team1", + "member_url": "https://example.com/members", + "name": "Team One", + "html_url": "https://example.com/html", + "slug": "team-one", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "group_id": "99" + }]`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + got, _, err := client.Enterprise.ListTeams(ctx, "e", opts) + if err != nil { + t.Fatalf("Enterprise.ListTeams returned error: %v", err) + } + + want := []*EnterpriseTeam{ + { + ID: 1, + URL: "https://example.com/team1", + MemberURL: "https://example.com/members", + Name: "Team One", + HTMLURL: "https://example.com/html", + Slug: "team-one", + GroupID: "99", + CreatedAt: *refTimestamp(1136178000), + UpdatedAt: *refTimestamp(1136178001), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.ListTeams = %+v, want %+v", got, want) + } + + const methodName = "ListTeams" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.ListTeams(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListTeams(ctx, "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_CreateTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := EnterpriseTeamCreateOrUpdateRequest{ + Name: "New Team", + } + + mux.HandleFunc("/enterprises/e/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 10, + "name": "New Team", + "slug": "new-team", + "url": "https://example.com/team" + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.CreateTeam(ctx, "e", input) + if err != nil { + t.Fatalf("Enterprise.CreateTeam returned error: %v", err) + } + + want := &EnterpriseTeam{ + ID: 10, + Name: "New Team", + Slug: "new-team", + URL: "https://example.com/team", + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.CreateTeam = %+v, want %+v", got, want) + } + + const methodName = "CreateTeam" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.CreateTeam(ctx, "e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 2, + "name": "Team One", + "slug": "t1" + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.GetTeam(ctx, "e", "t1") + if err != nil { + t.Fatalf("Enterprise.GetTeam returned error: %v", err) + } + + want := &EnterpriseTeam{ + ID: 2, + Name: "Team One", + Slug: "t1", + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.GetTeam = %+v, want %+v", got, want) + } + + const methodName = "GetTeam" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.GetTeam(ctx, "\n", "t1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetTeam(ctx, "e", "t1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_UpdateTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := EnterpriseTeamCreateOrUpdateRequest{ + Name: "Updated Team", + } + + mux.HandleFunc("/enterprises/e/teams/t1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 3, + "name": "Updated Team", + "slug": "t1" + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.UpdateTeam(ctx, "e", "t1", input) + if err != nil { + t.Fatalf("Enterprise.UpdateTeam returned error: %v", err) + } + + want := &EnterpriseTeam{ + ID: 3, + Name: "Updated Team", + Slug: "t1", + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.UpdateTeam = %+v, want %+v", got, want) + } + + const methodName = "UpdateTeam" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.UpdateTeam(ctx, "\n", "t1", input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.UpdateTeam(ctx, "e", "t1", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_DeleteTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Enterprise.DeleteTeam(ctx, "e", "t1") + if err != nil { + t.Fatalf("Enterprise.DeleteTeam returned error: %v", err) + } + + const methodName = "DeleteTeam" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.DeleteTeam(ctx, "\n", "t1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.DeleteTeam(ctx, "e", "t1") + }) +} + +func TestEnterpriseService_ListTeamMembers(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/memberships", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "login": "user1", + "id": 1001, + "url": "https://example.com/user1" + }]`) + }) + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + got, _, err := client.Enterprise.ListTeamMembers(ctx, "e", "t1", opts) + if err != nil { + t.Fatalf("Enterprise.ListTeamMembers returned error: %v", err) + } + + want := []*User{ + { + Login: Ptr("user1"), + ID: Ptr(int64(1001)), + URL: Ptr("https://example.com/user1"), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.ListTeamMembers = %+v, want %+v", got, want) + } + + const methodName = "ListTeamMembers" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.ListTeamMembers(ctx, "\n", "t1", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListTeamMembers(ctx, "e", "t1", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_BulkAddTeamMembers(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/memberships/add", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + + fmt.Fprint(w, `[{ + "login": "u1", + "id": 1 + },{ + "login": "u2", + "id": 2 + }]`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.BulkAddTeamMembers(ctx, "e", "t1", []string{"u1", "u2"}) + if err != nil { + t.Fatalf("BulkAddTeamMembers returned error: %v", err) + } + + want := []*User{ + {Login: Ptr("u1"), ID: Ptr(int64(1))}, + {Login: Ptr("u2"), ID: Ptr(int64(2))}, + } + + if !cmp.Equal(got, want) { + t.Errorf("BulkAddTeamMembers = %+v, want %+v", got, want) + } + + const methodName = "BulkAddTeamMembers" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.BulkAddTeamMembers(ctx, "\n", "t1", []string{"u1"}) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.BulkAddTeamMembers(ctx, "e", "t1", []string{"u1"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_BulkRemoveTeamMembers(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/memberships/remove", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + + fmt.Fprint(w, `[{ + "login": "u1", + "id": 1 + },{ + "login": "u2", + "id": 2 + }]`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.BulkRemoveTeamMembers(ctx, "e", "t1", []string{"u1", "u2"}) + if err != nil { + t.Fatalf("BulkRemoveTeamMembers returned error: %v", err) + } + + want := []*User{ + {Login: Ptr("u1"), ID: Ptr(int64(1))}, + {Login: Ptr("u2"), ID: Ptr(int64(2))}, + } + + if !cmp.Equal(got, want) { + t.Errorf("BulkRemoveTeamMembers = %+v, want %+v", got, want) + } + + const methodName = "BulkRemoveTeamMembers" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.BulkRemoveTeamMembers(ctx, "\n", "t1", []string{"u1"}) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.BulkRemoveTeamMembers(ctx, "e", "t1", []string{"u1"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetTeamMembership(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/memberships/u1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "login": "u1", + "id": 10 + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.GetTeamMembership(ctx, "e", "t1", "u1") + if err != nil { + t.Fatalf("GetTeamMembership returned error: %v", err) + } + + want := &User{ + Login: Ptr("u1"), + ID: Ptr(int64(10)), + } + + if !cmp.Equal(got, want) { + t.Errorf("GetTeamMembership = %+v, want %+v", got, want) + } + + const methodName = "GetTeamMembership" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.GetTeamMembership(ctx, "\n", "t1", "u1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetTeamMembership(ctx, "e", "t1", "u1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_AddTeamMember(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/memberships/u1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "login": "u1", + "id": 5 + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.AddTeamMember(ctx, "e", "t1", "u1") + if err != nil { + t.Fatalf("AddTeamMember returned error: %v", err) + } + + want := &User{ + Login: Ptr("u1"), + ID: Ptr(int64(5)), + } + + if !cmp.Equal(got, want) { + t.Errorf("AddTeamMember = %+v, want %+v", got, want) + } + + const methodName = "AddTeamMember" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.AddTeamMember(ctx, "\n", "t1", "u1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.AddTeamMember(ctx, "e", "t1", "u1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveTeamMember(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/memberships/u1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Enterprise.RemoveTeamMember(ctx, "e", "t1", "u1") + if err != nil { + t.Fatalf("RemoveTeamMember returned error: %v", err) + } + if resp == nil { + t.Fatal("RemoveTeamMember returned nil Response") + } + + const methodName = "RemoveTeamMember" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.RemoveTeamMember(ctx, "\n", "t1", "u1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.RemoveTeamMember(ctx, "e", "t1", "u1") + }) +} + +func TestEnterpriseService_ListAssignments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/organizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "login": "team-one", + "id": 1, + "node_id": "node-id", + "url": "https://example.com/team1", + "repos_url": "https://example.com/members", + "events_url": "https://example.com/events", + "hooks_url": "https://api.github.com/orgs/team-one/hooks", + "issues_url": "https://api.github.com/orgs/team-one/issues", + "members_url": "https://api.github.com/orgs/team-one/members", + "public_members_url": "https://api.github.com/orgs/team-one/public_members", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "description": "Team One" + } + ]`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + got, _, err := client.Enterprise.ListAssignments(ctx, "e", "t1", opts) + if err != nil { + t.Fatalf("Enterprise.ListAssignments returned error: %v", err) + } + + want := []*Organization{ + { + Login: Ptr("team-one"), + URL: Ptr("https://example.com/team1"), + NodeID: Ptr("node-id"), + ReposURL: Ptr("https://example.com/members"), + EventsURL: Ptr("https://example.com/events"), + ID: Ptr(int64(1)), + HooksURL: Ptr("https://api.github.com/orgs/team-one/hooks"), + IssuesURL: Ptr("https://api.github.com/orgs/team-one/issues"), + MembersURL: Ptr("https://api.github.com/orgs/team-one/members"), + PublicMembersURL: Ptr("https://api.github.com/orgs/team-one/public_members"), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + Description: Ptr("Team One"), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Enterprise.ListAssignments = %+v, want %+v", got, want) + } + + const methodName = "ListAssignments" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.ListAssignments(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.ListAssignments(ctx, "e", "t1", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_AddMultipleAssignments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/organizations/add", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + + fmt.Fprint(w, `[{ + "login": "o1", + "id": 1 + },{ + "login": "o2", + "id": 2 + }]`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.AddMultipleAssignments(ctx, "e", "t1", []string{"o1", "o2"}) + if err != nil { + t.Fatalf("AddMultipleAssignments returned error: %v", err) + } + + want := []*Organization{ + {Login: Ptr("o1"), ID: Ptr(int64(1))}, + {Login: Ptr("o2"), ID: Ptr(int64(2))}, + } + + if !cmp.Equal(got, want) { + t.Errorf("AddMultipleAssignments = %+v, want %+v", got, want) + } + + const methodName = "AddMultipleAssignments" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.AddMultipleAssignments(ctx, "\n", "t1", []string{"o1"}) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.AddMultipleAssignments(ctx, "e", "t1", []string{"o1"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveMultipleAssignments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/organizations/remove", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + + fmt.Fprint(w, `[{ + "login": "o1", + "id": 1 + },{ + "login": "o2", + "id": 2 + }]`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.RemoveMultipleAssignments(ctx, "e", "t1", []string{"o1", "o2"}) + if err != nil { + t.Fatalf("RemoveMultipleAssignments returned error: %v", err) + } + + want := []*Organization{ + {Login: Ptr("o1"), ID: Ptr(int64(1))}, + {Login: Ptr("o2"), ID: Ptr(int64(2))}, + } + + if !cmp.Equal(got, want) { + t.Errorf("RemoveMultipleAssignments = %+v, want %+v", got, want) + } + + const methodName = "RemoveMultipleAssignments" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.RemoveMultipleAssignments(ctx, "\n", "t1", []string{"o1"}) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.RemoveMultipleAssignments(ctx, "e", "t1", []string{"o1"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_GetAssignment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/organizations/o1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "login": "o1", + "id": 10 + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.GetAssignment(ctx, "e", "t1", "o1") + if err != nil { + t.Fatalf("GetAssignment returned error: %v", err) + } + + want := &Organization{ + Login: Ptr("o1"), + ID: Ptr(int64(10)), + } + + if !cmp.Equal(got, want) { + t.Errorf("GetAssignment = %+v, want %+v", got, want) + } + + const methodName = "GetAssignment" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.GetAssignment(ctx, "\n", "t1", "o1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.GetAssignment(ctx, "e", "t1", "o1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_AddAssignment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/organizations/o1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "login": "o1", + "id": 5 + }`) + }) + + ctx := t.Context() + got, _, err := client.Enterprise.AddAssignment(ctx, "e", "t1", "o1") + if err != nil { + t.Fatalf("AddAssignment returned error: %v", err) + } + + want := &Organization{ + Login: Ptr("o1"), + ID: Ptr(int64(5)), + } + + if !cmp.Equal(got, want) { + t.Errorf("AddAssignment = %+v, want %+v", got, want) + } + + const methodName = "AddAssignment" + testBadOptions(t, methodName, func() error { + _, _, err := client.Enterprise.AddAssignment(ctx, "\n", "t1", "o1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Enterprise.AddAssignment(ctx, "e", "t1", "o1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestEnterpriseService_RemoveAssignment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/teams/t1/organizations/o1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Enterprise.RemoveAssignment(ctx, "e", "t1", "o1") + if err != nil { + t.Fatalf("RemoveAssignment returned error: %v", err) + } + if resp == nil { + t.Fatal("RemoveAssignment returned nil Response") + } + + const methodName = "RemoveAssignment" + testBadOptions(t, methodName, func() error { + _, err := client.Enterprise.RemoveAssignment(ctx, "\n", "t1", "o1") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Enterprise.RemoveAssignment(ctx, "e", "t1", "o1") + }) +} diff --git a/github/event.go b/github/event.go index 0ba8f46a1b0..446db7fa01e 100644 --- a/github/event.go +++ b/github/event.go @@ -7,7 +7,6 @@ package github import ( "encoding/json" - "time" ) // Event represents a GitHub event. @@ -18,7 +17,7 @@ type Event struct { Repo *Repository `json:"repo,omitempty"` Actor *User `json:"actor,omitempty"` Org *Organization `json:"org,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` ID *string `json:"id,omitempty"` } @@ -28,93 +27,16 @@ func (e Event) String() string { // ParsePayload parses the event payload. For recognized event types, // a value of the corresponding struct type will be returned. -func (e *Event) ParsePayload() (payload interface{}, err error) { - switch *e.Type { - case "CheckRunEvent": - payload = &CheckRunEvent{} - case "CheckSuiteEvent": - payload = &CheckSuiteEvent{} - case "CommitCommentEvent": - payload = &CommitCommentEvent{} - case "CreateEvent": - payload = &CreateEvent{} - case "DeleteEvent": - payload = &DeleteEvent{} - case "DeployKeyEvent": - payload = &DeployKeyEvent{} - case "DeploymentEvent": - payload = &DeploymentEvent{} - case "DeploymentStatusEvent": - payload = &DeploymentStatusEvent{} - case "ForkEvent": - payload = &ForkEvent{} - case "GitHubAppAuthorizationEvent": - payload = &GitHubAppAuthorizationEvent{} - case "GollumEvent": - payload = &GollumEvent{} - case "InstallationEvent": - payload = &InstallationEvent{} - case "InstallationRepositoriesEvent": - payload = &InstallationRepositoriesEvent{} - case "IssueCommentEvent": - payload = &IssueCommentEvent{} - case "IssuesEvent": - payload = &IssuesEvent{} - case "LabelEvent": - payload = &LabelEvent{} - case "MarketplacePurchaseEvent": - payload = &MarketplacePurchaseEvent{} - case "MemberEvent": - payload = &MemberEvent{} - case "MembershipEvent": - payload = &MembershipEvent{} - case "MetaEvent": - payload = &MetaEvent{} - case "MilestoneEvent": - payload = &MilestoneEvent{} - case "OrganizationEvent": - payload = &OrganizationEvent{} - case "OrgBlockEvent": - payload = &OrgBlockEvent{} - case "PageBuildEvent": - payload = &PageBuildEvent{} - case "PingEvent": - payload = &PingEvent{} - case "ProjectEvent": - payload = &ProjectEvent{} - case "ProjectCardEvent": - payload = &ProjectCardEvent{} - case "ProjectColumnEvent": - payload = &ProjectColumnEvent{} - case "PublicEvent": - payload = &PublicEvent{} - case "PullRequestEvent": - payload = &PullRequestEvent{} - case "PullRequestReviewEvent": - payload = &PullRequestReviewEvent{} - case "PullRequestReviewCommentEvent": - payload = &PullRequestReviewCommentEvent{} - case "PushEvent": - payload = &PushEvent{} - case "ReleaseEvent": - payload = &ReleaseEvent{} - case "RepositoryEvent": - payload = &RepositoryEvent{} - case "RepositoryVulnerabilityAlertEvent": - payload = &RepositoryVulnerabilityAlertEvent{} - case "StarEvent": - payload = &StarEvent{} - case "StatusEvent": - payload = &StatusEvent{} - case "TeamEvent": - payload = &TeamEvent{} - case "TeamAddEvent": - payload = &TeamAddEvent{} - case "WatchEvent": - payload = &WatchEvent{} +func (e *Event) ParsePayload() (any, error) { + // It would be nice if e.Type were the snake_case name of the event, + // but the existing interface uses the struct name instead. + payload := EventForType(typeToMessageMapping[e.GetType()]) + + if err := json.Unmarshal(e.GetRawPayload(), &payload); err != nil { + return nil, err } - err = json.Unmarshal(*e.RawPayload, &payload) - return payload, err + + return payload, nil } // Payload returns the parsed event payload. For recognized event types, @@ -122,7 +44,7 @@ func (e *Event) ParsePayload() (payload interface{}, err error) { // // Deprecated: Use ParsePayload instead, which returns an error // rather than panics if JSON unmarshaling raw payload fails. -func (e *Event) Payload() (payload interface{}) { +func (e *Event) Payload() (payload any) { var err error payload, err = e.ParsePayload() if err != nil { diff --git a/github/event_test.go b/github/event_test.go new file mode 100644 index 00000000000..bbcf6d126b3 --- /dev/null +++ b/github/event_test.go @@ -0,0 +1,44 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "testing" +) + +func TestPayload_Panic(t *testing.T) { + t.Parallel() + defer func() { + if r := recover(); r == nil { + t.Error("Payload did not panic but should have") + } + }() + + body := json.RawMessage("[") // bogus JSON + e := &Event{Type: Ptr("UserEvent"), RawPayload: &body} + e.Payload() +} + +func TestPayload_NoPanic(t *testing.T) { + t.Parallel() + body := json.RawMessage("{}") + e := &Event{Type: Ptr("UserEvent"), RawPayload: &body} + e.Payload() +} + +func TestEmptyEvent_NoPanic(t *testing.T) { + t.Parallel() + e := &Event{} + if _, err := e.ParsePayload(); err == nil { + t.Error("ParsePayload unexpectedly succeeded on empty event") + } + + e = nil + if _, err := e.ParsePayload(); err == nil { + t.Error("ParsePayload unexpectedly succeeded on nil event") + } +} diff --git a/github/event_types.go b/github/event_types.go index d3e2123547d..288b89ef4a3 100644 --- a/github/event_types.go +++ b/github/event_types.go @@ -7,19 +7,48 @@ package github +import "encoding/json" + // RequestedAction is included in a CheckRunEvent when a user has invoked an action, // i.e. when the CheckRunEvent's Action field is "requested_action". type RequestedAction struct { Identifier string `json:"identifier"` // The integrator reference of the action requested by the user. } -// CheckRunEvent is triggered when a check run is "created", "updated", or "rerequested". +// BranchProtectionRuleEvent triggered when a check suite is "created", "edited", or "deleted". +// The Webhook event name is "branch_protection_rule". +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#branch_protection_rule +type BranchProtectionRuleEvent struct { + Action *string `json:"action,omitempty"` + Rule *BranchProtectionRule `json:"rule,omitempty"` + Changes *ProtectionChanges `json:"changes,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// BranchProtectionConfigurationEvent is triggered when there is a change to branch protection configurations for a repository. +// The Webhook event name is "branch_protection_configuration". +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#branch_protection_configuration +type BranchProtectionConfigurationEvent struct { + Action *string `json:"action,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// CheckRunEvent is triggered when a check run is "created", "completed", or "rerequested". // The Webhook event name is "check_run". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checkrunevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#check_run type CheckRunEvent struct { CheckRun *CheckRun `json:"check_run,omitempty"` - // The action performed. Possible values are: "created", "updated", "rerequested" or "requested_action". + // The action performed. Possible values are: "created", "completed", "rerequested" or "requested_action". Action *string `json:"action,omitempty"` // The following fields are only populated by Webhook events. @@ -35,7 +64,7 @@ type CheckRunEvent struct { // CheckSuiteEvent is triggered when a check suite is "completed", "requested", or "rerequested". // The Webhook event name is "check_suite". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checksuiteevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#check_suite type CheckSuiteEvent struct { CheckSuite *CheckSuite `json:"check_suite,omitempty"` // The action performed. Possible values are: "completed", "requested" or "rerequested". @@ -51,7 +80,7 @@ type CheckSuiteEvent struct { // CommitCommentEvent is triggered when a commit comment is created. // The Webhook event name is "commit_comment". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#commit_comment type CommitCommentEvent struct { Comment *RepositoryComment `json:"comment,omitempty"` @@ -60,6 +89,24 @@ type CommitCommentEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// ContentReferenceEvent is triggered when the body or comment of an issue or +// pull request includes a URL that matches a configured content reference +// domain. +// The Webhook event name is "content_reference". +// +// GitHub API docs: https://developer.github.com/webhooks/event-payloads/#content_reference +type ContentReferenceEvent struct { + Action *string `json:"action,omitempty"` + ContentReference *ContentReference `json:"content_reference,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` } // CreateEvent represents a created repository, branch, or tag. @@ -69,28 +116,66 @@ type CommitCommentEvent struct { // Additionally, webhooks will not receive this event for tags if more // than three tags are pushed at once. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#createevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/github-event-types#createevent type CreateEvent struct { Ref *string `json:"ref,omitempty"` // RefType is the object that was created. Possible values are: "repository", "branch", "tag". RefType *string `json:"ref_type,omitempty"` MasterBranch *string `json:"master_branch,omitempty"` Description *string `json:"description,omitempty"` + PusherType *string `json:"pusher_type,omitempty"` // The following fields are only populated by Webhook events. - PusherType *string `json:"pusher_type,omitempty"` Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` } +// CustomPropertyEvent represents a created, deleted or updated custom property. +// The Webhook event name is "custom_property". +// +// Note: this is related to custom property configuration at the enterprise or organization level. +// See CustomPropertyValuesEvent for activity related to custom property values for a repository. +// +// GitHub API docs: https://docs.github.com/en/webhooks/webhook-events-and-payloads#custom_property +type CustomPropertyEvent struct { + // Action possible values are: "created", "deleted", "updated". + Action *string `json:"action,omitempty"` + Definition *CustomProperty `json:"definition,omitempty"` + + // The following fields are only populated by Webhook events. + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` +} + +// CustomPropertyValuesEvent represents an update to a custom property. +// The Webhook event name is "custom_property_values". +// +// GitHub API docs: https://docs.github.com/en/webhooks/webhook-events-and-payloads#custom_property_values +type CustomPropertyValuesEvent struct { + // Action possible values are: "updated". + Action *string `json:"action,omitempty"` + NewPropertyValues []*CustomPropertyValue `json:"new_property_values,omitempty"` + OldPropertyValues []*CustomPropertyValue `json:"old_property_values,omitempty"` + + // The following fields are only populated by Webhook events. + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` +} + // DeleteEvent represents a deleted branch or tag. // The Webhook event name is "delete". // // Note: webhooks will not receive this event for tags if more than three tags // are deleted at once. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deleteevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/github-event-types#deleteevent type DeleteEvent struct { Ref *string `json:"ref,omitempty"` // RefType is the object that was deleted. Possible values are: "branch", "tag". @@ -101,12 +186,35 @@ type DeleteEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// DependabotAlertEvent is triggered when there is activity relating to Dependabot alerts. +// The Webhook event name is "dependabot_alert". +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#dependabot_alert +type DependabotAlertEvent struct { + Action *string `json:"action,omitempty"` + Alert *DependabotAlert `json:"alert,omitempty"` + + // The following fields are only populated by Webhook events. + Installation *Installation `json:"installation,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Organization *Organization `json:"organization,omitempty"` } // DeployKeyEvent is triggered when a deploy key is added or removed from a repository. // The Webhook event name is "deploy_key". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploykeyevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#deploy_key type DeployKeyEvent struct { // Action is the action that was performed. Possible values are: // "created" or "deleted". @@ -114,6 +222,17 @@ type DeployKeyEvent struct { // The deploy key resource. Key *Key `json:"key,omitempty"` + + // The Repository where the event occurred + Repo *Repository `json:"repository,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Organization *Organization `json:"organization,omitempty"` + + // The following fields are only populated by Webhook events. + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` } // DeploymentEvent represents a deployment. @@ -121,14 +240,79 @@ type DeployKeyEvent struct { // // Events of this type are not visible in timelines, they are only used to trigger hooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#deployment type DeploymentEvent struct { - Deployment *Deployment `json:"deployment,omitempty"` - Repo *Repository `json:"repository,omitempty"` + Deployment *Deployment `json:"deployment,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Workflow *Workflow `json:"workflow,omitempty"` + WorkflowRun *WorkflowRun `json:"workflow_run,omitempty"` // The following fields are only populated by Webhook events. Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// DeploymentProtectionRuleEvent represents a deployment protection rule event. +// The Webhook event name is "deployment_protection_rule". +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#deployment_protection_rule +type DeploymentProtectionRuleEvent struct { + Action *string `json:"action,omitempty"` + Environment *string `json:"environment,omitempty"` + Event *string `json:"event,omitempty"` + + // The URL GitHub provides for a third-party to use in order to pass/fail a deployment gate + DeploymentCallbackURL *string `json:"deployment_callback_url,omitempty"` + Deployment *Deployment `json:"deployment,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Organization *Organization `json:"organization,omitempty"` + PullRequests []*PullRequest `json:"pull_requests,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// DeploymentReviewEvent represents a deployment review event. +// The Webhook event name is "deployment_review". +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads?#deployment_review +type DeploymentReviewEvent struct { + // The action performed. Possible values are: "requested", "approved", or "rejected". + Action *string `json:"action,omitempty"` + + // The following will be populated only if requested. + Requester *User `json:"requester,omitempty"` + Environment *string `json:"environment,omitempty"` + + // The following will be populated only if approved or rejected. + Approver *User `json:"approver,omitempty"` + Comment *string `json:"comment,omitempty"` + WorkflowJobRuns []*WorkflowJobRun `json:"workflow_job_runs,omitempty"` + + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Reviewers []*RequiredReviewer `json:"reviewers,omitempty"` + Sender *User `json:"sender,omitempty"` + Since *string `json:"since,omitempty"` + WorkflowJobRun *WorkflowJobRun `json:"workflow_job_run,omitempty"` + WorkflowRun *WorkflowRun `json:"workflow_run,omitempty"` +} + +// WorkflowJobRun represents a workflow_job_run in a GitHub DeploymentReviewEvent. +type WorkflowJobRun struct { + Conclusion *string `json:"conclusion,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Environment *string `json:"environment,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Status *string `json:"status,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` } // DeploymentStatusEvent represents a deployment status. @@ -136,8 +320,9 @@ type DeploymentEvent struct { // // Events of this type are not visible in timelines, they are only used to trigger hooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#deployment_status type DeploymentStatusEvent struct { + Action *string `json:"action,omitempty"` Deployment *Deployment `json:"deployment,omitempty"` DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"` Repo *Repository `json:"repository,omitempty"` @@ -145,12 +330,114 @@ type DeploymentStatusEvent struct { // The following fields are only populated by Webhook events. Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// DiscussionCommentEvent represents a webhook event for a comment on discussion. +// The Webhook event name is "discussion_comment". +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion_comment +type DiscussionCommentEvent struct { + // Action is the action that was performed on the comment. + // Possible values are: "created", "edited", "deleted". ** check what all can be added + Action *string `json:"action,omitempty"` + Discussion *Discussion `json:"discussion,omitempty"` + Comment *CommentDiscussion `json:"comment,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// CommentDiscussion represents a comment in a GitHub DiscussionCommentEvent. +type CommentDiscussion struct { + // AuthorAssociation is the comment author's relationship to the repository. + // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Discussions REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/discussions/comments?apiVersion=2022-11-28#get-a-discussion-comment + AuthorAssociation *string `json:"author_association,omitempty"` + Body *string `json:"body,omitempty"` + ChildCommentCount *int `json:"child_comment_count,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + DiscussionID *int64 `json:"discussion_id,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + ParentID *int64 `json:"parent_id,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + User *User `json:"user,omitempty"` +} + +// DiscussionEvent represents a webhook event for a discussion. +// The Webhook event name is "discussion". +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#discussion +type DiscussionEvent struct { + // Action is the action that was performed. Possible values are: + // created, edited, deleted, pinned, unpinned, locked, unlocked, + // transferred, category_changed, answered, or unanswered. + Action *string `json:"action,omitempty"` + Discussion *Discussion `json:"discussion,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// Discussion represents a discussion in a GitHub DiscussionEvent. +type Discussion struct { + RepositoryURL *string `json:"repository_url,omitempty"` + DiscussionCategory *DiscussionCategory `json:"category,omitempty"` + AnswerHTMLURL *string `json:"answer_html_url,omitempty"` + AnswerChosenAt *Timestamp `json:"answer_chosen_at,omitempty"` + AnswerChosenBy *string `json:"answer_chosen_by,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Number *int `json:"number,omitempty"` + Title *string `json:"title,omitempty"` + User *User `json:"user,omitempty"` + State *string `json:"state,omitempty"` + Locked *bool `json:"locked,omitempty"` + Comments *int `json:"comments,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + // AuthorAssociation is the discussion author's relationship to the repository. + // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Discussions REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/discussions/discussions?apiVersion=2022-11-28#get-a-discussion + AuthorAssociation *string `json:"author_association,omitempty"` + ActiveLockReason *string `json:"active_lock_reason,omitempty"` + Body *string `json:"body,omitempty"` +} + +// DiscussionCategory represents a discussion category in a GitHub DiscussionEvent. +type DiscussionCategory struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + RepositoryID *int64 `json:"repository_id,omitempty"` + Emoji *string `json:"emoji,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Slug *string `json:"slug,omitempty"` + IsAnswerable *bool `json:"is_answerable,omitempty"` } // ForkEvent is triggered when a user forks a repository. // The Webhook event name is "fork". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#forkevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#fork type ForkEvent struct { // Forkee is the created repository. Forkee *Repository `json:"forkee,omitempty"` @@ -164,13 +451,14 @@ type ForkEvent struct { // GitHubAppAuthorizationEvent is triggered when a user's authorization for a // GitHub Application is revoked. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#githubappauthorizationevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#github_app_authorization type GitHubAppAuthorizationEvent struct { // The action performed. Possible value is: "revoked". Action *string `json:"action,omitempty"` // The following fields are only populated by Webhook events. - Sender *User `json:"sender,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` } // Page represents a single Wiki page. @@ -186,7 +474,7 @@ type Page struct { // GollumEvent is triggered when a Wiki page is created or updated. // The Webhook event name is "gollum". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#gollumevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#gollum type GollumEvent struct { Pages []*Page `json:"pages,omitempty"` @@ -194,81 +482,179 @@ type GollumEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } -// EditChange represents the changes when an issue, pull request, or comment has -// been edited. +// EditChange represents the changes when an issue, pull request, comment, +// or repository has been edited. type EditChange struct { - Title *struct { - From *string `json:"from,omitempty"` - } `json:"title,omitempty"` - Body *struct { - From *string `json:"from,omitempty"` - } `json:"body,omitempty"` + Title *EditTitle `json:"title,omitempty"` + Body *EditBody `json:"body,omitempty"` + Base *EditBase `json:"base,omitempty"` + Repo *EditRepo `json:"repository,omitempty"` + Owner *EditOwner `json:"owner,omitempty"` + DefaultBranch *EditDefaultBranch `json:"default_branch,omitempty"` + Topics *EditTopics `json:"topics,omitempty"` +} + +// EditTitle represents a pull-request title change. +type EditTitle struct { + From *string `json:"from,omitempty"` +} + +// EditBody represents a change of pull-request body. +type EditBody struct { + From *string `json:"from,omitempty"` +} + +// EditBase represents the change of a pull-request base branch. +type EditBase struct { + Ref *EditRef `json:"ref,omitempty"` + SHA *EditSHA `json:"sha,omitempty"` +} + +// EditRef represents a ref change of a pull-request. +type EditRef struct { + From *string `json:"from,omitempty"` +} + +// EditRepo represents a change of repository name. +type EditRepo struct { + Name *RepoName `json:"name,omitempty"` +} + +// EditOwner represents a change of repository ownership. +type EditOwner struct { + OwnerInfo *OwnerInfo `json:"from,omitempty"` +} + +// OwnerInfo represents the account info of the owner of the repo (could be User or Organization but both are User structs). +type OwnerInfo struct { + User *User `json:"user,omitempty"` + Org *User `json:"organization,omitempty"` +} + +// RepoName represents a change of repository name. +type RepoName struct { + From *string `json:"from,omitempty"` +} + +// EditTopics represents a change of repository topics. +type EditTopics struct { + From []string `json:"from,omitempty"` +} + +// EditSHA represents a sha change of a pull-request. +type EditSHA struct { + From *string `json:"from,omitempty"` +} + +// EditDefaultBranch represents a change of repository's default branch name. +type EditDefaultBranch struct { + From *string `json:"from,omitempty"` } // ProjectChange represents the changes when a project has been edited. type ProjectChange struct { - Name *struct { - From *string `json:"from,omitempty"` - } `json:"name,omitempty"` - Body *struct { - From *string `json:"from,omitempty"` - } `json:"body,omitempty"` + Name *ProjectName `json:"name,omitempty"` + Body *ProjectBody `json:"body,omitempty"` +} + +// ProjectName represents a project name change. +type ProjectName struct { + From *string `json:"from,omitempty"` +} + +// ProjectBody represents a project body change. +type ProjectBody struct { + From *string `json:"from,omitempty"` } // ProjectCardChange represents the changes when a project card has been edited. type ProjectCardChange struct { - Note *struct { - From *string `json:"from,omitempty"` - } `json:"note,omitempty"` + Note *ProjectCardNote `json:"note,omitempty"` +} + +// ProjectCardNote represents a change of a note of a project card. +type ProjectCardNote struct { + From *string `json:"from,omitempty"` } // ProjectColumnChange represents the changes when a project column has been edited. type ProjectColumnChange struct { - Name *struct { - From *string `json:"from,omitempty"` - } `json:"name,omitempty"` + Name *ProjectColumnName `json:"name,omitempty"` +} + +// ProjectColumnName represents a project column name change. +type ProjectColumnName struct { + From *string `json:"from,omitempty"` } // TeamChange represents the changes when a team has been edited. type TeamChange struct { - Description *struct { - From *string `json:"from,omitempty"` - } `json:"description,omitempty"` - Name *struct { - From *string `json:"from,omitempty"` - } `json:"name,omitempty"` - Privacy *struct { - From *string `json:"from,omitempty"` - } `json:"privacy,omitempty"` - Repository *struct { - Permissions *struct { - From *struct { - Admin *bool `json:"admin,omitempty"` - Pull *bool `json:"pull,omitempty"` - Push *bool `json:"push,omitempty"` - } `json:"from,omitempty"` - } `json:"permissions,omitempty"` - } `json:"repository,omitempty"` -} - -// InstallationEvent is triggered when a GitHub App has been installed or uninstalled. + Description *TeamDescription `json:"description,omitempty"` + Name *TeamName `json:"name,omitempty"` + Privacy *TeamPrivacy `json:"privacy,omitempty"` + Repository *TeamRepository `json:"repository,omitempty"` +} + +// TeamDescription represents a team description change. +type TeamDescription struct { + From *string `json:"from,omitempty"` +} + +// TeamName represents a team name change. +type TeamName struct { + From *string `json:"from,omitempty"` +} + +// TeamPrivacy represents a team privacy change. +type TeamPrivacy struct { + From *string `json:"from,omitempty"` +} + +// TeamRepository represents a team repository permission change. +type TeamRepository struct { + Permissions *TeamPermissions `json:"permissions,omitempty"` +} + +// TeamPermissions represents a team permission change. +type TeamPermissions struct { + From *TeamPermissionsFrom `json:"from,omitempty"` +} + +// TeamPermissionsFrom represents a team permission change. +type TeamPermissionsFrom struct { + Admin *bool `json:"admin,omitempty"` + Pull *bool `json:"pull,omitempty"` + Push *bool `json:"push,omitempty"` +} + +// InstallationEvent is triggered when a GitHub App has been installed, uninstalled, suspend, unsuspended +// or new permissions have been accepted. // The Webhook event name is "installation". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#installation type InstallationEvent struct { - // The action that was performed. Can be either "created" or "deleted". + // The action that was performed. Can be either "created", "deleted", "suspend", "unsuspend" or "new_permissions_accepted". Action *string `json:"action,omitempty"` Repositories []*Repository `json:"repositories,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + Requester *User `json:"requester,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } // InstallationRepositoriesEvent is triggered when a repository is added or // removed from an installation. The Webhook event name is "installation_repositories". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationrepositoriesevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#installation_repositories type InstallationRepositoriesEvent struct { // The action that was performed. Can be either "added" or "removed". Action *string `json:"action,omitempty"` @@ -277,13 +663,49 @@ type InstallationRepositoriesEvent struct { RepositorySelection *string `json:"repository_selection,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// InstallationLoginChange represents a change in login on an installation. +type InstallationLoginChange struct { + From *string `json:"from,omitempty"` +} + +// InstallationSlugChange represents a change in slug on an installation. +type InstallationSlugChange struct { + From *string `json:"from,omitempty"` +} + +// InstallationChanges represents a change in slug or login on an installation. +type InstallationChanges struct { + Login *InstallationLoginChange `json:"login,omitempty"` + Slug *InstallationSlugChange `json:"slug,omitempty"` +} + +// InstallationTargetEvent is triggered when there is activity on an installation from a user or organization account. +// The Webhook event name is "installation_target". +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#installation_target +type InstallationTargetEvent struct { + Account *User `json:"account,omitempty"` + Action *string `json:"action,omitempty"` + Changes *InstallationChanges `json:"changes,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + TargetType *string `json:"target_type,omitempty"` } // IssueCommentEvent is triggered when an issue comment is created on an issue // or pull request. // The Webhook event name is "issue_comment". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#issue_comment type IssueCommentEvent struct { // Action is the action that was performed on the comment. // Possible values are: "created", "edited", "deleted". @@ -296,6 +718,10 @@ type IssueCommentEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Organization *Organization `json:"organization,omitempty"` } // IssuesEvent is triggered when an issue is opened, edited, deleted, transferred, @@ -303,7 +729,7 @@ type IssueCommentEvent struct { // locked, unlocked, milestoned, or demilestoned. // The Webhook event name is "issues". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuesevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#issues type IssuesEvent struct { // Action is the action that was performed. Possible values are: "opened", // "edited", "deleted", "transferred", "pinned", "unpinned", "closed", "reopened", @@ -319,22 +745,28 @@ type IssuesEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } // LabelEvent is triggered when a repository's label is created, edited, or deleted. // The Webhook event name is "label" // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#labelevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#label type LabelEvent struct { // Action is the action that was performed. Possible values are: // "created", "edited", "deleted" - Action *string `json:"action,omitempty"` - Label *Label `json:"label,omitempty"` + Action *string `json:"action,omitempty"` + Label *Label `json:"label,omitempty"` + Changes *EditChange `json:"changes,omitempty"` // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` Repo *Repository `json:"repository,omitempty"` Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` } @@ -342,7 +774,7 @@ type LabelEvent struct { // their GitHub Marketplace plan. // Webhook event name "marketplace_purchase". // -// Github API docs: https://developer.github.com/v3/activity/events/types/#marketplacepurchaseevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#marketplace_purchase type MarketplacePurchaseEvent struct { // Action is the action that was performed. Possible values are: // "purchased", "cancelled", "pending_change", "pending_change_cancelled", "changed". @@ -354,21 +786,48 @@ type MarketplacePurchaseEvent struct { PreviousMarketplacePurchase *MarketplacePurchase `json:"previous_marketplace_purchase,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// MemberChangesPermission represents changes to a repository collaborator's permissions. +type MemberChangesPermission struct { + From *string `json:"from,omitempty"` + To *string `json:"to,omitempty"` } -// MemberEvent is triggered when a user is added as a collaborator to a repository. +// MemberChangesRoleName represents changes to a repository collaborator's role. +type MemberChangesRoleName struct { + From *string `json:"from,omitempty"` + To *string `json:"to,omitempty"` +} + +// MemberChanges represents changes to a repository collaborator's role or permission. +type MemberChanges struct { + Permission *MemberChangesPermission `json:"permission,omitempty"` + RoleName *MemberChangesRoleName `json:"role_name,omitempty"` +} + +// MemberEvent is triggered when a user's membership as a collaborator to a repository changes. // The Webhook event name is "member". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#memberevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#member type MemberEvent struct { - // Action is the action that was performed. Possible value is: "added". - Action *string `json:"action,omitempty"` - Member *User `json:"member,omitempty"` + // Action is the action that was performed. Possible values are: "added", "edited", "removed". + Action *string `json:"action,omitempty"` + Member *User `json:"member,omitempty"` + Changes *MemberChanges `json:"changes,omitempty"` // The following fields are only populated by Webhook events. Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } // MembershipEvent is triggered when a user is added or removed from a team. @@ -377,7 +836,7 @@ type MemberEvent struct { // Events of this type are not visible in timelines, they are only used to // trigger organization webhooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#membershipevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#membership type MembershipEvent struct { // Action is the action that was performed. Possible values are: "added", "removed". Action *string `json:"action,omitempty"` @@ -392,12 +851,45 @@ type MembershipEvent struct { Installation *Installation `json:"installation,omitempty"` } +// MergeGroup represents the merge group in a merge queue. +type MergeGroup struct { + // The SHA of the merge group. + HeadSHA *string `json:"head_sha,omitempty"` + // The full ref of the merge group. + HeadRef *string `json:"head_ref,omitempty"` + // The SHA of the merge group's parent commit. + BaseSHA *string `json:"base_sha,omitempty"` + // The full ref of the branch into which the merge group will be merged. + BaseRef *string `json:"base_ref,omitempty"` + // An expanded representation of the head_sha commit. + HeadCommit *Commit `json:"head_commit,omitempty"` +} + +// MergeGroupEvent represents activity related to merge groups in a merge queue. The type of activity is specified +// in the action property of the payload object. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#merge_group +type MergeGroupEvent struct { + // The action that was performed. Possible values are: "checks_requested", "destroyed". + Action *string `json:"action,omitempty"` + // Reason is populated when the action is "destroyed". Possible values: "merged", "invalidated", "dequeued". + Reason *string `json:"reason,omitempty"` + // The merge group. + MergeGroup *MergeGroup `json:"merge_group,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Sender *User `json:"sender,omitempty"` +} + // MetaEvent is triggered when the webhook that this event is configured on is deleted. // This event will only listen for changes to the particular hook the event is installed on. // Therefore, it must be selected for each hook that you'd like to receive meta events for. // The Webhook event name is "meta". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#metaevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#meta type MetaEvent struct { // Action is the action that was performed. Possible value is: "deleted". Action *string `json:"action,omitempty"` @@ -407,12 +899,18 @@ type MetaEvent struct { // This will contain different keys based on the type of webhook it is: repository, // organization, business, app, or GitHub Marketplace. Hook *Hook `json:"hook,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` } // MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted. // The Webhook event name is "milestone". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#milestoneevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#milestone type MilestoneEvent struct { // Action is the action that was performed. Possible values are: // "created", "closed", "opened", "edited", "deleted" @@ -432,7 +930,7 @@ type MilestoneEvent struct { // Events of this type are not visible in timelines. These events are only used to trigger organization hooks. // Webhook event name is "organization". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#organizationevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#organization type OrganizationEvent struct { // Action is the action that was performed. // Possible values are: "deleted", "renamed", "member_added", "member_removed", or "member_invited". @@ -453,7 +951,7 @@ type OrganizationEvent struct { // OrgBlockEvent is triggered when an organization blocks or unblocks a user. // The Webhook event name is "org_block". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#orgblockevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#org_block type OrgBlockEvent struct { // Action is the action that was performed. // Can be "blocked" or "unblocked". @@ -466,6 +964,25 @@ type OrgBlockEvent struct { Installation *Installation `json:"installation,omitempty"` } +// PackageEvent represents activity related to GitHub Packages. +// The Webhook event name is "package". +// +// This event is triggered when a GitHub Package is published or updated. +// +// GitHub API docs: https://developer.github.com/webhooks/event-payloads/#package +type PackageEvent struct { + // Action is the action that was performed. + // Can be "published" or "updated". + Action *string `json:"action,omitempty"` + Package *Package `json:"package,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + + // The following fields are only populated by Webhook events. + Installation *Installation `json:"installation,omitempty"` +} + // PageBuildEvent represents an attempted build of a GitHub Pages site, whether // successful or not. // The Webhook event name is "page_build". @@ -475,7 +992,7 @@ type OrgBlockEvent struct { // // Events of this type are not visible in timelines, they are only used to trigger hooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#page_build type PageBuildEvent struct { Build *PagesBuild `json:"build,omitempty"` @@ -484,6 +1001,82 @@ type PageBuildEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// PersonalAccessTokenRequestEvent occurs when there is activity relating to a +// request for a fine-grained personal access token to access resources that +// belong to a resource owner that requires approval for token access. +// The webhook event name is "personal_access_token_request". +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#personal_access_token_request +type PersonalAccessTokenRequestEvent struct { + // Action is the action that was performed. Possible values are: + // "approved", "cancelled", "created" or "denied" + Action *string `json:"action,omitempty"` + PersonalAccessTokenRequest *PersonalAccessTokenRequest `json:"personal_access_token_request,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// PersonalAccessTokenRequest contains the details of a PersonalAccessTokenRequestEvent. +type PersonalAccessTokenRequest struct { + // Unique identifier of the request for access via fine-grained personal + // access token. Used as the pat_request_id parameter in the list and review + // API calls. + ID *int64 `json:"id,omitempty"` + Owner *User `json:"owner,omitempty"` + + // New requested permissions, categorized by type of permission. + PermissionsAdded *PersonalAccessTokenPermissions `json:"permissions_added,omitempty"` + + // Requested permissions that elevate access for a previously approved + // request for access, categorized by type of permission. + PermissionsUpgraded *PersonalAccessTokenPermissions `json:"permissions_upgraded,omitempty"` + + // Permissions requested, categorized by type of permission. + // This field incorporates permissions_added and permissions_upgraded. + PermissionsResult *PersonalAccessTokenPermissions `json:"permissions_result,omitempty"` + + // Type of repository selection requested. Possible values are: + // "none", "all" or "subset" + RepositorySelection *string `json:"repository_selection,omitempty"` + + // The number of repositories the token is requesting access to. + // This field is only populated when repository_selection is subset. + RepositoryCount *int64 `json:"repository_count,omitempty"` + + // An array of repository objects the token is requesting access to. + // This field is only populated when repository_selection is subset. + Repositories []*Repository `json:"repositories,omitempty"` + + // Date and time when the request for access was created. + CreatedAt *Timestamp `json:"created_at,omitempty"` + + // Whether the associated fine-grained personal access token has expired. + TokenExpired *bool `json:"token_expired,omitempty"` + + // Date and time when the associated fine-grained personal access token expires. + TokenExpiresAt *Timestamp `json:"token_expires_at,omitempty"` + + // Date and time when the associated fine-grained personal access token was last used for authentication. + TokenLastUsedAt *Timestamp `json:"token_last_used_at,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// PersonalAccessTokenPermissions represents the original or newly requested +// scope of permissions for a fine-grained personal access token within a PersonalAccessTokenRequest. +type PersonalAccessTokenPermissions struct { + Org map[string]string `json:"organization,omitempty"` + Repo map[string]string `json:"repository,omitempty"` + Other map[string]string `json:"other,omitempty"` } // PingEvent is triggered when a Webhook is added to GitHub. @@ -495,18 +1088,7 @@ type PingEvent struct { // The ID of the webhook that triggered the ping. HookID *int64 `json:"hook_id,omitempty"` // The webhook configuration. - Hook *Hook `json:"hook,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// ProjectEvent is triggered when project is created, modified or deleted. -// The webhook event name is "project". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectevent -type ProjectEvent struct { - Action *string `json:"action,omitempty"` - Changes *ProjectChange `json:"changes,omitempty"` - Project *Project `json:"project,omitempty"` + Hook *Hook `json:"hook,omitempty"` // The following fields are only populated by Webhook events. Repo *Repository `json:"repository,omitempty"` @@ -515,50 +1097,71 @@ type ProjectEvent struct { Installation *Installation `json:"installation,omitempty"` } -// ProjectCardEvent is triggered when a project card is created, updated, moved, converted to an issue, or deleted. -// The webhook event name is "project_card". +// ProjectV2Event is triggered when there is activity relating to an organization-level project. +// The Webhook event name is "projects_v2". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcardevent -type ProjectCardEvent struct { - Action *string `json:"action,omitempty"` - Changes *ProjectCardChange `json:"changes,omitempty"` - AfterID *int64 `json:"after_id,omitempty"` - ProjectCard *ProjectCard `json:"project_card,omitempty"` +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#projects_v2 +type ProjectV2Event struct { + Action *string `json:"action,omitempty"` + ProjectsV2 *ProjectV2 `json:"projects_v2,omitempty"` // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` + Installation *Installation `json:"installation,omitempty"` Org *Organization `json:"organization,omitempty"` Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` } -// ProjectColumnEvent is triggered when a project column is created, updated, moved, or deleted. -// The webhook event name is "project_column". +// ProjectV2ItemEvent is triggered when there is activity relating to an item on an organization-level project. +// The Webhook event name is "projects_v2_item". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcolumnevent -type ProjectColumnEvent struct { +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#projects_v2_item +type ProjectV2ItemEvent struct { Action *string `json:"action,omitempty"` - Changes *ProjectColumnChange `json:"changes,omitempty"` - AfterID *int64 `json:"after_id,omitempty"` - ProjectColumn *ProjectColumn `json:"project_column,omitempty"` + Changes *ProjectV2ItemChange `json:"changes,omitempty"` + ProjectV2Item *ProjectV2Item `json:"projects_v2_item,omitempty"` // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` + Installation *Installation `json:"installation,omitempty"` Org *Organization `json:"organization,omitempty"` Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` +} + +// ProjectV2ItemChange represents a project v2 item change. +type ProjectV2ItemChange struct { + ArchivedAt *ArchivedAt `json:"archived_at,omitempty"` + FieldValue *FieldValue `json:"field_value,omitempty"` +} + +// ArchivedAt represents an archiving date change. +type ArchivedAt struct { + From *Timestamp `json:"from,omitempty"` + To *Timestamp `json:"to,omitempty"` +} + +// FieldValue represents an editing field value change. +type FieldValue struct { + FieldNodeID *string `json:"field_node_id,omitempty"` + FieldType *string `json:"field_type,omitempty"` + FieldName *string `json:"field_name,omitempty"` + ProjectNumber *int64 `json:"project_number,omitempty"` + From json.RawMessage `json:"from,omitempty"` + To json.RawMessage `json:"to,omitempty"` } // PublicEvent is triggered when a private repository is open sourced. // According to GitHub: "Without a doubt: the best GitHub event." // The Webhook event name is "public". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#publicevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#public type PublicEvent struct { // The following fields are only populated by Webhook events. Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } // PullRequestEvent is triggered when a pull request is assigned, unassigned, labeled, @@ -566,7 +1169,7 @@ type PublicEvent struct { // locked, unlocked, a pull request review is requested, or a review request is removed. // The Webhook event name is "pull_request". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/github-event-types#pullrequestevent type PullRequestEvent struct { // Action is the action that was performed. Possible values are: // "assigned", "unassigned", "review_requested", "review_request_removed", "labeled", "unlabeled", @@ -592,18 +1195,26 @@ type PullRequestEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` - Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries. + Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries. + Reason *string `json:"reason,omitempty"` // Populated in "dequeued" event deliveries. // The following field is only present when the webhook is triggered on // a repository belonging to an organization. Organization *Organization `json:"organization,omitempty"` + + // The following fields are only populated when the Action is "synchronize". + Before *string `json:"before,omitempty"` + After *string `json:"after,omitempty"` + + // The following will be populated if the event was performed by an App + PerformedViaGithubApp *App `json:"performed_via_github_app,omitempty"` } // PullRequestReviewEvent is triggered when a review is submitted on a pull // request. // The Webhook event name is "pull_request_review". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#pull_request_review type PullRequestReviewEvent struct { // Action is always "submitted". Action *string `json:"action,omitempty"` @@ -624,7 +1235,7 @@ type PullRequestReviewEvent struct { // portion of the unified diff of a pull request. // The Webhook event name is "pull_request_review_comment". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#pull_request_review_comment type PullRequestReviewCommentEvent struct { // Action is the action that was performed on the comment. // Possible values are: "created", "edited", "deleted". @@ -637,21 +1248,109 @@ type PullRequestReviewCommentEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// PullRequestReviewThreadEvent is triggered when a comment made as part of a +// review of a pull request is marked resolved or unresolved. +// The Webhook event name is "pull_request_review_thread". +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#pull_request_review_thread +type PullRequestReviewThreadEvent struct { + // Action is the action that was performed on the comment. + // Possible values are: "resolved", "unresolved". + Action *string `json:"action,omitempty"` + Thread *PullRequestThread `json:"thread,omitempty"` + PullRequest *PullRequest `json:"pull_request,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// PullRequestTargetEvent is triggered when a pull request is assigned, unassigned, labeled, +// unlabeled, opened, edited, closed, reopened, synchronize, ready_for_review, +// locked, unlocked, a pull request review is requested, or a review request is removed. +// The Webhook event name is "pull_request_target". +// +// GitHub API docs: https://docs.github.com/actions/events-that-trigger-workflows#pull_request_target +type PullRequestTargetEvent struct { + // Action is the action that was performed. Possible values are: + // "assigned", "unassigned", "labeled", "unlabeled", "opened", "edited", "closed", "reopened", + // "ready_for_review", "locked", "unlocked", "review_requested" or "review_request_removed". + // If the action is "closed" and the "merged" key is "false", the pull request was closed with unmerged commits. + // If the action is "closed" and the "merged" key is "true", the pull request was merged. + // While webhooks are also triggered when a pull request is synchronized, Events API timelines + // don't include pull request events with the "synchronize" action. + Action *string `json:"action,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Number *int `json:"number,omitempty"` + PullRequest *PullRequest `json:"pull_request,omitempty"` + + // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` + // RequestedReviewer is populated in "review_requested", "review_request_removed" event deliveries. + // A request affecting multiple reviewers at once is split into multiple + // such event deliveries, each with a single, different RequestedReviewer. + RequestedReviewer *User `json:"requested_reviewer,omitempty"` + // In the event that a team is requested instead of a user, "requested_team" gets sent in place of + // "requested_user" with the same delivery behavior. + RequestedTeam *Team `json:"requested_team,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries. + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Organization *Organization `json:"organization,omitempty"` + + // The following fields are only populated when the Action is "synchronize". + Before *string `json:"before,omitempty"` + After *string `json:"after,omitempty"` + + // The following will be populated if the event was performed by an App + PerformedViaGithubApp *App `json:"performed_via_github_app,omitempty"` } // PushEvent represents a git push to a GitHub repository. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#push type PushEvent struct { - PushID *int64 `json:"push_id,omitempty"` - Head *string `json:"head,omitempty"` - Ref *string `json:"ref,omitempty"` - Size *int `json:"size,omitempty"` - Commits []PushEventCommit `json:"commits,omitempty"` - Before *string `json:"before,omitempty"` - DistinctSize *int `json:"distinct_size,omitempty"` + PushID *int64 `json:"push_id,omitempty"` + Head *string `json:"head,omitempty"` + Ref *string `json:"ref,omitempty"` + // Size is the number of commits in the push. + // + // Deprecated: GitHub will remove commit counts from Events API payloads on October 7, 2025. + // Use the Commits REST API endpoint to get commit information. + // See: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#list-commits + Size *int `json:"size,omitempty"` + // Commits is the list of commits in the push event. + // + // This field is only populated for webhook events. + // It has been removed from Events API payloads on October 7, 2025. + // Use the Commits REST API endpoint to get detailed commit information. + // See: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#list-commits + Commits []*HeadCommit `json:"commits,omitempty"` + Before *string `json:"before,omitempty"` + // DistinctSize is the number of distinct commits in the push. + // + // Deprecated: GitHub will remove commit counts from Events API payloads on October 7, 2025. + // Use the Compare REST API endpoint to get detailed comparison information. + // See: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits + DistinctSize *int `json:"distinct_size,omitempty"` // The following fields are only populated by Webhook events. + Action *string `json:"action,omitempty"` After *string `json:"after,omitempty"` Created *bool `json:"created,omitempty"` Deleted *bool `json:"deleted,omitempty"` @@ -659,18 +1358,22 @@ type PushEvent struct { BaseRef *string `json:"base_ref,omitempty"` Compare *string `json:"compare,omitempty"` Repo *PushEventRepository `json:"repository,omitempty"` - HeadCommit *PushEventCommit `json:"head_commit,omitempty"` - Pusher *User `json:"pusher,omitempty"` + HeadCommit *HeadCommit `json:"head_commit,omitempty"` + Pusher *CommitAuthor `json:"pusher,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Organization *Organization `json:"organization,omitempty"` } func (p PushEvent) String() string { return Stringify(p) } -// PushEventCommit represents a git commit in a GitHub PushEvent. -type PushEventCommit struct { +// HeadCommit represents a git commit in a GitHub PushEvent. +type HeadCommit struct { Message *string `json:"message,omitempty"` Author *CommitAuthor `json:"author,omitempty"` URL *string `json:"url,omitempty"` @@ -689,46 +1392,50 @@ type PushEventCommit struct { Modified []string `json:"modified,omitempty"` } -func (p PushEventCommit) String() string { - return Stringify(p) +func (h HeadCommit) String() string { + return Stringify(h) } // PushEventRepository represents the repo object in a PushEvent payload. type PushEventRepository struct { - ID *int64 `json:"id,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Name *string `json:"name,omitempty"` - FullName *string `json:"full_name,omitempty"` - Owner *User `json:"owner,omitempty"` - Private *bool `json:"private,omitempty"` - Description *string `json:"description,omitempty"` - Fork *bool `json:"fork,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PushedAt *Timestamp `json:"pushed_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - Homepage *string `json:"homepage,omitempty"` - PullsURL *string `json:"pulls_url,omitempty"` - Size *int `json:"size,omitempty"` - StargazersCount *int `json:"stargazers_count,omitempty"` - WatchersCount *int `json:"watchers_count,omitempty"` - Language *string `json:"language,omitempty"` - HasIssues *bool `json:"has_issues,omitempty"` - HasDownloads *bool `json:"has_downloads,omitempty"` - HasWiki *bool `json:"has_wiki,omitempty"` - HasPages *bool `json:"has_pages,omitempty"` - ForksCount *int `json:"forks_count,omitempty"` - OpenIssuesCount *int `json:"open_issues_count,omitempty"` - DefaultBranch *string `json:"default_branch,omitempty"` - MasterBranch *string `json:"master_branch,omitempty"` - Organization *string `json:"organization,omitempty"` - URL *string `json:"url,omitempty"` - ArchiveURL *string `json:"archive_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - SSHURL *string `json:"ssh_url,omitempty"` - CloneURL *string `json:"clone_url,omitempty"` - SVNURL *string `json:"svn_url,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + FullName *string `json:"full_name,omitempty"` + Owner *User `json:"owner,omitempty"` + Private *bool `json:"private,omitempty"` + Description *string `json:"description,omitempty"` + Fork *bool `json:"fork,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PushedAt *Timestamp `json:"pushed_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Homepage *string `json:"homepage,omitempty"` + PullsURL *string `json:"pulls_url,omitempty"` + Size *int `json:"size,omitempty"` + StargazersCount *int `json:"stargazers_count,omitempty"` + WatchersCount *int `json:"watchers_count,omitempty"` + Language *string `json:"language,omitempty"` + HasIssues *bool `json:"has_issues,omitempty"` + HasDownloads *bool `json:"has_downloads,omitempty"` + HasWiki *bool `json:"has_wiki,omitempty"` + HasPages *bool `json:"has_pages,omitempty"` + ForksCount *int `json:"forks_count,omitempty"` + Archived *bool `json:"archived,omitempty"` + Disabled *bool `json:"disabled,omitempty"` + OpenIssuesCount *int `json:"open_issues_count,omitempty"` + DefaultBranch *string `json:"default_branch,omitempty"` + MasterBranch *string `json:"master_branch,omitempty"` + Organization *string `json:"organization,omitempty"` + URL *string `json:"url,omitempty"` + ArchiveURL *string `json:"archive_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + SSHURL *string `json:"ssh_url,omitempty"` + CloneURL *string `json:"clone_url,omitempty"` + SVNURL *string `json:"svn_url,omitempty"` + Topics []string `json:"topics,omitempty"` + CustomProperties map[string]any `json:"custom_properties,omitempty"` } // PushEventRepoOwner is a basic representation of user/org in a PushEvent payload. @@ -737,11 +1444,31 @@ type PushEventRepoOwner struct { Email *string `json:"email,omitempty"` } +// RegistryPackageEvent represents activity related to GitHub Packages. +// The Webhook event name is "registry_package". +// +// This event is triggered when a GitHub Package is published or updated. +// +// GitHub API docs: https://docs.github.com/en/webhooks/webhook-events-and-payloads#registry_package +type RegistryPackageEvent struct { + // Action is the action that was performed. + // Can be "published" or "updated". + Action *string `json:"action,omitempty"` + RegistryPackage *Package `json:"registry_package,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Sender *User `json:"sender,omitempty"` + + // The following fields are only populated by Webhook events. + Installation *Installation `json:"installation,omitempty"` +} + // ReleaseEvent is triggered when a release is published, unpublished, created, -// edited, deleted, or prerelased. +// edited, deleted, or prereleased. // The Webhook event name is "release". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#releaseevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#release type ReleaseEvent struct { // Action is the action that was performed. Possible values are: "published", "unpublished", // "created", "edited", "deleted", or "prereleased". @@ -752,17 +1479,21 @@ type ReleaseEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } // RepositoryEvent is triggered when a repository is created, archived, unarchived, // renamed, edited, transferred, made public, or made private. Organization hooks are -// also trigerred when a repository is deleted. +// also triggered when a repository is deleted. // The Webhook event name is "repository". // // Events of this type are not visible in timelines, they are only used to // trigger organization webhooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#repository type RepositoryEvent struct { // Action is the action that was performed. Possible values are: "created", // "deleted" (organization hooks only), "archived", "unarchived", "edited", "renamed", @@ -771,42 +1502,229 @@ type RepositoryEvent struct { Repo *Repository `json:"repository,omitempty"` // The following fields are only populated by Webhook events. + Changes *EditChange `json:"changes,omitempty"` Org *Organization `json:"organization,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` } +// RepositoryDispatchEvent is triggered when a client sends a POST request to the repository dispatch event endpoint. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#repository_dispatch +type RepositoryDispatchEvent struct { + // Action is the event_type that submitted with the repository dispatch payload. Value can be any string. + Action *string `json:"action,omitempty"` + Branch *string `json:"branch,omitempty"` + ClientPayload json.RawMessage `json:"client_payload,omitempty"` + Repo *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// RepositoryImportEvent represents the activity related to a repository being imported to GitHub. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#repository_import +type RepositoryImportEvent struct { + // Status represents the final state of the import. This can be one of "success", "cancelled", or "failure". + Status *string `json:"status,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` +} + +// RepositoryRulesetEvent triggers whenever there is a change to the repository's ruleset configuration. +// +// This can include updates to protection rules, required status checks, code owners, or other related configurations. +// +// GitHub API docs: https://docs.github.com/en/webhooks/webhook-events-and-payloads#repository_ruleset +type RepositoryRulesetEvent struct { + Action *string `json:"action,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repository *Repository `json:"repository,omitempty"` + RepositoryRuleset *RepositoryRuleset `json:"repository_ruleset"` + Changes *RepositoryRulesetChanges `json:"changes,omitempty"` + Sender *User `json:"sender"` +} + +// RepositoryRulesetChanges represents the changes made to a repository ruleset. +type RepositoryRulesetChanges struct { + Name *RepositoryRulesetChangeSource `json:"name,omitempty"` + Enforcement *RepositoryRulesetChangeSource `json:"enforcement,omitempty"` + Conditions *RepositoryRulesetChangedConditions `json:"conditions,omitempty"` + Rules *RepositoryRulesetChangedRules `json:"rules,omitempty"` +} + +// RepositoryRulesetChangeSource represents a source change for the ruleset. +type RepositoryRulesetChangeSource struct { + From *string `json:"from,omitempty"` +} + +// RepositoryRulesetChangeSources represents multiple source changes for the ruleset. +type RepositoryRulesetChangeSources struct { + From []string `json:"from,omitempty"` +} + +// RepositoryRulesetChangedConditions holds changes to conditions in a ruleset. +type RepositoryRulesetChangedConditions struct { + Added []*RepositoryRulesetConditions `json:"added,omitempty"` + Deleted []*RepositoryRulesetConditions `json:"deleted,omitempty"` + Updated []*RepositoryRulesetUpdatedConditions `json:"updated,omitempty"` +} + +// RepositoryRulesetUpdatedConditions represents the edited updates to conditions in a ruleset. +type RepositoryRulesetUpdatedConditions struct { + Condition *RepositoryRulesetConditions `json:"condition,omitempty"` + Changes *RepositoryRulesetUpdatedCondition `json:"changes,omitempty"` +} + +// RepositoryRulesetUpdatedCondition represents the changes to a condition in a ruleset. +type RepositoryRulesetUpdatedCondition struct { + ConditionType *RepositoryRulesetChangeSource `json:"condition_type,omitempty"` + Target *RepositoryRulesetChangeSource `json:"target,omitempty"` + Include *RepositoryRulesetChangeSources `json:"include,omitempty"` + Exclude *RepositoryRulesetChangeSources `json:"exclude,omitempty"` +} + +// RepositoryRulesetChangedRules holds changes to rules in a ruleset. +type RepositoryRulesetChangedRules struct { + Added []*RepositoryRule `json:"added,omitempty"` + Deleted []*RepositoryRule `json:"deleted,omitempty"` + Updated []*RepositoryRulesetUpdatedRules `json:"updated,omitempty"` +} + +// RepositoryRulesetUpdatedRules holds updates to rules in a ruleset. +type RepositoryRulesetUpdatedRules struct { + Rule *RepositoryRule `json:"rule,omitempty"` + Changes *RepositoryRulesetChangedRule `json:"changes,omitempty"` +} + +// RepositoryRulesetChangedRule holds changes made to a rule in a ruleset. +type RepositoryRulesetChangedRule struct { + Configuration *RepositoryRulesetChangeSource `json:"configuration,omitempty"` + RuleType *RepositoryRulesetChangeSource `json:"rule_type,omitempty"` + Pattern *RepositoryRulesetChangeSource `json:"pattern,omitempty"` +} + // RepositoryVulnerabilityAlertEvent is triggered when a security alert is created, dismissed, or resolved. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryvulnerabilityalertevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#repository_vulnerability_alert type RepositoryVulnerabilityAlertEvent struct { // Action is the action that was performed. Possible values are: "create", "dismiss", "resolve". Action *string `json:"action,omitempty"` - //The security alert of the vulnerable dependency. - Alert *struct { - ID *int64 `json:"id,omitempty"` - AffectedRange *string `json:"affected_range,omitempty"` - AffectedPackageName *string `json:"affected_package_name,omitempty"` - ExternalReference *string `json:"external_reference,omitempty"` - ExternalIdentifier *string `json:"external_identifier,omitempty"` - FixedIn *string `json:"fixed_in,omitempty"` - Dismisser *User `json:"dismisser,omitempty"` - DismissReason *string `json:"dismiss_reason,omitempty"` - DismissedAt *Timestamp `json:"dismissed_at,omitempty"` - } `json:"alert,omitempty"` + // The security alert of the vulnerable dependency. + Alert *RepositoryVulnerabilityAlert `json:"alert,omitempty"` + + // The repository of the vulnerable dependency. + Repository *Repository `json:"repository,omitempty"` + + // The following fields are only populated by Webhook events. + Installation *Installation `json:"installation,omitempty"` + + // The user that triggered the event. + Sender *User `json:"sender,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// RepositoryVulnerabilityAlert represents a repository security alert. +type RepositoryVulnerabilityAlert struct { + ID *int64 `json:"id,omitempty"` + AffectedRange *string `json:"affected_range,omitempty"` + AffectedPackageName *string `json:"affected_package_name,omitempty"` + ExternalReference *string `json:"external_reference,omitempty"` + ExternalIdentifier *string `json:"external_identifier,omitempty"` + GitHubSecurityAdvisoryID *string `json:"ghsa_id,omitempty"` + Severity *string `json:"severity,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + FixedIn *string `json:"fixed_in,omitempty"` + Dismisser *User `json:"dismisser,omitempty"` + DismissReason *string `json:"dismiss_reason,omitempty"` + DismissedAt *Timestamp `json:"dismissed_at,omitempty"` +} + +// SecretScanningAlertEvent is triggered when a secret scanning alert occurs in a repository. +// The Webhook name is secret_scanning_alert. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#secret_scanning_alert +type SecretScanningAlertEvent struct { + // Action is the action that was performed. Possible values are: "created", "resolved", or "reopened". + Action *string `json:"action,omitempty"` + + // Alert is the secret scanning alert involved in the event. + Alert *SecretScanningAlert `json:"alert,omitempty"` + + // Only populated by the "resolved" and "reopen" actions + Sender *User `json:"sender,omitempty"` + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// SecretScanningAlertLocationEvent is triggered when there is activity relating to the locations of a secret in a secret scanning alert. +// The Webhook event name is "secret_scanning_alert_location". +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#secret_scanning_alert_location +type SecretScanningAlertLocationEvent struct { + Action *string `json:"action,omitempty"` + Alert *SecretScanningAlert `json:"alert,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Location *SecretScanningAlertLocation `json:"location,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` +} + +// SecurityAndAnalysisEvent is triggered when code security and analysis features +// are enabled or disabled for a repository. +// +// GitHub API docs: https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads#security_and_analysis +type SecurityAndAnalysisEvent struct { + Changes *SecurityAndAnalysisChange `json:"changes,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` +} + +// SecurityAndAnalysisChange represents the changes when security and analysis +// features are enabled or disabled for a repository. +type SecurityAndAnalysisChange struct { + From *SecurityAndAnalysisChangeFrom `json:"from,omitempty"` +} + +// SecurityAndAnalysisChangeFrom represents which change was made when security +// and analysis features are enabled or disabled for a repository. +type SecurityAndAnalysisChangeFrom struct { + SecurityAndAnalysis *SecurityAndAnalysis `json:"security_and_analysis,omitempty"` } // StarEvent is triggered when a star is added or removed from a repository. // The Webhook event name is "star". // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#starevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#star type StarEvent struct { // Action is the action that was performed. Possible values are: "created" or "deleted". Action *string `json:"action,omitempty"` // StarredAt is the time the star was created. It will be null for the "deleted" action. StarredAt *Timestamp `json:"starred_at,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` } // StatusEvent is triggered when the status of a Git commit changes. @@ -815,7 +1733,7 @@ type StarEvent struct { // Events of this type are not visible in timelines, they are only used to // trigger hooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#statusevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#status type StatusEvent struct { SHA *string `json:"sha,omitempty"` // State is the new state. Possible values are: "pending", "success", "failure", "error". @@ -834,6 +1752,10 @@ type StatusEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` } // TeamEvent is triggered when an organization's team is created, modified or deleted. @@ -842,7 +1764,7 @@ type StatusEvent struct { // Events of this type are not visible in timelines. These events are only used // to trigger hooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#team type TeamEvent struct { Action *string `json:"action,omitempty"` Team *Team `json:"team,omitempty"` @@ -861,7 +1783,7 @@ type TeamEvent struct { // Events of this type are not visible in timelines. These events are only used // to trigger hooks. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamaddevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#team_add type TeamAddEvent struct { Team *Team `json:"team,omitempty"` Repo *Repository `json:"repository,omitempty"` @@ -872,13 +1794,30 @@ type TeamAddEvent struct { Installation *Installation `json:"installation,omitempty"` } +// UserEvent is triggered when a user is created or deleted. +// The Webhook event name is "user". +// +// Only global webhooks can subscribe to this event type. +// +// GitHub API docs: https://developer.github.com/enterprise/v3/activity/events/types/#userevent-enterprise +type UserEvent struct { + User *User `json:"user,omitempty"` + // The action performed. Possible values are: "created" or "deleted". + Action *string `json:"action,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Sender *User `json:"sender,omitempty"` + + // The following fields are only populated by Webhook events. + Installation *Installation `json:"installation,omitempty"` +} + // WatchEvent is related to starring a repository, not watching. See this API // blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/ // // The event’s actor is the user who starred a repository, and the event’s // repository is the repository that was starred. // -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#watchevent +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#watch type WatchEvent struct { // Action is the action that was performed. Possible value is: "started". Action *string `json:"action,omitempty"` @@ -887,4 +1826,183 @@ type WatchEvent struct { Repo *Repository `json:"repository,omitempty"` Sender *User `json:"sender,omitempty"` Installation *Installation `json:"installation,omitempty"` + + // The following field is only present when the webhook is triggered on + // a repository belonging to an organization. + Org *Organization `json:"organization,omitempty"` +} + +// WorkflowDispatchEvent is triggered when someone triggers a workflow run on GitHub or +// sends a POST request to the endpoint to create a workflow dispatch event. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#workflow_dispatch +type WorkflowDispatchEvent struct { + Inputs json.RawMessage `json:"inputs,omitempty"` + Ref *string `json:"ref,omitempty"` + Workflow *string `json:"workflow,omitempty"` + + // The following fields are only populated by Webhook events. + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// WorkflowJobEvent is triggered when a job is queued, started or completed. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job +type WorkflowJobEvent struct { + WorkflowJob *WorkflowJob `json:"workflow_job,omitempty"` + + Action *string `json:"action,omitempty"` + + // The following fields are only populated by Webhook events. + + // Org is not nil when the webhook is configured for an organization or the event + // occurs from activity in a repository owned by an organization. + Org *Organization `json:"organization,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Deployment *Deployment `json:"deployment,omitempty"` +} + +// WorkflowRunEvent is triggered when a GitHub Actions workflow run is requested or completed. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#workflow_run +type WorkflowRunEvent struct { + Action *string `json:"action,omitempty"` + Workflow *Workflow `json:"workflow,omitempty"` + WorkflowRun *WorkflowRun `json:"workflow_run,omitempty"` + + // The following fields are only populated by Webhook events. + Org *Organization `json:"organization,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// SecurityAdvisory represents the advisory object in SecurityAdvisoryEvent payload. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#security_advisory +type SecurityAdvisory struct { + CVSS *AdvisoryCVSS `json:"cvss,omitempty"` + CVSSSeverities *AdvisoryCVSSSeverities `json:"cvss_severities,omitempty"` + CWEs []*AdvisoryCWEs `json:"cwes,omitempty"` + GHSAID *string `json:"ghsa_id,omitempty"` + Summary *string `json:"summary,omitempty"` + Description *string `json:"description,omitempty"` + Severity *string `json:"severity,omitempty"` + Identifiers []*AdvisoryIdentifier `json:"identifiers,omitempty"` + References []*AdvisoryReference `json:"references,omitempty"` + PublishedAt *Timestamp `json:"published_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + WithdrawnAt *Timestamp `json:"withdrawn_at,omitempty"` + Vulnerabilities []*AdvisoryVulnerability `json:"vulnerabilities,omitempty"` + CVEID *string `json:"cve_id,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Author *User `json:"author,omitempty"` + Publisher *User `json:"publisher,omitempty"` + State *string `json:"state,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + Submission *SecurityAdvisorySubmission `json:"submission,omitempty"` + CWEIDs []string `json:"cwe_ids,omitempty"` + Credits []*RepoAdvisoryCredit `json:"credits,omitempty"` + CreditsDetailed []*RepoAdvisoryCreditDetailed `json:"credits_detailed,omitempty"` + CollaboratingUsers []*User `json:"collaborating_users,omitempty"` + CollaboratingTeams []*Team `json:"collaborating_teams,omitempty"` + PrivateFork *Repository `json:"private_fork,omitempty"` +} + +// AdvisoryIdentifier represents the identifier for a Security Advisory. +type AdvisoryIdentifier struct { + Value *string `json:"value,omitempty"` + Type *string `json:"type,omitempty"` +} + +// AdvisoryReference represents the reference url for the security advisory. +type AdvisoryReference struct { + URL *string `json:"url,omitempty"` +} + +// AdvisoryVulnerability represents the vulnerability object for a Security Advisory. +type AdvisoryVulnerability struct { + Package *VulnerabilityPackage `json:"package,omitempty"` + Severity *string `json:"severity,omitempty"` + VulnerableVersionRange *string `json:"vulnerable_version_range,omitempty"` + FirstPatchedVersion *FirstPatchedVersion `json:"first_patched_version,omitempty"` + + // PatchedVersions and VulnerableFunctions are used in the following APIs: + // - https://docs.github.com/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories-for-an-organization + // - https://docs.github.com/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories + PatchedVersions *string `json:"patched_versions,omitempty"` + VulnerableFunctions []string `json:"vulnerable_functions,omitempty"` +} + +// VulnerabilityPackage represents the package object for an Advisory Vulnerability. +type VulnerabilityPackage struct { + Ecosystem *string `json:"ecosystem,omitempty"` + Name *string `json:"name,omitempty"` +} + +// FirstPatchedVersion represents the identifier for the first patched version of that vulnerability. +type FirstPatchedVersion struct { + Identifier *string `json:"identifier,omitempty"` +} + +// SecurityAdvisoryEvent is triggered when a security-related vulnerability is found in software on GitHub. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#security_advisory +type SecurityAdvisoryEvent struct { + Action *string `json:"action,omitempty"` + SecurityAdvisory *SecurityAdvisory `json:"security_advisory,omitempty"` + + // The following fields are only populated by Webhook events. + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Sender *User `json:"sender,omitempty"` +} + +// CodeScanningAlertEvent is triggered when a code scanning finds a potential vulnerability or error in your code. +// +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#code_scanning_alert +type CodeScanningAlertEvent struct { + Action *string `json:"action,omitempty"` + Alert *Alert `json:"alert,omitempty"` + Ref *string `json:"ref,omitempty"` + // CommitOID is the commit SHA of the code scanning alert + CommitOID *string `json:"commit_oid,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Org *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + + Installation *Installation `json:"installation,omitempty"` +} + +// SponsorshipEvent represents a sponsorship event in GitHub. +// +// GitHub API docs: https://docs.github.com/rest/overview/github-event-types?apiVersion=2022-11-28#sponsorshipevent +type SponsorshipEvent struct { + Action *string `json:"action,omitempty"` + EffectiveDate *string `json:"effective_date,omitempty"` + Changes *SponsorshipChanges `json:"changes,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Sender *User `json:"sender,omitempty"` + Installation *Installation `json:"installation,omitempty"` +} + +// SponsorshipChanges represents changes made to the sponsorship. +type SponsorshipChanges struct { + Tier *SponsorshipTier `json:"tier,omitempty"` + PrivacyLevel *string `json:"privacy_level,omitempty"` +} + +// SponsorshipTier represents the tier information of a sponsorship. +type SponsorshipTier struct { + From *string `json:"from,omitempty"` } diff --git a/github/example_iterators_test.go b/github/example_iterators_test.go new file mode 100644 index 00000000000..a5111b68889 --- /dev/null +++ b/github/example_iterators_test.go @@ -0,0 +1,33 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github_test + +import ( + "context" + "fmt" + "log" + + "github.com/google/go-github/v90/github" +) + +func ExampleRepositoriesService_ListByUserIter() { + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + ctx := context.Background() + + // List all repositories for a user using the iterator. + // This automatically handles pagination. + // Note that if `opts` is `nil`, a new empty `opts` will be created and used within the iterator. + opts := &github.RepositoryListByUserOptions{Type: "public"} + for repo, err := range client.Repositories.ListByUserIter(ctx, "octocat", opts) { + if err != nil { + log.Fatalf("Error listing repositories by user: %v", err) + } + fmt.Println(repo.GetName()) + } +} diff --git a/github/examples_test.go b/github/examples_test.go index a47f927ec60..e1c373b841d 100644 --- a/github/examples_test.go +++ b/github/examples_test.go @@ -12,50 +12,60 @@ import ( "fmt" "log" - "github.com/google/go-github/v28/github" + "github.com/google/go-github/v90/github" ) -func ExampleClient_Markdown() { - client := github.NewClient(nil) +func ExampleMarkdownService_Render() { + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } input := "# heading #\n\nLink to issue #1" opt := &github.MarkdownOptions{Mode: "gfm", Context: "google/go-github"} - output, _, err := client.Markdown(context.Background(), input, opt) + ctx := context.Background() + output, _, err := client.Markdown.Render(ctx, input, opt) if err != nil { - fmt.Println(err) + log.Fatalf("Error rendering markdown: %v", err) } fmt.Println(output) } func ExampleRepositoriesService_GetReadme() { - client := github.NewClient(nil) + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } - readme, _, err := client.Repositories.GetReadme(context.Background(), "google", "go-github", nil) + ctx := context.Background() + readme, _, err := client.Repositories.GetReadme(ctx, "google", "go-github", nil) if err != nil { - fmt.Println(err) - return + log.Fatalf("Error getting README: %v", err) } content, err := readme.GetContent() if err != nil { - fmt.Println(err) - return + log.Fatalf("Error getting README content: %v", err) } fmt.Printf("google/go-github README:\n%v\n", content) } -func ExampleRepositoriesService_List() { - client := github.NewClient(nil) +func ExampleRepositoriesService_ListByUser() { + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } user := "willnorris" - opt := &github.RepositoryListOptions{Type: "owner", Sort: "updated", Direction: "desc"} + opt := &github.RepositoryListByUserOptions{Type: "owner", Sort: "updated", Direction: "desc"} - repos, _, err := client.Repositories.List(context.Background(), user, opt) + ctx := context.Background() + repos, _, err := client.Repositories.ListByUser(ctx, user, opt) if err != nil { - fmt.Println(err) + log.Fatalf("error listing repositories by user: %v", err) } fmt.Printf("Recently updated repositories by %q: %v", user, github.Stringify(repos)) @@ -66,11 +76,14 @@ func ExampleRepositoriesService_CreateFile() { // Contents API. Only 1 file per commit can be managed through that API. // Note that authentication is needed here as you are performing a modification - // so you will need to modify the example to provide an oauth client to - // github.NewClient() instead of nil. See the following documentation for more - // information on how to authenticate with the client: - // https://godoc.org/github.com/google/go-github/github#hdr-Authentication - client := github.NewClient(nil) + // so you will need to modify the example to provide authentication to + // github.NewClient(). See the following documentation for more information + // on how to authenticate with the client: + // https://pkg.go.dev/github.com/google/go-github/v90/github#hdr-Authentication + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } ctx := context.Background() fileContent := []byte("This is the content of my file\nand the 2nd line of it") @@ -78,30 +91,33 @@ func ExampleRepositoriesService_CreateFile() { // Note: the file needs to be absent from the repository as you are not // specifying a SHA reference here. opts := &github.RepositoryContentFileOptions{ - Message: github.String("This is my commit message"), + Message: github.Ptr("This is my commit message"), Content: fileContent, - Branch: github.String("master"), - Committer: &github.CommitAuthor{Name: github.String("FirstName LastName"), Email: github.String("user@example.com")}, + Branch: github.Ptr("master"), + Committer: &github.CommitAuthor{Name: github.Ptr("FirstName LastName"), Email: github.Ptr("user@example.com")}, } - _, _, err := client.Repositories.CreateFile(ctx, "myOrganization", "myRepository", "myNewFile.md", opts) - if err != nil { - fmt.Println(err) - return + if _, _, err := client.Repositories.CreateFile(ctx, "myOrganization", "myRepository", "myNewFile.md", opts); err != nil { + log.Fatalf("Error creating file: %v", err) } } func ExampleUsersService_ListAll() { - client := github.NewClient(nil) + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + + ctx := context.Background() opts := &github.UserListOptions{} for { - users, _, err := client.Users.ListAll(context.Background(), opts) + users, _, err := client.Users.ListAll(ctx, opts) if err != nil { - log.Fatalf("error listing users: %v", err) + log.Fatalf("Error listing users: %v", err) } if len(users) == 0 { break } - opts.Since = *users[len(users)-1].ID + opts.Since = users[len(users)-1].GetID() // Process users... } } @@ -110,27 +126,30 @@ func ExamplePullRequestsService_Create() { // In this example we're creating a PR and displaying the HTML url at the end. // Note that authentication is needed here as you are performing a modification - // so you will need to modify the example to provide an oauth client to - // github.NewClient() instead of nil. See the following documentation for more - // information on how to authenticate with the client: - // https://godoc.org/github.com/google/go-github/github#hdr-Authentication - client := github.NewClient(nil) + // so you will need to modify the example to provide authentication to + // github.NewClient(). See the following documentation for more information + // on how to authenticate with the client: + // https://pkg.go.dev/github.com/google/go-github/v90/github#hdr-Authentication + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } - newPR := &github.NewPullRequest{ - Title: github.String("My awesome pull request"), - Head: github.String("branch_to_merge"), - Base: github.String("master"), - Body: github.String("This is the description of the PR created with the package `github.com/google/go-github/github`"), - MaintainerCanModify: github.Bool(true), + newPR := github.CreatePullRequest{ + Title: github.Ptr("My awesome pull request"), + Head: "branch_to_merge", + Base: "master", + Body: github.Ptr("This is the description of the PR created with the package `github.com/google/go-github/github`"), + MaintainerCanModify: github.Ptr(true), } - pr, _, err := client.PullRequests.Create(context.Background(), "myOrganization", "myRepository", newPR) + ctx := context.Background() + pr, _, err := client.PullRequests.Create(ctx, "myOrganization", "myRepository", newPR) if err != nil { - fmt.Println(err) - return + log.Fatalf("Error creating pull request: %v", err) } - fmt.Printf("PR created: %s\n", pr.GetHTMLURL()) + fmt.Printf("PR created: %v\n", pr.GetHTMLURL()) } func ExampleTeamsService_ListTeams() { @@ -138,11 +157,14 @@ func ExampleTeamsService_ListTeams() { // Note that authentication is needed here as you are performing a lookup on // an organization's administrative configuration, so you will need to modify - // the example to provide an oauth client to github.NewClient() instead of nil. - // See the following documentation for more information on how to authenticate - // with the client: - // https://godoc.org/github.com/google/go-github/github#hdr-Authentication - client := github.NewClient(nil) + // the example to to provide authentication to github.NewClient(). See the + // following documentation for more information on how to authenticate with + // the client: + // https://pkg.go.dev/github.com/google/go-github/v90/github#hdr-Authentication + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } teamName := "Developers team" ctx := context.Background() @@ -151,12 +173,11 @@ func ExampleTeamsService_ListTeams() { for { teams, resp, err := client.Teams.ListTeams(ctx, "myOrganization", opts) if err != nil { - fmt.Println(err) - return + log.Fatalf("error listing teams: %v", err) } for _, t := range teams { if t.GetName() == teamName { - fmt.Printf("Team %q has ID %d\n", teamName, t.GetID()) + fmt.Printf("Team %q has ID %v\n", teamName, t.GetID()) return } } @@ -168,3 +189,22 @@ func ExampleTeamsService_ListTeams() { fmt.Printf("Team %q was not found\n", teamName) } + +func ExampleUsersService_ListUserSocialAccounts() { + client, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) + } + ctx := context.Background() + opts := &github.ListOptions{} + for { + accounts, resp, err := client.Users.ListUserSocialAccounts(ctx, "shreyjain13", opts) + if err != nil { + log.Fatalf("Error listing user social accounts: %v", err) + } + if resp.NextPage == 0 || len(accounts) == 0 { + break + } + opts.Page = resp.NextPage + } +} diff --git a/github/fuzz_messages_test.go b/github/fuzz_messages_test.go new file mode 100644 index 00000000000..137bfb3cc1d --- /dev/null +++ b/github/fuzz_messages_test.go @@ -0,0 +1,82 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "testing" +) + +// FuzzParseWebHook tests ParseWebHook against arbitrary event types and payloads. +// It verifies that no input triggers a panic or nil pointer dereference. +// +// This fuzz test is intended for integration with OSS-Fuzz (https://google.github.io/oss-fuzz/) +// for continuous fuzzing in the cloud. +// +// To run: +// +// go test -fuzz=^FuzzParseWebHook$ -fuzztime=30s . +func FuzzParseWebHook(f *testing.F) { + seeds := []struct { + eventType string + payload string + }{ + {"push", `{"ref": "refs/heads/main", "before": "000000", "after": "123456", "commits": [{"id": "abc", "message": "msg", "added": [], "removed": [], "modified": []}]}`}, + {"pull_request", `{"action": "opened", "number": 1, "pull_request": {"title": "test", "state": "open", "user": {"login": "u"}}}`}, + {"issues", `{"action": "opened", "issue": {"number": 42, "title": "bug", "state": "open"}}`}, + {"release", `{"action": "published", "release": {"tag_name": "v1.0.0", "draft": false}}`}, + {"check_run", `{"action": "created", "check_run": {"status": "in_progress", "id": 1}}`}, + {"check_suite", `{"action": "completed", "check_suite": {"id": 1, "status": "completed"}}`}, + {"workflow_run", `{"action": "requested", "workflow_run": {"id": 123, "status": "queued"}}`}, + {"workflow_job", `{"action": "queued", "workflow_job": {"id": 1, "status": "queued"}}`}, + {"discussion", `{"action": "created", "discussion": {"title": "hello", "number": 1}}`}, + {"ping", `{"zen": "Keep it logically awesome.", "hook_id": 1}`}, + {"repository", `{"action": "created", "repository": {"name": "test-repo", "private": false}}`}, + {"star", `{"action": "created", "starred_at": "2026-03-11T00:00:00Z"}`}, + {"create", `{"ref": "main", "ref_type": "branch"}`}, + {"delete", `{"ref": "old-branch", "ref_type": "branch"}`}, + {"fork", `{"forkee": {"name": "forked-repo"}}`}, + {"deployment", `{"action": "created", "deployment": {"id": 1, "ref": "main"}}`}, + {"deployment_status", `{"action": "created", "deployment_status": {"id": 1, "state": "pending"}}`}, + {"member", `{"action": "added", "member": {"login": "user"}}`}, + {"public", `{"repository": {"name": "now-public"}}`}, + {"commit_comment", `{"action": "created", "comment": {"id": 1, "body": "comment"}}`}, + } + for _, s := range seeds { + f.Add(s.eventType, []byte(s.payload)) + } + + for _, messageType := range MessageTypes() { + proto := EventForType(messageType) + if proto == nil { + f.Add(messageType, []byte(`{}`)) + continue + } + // Generate a seed by marshaling the zero-value struct so the fuzzer + // starts from a structurally valid JSON skeleton for each event type. + b, err := json.Marshal(proto) + if err != nil { + f.Add(messageType, []byte(`{}`)) + continue + } + f.Add(messageType, b) + } + + f.Fuzz(func(_ *testing.T, eventType string, payload []byte) { + if len(payload) > 1<<20 { + return + } + event, err := ParseWebHook(eventType, payload) + if err != nil { + return + } + if event != nil { + // Traverse all fields recursively to catch nil pointer dereferences + _ = fmt.Sprintf("%+v", event) + } + }) +} diff --git a/github/gen-accessors.go b/github/gen-accessors.go index 4c5e8eec7e7..aa63bfc16ab 100644 --- a/github/gen-accessors.go +++ b/github/gen-accessors.go @@ -3,27 +3,35 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build ignore +//go:build ignore -// gen-accessors generates accessor methods for structs with pointer fields. +// gen-accessors generates accessor methods for all struct fields. +// This is so that interfaces can be easily crafted by users of this repo +// within their own code bases. +// See https://github.com/google/go-github/issues/4059 for details. // // It is meant to be used by go-github contributors in conjunction with the // go generate tool before sending a PR to GitHub. // Please see the CONTRIBUTING.md file for more information. +// +// Usage: +// +// go run gen-accessors.go [-v [file1.go file2.go ...]] package main import ( "bytes" + "errors" "flag" "fmt" "go/ast" "go/format" "go/parser" "go/token" - "io/ioutil" + "io/fs" "log" "os" - "sort" + "slices" "strings" "text/template" ) @@ -33,26 +41,40 @@ const ( ) var ( + check = flag.Bool("check", false, "Check whether generated files are up to date") verbose = flag.Bool("v", false, "Print verbose log messages") sourceTmpl = template.Must(template.New("source").Parse(source)) + testTmpl = template.Must(template.New("test").Parse(test)) - // blacklistStructMethod lists "struct.method" combos to skip. - blacklistStructMethod = map[string]bool{ - "RepositoryContent.GetContent": true, + // skipStructMethods lists "struct.method" combos to skip. + skipStructMethods = map[string]bool{ + "AbuseRateLimitError.GetResponse": true, "Client.GetBaseURL": true, "Client.GetUploadURL": true, "ErrorResponse.GetResponse": true, + "MarketplaceService.GetStubbed": true, + "PackageVersion.GetBody": true, + "PackageVersion.GetMetadata": true, "RateLimitError.GetResponse": true, - "AbuseRateLimitError.GetResponse": true, + "RepositoryContent.GetContent": true, } - // blacklistStruct lists structs to skip. - blacklistStruct = map[string]bool{ + // skipStructs lists structs to skip. + skipStructs = map[string]bool{ "Client": true, } + + // whitelistSliceGetters lists "struct.field" to add getter method + whitelistSliceGetters = map[string]bool{ + "PushEvent.Commits": true, + } ) -func logf(fmt string, args ...interface{}) { +func isCheck() bool { + return *check || os.Getenv("CHECK") == "1" +} + +func logf(fmt string, args ...any) { if *verbose { log.Printf(fmt, args...) } @@ -60,6 +82,18 @@ func logf(fmt string, args ...interface{}) { func main() { flag.Parse() + + // For debugging purposes, processing just a single or a few files is helpful: + var processOnly map[string]bool + if *verbose { // Only create the map if args are provided. + for _, arg := range flag.Args() { + if processOnly == nil { + processOnly = map[string]bool{} + } + processOnly[arg] = true + } + } + fset := token.NewFileSet() pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0) @@ -76,6 +110,10 @@ func main() { Imports: map[string]string{}, } for filename, f := range pkg.Files { + if *verbose && processOnly != nil && !processOnly[filename] { + continue + } + logf("Processing %v...", filename) if err := t.processAST(f); err != nil { log.Fatal(err) @@ -104,18 +142,21 @@ func (t *templateData) processAST(f *ast.File) error { logf("Struct %v is unexported; skipping.", ts.Name) continue } - // Check if the struct is blacklisted. - if blacklistStruct[ts.Name.Name] { - logf("Struct %v is blacklisted; skipping.", ts.Name) + // Check if the struct should be skipped. + if skipStructs[ts.Name.Name] { + logf("Struct %v is in skip list; skipping.", ts.Name) + continue + } + if _, ok := ts.Type.(*ast.Ident); ok { // e.g. type SomeService service continue } st, ok := ts.Type.(*ast.StructType) if !ok { + logf("Skipping TypeSpec of type %T", ts.Type) continue } for _, field := range st.Fields.List { - se, ok := field.Type.(*ast.StarExpr) - if len(field.Names) == 0 || !ok { + if len(field.Names) == 0 { continue } @@ -125,19 +166,44 @@ func (t *templateData) processAST(f *ast.File) error { logf("Field %v is unexported; skipping.", fieldName) continue } - // Check if "struct.method" is blacklisted. - if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] { - logf("Method %v is blacklisted; skipping.", key) + // Check if "struct.method" should be skipped. + if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); skipStructMethods[key] { + logf("Method %v is skip list; skipping.", key) + continue + } + + se, ok := field.Type.(*ast.StarExpr) + if !ok { + switch x := field.Type.(type) { + case *ast.MapType: + logf("processAST: addMapType(x, %q, %q)", ts.Name.String(), fieldName.String()) + t.addMapType(x, ts.Name.String(), fieldName.String(), false) + continue + case *ast.ArrayType: + logf("processAST: addArrayType(x, %q, %q)", ts.Name.String(), fieldName.String()) + t.addArrayType(x, ts.Name.String(), fieldName.String(), false) + continue + case *ast.Ident: + logf("processAST: addSimpleValueIdent(x, %q, %q)", ts.Name.String(), fieldName.String()) + t.addSimpleValueIdent(x, ts.Name.String(), fieldName.String()) + continue + case *ast.SelectorExpr: + logf("processAST: addSimpleValueSelectorExpr(x, %q, %q)", ts.Name.String(), fieldName.String()) + t.addSimpleValueSelectorExpr(x, ts.Name.String(), fieldName.String()) + continue + } + + logf("Skipping field type %T, fieldName=%v", field.Type, fieldName) continue } switch x := se.X.(type) { case *ast.ArrayType: - t.addArrayType(x, ts.Name.String(), fieldName.String()) + t.addArrayType(x, ts.Name.String(), fieldName.String(), true) case *ast.Ident: t.addIdent(x, ts.Name.String(), fieldName.String()) case *ast.MapType: - t.addMapType(x, ts.Name.String(), fieldName.String()) + t.addMapType(x, ts.Name.String(), fieldName.String(), true) case *ast.SelectorExpr: t.addSelectorExpr(x, ts.Name.String(), fieldName.String()) default: @@ -160,19 +226,56 @@ func (t *templateData) dump() error { } // Sort getters by ReceiverType.FieldName. - sort.Sort(byName(t.Getters)) + slices.SortStableFunc(t.Getters, func(a, b *getter) int { + return strings.Compare(a.sortVal, b.sortVal) + }) + + processTemplate := func(tmpl *template.Template, filename string) error { + var buf bytes.Buffer + if err := tmpl.Execute(&buf, t); err != nil { + return err + } + clean, err := format.Source(buf.Bytes()) + if err != nil { + return fmt.Errorf("format.Source:\n%v\n%v", buf.String(), err) + } - var buf bytes.Buffer - if err := sourceTmpl.Execute(&buf, t); err != nil { - return err + if isCheck() { + logf("Checking %v...", filename) + old, err := os.ReadFile(filename) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return fmt.Errorf("Missing file: %v\n", filename) + } + return err + } + + if !bytes.Equal(old, clean) { + return fmt.Errorf("Generated files are out of date. Please run go generate ./... and commit the results") + } + return nil + } + + logf("Writing %v...", filename) + if err := os.Chmod(filename, 0o644); err != nil { + return fmt.Errorf("os.Chmod(%q, 0644): %v", filename, err) + } + + if err := os.WriteFile(filename, clean, 0o444); err != nil { + return err + } + + if err := os.Chmod(filename, 0o444); err != nil { + return fmt.Errorf("os.Chmod(%q, 0444): %v", filename, err) + } + + return nil } - clean, err := format.Source(buf.Bytes()) - if err != nil { + + if err := processTemplate(sourceTmpl, t.filename); err != nil { return err } - - logf("Writing %v...", t.filename) - return ioutil.WriteFile(t.filename, clean, 0644) + return processTemplate(testTmpl, strings.ReplaceAll(t.filename, ".go", "_test.go")) } func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter { @@ -187,40 +290,87 @@ func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct } } -func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) { +func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string, isAPointer bool) { var eltType string + var ng *getter switch elt := x.Elt.(type) { case *ast.Ident: eltType = elt.String() + ng = newGetter(receiverType, fieldName, "[]"+eltType, "nil", false) + case *ast.StarExpr: + ident, ok := elt.X.(*ast.Ident) + if !ok { + return + } + ng = newGetter(receiverType, fieldName, "[]*"+ident.String(), "nil", false) default: logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt) return } - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false)) + ng.ArrayType = !isAPointer + t.Getters = append(t.Getters, ng) +} + +func (t *templateData) addSimpleValueIdent(x *ast.Ident, receiverType, fieldName string) { + getter := genIdentGetter(x, receiverType, fieldName) + getter.IsSimpleValue = true + logf("addSimpleValueIdent: Processing %q - fieldName=%q, getter.ZeroValue=%q, x.Obj=%#v", x.String(), fieldName, getter.ZeroValue, x.Obj) + if getter.ZeroValue == "nil" { + if x.Obj == nil { + switch x.String() { + case "any": // NOOP - leave as `nil` + default: + getter.ZeroValue = x.String() + "{}" + } + } else { + if ts, ok := x.Obj.Decl.(*ast.TypeSpec); ok { + logf("addSimpleValueIdent: Processing %q of type %T", x.String(), ts.Type) + switch xX := ts.Type.(type) { + case *ast.Ident: + logf("addSimpleValueIdent: Processing %q of type %T - zero value is %q", x.String(), ts.Type, getter.ZeroValue) + getter.ZeroValue = zeroValueOfIdent(xX) + case *ast.StructType: + getter.ZeroValue = x.String() + "{}" + logf("addSimpleValueIdent: Processing %q of type %T - zero value is %q", x.String(), ts.Type, getter.ZeroValue) + case *ast.InterfaceType, *ast.ArrayType: // NOOP - leave as `nil` + logf("addSimpleValueIdent: Processing %q of type %T - zero value is %q", x.String(), ts.Type, getter.ZeroValue) + default: + log.Fatalf("addSimpleValueIdent: unhandled case %T", xX) + } + } + } + } + t.Getters = append(t.Getters, getter) } func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) { - var zeroValue string - var namedStruct = false + getter := genIdentGetter(x, receiverType, fieldName) + t.Getters = append(t.Getters, getter) +} + +func zeroValueOfIdent(x *ast.Ident) string { switch x.String() { - case "int", "int64": - zeroValue = "0" + case "int", "int64", "float64", "uint8", "uint16": + return "0" case "string": - zeroValue = `""` + return `""` case "bool": - zeroValue = "false" + return "false" case "Timestamp": - zeroValue = "Timestamp{}" + return "Timestamp{}" default: - zeroValue = "nil" - namedStruct = true + return "nil" } +} - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct)) +func genIdentGetter(x *ast.Ident, receiverType, fieldName string) *getter { + zeroValue := zeroValueOfIdent(x) + namedStruct := zeroValue == "nil" + return newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct) } -func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) { +func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string, isAPointer bool) { var keyType string switch key := x.Key.(type) { case *ast.Ident: @@ -241,13 +391,33 @@ func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType) zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType) - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) + ng := newGetter(receiverType, fieldName, fieldType, zeroValue, false) + ng.MapType = !isAPointer + t.Getters = append(t.Getters, ng) +} + +func (t *templateData) addSimpleValueSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) { + getter := t.genSelectorExprGetter(x, receiverType, fieldName) + if getter == nil { + return + } + getter.IsSimpleValue = true + logf("addSimpleValueSelectorExpr: Processing field name %q - %#v - zero value is %q", fieldName, x, getter.ZeroValue) + t.Getters = append(t.Getters, getter) } func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) { - if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field. + getter := t.genSelectorExprGetter(x, receiverType, fieldName) + if getter == nil { return } + t.Getters = append(t.Getters, getter) +} + +func (t *templateData) genSelectorExprGetter(x *ast.SelectorExpr, receiverType, fieldName string) *getter { + if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field. + return nil + } var xX string if xx, ok := x.X.(*ast.Ident); ok { @@ -266,10 +436,12 @@ func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldN if xX == "time" && x.Sel.Name == "Duration" { zeroValue = "0" } - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) + return newGetter(receiverType, fieldName, fieldType, zeroValue, false) default: logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x) } + + return nil } type templateData struct { @@ -281,28 +453,27 @@ type templateData struct { } type getter struct { - sortVal string // Lower-case version of "ReceiverType.FieldName". - ReceiverVar string // The one-letter variable name to match the ReceiverType. - ReceiverType string - FieldName string - FieldType string - ZeroValue string - NamedStruct bool // Getter for named struct. + sortVal string // Lower-case version of "ReceiverType.FieldName". + ReceiverVar string // The one-letter variable name to match the ReceiverType. + ReceiverType string + FieldName string + FieldType string + ZeroValue string + NamedStruct bool // Getter for named struct. + MapType bool + ArrayType bool + IsSimpleValue bool } -type byName []*getter - -func (b byName) Len() int { return len(b) } -func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal } -func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +const source = `// Code generated by gen-accessors; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch -const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. +// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Code generated by gen-accessors; DO NOT EDIT. - package {{.Package}} {{with .Imports}} import ( @@ -312,7 +483,15 @@ import ( ) {{end}} {{range .Getters}} -{{if .NamedStruct}} +{{if .IsSimpleValue}} +// Get{{.FieldName}} returns the {{.FieldName}} field. +func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { + if {{.ReceiverVar}} == nil { + return {{.ZeroValue}} + } + return {{.ReceiverVar}}.{{.FieldName}} +} +{{else if .NamedStruct}} // Get{{.FieldName}} returns the {{.FieldName}} field. func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} { if {{.ReceiverVar}} == nil { @@ -320,6 +499,14 @@ func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} { } return {{.ReceiverVar}}.{{.FieldName}} } +{{else if or .MapType .ArrayType }} +// Get{{.FieldName}} returns the {{.FieldName}} {{if .MapType}}map{{else if .ArrayType }}slice{{end}} if it's non-nil, {{if .MapType}}an empty map{{else if .ArrayType }}nil{{end}} otherwise. +func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { + if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil { + return {{.ZeroValue}} + } + return {{.ReceiverVar}}.{{.FieldName}} +} {{else}} // Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise. func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { @@ -331,3 +518,64 @@ func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { {{end}} {{end}} ` + +const test = `// Code generated by gen-accessors; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + +// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package {{.Package}} +{{with .Imports}} +import ( + "testing" + {{range . -}} + "{{.}}" + {{end -}} +) +{{end}} +{{range .Getters}} +{{if .IsSimpleValue}} +func Test{{.ReceiverType}}_Get{{.FieldName}}(tt *testing.T) { + tt.Parallel() + {{.ReceiverVar}} := &{{.ReceiverType}}{} + {{.ReceiverVar}}.Get{{.FieldName}}() + {{.ReceiverVar}} = nil + {{.ReceiverVar}}.Get{{.FieldName}}() +} +{{else if .NamedStruct}} +func Test{{.ReceiverType}}_Get{{.FieldName}}(tt *testing.T) { + tt.Parallel() + {{.ReceiverVar}} := &{{.ReceiverType}}{} + {{.ReceiverVar}}.Get{{.FieldName}}() + {{.ReceiverVar}} = nil + {{.ReceiverVar}}.Get{{.FieldName}}() +} +{{else if or .MapType .ArrayType}} +func Test{{.ReceiverType}}_Get{{.FieldName}}(tt *testing.T) { + tt.Parallel() + zeroValue := {{.FieldType}}{} + {{.ReceiverVar}} := &{{.ReceiverType}}{ {{.FieldName}}: zeroValue } + {{.ReceiverVar}}.Get{{.FieldName}}() + {{.ReceiverVar}} = &{{.ReceiverType}}{} + {{.ReceiverVar}}.Get{{.FieldName}}() + {{.ReceiverVar}} = nil + {{.ReceiverVar}}.Get{{.FieldName}}() +} +{{else}} +func Test{{.ReceiverType}}_Get{{.FieldName}}(tt *testing.T) { + tt.Parallel() + var zeroValue {{.FieldType}} + {{.ReceiverVar}} := &{{.ReceiverType}}{ {{.FieldName}}: &zeroValue } + {{.ReceiverVar}}.Get{{.FieldName}}() + {{.ReceiverVar}} = &{{.ReceiverType}}{} + {{.ReceiverVar}}.Get{{.FieldName}}() + {{.ReceiverVar}} = nil + {{.ReceiverVar}}.Get{{.FieldName}}() +} +{{end}} +{{end}} +` diff --git a/github/gen-iterators.go b/github/gen-iterators.go new file mode 100644 index 00000000000..a623cf1c151 --- /dev/null +++ b/github/gen-iterators.go @@ -0,0 +1,841 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +// gen-iterators generates iterator methods for List methods. +// +// It is meant to be used by go-github contributors in conjunction with the +// go generate tool before sending a PR to GitHub. +// Please see the CONTRIBUTING.md file for more information. +package main + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/fs" + "log" + "os" + "reflect" + "slices" + "strconv" + "strings" + "text/template" +) + +const ( + fileSuffix = "-iterators.go" +) + +var ( + check = flag.Bool("check", false, "Check whether generated files are up to date") + verbose = flag.Bool("v", false, "Print verbose log messages") + + sourceTmpl = template.Must(template.New("source").Funcs(template.FuncMap{ + "hasPrefix": strings.HasPrefix, + }).Parse(source)) + + testTmpl = template.Must(template.New("test").Parse(test)) +) + +func isCheck() bool { + return *check || os.Getenv("CHECK") == "1" +} + +func logf(fmt string, args ...any) { + if *verbose { + log.Printf(fmt, args...) + } +} + +func main() { + flag.Parse() + fset := token.NewFileSet() + + // Parse the current directory + pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0) + if err != nil { + log.Fatal(err) + return + } + + for pkgName, pkg := range pkgs { + t := &templateData{ + filename: pkgName + fileSuffix, + Package: pkgName, + Methods: []*method{}, + Structs: make(map[string]*structDef), + } + + for _, f := range pkg.Files { + t.processStructs(f) + } + + for _, f := range pkg.Files { + if err := t.processMethods(f); err != nil { + log.Fatal(err) + } + } + + if err := t.dump(); err != nil { + log.Fatal(err) + } + } + logf("Done.") +} + +func sourceFilter(fi os.FileInfo) bool { + return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix) && !strings.HasPrefix(fi.Name(), "gen-") +} + +type templateData struct { + filename string + Package string + Methods []*method + Structs map[string]*structDef +} + +type structDef struct { + Name string + Fields map[string]string + FieldJSON map[string]string + Embeds []string +} + +type method struct { + RecvType string + RecvVar string + ClientField string + MethodName string + IterMethod string + Args string + CallArgs string + TestCallArgs string + ZeroArgs string + ReturnType string + OptsType string + OptsName string + OptsIsPtr bool + UseListCursorOptions bool + UseListOptions bool + UsePage bool + UseAfter bool + UseCursor bool + WrappedItemsField string + TestJSON1 string + TestJSON2 string + TestJSON3 string +} + +type methodInfo struct { + RecvTypeRaw string + RecvType string + RecvVar string + ClientField string + Args string + CallArgs string + TestCallArgs string + ZeroArgs string + OptsType string + OptsName string + OptsIsPtr bool + UseListCursorOptions bool + UseListOptions bool + UsePage bool + UseAfter bool + UseCursor bool +} + +// useCursorPagination identifies method names that require `Cursor` pagination +// instead of using `After`. +var useCursorPagination = map[string]bool{ + "AppsService.ListHookDeliveries": true, + "OrganizationsService.ListHookDeliveries": true, + "RepositoriesService.ListHookDeliveries": true, +} + +// customNames provides custom names for iterator methods where the default methodName + "Iter" would be confusing. +var customNames = map[string]string{ + "RepositoriesService.GetCommit": "ListCommitFiles", + "RepositoriesService.CompareCommits": "ListCommitComparisonFiles", + "RepositoriesService.GetCombinedStatus": "ListCombinedStatus", +} + +// sliceToBeUsedForIteration identifies methods where the wrapper struct contains multiple []*T fields, +// and specifies which field should be used for iteration. +var sliceToBeUsedForIteration = map[string]string{ + "RepositoriesService.GetCommit": "Files", + "RepositoriesService.CompareCommits": "Files", +} + +// customTestJSON maps method names to the JSON response they expect in tests. +// This is needed for methods that internally unmarshal a wrapper struct +// even though they return a slice. +var customTestJSON = map[string]string{ + "ListAllTopics": `{"names": []}`, + "ListUserInstallations": `{"installations": []}`, +} + +func (t *templateData) processStructs(f *ast.File) { + for _, decl := range f.Decls { + gd, ok := decl.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + st, ok := ts.Type.(*ast.StructType) + if !ok { + continue + } + + sd := &structDef{ + Name: ts.Name.Name, + Fields: make(map[string]string), + FieldJSON: make(map[string]string), + } + + fieldJSON := "" + for _, field := range st.Fields.List { + typeStr := typeToString(field.Type) + fieldJSON = "" + if field.Tag != nil { + if unquotedTag, err := strconv.Unquote(field.Tag.Value); err == nil { + fieldJSON = reflect.StructTag(unquotedTag).Get("json") + if idx := strings.Index(fieldJSON, ","); idx >= 0 { + fieldJSON = fieldJSON[:idx] + } + if fieldJSON == "-" { + fieldJSON = "" + } + } + } + if len(field.Names) == 0 { + sd.Embeds = append(sd.Embeds, strings.TrimPrefix(typeStr, "*")) + } else { + for _, name := range field.Names { + sd.Fields[name.Name] = typeStr + if fieldJSON != "" { + sd.FieldJSON[name.Name] = fieldJSON + } + } + } + } + t.Structs[sd.Name] = sd + } + } +} + +func (t *templateData) hasListCursorOptions(structName string) bool { + return t.hasOptions(structName, "ListCursorOptions") +} + +func (t *templateData) hasListOptions(structName string) bool { + return t.hasOptions(structName, "ListOptions") +} + +func (t *templateData) hasOptions(structName, optionsType string) bool { + sd, ok := t.Structs[structName] + if !ok { + return false + } + for _, embed := range sd.Embeds { + if embed == optionsType { + return true + } + if t.hasOptions(embed, optionsType) { + return true + } + } + return false +} + +func (t *templateData) hasIntPage(structName string) bool { + sd, ok := t.Structs[structName] + if !ok { + return false + } + if typeStr, ok := sd.Fields["Page"]; ok { + return typeStr == "int" + } + for _, embed := range sd.Embeds { + if t.hasIntPage(embed) { + return true + } + } + return false +} + +func (t *templateData) hasStringAfter(structName string) bool { + sd, ok := t.Structs[structName] + if !ok { + return false + } + if typeStr, ok := sd.Fields["After"]; ok { + return typeStr == "string" + } + for _, embed := range sd.Embeds { + if t.hasStringAfter(embed) { + return true + } + } + return false +} + +func getZeroValue(typeStr string) string { + switch typeStr { + case "int", "int64", "int32": + return "0" + case "string": + return `""` + case "bool": + return "false" + case "context.Context": + return "t.Context()" + default: + return "nil" + } +} + +func (t *templateData) processMethods(f *ast.File) error { + for _, decl := range f.Decls { + fd, ok := decl.(*ast.FuncDecl) + if !ok || fd.Recv == nil { + continue + } + + methodKey := strings.TrimPrefix(typeToString(fd.Recv.List[0].Type), "*") + "." + fd.Name.Name + if !fd.Name.IsExported() || (!strings.HasPrefix(fd.Name.Name, "List") && customNames[methodKey] == "") { + continue + } + + if strings.Contains(fd.Name.Name, "MatchingRefs") { + continue + } + + if fd.Type.Results == nil || len(fd.Type.Results.List) != 3 { + continue + } + + methodInfo, ok := t.isMethodIterable(fd) + if !ok { + continue + } + + switch retType := fd.Type.Results.List[0].Type.(type) { + case *ast.ArrayType: + t.processReturnArrayType(fd, retType, methodInfo) + case *ast.StarExpr: + t.processReturnStarExpr(fd, retType, methodInfo) + default: + log.Fatalf("unhandled return type: %T", retType) + } + } + return nil +} + +func (t *templateData) isMethodIterable(fd *ast.FuncDecl) (*methodInfo, bool) { + if !validateMethodShape(fd) { + return nil, false + } + + methodInfo, ok := t.collectMethodInfo(fd) + if !ok { + return nil, false + } + + return methodInfo, true +} + +func validateMethodShape(fd *ast.FuncDecl) bool { + if typeToString(fd.Type.Results.List[1].Type) != "*Response" { + return false + } + if typeToString(fd.Type.Results.List[2].Type) != "error" { + return false + } + + recvType := typeToString(fd.Recv.List[0].Type) + if !strings.HasPrefix(recvType, "*") || !strings.HasSuffix(recvType, "Service") { + return false + } + + return true +} + +func (t *templateData) collectMethodInfo(fd *ast.FuncDecl) (*methodInfo, bool) { + recvType := typeToString(fd.Recv.List[0].Type) + recvVar := "" + if len(fd.Recv.List[0].Names) > 0 { + recvVar = fd.Recv.List[0].Names[0].Name + } + + args := []string{} + callArgs := []string{} + testCallArgs := []string{} + zeroArgs := []string{} + var optsType string + var optsName string + hasOpts := false + optsIsPtr := false + + for _, field := range fd.Type.Params.List { + typeStr := typeToString(field.Type) + zeroArg := getZeroValue(typeStr) + for _, name := range field.Names { + args = append(args, fmt.Sprintf("%v %v", name.Name, typeStr)) + callArgs = append(callArgs, name.Name) + zeroArgs = append(zeroArgs, zeroArg) + + if strings.HasSuffix(typeStr, "Options") { + optsType = strings.TrimPrefix(typeStr, "*") + optsName = name.Name + hasOpts = true + optsIsPtr = strings.HasPrefix(typeStr, "*") + } + } + // second pass: generate testCallArgs after optsName is identified + for _, name := range field.Names { + if name.Name == optsName { + testCallArgs = append(testCallArgs, name.Name) + } else { + testCallArgs = append(testCallArgs, zeroArg) + } + } + } + + if !hasOpts { + return nil, false + } + + useListCursorOptions := t.hasListCursorOptions(optsType) + useListOptions := t.hasListOptions(optsType) + usePage := t.hasIntPage(optsType) + useAfter := t.hasStringAfter(optsType) + recType := strings.TrimPrefix(recvType, "*") + var useCursor bool + if useCursorPagination[recType+"."+fd.Name.Name] { + useCursor = true + useAfter = false + } + + if !useListCursorOptions && !useListOptions && !usePage && !useAfter && !useCursor { + logf("Skipping %v.%v: opts %v does not have ListCursorOptions, ListOptions, Page int, or After string", recvType, fd.Name.Name, optsType) + return nil, false + } + + clientField := strings.TrimSuffix(recType, "Service") + if clientField == "Migration" { + clientField = "Migrations" + } + if clientField == "s" { + logf("WARNING: clientField is 's' for %v.%v (recvType=%v)", recvType, fd.Name.Name, recType) + } + + return &methodInfo{ + RecvTypeRaw: recvType, + RecvType: recType, + RecvVar: recvVar, + ClientField: clientField, + Args: strings.Join(args, ", "), + CallArgs: strings.Join(callArgs, ", "), + TestCallArgs: strings.Join(testCallArgs, ", "), + ZeroArgs: strings.Join(zeroArgs, ", "), + OptsType: optsType, + OptsName: optsName, + OptsIsPtr: optsIsPtr, + UseListCursorOptions: useListCursorOptions, + UseListOptions: useListOptions, + UsePage: usePage, + UseAfter: useAfter, + UseCursor: useCursor, + }, true +} + +func getIterName(methodInfo *methodInfo, methodName string) string { + if customName, ok := customNames[methodInfo.RecvType+"."+methodName]; ok { + return customName + "Iter" + } + return methodName + "Iter" +} + +func (t *templateData) processReturnArrayType(fd *ast.FuncDecl, sliceRet *ast.ArrayType, methodInfo *methodInfo) { + testJSON, emptyReturnValue := "[]", "{}" + if val, ok := customTestJSON[fd.Name.Name]; ok { + testJSON = val + } + + eltType := typeToString(sliceRet.Elt) + if eltType == "string" { + emptyReturnValue = `""` + } + testJSON1 := strings.ReplaceAll(testJSON, "[]", fmt.Sprintf("[%v,%[1]v,%[1]v]", emptyReturnValue)) // Call 1 - return 3 items + testJSON2 := strings.ReplaceAll(testJSON, "[]", fmt.Sprintf("[%v,%[1]v,%[1]v,%[1]v]", emptyReturnValue)) // Call 1 part 2 - return 4 items + testJSON3 := strings.ReplaceAll(testJSON, "[]", fmt.Sprintf("[%v,%[1]v]", emptyReturnValue)) // Call 2 - return 2 items + + m := &method{ + RecvType: methodInfo.RecvType, + RecvVar: methodInfo.RecvVar, + ClientField: methodInfo.ClientField, + MethodName: fd.Name.Name, + IterMethod: getIterName(methodInfo, fd.Name.Name), + Args: methodInfo.Args, + CallArgs: methodInfo.CallArgs, + TestCallArgs: methodInfo.TestCallArgs, + ZeroArgs: methodInfo.ZeroArgs, + ReturnType: eltType, + OptsType: methodInfo.OptsType, + OptsName: methodInfo.OptsName, + OptsIsPtr: methodInfo.OptsIsPtr, + UseListCursorOptions: methodInfo.UseListCursorOptions, + UseListOptions: methodInfo.UseListOptions, + UsePage: methodInfo.UsePage, + UseAfter: methodInfo.UseAfter, + UseCursor: methodInfo.UseCursor, + TestJSON1: testJSON1, + TestJSON2: testJSON2, + TestJSON3: testJSON3, + } + t.Methods = append(t.Methods, m) +} + +func (t *templateData) processReturnStarExpr(fd *ast.FuncDecl, starRet *ast.StarExpr, methodInfo *methodInfo) { + wrapperType := typeToString(starRet.X) + wrapperDef, ok := t.Structs[wrapperType] + if !ok { + logf("Skipping %v.%v: wrapper type %v not found", methodInfo.RecvTypeRaw, fd.Name.Name, wrapperType) + return + } + + var itemsField, itemsType string + if field, ok := sliceToBeUsedForIteration[methodInfo.RecvType+"."+fd.Name.Name]; ok { + itemsField = field + if itemsType, ok = wrapperDef.Fields[itemsField]; !ok || !strings.HasPrefix(itemsType, "[]*") { + logf("Skipping %v.%v: specified items field %v not found or not of type []*T in wrapper %v", methodInfo.RecvTypeRaw, fd.Name.Name, itemsField, wrapperType) + return + } + } else if itemsField, itemsType, ok = findSinglePointerSliceField(wrapperDef); !ok { + logf("Skipping %v.%v: wrapper %v does not contain exactly one []*T field", methodInfo.RecvTypeRaw, fd.Name.Name, wrapperType) + return + } + + testJSON, emptyReturnValue := "[]", "{}" + if jsonField, ok := wrapperDef.FieldJSON[itemsField]; ok && jsonField != "" { + testJSON = fmt.Sprintf(`{"%v": []}`, jsonField) + } else { + testJSON = fmt.Sprintf(`{"%v": []}`, lowerFirst(itemsField)) + } + if val, ok := customTestJSON[fd.Name.Name]; ok { + testJSON = val + } + + eltType := strings.TrimPrefix(itemsType, "[]") + if eltType == "string" { + emptyReturnValue = `""` + } + testJSON1 := strings.ReplaceAll(testJSON, "[]", fmt.Sprintf("[%v,%[1]v,%[1]v]", emptyReturnValue)) // Call 1 - return 3 items + testJSON2 := strings.ReplaceAll(testJSON, "[]", fmt.Sprintf("[%v,%[1]v,%[1]v,%[1]v]", emptyReturnValue)) // Call 1 part 2 - return 4 items + testJSON3 := strings.ReplaceAll(testJSON, "[]", fmt.Sprintf("[%v,%[1]v]", emptyReturnValue)) // Call 2 - return 2 items + + m := &method{ + RecvType: methodInfo.RecvType, + RecvVar: methodInfo.RecvVar, + ClientField: methodInfo.ClientField, + MethodName: fd.Name.Name, + IterMethod: getIterName(methodInfo, fd.Name.Name), + Args: methodInfo.Args, + CallArgs: methodInfo.CallArgs, + TestCallArgs: methodInfo.TestCallArgs, + ZeroArgs: methodInfo.ZeroArgs, + ReturnType: eltType, + OptsType: methodInfo.OptsType, + OptsName: methodInfo.OptsName, + OptsIsPtr: methodInfo.OptsIsPtr, + UseListCursorOptions: methodInfo.UseListCursorOptions, + UseListOptions: methodInfo.UseListOptions, + UsePage: methodInfo.UsePage, + UseAfter: methodInfo.UseAfter, + UseCursor: methodInfo.UseCursor, + WrappedItemsField: itemsField, + TestJSON1: testJSON1, + TestJSON2: testJSON2, + TestJSON3: testJSON3, + } + t.Methods = append(t.Methods, m) +} + +func findSinglePointerSliceField(sd *structDef) (fieldName, fieldType string, ok bool) { + matches := []string{} + for name, typeStr := range sd.Fields { + if strings.HasPrefix(typeStr, "[]*") { + matches = append(matches, name) + } + } + if len(matches) != 1 { + return "", "", false + } + fieldName = matches[0] + return fieldName, sd.Fields[fieldName], true +} + +func lowerFirst(s string) string { + if s == "" { + return s + } + return strings.ToLower(s[:1]) + s[1:] +} + +func typeToString(expr ast.Expr) string { + switch x := expr.(type) { + case *ast.Ident: + return x.Name + case *ast.StarExpr: + return "*" + typeToString(x.X) + case *ast.SelectorExpr: + return typeToString(x.X) + "." + x.Sel.Name + case *ast.ArrayType: + return "[]" + typeToString(x.Elt) + case *ast.MapType: + return fmt.Sprintf("map[%v]%v", typeToString(x.Key), typeToString(x.Value)) + default: + return "" + } +} + +func (t *templateData) dump() error { + if len(t.Methods) == 0 { + return nil + } + + slices.SortStableFunc(t.Methods, func(a, b *method) int { + if a.RecvType != b.RecvType { + return strings.Compare(a.RecvType, b.RecvType) + } + return strings.Compare(a.MethodName, b.MethodName) + }) + + processTemplate := func(tmpl *template.Template, filename string) error { + var buf bytes.Buffer + if err := tmpl.Execute(&buf, t); err != nil { + return err + } + clean, err := format.Source(buf.Bytes()) + if err != nil { + return fmt.Errorf("format.Source: %v\n%s", err, buf.String()) + } + if isCheck() { + logf("Checking %v...", filename) + old, err := os.ReadFile(filename) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return fmt.Errorf("Missing file: %v\n", filename) + } + return err + } + + if !bytes.Equal(old, clean) { + return fmt.Errorf("Generated files are out of date. Please run go generate ./... and commit the results") + } + return nil + } + + logf("Writing %v...", filename) + return os.WriteFile(filename, clean, 0o644) + } + + if err := processTemplate(sourceTmpl, t.filename); err != nil { + return err + } + return processTemplate(testTmpl, strings.ReplaceAll(t.filename, ".go", "_test.go")) +} + +const doNotEditHeader = `// Code generated by gen-iterators; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +` + +const source = doNotEditHeader + ` +package {{.Package}} + +import ( + "context" + "iter" +) + +{{range .Methods}} +// {{.IterMethod}} returns an iterator that paginates through all results of {{.MethodName}}. +func ({{.RecvVar}} *{{.RecvType}}) {{.IterMethod}}({{.Args}}) iter.Seq2[{{.ReturnType}}, error] { + return func(yield func({{.ReturnType}}, error) bool) { + {{if .OptsIsPtr -}} + // Create a copy of opts to avoid mutating the caller's struct + if {{.OptsName}} == nil { + {{.OptsName}} = &{{.OptsType}}{} + } else { + {{.OptsName}} = Ptr(*{{.OptsName}}) + } + + {{end}} + for { + results, resp, err := {{.RecvVar}}.{{.MethodName}}({{.CallArgs}}) + if err != nil { + yield({{if hasPrefix .ReturnType "*"}}nil{{else}}*new({{.ReturnType}}){{end}}, err) + return + } + + {{if .WrappedItemsField -}} + var iterItems []{{.ReturnType}} + if results != nil { + iterItems = results.{{.WrappedItemsField}} + } + for _, item := range iterItems { + {{else -}} + for _, item := range results { + {{end -}} + if !yield(item, nil) { + return + } + } + + {{if and .UseListCursorOptions .UseListOptions}} + if resp.After == "" && resp.NextPage == 0 { + break + } + {{.OptsName}}.ListCursorOptions.After = resp.After + {{.OptsName}}.ListOptions.Page = resp.NextPage + {{else if .UseListCursorOptions}} + if resp.After == "" { + break + } + {{.OptsName}}.ListCursorOptions.After = resp.After + {{else if .UseListOptions}} + if resp.NextPage == 0 { + break + } + {{.OptsName}}.ListOptions.Page = resp.NextPage + {{else if .UsePage}} + if resp.NextPage == 0 { + break + } + {{.OptsName}}.Page = resp.NextPage + {{else if .UseAfter}} + if resp.After == "" { + break + } + {{.OptsName}}.After = resp.After + {{else if .UseCursor}} + if resp.Cursor == "" { + break + } + {{.OptsName}}.Cursor = resp.Cursor + {{end -}} + } + } +} +{{end}} +` + +const test = doNotEditHeader + ` +package {{.Package}} + +import ( + "fmt" + "net/http" + "testing" +) + +{{range .Methods}} +func Test{{.RecvType}}_{{.IterMethod}}(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + {{- if .UseCursor}} + w.Header().Set("Link", ` + "`" + `; rel="next"` + "`" + `) + {{else if or .UseListCursorOptions .UseAfter}} + w.Header().Set("Link", ` + "`" + `; rel="next"` + "`" + `) + {{else}} + w.Header().Set("Link", ` + "`" + `; rel="next"` + "`" + `) + {{end -}} + fmt.Fprint(w, ` + "`" + `{{.TestJSON1}}` + "`" + `) + case 2: + fmt.Fprint(w, ` + "`" + `{{.TestJSON2}}` + "`" + `) + case 3: + fmt.Fprint(w, ` + "`" + `{{.TestJSON3}}` + "`" + `) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, ` + "`" + `{{.TestJSON3}}` + "`" + `) + } + }) + + iter := client.{{.ClientField}}.{{.IterMethod}}({{.ZeroArgs}}) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.{{.ClientField}}.{{.IterMethod}} call 1 got %v items; want %v", gotItems, want) + } + + {{.OptsName}} := &{{.OptsType}}{} + iter = client.{{.ClientField}}.{{.IterMethod}}({{.TestCallArgs}}) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.{{.ClientField}}.{{.IterMethod}} call 2 got %v items; want %v", gotItems, want) + } + + iter = client.{{.ClientField}}.{{.IterMethod}}({{.ZeroArgs}}) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.{{.ClientField}}.{{.IterMethod}} call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.{{.ClientField}}.{{.IterMethod}}({{.ZeroArgs}}) + gotItems = 0 + iter(func(item {{.ReturnType}}, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.{{.ClientField}}.{{.IterMethod}} call 4 got %v items; want 1 (an error)", gotItems) + } +} +{{end}} +` diff --git a/github/gen-stringify-test.go b/github/gen-stringify-test.go index 7803801e632..2cd9daafdbe 100644 --- a/github/gen-stringify-test.go +++ b/github/gen-stringify-test.go @@ -3,7 +3,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build ignore +//go:build ignore // gen-stringify-test generates test methods to test the String methods. // @@ -17,13 +17,14 @@ package main import ( "bytes" + "errors" "flag" "fmt" "go/ast" "go/format" "go/parser" "go/token" - "io/ioutil" + "io/fs" "log" "os" "strings" @@ -37,12 +38,13 @@ const ( ) var ( + check = flag.Bool("check", false, "Check whether generated files are up to date") verbose = flag.Bool("v", false, "Print verbose log messages") - // blacklistStructMethod lists "struct.method" combos to skip. - blacklistStructMethod = map[string]bool{} - // blacklistStruct lists structs to skip. - blacklistStruct = map[string]bool{ + // skipStructMethods lists "struct.method" combos to skip. + skipStructMethods = map[string]bool{} + // skipStructs lists structs to skip. + skipStructs = map[string]bool{ "RateLimits": true, } @@ -55,18 +57,28 @@ var ( }, "processZeroValue": func(v string) string { switch v { - case "Bool(false)": + case "false": return "false" - case "Float64(0.0)": + case "Ptr(false)": + return "false" + case "Ptr(0.0)": return "0" - case "0", "Int(0)", "Int64(0)": + case "0", "Ptr(0)", "Ptr(int64(0))": return "0" - case `""`, `String("")`: + case `""`, `Ptr("")`: return `""` case "Timestamp{}", "&Timestamp{}": return "github.Timestamp{0001-01-01 00:00:00 +0000 UTC}" case "nil": return "map[]" + case `[]int{0}`: + return `[0]` + case `[]string{""}`: + return `[""]` + case "[]Scope{ScopeNone}": + return `["(no scope)"]` + case "[]any{nil}": + return "[]" } log.Fatalf("Unhandled zero value: %q", v) return "" @@ -76,6 +88,10 @@ var ( sourceTmpl = template.Must(template.New("source").Funcs(funcMap).Parse(source)) ) +func isCheck() bool { + return *check || os.Getenv("CHECK") == "1" +} + func main() { flag.Parse() fset := token.NewFileSet() @@ -143,7 +159,18 @@ func (t *templateData) processAST(f *ast.File) error { logf("Got FuncDecl: Name=%q, id.Name=%#v", fn.Name.Name, id.Name) t.StringFuncs[id.Name] = true } else { - logf("Ignoring FuncDecl: Name=%q, Type=%T", fn.Name.Name, fn.Recv.List[0].Type) + star, ok := fn.Recv.List[0].Type.(*ast.StarExpr) + if ok && fn.Name.Name == "String" { + id, ok := star.X.(*ast.Ident) + if ok { + logf("Got FuncDecl: Name=%q, id.Name=%#v", fn.Name.Name, id.Name) + t.StringFuncs[id.Name] = true + } else { + logf("Ignoring FuncDecl: Name=%q, Type=%T", fn.Name.Name, fn.Recv.List[0].Type) + } + } else { + logf("Ignoring FuncDecl: Name=%q, Type=%T", fn.Name.Name, fn.Recv.List[0].Type) + } } } else { logf("Ignoring FuncDecl: Name=%q, fn=%#v", fn.Name.Name, fn) @@ -156,6 +183,7 @@ func (t *templateData) processAST(f *ast.File) error { logf("Ignoring AST decl type %T", decl) continue } + for _, spec := range gd.Specs { ts, ok := spec.(*ast.TypeSpec) if !ok { @@ -166,14 +194,14 @@ func (t *templateData) processAST(f *ast.File) error { logf("Struct %v is unexported; skipping.", ts.Name) continue } - // Check if the struct is blacklisted. - if blacklistStruct[ts.Name.Name] { - logf("Struct %v is blacklisted; skipping.", ts.Name) + // Check if the struct should be skipped. + if skipStructs[ts.Name.Name] { + logf("Struct %v is in skip list; skipping.", ts.Name) continue } st, ok := ts.Type.(*ast.StructType) if !ok { - logf("Ignoring AST type %T, Name=%q", ts.Type, ts.Name.String()) + logf("Ignoring AST type %T, Name=%q", ts.Type, ts.Name) continue } for _, field := range st.Fields.List { @@ -187,14 +215,16 @@ func (t *templateData) processAST(f *ast.File) error { continue } - if _, ok := field.Type.(*ast.MapType); ok { - t.addMapType(ts.Name.String(), fieldName.String()) - continue + if at, ok := field.Type.(*ast.ArrayType); ok { + if id, ok := at.Elt.(*ast.Ident); ok { + t.addIdentSlice(id, ts.Name.String(), fieldName.String()) + continue + } } se, ok := field.Type.(*ast.StarExpr) if !ok { - logf("Ignoring type %T for Name=%q, FieldName=%q", field.Type, ts.Name.String(), fieldName.String()) + logf("Ignoring type %T for Name=%q, FieldName=%q", field.Type, ts.Name, fieldName) continue } @@ -203,9 +233,9 @@ func (t *templateData) processAST(f *ast.File) error { logf("Field %v is unexported; skipping.", fieldName) continue } - // Check if "struct.method" is blacklisted. - if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] { - logf("Method %v is blacklisted; skipping.", key) + // Check if "struct.method" should be skipped. + if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); skipStructMethods[key] { + logf("Method %v is in skip list; skipping.", key) continue } @@ -230,7 +260,7 @@ func (t *templateData) addMapType(receiverType, fieldName string) { func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) { var zeroValue string - var namedStruct = false + var namedStruct bool switch x.String() { case "int": zeroValue = "0" @@ -254,18 +284,18 @@ func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) { func (t *templateData) addIdentPtr(x *ast.Ident, receiverType, fieldName string) { var zeroValue string - var namedStruct = false + var namedStruct bool switch x.String() { case "int": - zeroValue = "Int(0)" + zeroValue = "Ptr(0)" case "int64": - zeroValue = "Int64(0)" + zeroValue = "Ptr(int64(0))" case "float64": - zeroValue = "Float64(0.0)" + zeroValue = "Ptr(0.0)" case "string": - zeroValue = `String("")` + zeroValue = `Ptr("")` case "bool": - zeroValue = "Bool(false)" + zeroValue = "Ptr(false)" case "Timestamp": zeroValue = "&Timestamp{}" default: @@ -276,6 +306,34 @@ func (t *templateData) addIdentPtr(x *ast.Ident, receiverType, fieldName string) t.StructFields[receiverType] = append(t.StructFields[receiverType], newStructField(receiverType, fieldName, x.String(), zeroValue, namedStruct)) } +func (t *templateData) addIdentSlice(x *ast.Ident, receiverType, fieldName string) { + var zeroValue string + var namedStruct bool + switch x.String() { + case "int": + zeroValue = "[]int{0}" + case "int64": + zeroValue = "[]int64{0}" + case "float64": + zeroValue = "[]float64{0}" + case "string": + zeroValue = `[]string{""}` + case "bool": + zeroValue = "[]bool{false}" + case "Scope": + zeroValue = "[]Scope{ScopeNone}" + case "any": + zeroValue = "[]any{nil}" + // case "Timestamp": + // zeroValue = "&Timestamp{}" + default: + zeroValue = "nil" + namedStruct = true + } + + t.StructFields[receiverType] = append(t.StructFields[receiverType], newStructField(receiverType, fieldName, x.String(), zeroValue, namedStruct)) +} + func (t *templateData) dump() error { if len(t.StructFields) == 0 { logf("No StructFields for %v; skipping.", t.filename) @@ -300,12 +358,40 @@ func (t *templateData) dump() error { } clean, err := format.Source(buf.Bytes()) if err != nil { - log.Printf("failed-to-format source:\n%v", buf.String()) + log.Printf("failed-to-format source:\n%v", buf) return err } + if isCheck() { + logf("Checking %v...", t.filename) + old, err := os.ReadFile(t.filename) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return fmt.Errorf("Missing file: %v\n", t.filename) + } + return err + } + + if !bytes.Equal(old, clean) { + return fmt.Errorf("Generated files are out of date. Please run go generate ./... and commit the results") + } + return nil + } + logf("Writing %v...", t.filename) - return ioutil.WriteFile(t.filename, clean, 0644) + if err := os.Chmod(t.filename, 0o644); err != nil { + return fmt.Errorf("os.Chmod(%q, 0644): %v", t.filename, err) + } + + if err := os.WriteFile(t.filename, clean, 0o444); err != nil { + return err + } + + if err := os.Chmod(t.filename, 0o444); err != nil { + return fmt.Errorf("os.Chmod(%q, 0444): %v", t.filename, err) + } + + return nil } func newStructField(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *structField { @@ -320,19 +406,21 @@ func newStructField(receiverType, fieldName, fieldType, zeroValue string, namedS } } -func logf(fmt string, args ...interface{}) { +func logf(fmt string, args ...any) { if *verbose { log.Printf(fmt, args...) } } -const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. +const source = `// Code generated by gen-stringify-tests; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + +// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Code generated by gen-stringify-tests; DO NOT EDIT. - package {{ $package := .Package}}{{$package}} {{with .Imports}} import ( @@ -341,9 +429,9 @@ import ( {{end -}} ) {{end}} -func Float64(v float64) *float64 { return &v } {{range $key, $value := .StructFields}} func Test{{ $key }}_String(t *testing.T) { + t.Parallel() v := {{ $key }}{ {{range .}}{{if .NamedStruct}} {{ .FieldName }}: &{{ .FieldType }}{},{{else}} {{ .FieldName }}: {{.ZeroValue}},{{end}}{{end}} diff --git a/github/gists.go b/github/gists.go index 36d9361967c..a339286c062 100644 --- a/github/gists.go +++ b/github/gists.go @@ -1,6 +1,6 @@ // Copyright 2013 The go-github AUTHORS. All rights reserved. // -// Use of this source code is governed by BSD-style +// Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package github @@ -14,7 +14,7 @@ import ( // GistsService handles communication with the Gist related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/gists/ +// GitHub API docs: https://docs.github.com/rest/gists?apiVersion=2022-11-28 type GistsService service // Gist represents a GitHub's gist. @@ -28,8 +28,8 @@ type Gist struct { HTMLURL *string `json:"html_url,omitempty"` GitPullURL *string `json:"git_pull_url,omitempty"` GitPushURL *string `json:"git_push_url,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` NodeID *string `json:"node_id,omitempty"` } @@ -37,6 +37,22 @@ func (g Gist) String() string { return Stringify(g) } +// CreateGistRequest represents the input for creating a gist. +type CreateGistRequest struct { + Description *string `json:"description,omitempty"` + Public *bool `json:"public,omitempty"` + // Files is the set of files that make up the gist, keyed by filename. + Files map[GistFilename]*CreateGistFile `json:"files"` +} + +// UpdateGistRequest represents the input for updating a gist. +type UpdateGistRequest struct { + Description *string `json:"description,omitempty"` + // Files is the set of files to add, change or rename, keyed by filename. + // Mapping a filename to a nil value deletes that file from the gist. + Files map[GistFilename]*UpdateGistFile `json:"files,omitempty"` +} + // GistFilename represents filename on a gist. type GistFilename string @@ -54,6 +70,23 @@ func (g GistFile) String() string { return Stringify(g) } +// CreateGistFile represents a file within a CreateGistRequest, keyed by filename +// in the request's Files map. +type CreateGistFile struct { + // Content is the contents of the file. + Content string `json:"content"` +} + +// UpdateGistFile represents a file within an UpdateGistRequest, keyed by filename +// in the request's Files map. Mapping a filename to a nil *UpdateGistFile deletes +// that file from the gist. +type UpdateGistFile struct { + // Content is the new contents of the file. + Content *string `json:"content,omitempty"` + // Filename, if set, renames the file. + Filename *string `json:"filename,omitempty"` +} + // GistCommit represents a commit on a gist. type GistCommit struct { URL *string `json:"url,omitempty"` @@ -96,26 +129,31 @@ type GistListOptions struct { // is authenticated, it will returns all gists for the authenticated // user. // -// GitHub API docs: https://developer.github.com/v3/gists/#list-gists -func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptions) ([]*Gist, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#list-gists-for-the-authenticated-user +// +//meta:operation GET /gists +//meta:operation GET /users/{username}/gists +func (s *GistsService) List(ctx context.Context, user string, opts *GistListOptions) ([]*Gist, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/gists", user) } else { u = "gists" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var gists []*Gist - resp, err := s.client.Do(ctx, req, &gists) + resp, err := s.client.Do(req, &gists) if err != nil { return nil, resp, err } @@ -125,20 +163,22 @@ func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptio // ListAll lists all public gists. // -// GitHub API docs: https://developer.github.com/v3/gists/#list-gists -func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { - u, err := addOptions("gists/public", opt) +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#list-public-gists +// +//meta:operation GET /gists/public +func (s *GistsService) ListAll(ctx context.Context, opts *GistListOptions) ([]*Gist, *Response, error) { + u, err := addOptions("gists/public", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var gists []*Gist - resp, err := s.client.Do(ctx, req, &gists) + resp, err := s.client.Do(req, &gists) if err != nil { return nil, resp, err } @@ -148,20 +188,22 @@ func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gi // ListStarred lists starred gists of authenticated user. // -// GitHub API docs: https://developer.github.com/v3/gists/#list-gists -func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { - u, err := addOptions("gists/starred", opt) +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#list-starred-gists +// +//meta:operation GET /gists/starred +func (s *GistsService) ListStarred(ctx context.Context, opts *GistListOptions) ([]*Gist, *Response, error) { + u, err := addOptions("gists/starred", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var gists []*Gist - resp, err := s.client.Do(ctx, req, &gists) + resp, err := s.client.Do(req, &gists) if err != nil { return nil, resp, err } @@ -171,16 +213,18 @@ func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([ // Get a single gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#get-a-single-gist +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#get-a-gist +// +//meta:operation GET /gists/{gist_id} func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, error) { u := fmt.Sprintf("gists/%v", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - gist := new(Gist) - resp, err := s.client.Do(ctx, req, gist) + var gist *Gist + resp, err := s.client.Do(req, &gist) if err != nil { return nil, resp, err } @@ -190,16 +234,18 @@ func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, er // GetRevision gets a specific revision of a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#get-a-gist-revision +// +//meta:operation GET /gists/{gist_id}/{sha} func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist, *Response, error) { u := fmt.Sprintf("gists/%v/%v", id, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - gist := new(Gist) - resp, err := s.client.Do(ctx, req, gist) + var gist *Gist + resp, err := s.client.Do(req, &gist) if err != nil { return nil, resp, err } @@ -209,16 +255,18 @@ func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist, // Create a gist for authenticated user. // -// GitHub API docs: https://developer.github.com/v3/gists/#create-a-gist -func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#create-a-gist +// +//meta:operation POST /gists +func (s *GistsService) Create(ctx context.Context, body CreateGistRequest) (*Gist, *Response, error) { u := "gists" - req, err := s.client.NewRequest("POST", u, gist) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - g := new(Gist) - resp, err := s.client.Do(ctx, req, g) + var g *Gist + resp, err := s.client.Do(req, &g) if err != nil { return nil, resp, err } @@ -226,18 +274,20 @@ func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response return g, resp, nil } -// Edit a gist. +// Update a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#edit-a-gist -func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#update-a-gist +// +//meta:operation PATCH /gists/{gist_id} +func (s *GistsService) Update(ctx context.Context, id string, body UpdateGistRequest) (*Gist, *Response, error) { u := fmt.Sprintf("gists/%v", id) - req, err := s.client.NewRequest("PATCH", u, gist) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - g := new(Gist) - resp, err := s.client.Do(ctx, req, g) + var g *Gist + resp, err := s.client.Do(req, &g) if err != nil { return nil, resp, err } @@ -247,21 +297,23 @@ func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist, // ListCommits lists commits of a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-commits -func (s *GistsService) ListCommits(ctx context.Context, id string, opt *ListOptions) ([]*GistCommit, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#list-gist-commits +// +//meta:operation GET /gists/{gist_id}/commits +func (s *GistsService) ListCommits(ctx context.Context, id string, opts *ListOptions) ([]*GistCommit, *Response, error) { u := fmt.Sprintf("gists/%v/commits", id) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var gistCommits []*GistCommit - resp, err := s.client.Do(ctx, req, &gistCommits) + resp, err := s.client.Do(req, &gistCommits) if err != nil { return nil, resp, err } @@ -271,66 +323,80 @@ func (s *GistsService) ListCommits(ctx context.Context, id string, opt *ListOpti // Delete a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#delete-a-gist +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#delete-a-gist +// +//meta:operation DELETE /gists/{gist_id} func (s *GistsService) Delete(ctx context.Context, id string) (*Response, error) { u := fmt.Sprintf("gists/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } // Star a gist on behalf of authenticated user. // -// GitHub API docs: https://developer.github.com/v3/gists/#star-a-gist +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#star-a-gist +// +//meta:operation PUT /gists/{gist_id}/star func (s *GistsService) Star(ctx context.Context, id string) (*Response, error) { u := fmt.Sprintf("gists/%v/star", id) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } // Unstar a gist on a behalf of authenticated user. // -// GitHub API docs: https://developer.github.com/v3/gists/#unstar-a-gist +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#unstar-a-gist +// +//meta:operation DELETE /gists/{gist_id}/star func (s *GistsService) Unstar(ctx context.Context, id string) (*Response, error) { u := fmt.Sprintf("gists/%v/star", id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } // IsStarred checks if a gist is starred by authenticated user. // -// GitHub API docs: https://developer.github.com/v3/gists/#check-if-a-gist-is-starred +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#check-if-a-gist-is-starred +// +//meta:operation GET /gists/{gist_id}/star func (s *GistsService) IsStarred(ctx context.Context, id string) (bool, *Response, error) { u := fmt.Sprintf("gists/%v/star", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + + resp, err := s.client.Do(req, nil) starred, err := parseBoolResponse(err) return starred, resp, err } // Fork a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#fork-a-gist +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#fork-a-gist +// +//meta:operation POST /gists/{gist_id}/forks func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, error) { u := fmt.Sprintf("gists/%v/forks", id) - req, err := s.client.NewRequest("POST", u, nil) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, nil, err } - g := new(Gist) - resp, err := s.client.Do(ctx, req, g) + var g *Gist + resp, err := s.client.Do(req, &g) if err != nil { return nil, resp, err } @@ -340,21 +406,23 @@ func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, e // ListForks lists forks of a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-forks -func (s *GistsService) ListForks(ctx context.Context, id string, opt *ListOptions) ([]*GistFork, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/gists?apiVersion=2022-11-28#list-gist-forks +// +//meta:operation GET /gists/{gist_id}/forks +func (s *GistsService) ListForks(ctx context.Context, id string, opts *ListOptions) ([]*GistFork, *Response, error) { u := fmt.Sprintf("gists/%v/forks", id) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var gistForks []*GistFork - resp, err := s.client.Do(ctx, req, &gistForks) + resp, err := s.client.Do(req, &gistForks) if err != nil { return nil, resp, err } diff --git a/github/gists_comments.go b/github/gists_comments.go index d5322e3d852..d2e769604fa 100644 --- a/github/gists_comments.go +++ b/github/gists_comments.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // GistComment represents a Gist comment. @@ -17,30 +16,44 @@ type GistComment struct { URL *string `json:"url,omitempty"` Body *string `json:"body,omitempty"` User *User `json:"user,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` } func (g GistComment) String() string { return Stringify(g) } +// CreateGistCommentRequest represents the input for creating a gist comment. +type CreateGistCommentRequest struct { + // Body is the comment text. + Body string `json:"body"` +} + +// UpdateGistCommentRequest represents the input for updating a gist comment. +type UpdateGistCommentRequest struct { + // Body is the comment text. + Body string `json:"body"` +} + // ListComments lists all comments for a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist -func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *ListOptions) ([]*GistComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/comments?apiVersion=2022-11-28#list-gist-comments +// +//meta:operation GET /gists/{gist_id}/comments +func (s *GistsService) ListComments(ctx context.Context, gistID string, opts *ListOptions) ([]*GistComment, *Response, error) { u := fmt.Sprintf("gists/%v/comments", gistID) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var comments []*GistComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -50,16 +63,18 @@ func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *Lis // GetComment retrieves a single comment from a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment +// GitHub API docs: https://docs.github.com/rest/gists/comments?apiVersion=2022-11-28#get-a-gist-comment +// +//meta:operation GET /gists/{gist_id}/comments/{comment_id} func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int64) (*GistComment, *Response, error) { u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - c := new(GistComment) - resp, err := s.client.Do(ctx, req, c) + var c *GistComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -69,16 +84,18 @@ func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID // CreateComment creates a comment for a gist. // -// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-comment -func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment *GistComment) (*GistComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/comments?apiVersion=2022-11-28#create-a-gist-comment +// +//meta:operation POST /gists/{gist_id}/comments +func (s *GistsService) CreateComment(ctx context.Context, gistID string, body CreateGistCommentRequest) (*GistComment, *Response, error) { u := fmt.Sprintf("gists/%v/comments", gistID) - req, err := s.client.NewRequest("POST", u, comment) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - c := new(GistComment) - resp, err := s.client.Do(ctx, req, c) + var c *GistComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -86,18 +103,20 @@ func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment return c, resp, nil } -// EditComment edits an existing gist comment. +// UpdateComment updates an existing gist comment. // -// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment -func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int64, comment *GistComment) (*GistComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gists/comments?apiVersion=2022-11-28#update-a-gist-comment +// +//meta:operation PATCH /gists/{gist_id}/comments/{comment_id} +func (s *GistsService) UpdateComment(ctx context.Context, gistID string, commentID int64, body UpdateGistCommentRequest) (*GistComment, *Response, error) { u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) - req, err := s.client.NewRequest("PATCH", u, comment) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - c := new(GistComment) - resp, err := s.client.Do(ctx, req, c) + var c *GistComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -107,13 +126,15 @@ func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID // DeleteComment deletes a gist comment. // -// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment +// GitHub API docs: https://docs.github.com/rest/gists/comments?apiVersion=2022-11-28#delete-a-gist-comment +// +//meta:operation DELETE /gists/{gist_id}/comments/{comment_id} func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int64) (*Response, error) { u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/gists_comments_test.go b/github/gists_comments_test.go index b4345c40a71..a18022cbb93 100644 --- a/github/gists_comments_test.go +++ b/github/gists_comments_test.go @@ -6,73 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" -) -func TestGistComments_marshall(t *testing.T) { - testJSONMarshal(t, &GistComment{}, "{}") - - createdAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) - - u := &GistComment{ - ID: Int64(1), - URL: String("u"), - Body: String("test gist comment"), - User: &User{ - Login: String("ll"), - ID: Int64(123), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - URL: String("u"), - }, - CreatedAt: &createdAt, - } + "github.com/google/go-cmp/cmp" +) - want := `{ - "id": 1, - "url": "u", - "body": "test gist comment", - "user": { - "login": "ll", - "id": 123, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "url": "u" - }, - "created_at": "2002-02-10T15:30:00Z" - }` - - testJSONMarshal(t, u, want) -} func TestGistsService_ListComments(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -81,145 +24,209 @@ func TestGistsService_ListComments(t *testing.T) { }) opt := &ListOptions{Page: 2} - comments, _, err := client.Gists.ListComments(context.Background(), "1", opt) + ctx := t.Context() + comments, _, err := client.Gists.ListComments(ctx, "1", opt) if err != nil { - t.Errorf("Gists.Comments returned error: %v", err) + t.Errorf("Gists.ListComments returned error: %v", err) } - want := []*GistComment{{ID: Int64(1)}} - if !reflect.DeepEqual(comments, want) { + want := []*GistComment{{ID: Ptr(int64(1))}} + if !cmp.Equal(comments, want) { t.Errorf("Gists.ListComments returned %+v, want %+v", comments, want) } + + const methodName = "ListComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.ListComments(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListComments(ctx, "1", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_ListComments_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.ListComments(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Gists.ListComments(ctx, "%", nil) testURLParseError(t, err) } func TestGistsService_GetComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id": 1}`) }) - comment, _, err := client.Gists.GetComment(context.Background(), "1", 2) + ctx := t.Context() + comment, _, err := client.Gists.GetComment(ctx, "1", 2) if err != nil { t.Errorf("Gists.GetComment returned error: %v", err) } - want := &GistComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &GistComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Gists.GetComment returned %+v, want %+v", comment, want) } + + const methodName = "GetComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.GetComment(ctx, "\n", -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.GetComment(ctx, "1", 2) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_GetComment_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.GetComment(context.Background(), "%", 1) + ctx := t.Context() + _, _, err := client.Gists.GetComment(ctx, "%", 1) testURLParseError(t, err) } func TestGistsService_CreateComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &GistComment{ID: Int64(1), Body: String("b")} + input := CreateGistCommentRequest{Body: "b"} mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { - v := new(GistComment) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Gists.CreateComment(context.Background(), "1", input) + ctx := t.Context() + comment, _, err := client.Gists.CreateComment(ctx, "1", input) if err != nil { t.Errorf("Gists.CreateComment returned error: %v", err) } - want := &GistComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &GistComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Gists.CreateComment returned %+v, want %+v", comment, want) } + + const methodName = "CreateComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.CreateComment(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.CreateComment(ctx, "1", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_CreateComment_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.CreateComment(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Gists.CreateComment(ctx, "%", CreateGistCommentRequest{}) testURLParseError(t, err) } -func TestGistsService_EditComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestGistsService_UpdateComment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &GistComment{ID: Int64(1), Body: String("b")} + input := UpdateGistCommentRequest{Body: "b"} mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { - v := new(GistComment) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Gists.EditComment(context.Background(), "1", 2, input) + ctx := t.Context() + comment, _, err := client.Gists.UpdateComment(ctx, "1", 2, input) if err != nil { - t.Errorf("Gists.EditComment returned error: %v", err) + t.Errorf("Gists.UpdateComment returned error: %v", err) } - want := &GistComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { - t.Errorf("Gists.EditComment returned %+v, want %+v", comment, want) + want := &GistComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { + t.Errorf("Gists.UpdateComment returned %+v, want %+v", comment, want) } + + const methodName = "UpdateComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.UpdateComment(ctx, "\n", -2, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.UpdateComment(ctx, "1", 2, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestGistsService_EditComment_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestGistsService_UpdateComment_invalidID(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.EditComment(context.Background(), "%", 1, nil) + ctx := t.Context() + _, _, err := client.Gists.UpdateComment(ctx, "%", 1, UpdateGistCommentRequest{}) testURLParseError(t, err) } func TestGistsService_DeleteComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/gists/1/comments/2", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Gists.DeleteComment(context.Background(), "1", 2) + ctx := t.Context() + _, err := client.Gists.DeleteComment(ctx, "1", 2) if err != nil { - t.Errorf("Gists.Delete returned error: %v", err) + t.Errorf("Gists.DeleteComment returned error: %v", err) } + + const methodName = "DeleteComment" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Gists.DeleteComment(ctx, "\n", -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Gists.DeleteComment(ctx, "1", 2) + }) } func TestGistsService_DeleteComment_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Gists.DeleteComment(context.Background(), "%", 1) + ctx := t.Context() + _, err := client.Gists.DeleteComment(ctx, "%", 1) testURLParseError(t, err) } diff --git a/github/gists_test.go b/github/gists_test.go index d24e0e40b83..6a5b2859e5e 100644 --- a/github/gists_test.go +++ b/github/gists_test.go @@ -6,406 +6,267 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" -) - -func TestGist_marshall(t *testing.T) { - testJSONMarshal(t, &Gist{}, "{}") - - createdAt := time.Date(2010, time.February, 10, 10, 10, 0, 0, time.UTC) - updatedAt := time.Date(2010, time.February, 10, 10, 10, 0, 0, time.UTC) - - u := &Gist{ - ID: String("i"), - Description: String("description"), - Public: Bool(true), - Owner: &User{ - Login: String("ll"), - ID: Int64(123), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - URL: String("u"), - }, - Files: map[GistFilename]GistFile{ - "gistfile.py": { - Size: Int(167), - Filename: String("gistfile.py"), - Language: String("Python"), - Type: String("application/x-python"), - RawURL: String("raw-url"), - Content: String("c"), - }, - }, - Comments: Int(1), - HTMLURL: String("html-url"), - GitPullURL: String("gitpull-url"), - GitPushURL: String("gitpush-url"), - CreatedAt: &createdAt, - UpdatedAt: &updatedAt, - NodeID: String("node"), - } - - want := `{ - "id": "i", - "description": "description", - "public": true, - "owner": { - "login": "ll", - "id": 123, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "url": "u" - }, - "files": { - "gistfile.py": { - "size": 167, - "filename": "gistfile.py", - "language": "Python", - "type": "application/x-python", - "raw_url": "raw-url", - "content": "c" - } - }, - "comments": 1, - "html_url": "html-url", - "git_pull_url": "gitpull-url", - "git_push_url": "gitpush-url", - "created_at": "2010-02-10T10:10:00Z", - "updated_at": "2010-02-10T10:10:00Z", - "node_id": "node" - }` - - testJSONMarshal(t, u, want) -} - -func TestGistCommit_marshall(t *testing.T) { - testJSONMarshal(t, &GistCommit{}, "{}") - - u := &GistCommit{ - URL: String("u"), - Version: String("v"), - User: &User{ - Login: String("ll"), - ID: Int64(123), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - URL: String("u"), - }, - ChangeStatus: &CommitStats{ - Additions: Int(1), - Deletions: Int(1), - Total: Int(2), - }, - CommittedAt: &Timestamp{referenceTime}, - NodeID: String("node"), - } - - want := `{ - "url": "u", - "version": "v", - "user": { - "login": "ll", - "id": 123, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "url": "u" - }, - "change_status": { - "additions": 1, - "deletions": 1, - "total": 2 - }, - "committed_at": ` + referenceTimeStr + `, - "node_id": "node" - }` - - testJSONMarshal(t, u, want) -} -func TestGistFork_marshall(t *testing.T) { - testJSONMarshal(t, &GistFork{}, "{}") - - u := &GistFork{ - URL: String("u"), - User: &User{ - Login: String("ll"), - ID: Int64(123), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - URL: String("u"), - }, - ID: String("id"), - CreatedAt: &Timestamp{referenceTime}, - UpdatedAt: &Timestamp{referenceTime}, - NodeID: String("node"), - } - - want := `{ - "url": "u", - "user": { - "login": "ll", - "id": 123, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "url": "u" - }, - "id": "id", - "created_at": ` + referenceTimeStr + `, - "updated_at": ` + referenceTimeStr + `, - "node_id": "node" - }` - - testJSONMarshal(t, u, want) -} + "github.com/google/go-cmp/cmp" +) func TestGistsService_List_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - since := "2013-01-01T00:00:00Z" + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/gists", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{ - "since": since, + "since": referenceTimeRaw, }) fmt.Fprint(w, `[{"id": "1"}]`) }) - opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} - gists, _, err := client.Gists.List(context.Background(), "u", opt) + opt := &GistListOptions{Since: referenceTime} + ctx := t.Context() + gists, _, err := client.Gists.List(ctx, "u", opt) if err != nil { t.Errorf("Gists.List returned error: %v", err) } - want := []*Gist{{ID: String("1")}} - if !reflect.DeepEqual(gists, want) { + want := []*Gist{{ID: Ptr("1")}} + if !cmp.Equal(gists, want) { t.Errorf("Gists.List returned %+v, want %+v", gists, want) } + + const methodName = "List" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.List(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.List(ctx, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_List_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id": "1"}]`) }) - gists, _, err := client.Gists.List(context.Background(), "", nil) + ctx := t.Context() + gists, _, err := client.Gists.List(ctx, "", nil) if err != nil { t.Errorf("Gists.List returned error: %v", err) } - want := []*Gist{{ID: String("1")}} - if !reflect.DeepEqual(gists, want) { + want := []*Gist{{ID: Ptr("1")}} + if !cmp.Equal(gists, want) { t.Errorf("Gists.List returned %+v, want %+v", gists, want) } + + const methodName = "List" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.List(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.List(ctx, "", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_List_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.List(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Gists.List(ctx, "%", nil) testURLParseError(t, err) } func TestGistsService_ListAll(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - since := "2013-01-01T00:00:00Z" + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/public", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{ - "since": since, + "since": referenceTimeRaw, }) fmt.Fprint(w, `[{"id": "1"}]`) }) - opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} - gists, _, err := client.Gists.ListAll(context.Background(), opt) + opt := &GistListOptions{Since: referenceTime} + ctx := t.Context() + gists, _, err := client.Gists.ListAll(ctx, opt) if err != nil { t.Errorf("Gists.ListAll returned error: %v", err) } - want := []*Gist{{ID: String("1")}} - if !reflect.DeepEqual(gists, want) { + want := []*Gist{{ID: Ptr("1")}} + if !cmp.Equal(gists, want) { t.Errorf("Gists.ListAll returned %+v, want %+v", gists, want) } + + const methodName = "ListAll" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListAll(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_ListStarred(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - since := "2013-01-01T00:00:00Z" + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/starred", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{ - "since": since, + "since": referenceTimeRaw, }) fmt.Fprint(w, `[{"id": "1"}]`) }) - opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} - gists, _, err := client.Gists.ListStarred(context.Background(), opt) + opt := &GistListOptions{Since: referenceTime} + ctx := t.Context() + gists, _, err := client.Gists.ListStarred(ctx, opt) if err != nil { t.Errorf("Gists.ListStarred returned error: %v", err) } - want := []*Gist{{ID: String("1")}} - if !reflect.DeepEqual(gists, want) { + want := []*Gist{{ID: Ptr("1")}} + if !cmp.Equal(gists, want) { t.Errorf("Gists.ListStarred returned %+v, want %+v", gists, want) } + + const methodName = "ListStarred" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListStarred(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id": "1"}`) }) - gist, _, err := client.Gists.Get(context.Background(), "1") + ctx := t.Context() + gist, _, err := client.Gists.Get(ctx, "1") if err != nil { t.Errorf("Gists.Get returned error: %v", err) } - want := &Gist{ID: String("1")} - if !reflect.DeepEqual(gist, want) { + want := &Gist{ID: Ptr("1")} + if !cmp.Equal(gist, want) { t.Errorf("Gists.Get returned %+v, want %+v", gist, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.Get(ctx, "1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_Get_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.Get(context.Background(), "%") + ctx := t.Context() + _, _, err := client.Gists.Get(ctx, "%") testURLParseError(t, err) } func TestGistsService_GetRevision(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id": "1"}`) }) - gist, _, err := client.Gists.GetRevision(context.Background(), "1", "s") + ctx := t.Context() + gist, _, err := client.Gists.GetRevision(ctx, "1", "s") if err != nil { - t.Errorf("Gists.Get returned error: %v", err) + t.Errorf("Gists.GetRevision returned error: %v", err) } - want := &Gist{ID: String("1")} - if !reflect.DeepEqual(gist, want) { - t.Errorf("Gists.Get returned %+v, want %+v", gist, want) + want := &Gist{ID: Ptr("1")} + if !cmp.Equal(gist, want) { + t.Errorf("Gists.GetRevision returned %+v, want %+v", gist, want) } + + const methodName = "GetRevision" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.GetRevision(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.GetRevision(ctx, "1", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_GetRevision_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.GetRevision(context.Background(), "%", "%") + ctx := t.Context() + _, _, err := client.Gists.GetRevision(ctx, "%", "%") testURLParseError(t, err) } func TestGistsService_Create(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &Gist{ - Description: String("Gist description"), - Public: Bool(false), - Files: map[GistFilename]GistFile{ - "test.txt": {Content: String("Gist file content")}, + t.Parallel() + client, mux, _ := setup(t) + + input := CreateGistRequest{ + Description: Ptr("Gist description"), + Public: Ptr(false), + Files: map[GistFilename]*CreateGistFile{ + "test.txt": {Content: "Gist file content"}, }, } mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { - v := new(Gist) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, ` @@ -421,43 +282,50 @@ func TestGistsService_Create(t *testing.T) { }`) }) - gist, _, err := client.Gists.Create(context.Background(), input) + ctx := t.Context() + gist, _, err := client.Gists.Create(ctx, input) if err != nil { t.Errorf("Gists.Create returned error: %v", err) } want := &Gist{ - ID: String("1"), - Description: String("Gist description"), - Public: Bool(false), + ID: Ptr("1"), + Description: Ptr("Gist description"), + Public: Ptr(false), Files: map[GistFilename]GistFile{ - "test.txt": {Filename: String("test.txt")}, + "test.txt": {Filename: Ptr("test.txt")}, }, } - if !reflect.DeepEqual(gist, want) { + if !cmp.Equal(gist, want) { t.Errorf("Gists.Create returned %+v, want %+v", gist, want) } + + const methodName = "Create" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.Create(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestGistsService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestGistsService_Update(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &Gist{ - Description: String("New description"), - Files: map[GistFilename]GistFile{ - "new.txt": {Content: String("new file content")}, + input := UpdateGistRequest{ + Description: Ptr("New description"), + Files: map[GistFilename]*UpdateGistFile{ + "new.txt": {Content: Ptr("new file content")}, + // A nil value deletes the file from the gist. + "old.txt": nil, }, } mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { - v := new(Gist) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, ` @@ -476,36 +344,52 @@ func TestGistsService_Edit(t *testing.T) { }`) }) - gist, _, err := client.Gists.Edit(context.Background(), "1", input) + ctx := t.Context() + gist, _, err := client.Gists.Update(ctx, "1", input) if err != nil { - t.Errorf("Gists.Edit returned error: %v", err) + t.Errorf("Gists.Update returned error: %v", err) } want := &Gist{ - ID: String("1"), - Description: String("new description"), - Public: Bool(false), + ID: Ptr("1"), + Description: Ptr("new description"), + Public: Ptr(false), Files: map[GistFilename]GistFile{ - "test.txt": {Filename: String("test.txt")}, - "new.txt": {Filename: String("new.txt")}, + "test.txt": {Filename: Ptr("test.txt")}, + "new.txt": {Filename: Ptr("new.txt")}, }, } - if !reflect.DeepEqual(gist, want) { - t.Errorf("Gists.Edit returned %+v, want %+v", gist, want) + if !cmp.Equal(gist, want) { + t.Errorf("Gists.Update returned %+v, want %+v", gist, want) } + + const methodName = "Update" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.Update(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.Update(ctx, "1", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestGistsService_Edit_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestGistsService_Update_invalidID(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.Edit(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Gists.Update(ctx, "%", UpdateGistRequest{}) testURLParseError(t, err) } func TestGistsService_ListCommits(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -523,36 +407,52 @@ func TestGistsService_ListCommits(t *testing.T) { "additions": 180, "total": 180 }, - "committed_at": "2010-01-01T00:00:00Z" + "committed_at": `+referenceTimeStr+` } ] `) }) - gistCommits, _, err := client.Gists.ListCommits(context.Background(), "1", nil) + ctx := t.Context() + gistCommits, _, err := client.Gists.ListCommits(ctx, "1", nil) if err != nil { t.Errorf("Gists.ListCommits returned error: %v", err) } want := []*GistCommit{{ - URL: String("https://api.github.com/gists/1/1"), - Version: String("1"), - User: &User{ID: Int64(1)}, - CommittedAt: &Timestamp{time.Date(2010, time.January, 1, 00, 00, 00, 0, time.UTC)}, + URL: Ptr("https://api.github.com/gists/1/1"), + Version: Ptr("1"), + User: &User{ID: Ptr(int64(1))}, + CommittedAt: &referenceTimestamp, ChangeStatus: &CommitStats{ - Additions: Int(180), - Deletions: Int(0), - Total: Int(180), - }}} + Additions: Ptr(180), + Deletions: Ptr(0), + Total: Ptr(180), + }, + }} - if !reflect.DeepEqual(gistCommits, want) { + if !cmp.Equal(gistCommits, want) { t.Errorf("Gists.ListCommits returned %+v, want %+v", gistCommits, want) } + + const methodName = "ListCommits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.ListCommits(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListCommits(ctx, "1", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_ListCommits_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -562,153 +462,251 @@ func TestGistsService_ListCommits_withOptions(t *testing.T) { fmt.Fprint(w, `[]`) }) - _, _, err := client.Gists.ListCommits(context.Background(), "1", &ListOptions{Page: 2}) + ctx := t.Context() + _, _, err := client.Gists.ListCommits(ctx, "1", &ListOptions{Page: 2}) if err != nil { t.Errorf("Gists.ListCommits returned error: %v", err) } + + const methodName = "ListCommits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.ListCommits(ctx, "\n", &ListOptions{Page: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListCommits(ctx, "1", &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_ListCommits_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.ListCommits(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Gists.ListCommits(ctx, "%", nil) testURLParseError(t, err) } func TestGistsService_Delete(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/gists/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Gists.Delete(context.Background(), "1") + ctx := t.Context() + _, err := client.Gists.Delete(ctx, "1") if err != nil { t.Errorf("Gists.Delete returned error: %v", err) } + + const methodName = "Delete" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Gists.Delete(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Gists.Delete(ctx, "1") + }) } func TestGistsService_Delete_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Gists.Delete(context.Background(), "%") + ctx := t.Context() + _, err := client.Gists.Delete(ctx, "%") testURLParseError(t, err) } func TestGistsService_Star(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/gists/1/star", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") }) - _, err := client.Gists.Star(context.Background(), "1") + ctx := t.Context() + _, err := client.Gists.Star(ctx, "1") if err != nil { t.Errorf("Gists.Star returned error: %v", err) } + + const methodName = "Star" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Gists.Star(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Gists.Star(ctx, "1") + }) } func TestGistsService_Star_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Gists.Star(context.Background(), "%") + ctx := t.Context() + _, err := client.Gists.Star(ctx, "%") testURLParseError(t, err) } func TestGistsService_Unstar(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/gists/1/star", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Gists.Unstar(context.Background(), "1") + ctx := t.Context() + _, err := client.Gists.Unstar(ctx, "1") if err != nil { t.Errorf("Gists.Unstar returned error: %v", err) } + + const methodName = "Unstar" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Gists.Unstar(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Gists.Unstar(ctx, "1") + }) } func TestGistsService_Unstar_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Gists.Unstar(context.Background(), "%") + ctx := t.Context() + _, err := client.Gists.Unstar(ctx, "%") testURLParseError(t, err) } func TestGistsService_IsStarred_hasStar(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - star, _, err := client.Gists.IsStarred(context.Background(), "1") + ctx := t.Context() + star, _, err := client.Gists.IsStarred(ctx, "1") if err != nil { - t.Errorf("Gists.Starred returned error: %v", err) + t.Errorf("Gists.IsStarred returned error: %v", err) } if want := true; star != want { - t.Errorf("Gists.Starred returned %+v, want %+v", star, want) + t.Errorf("Gists.IsStarred returned %+v, want %+v", star, want) } + + const methodName = "IsStarred" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.IsStarred(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.IsStarred(ctx, "1") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestGistsService_IsStarred_noStar(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - star, _, err := client.Gists.IsStarred(context.Background(), "1") + ctx := t.Context() + star, _, err := client.Gists.IsStarred(ctx, "1") if err != nil { - t.Errorf("Gists.Starred returned error: %v", err) + t.Errorf("Gists.IsStarred returned error: %v", err) } if want := false; star != want { - t.Errorf("Gists.Starred returned %+v, want %+v", star, want) + t.Errorf("Gists.IsStarred returned %+v, want %+v", star, want) } + + const methodName = "IsStarred" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.IsStarred(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.IsStarred(ctx, "1") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestGistsService_IsStarred_invalidID(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gists.IsStarred(context.Background(), "%") + ctx := t.Context() + _, _, err := client.Gists.IsStarred(ctx, "%") testURLParseError(t, err) } func TestGistsService_Fork(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") fmt.Fprint(w, `{"id": "2"}`) }) - gist, _, err := client.Gists.Fork(context.Background(), "1") + ctx := t.Context() + gist, _, err := client.Gists.Fork(ctx, "1") if err != nil { t.Errorf("Gists.Fork returned error: %v", err) } - want := &Gist{ID: String("2")} - if !reflect.DeepEqual(gist, want) { + want := &Gist{ID: Ptr("2")} + if !cmp.Equal(gist, want) { t.Errorf("Gists.Fork returned %+v, want %+v", gist, want) } + + const methodName = "Fork" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.Fork(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.Fork(ctx, "1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_ListForks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -718,34 +716,49 @@ func TestGistsService_ListForks(t *testing.T) { {"url": "https://api.github.com/gists/1", "user": {"id": 1}, "id": "1", - "created_at": "2010-01-01T00:00:00Z", - "updated_at": "2013-01-01T00:00:00Z" + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` } ] `) }) - gistForks, _, err := client.Gists.ListForks(context.Background(), "1", nil) + ctx := t.Context() + gistForks, _, err := client.Gists.ListForks(ctx, "1", nil) if err != nil { t.Errorf("Gists.ListForks returned error: %v", err) } want := []*GistFork{{ - URL: String("https://api.github.com/gists/1"), - ID: String("1"), - User: &User{ID: Int64(1)}, - CreatedAt: &Timestamp{time.Date(2010, time.January, 1, 00, 00, 00, 0, time.UTC)}, - UpdatedAt: &Timestamp{time.Date(2013, time.January, 1, 00, 00, 00, 0, time.UTC)}}} - - if !reflect.DeepEqual(gistForks, want) { + URL: Ptr("https://api.github.com/gists/1"), + ID: Ptr("1"), + User: &User{ID: Ptr(int64(1))}, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + }} + + if !cmp.Equal(gistForks, want) { t.Errorf("Gists.ListForks returned %+v, want %+v", gistForks, want) } + const methodName = "ListForks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.ListForks(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListForks(ctx, "1", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGistsService_ListForks_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -755,44 +768,28 @@ func TestGistsService_ListForks_withOptions(t *testing.T) { fmt.Fprint(w, `[]`) }) - gistForks, _, err := client.Gists.ListForks(context.Background(), "1", &ListOptions{Page: 2}) + ctx := t.Context() + gistForks, _, err := client.Gists.ListForks(ctx, "1", &ListOptions{Page: 2}) if err != nil { t.Errorf("Gists.ListForks returned error: %v", err) } want := []*GistFork{} - if !reflect.DeepEqual(gistForks, want) { + if !cmp.Equal(gistForks, want) { t.Errorf("Gists.ListForks returned %+v, want %+v", gistForks, want) } - // Test addOptions failure - _, _, err = client.Gists.ListForks(context.Background(), "%", &ListOptions{}) - if err == nil { - t.Error("Gists.ListForks returned err = nil") - } - - // Test client.NewRequest failure - got, resp, err := client.Gists.ListForks(context.Background(), "%", nil) - if got != nil { - t.Errorf("Gists.ListForks = %#v, want nil", got) - } - if resp != nil { - t.Errorf("Gists.ListForks resp = %#v, want nil", resp) - } - if err == nil { - t.Error("Gists.ListForks err = nil, want error") - } + const methodName = "ListForks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gists.ListForks(ctx, "\n", &ListOptions{Page: 2}) + return err + }) - // Test client.Do failure - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Gists.ListForks(context.Background(), "1", &ListOptions{Page: 2}) - if got != nil { - t.Errorf("Gists.ListForks returned = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("Gists.ListForks returned resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rGists.ListForks returned err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gists.ListForks(ctx, "1", &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/git.go b/github/git.go index 1ce47437bd3..ce172cd06a0 100644 --- a/github/git.go +++ b/github/git.go @@ -8,5 +8,5 @@ package github // GitService handles communication with the git data related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/git/ +// GitHub API docs: https://docs.github.com/rest/git?apiVersion=2022-11-28 type GitService service diff --git a/github/git_blobs.go b/github/git_blobs.go index 70aee14a7ae..1db906df005 100644 --- a/github/git_blobs.go +++ b/github/git_blobs.go @@ -23,47 +23,66 @@ type Blob struct { // GetBlob fetches a blob from a repo given a SHA. // -// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob -func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha string) (*Blob, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/blobs?apiVersion=2022-11-28#get-a-blob +// +//meta:operation GET /repos/{owner}/{repo}/git/blobs/{file_sha} +func (s *GitService) GetBlob(ctx context.Context, owner, repo, sha string) (*Blob, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - blob := new(Blob) - resp, err := s.client.Do(ctx, req, blob) - return blob, resp, err + var blob *Blob + resp, err := s.client.Do(req, &blob) + if err != nil { + return nil, resp, err + } + + return blob, resp, nil } // GetBlobRaw fetches a blob's contents from a repo. // Unlike GetBlob, it returns the raw bytes rather than the base64-encoded data. // -// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob +// GitHub API docs: https://docs.github.com/rest/git/blobs?apiVersion=2022-11-28#get-a-blob +// +//meta:operation GET /repos/{owner}/{repo}/git/blobs/{file_sha} func (s *GitService) GetBlobRaw(ctx context.Context, owner, repo, sha string) ([]byte, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } + req.Header.Set("Accept", "application/vnd.github.v3.raw") var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) - return buf.Bytes(), resp, err + resp, err := s.client.Do(req, &buf) + if err != nil { + return nil, resp, err + } + + return buf.Bytes(), resp, nil } // CreateBlob creates a blob object. // -// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob -func (s *GitService) CreateBlob(ctx context.Context, owner string, repo string, blob *Blob) (*Blob, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/blobs?apiVersion=2022-11-28#create-a-blob +// +//meta:operation POST /repos/{owner}/{repo}/git/blobs +func (s *GitService) CreateBlob(ctx context.Context, owner, repo string, body Blob) (*Blob, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) - req, err := s.client.NewRequest("POST", u, blob) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - t := new(Blob) - resp, err := s.client.Do(ctx, req, t) - return t, resp, err + var v *Blob + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, nil } diff --git a/github/git_blobs_test.go b/github/git_blobs_test.go index 9945d352ebf..cfc02da02de 100644 --- a/github/git_blobs_test.go +++ b/github/git_blobs_test.go @@ -7,17 +7,16 @@ package github import ( "bytes" - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestGitService_GetBlob(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -28,32 +27,48 @@ func TestGitService_GetBlob(t *testing.T) { }`) }) - blob, _, err := client.Git.GetBlob(context.Background(), "o", "r", "s") + ctx := t.Context() + blob, _, err := client.Git.GetBlob(ctx, "o", "r", "s") if err != nil { t.Errorf("Git.GetBlob returned error: %v", err) } want := Blob{ - SHA: String("s"), - Content: String("blob content"), + SHA: Ptr("s"), + Content: Ptr("blob content"), } - if !reflect.DeepEqual(*blob, want) { - t.Errorf("Blob.Get returned %+v, want %+v", *blob, want) + if !cmp.Equal(*blob, want) { + t.Errorf("Git.GetBlob returned %+v, want %+v", *blob, want) } + + const methodName = "GetBlob" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetBlob(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetBlob(ctx, "o", "r", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_GetBlob_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Git.GetBlob(context.Background(), "%", "%", "%") + ctx := t.Context() + _, _, err := client.Git.GetBlob(ctx, "%", "%", "%") testURLParseError(t, err) } func TestGitService_GetBlobRaw(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -62,7 +77,8 @@ func TestGitService_GetBlobRaw(t *testing.T) { fmt.Fprint(w, `raw contents here`) }) - blob, _, err := client.Git.GetBlobRaw(context.Background(), "o", "r", "s") + ctx := t.Context() + blob, _, err := client.Git.GetBlobRaw(ctx, "o", "r", "s") if err != nil { t.Errorf("Git.GetBlobRaw returned error: %v", err) } @@ -71,30 +87,36 @@ func TestGitService_GetBlobRaw(t *testing.T) { if !bytes.Equal(blob, want) { t.Errorf("GetBlobRaw returned %q, want %q", blob, want) } + + const methodName = "GetBlobRaw" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetBlobRaw(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetBlobRaw(ctx, "o", "r", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateBlob(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &Blob{ - SHA: String("s"), - Content: String("blob content"), - Encoding: String("utf-8"), - Size: Int(12), + t.Parallel() + client, mux, _ := setup(t) + + input := Blob{ + SHA: Ptr("s"), + Content: Ptr("blob content"), + Encoding: Ptr("utf-8"), + Size: Ptr(12), } mux.HandleFunc("/repos/o/r/git/blobs", func(w http.ResponseWriter, r *http.Request) { - v := new(Blob) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - - want := input - if !reflect.DeepEqual(v, want) { - t.Errorf("Git.CreateBlob request body: %+v, want %+v", v, want) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{ "sha": "s", "content": "blob content", @@ -103,22 +125,38 @@ func TestGitService_CreateBlob(t *testing.T) { }`) }) - blob, _, err := client.Git.CreateBlob(context.Background(), "o", "r", input) + ctx := t.Context() + blob, _, err := client.Git.CreateBlob(ctx, "o", "r", input) if err != nil { t.Errorf("Git.CreateBlob returned error: %v", err) } want := input - if !reflect.DeepEqual(*blob, *want) { - t.Errorf("Git.CreateBlob returned %+v, want %+v", *blob, *want) + if !cmp.Equal(*blob, want) { + t.Errorf("Git.CreateBlob returned %+v, want %+v", *blob, want) } + + const methodName = "CreateBlob" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateBlob(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateBlob(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateBlob_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Git.CreateBlob(context.Background(), "%", "%", &Blob{}) + ctx := t.Context() + _, _, err := client.Git.CreateBlob(ctx, "%", "%", Blob{}) testURLParseError(t, err) } diff --git a/github/git_commits.go b/github/git_commits.go index 741961980a1..0efad197cdd 100644 --- a/github/git_commits.go +++ b/github/git_commits.go @@ -10,10 +10,8 @@ import ( "context" "errors" "fmt" + "io" "strings" - "time" - - "golang.org/x/crypto/openpgp" ) // SignatureVerification represents GPG signature verification. @@ -24,6 +22,26 @@ type SignatureVerification struct { Payload *string `json:"payload,omitempty"` } +// MessageSigner is used by GitService.CreateCommit to sign a commit. +// +// To create a MessageSigner that signs a commit with a [golang.org/x/crypto/openpgp.Entity], +// or [github.com/ProtonMail/go-crypto/openpgp.Entity], use: +// +// commit.Signer = github.MessageSignerFunc(func(w io.Writer, r io.Reader) error { +// return openpgp.ArmoredDetachSign(w, openpgpEntity, r, nil) +// }) +type MessageSigner interface { + Sign(w io.Writer, r io.Reader) error +} + +// MessageSignerFunc is a single function implementation of MessageSigner. +type MessageSignerFunc func(w io.Writer, r io.Reader) error + +// Sign implements the MessageSigner interface for MessageSignerFunc. +func (f MessageSignerFunc) Sign(w io.Writer, r io.Reader) error { + return f(w, r) +} + // Commit represents a GitHub commit. type Commit struct { SHA *string `json:"sha,omitempty"` @@ -31,8 +49,7 @@ type Commit struct { Committer *CommitAuthor `json:"committer,omitempty"` Message *string `json:"message,omitempty"` Tree *Tree `json:"tree,omitempty"` - Parents []Commit `json:"parents,omitempty"` - Stats *CommitStats `json:"stats,omitempty"` + Parents []*Commit `json:"parents,omitempty"` HTMLURL *string `json:"html_url,omitempty"` URL *string `json:"url,omitempty"` Verification *SignatureVerification `json:"verification,omitempty"` @@ -42,11 +59,6 @@ type Commit struct { // is only populated for requests that fetch GitHub data like // Pulls.ListCommits, Repositories.ListCommits, etc. CommentCount *int `json:"comment_count,omitempty"` - - // SigningKey denotes a key to sign the commit with. If not nil this key will - // be used to sign the commit. The private key must be present and already - // decrypted. Ignored if Verification.Signature is defined. - SigningKey *openpgp.Entity `json:"-"` } func (c Commit) String() string { @@ -56,7 +68,7 @@ func (c Commit) String() string { // CommitAuthor represents the author or committer of a commit. The commit // author may not correspond to a GitHub User. type CommitAuthor struct { - Date *time.Time `json:"date,omitempty"` + Date *Timestamp `json:"date,omitempty"` Name *string `json:"name,omitempty"` Email *string `json:"email,omitempty"` @@ -70,16 +82,18 @@ func (c CommitAuthor) String() string { // GetCommit fetches the Commit object for a given SHA. // -// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit -func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/commits?apiVersion=2022-11-28#get-a-commit-object +// +//meta:operation GET /repos/{owner}/{repo}/git/commits/{commit_sha} +func (s *GitService) GetCommit(ctx context.Context, owner, repo, sha string) (*Commit, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - c := new(Commit) - resp, err := s.client.Do(ctx, req, c) + var c *Commit + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -97,6 +111,14 @@ type createCommit struct { Signature *string `json:"signature,omitempty"` } +// CreateCommitOptions specifies optional parameters to creates +// a new commit in a repository. +type CreateCommitOptions struct { + // CreateCommit will sign the commit with this signer. See MessageSigner doc for more details. + // Ignored on commits where Verification.Signature is defined. + Signer MessageSigner +} + // CreateCommit creates a new commit in a repository. // commit must not be nil. // @@ -104,10 +126,12 @@ type createCommit struct { // data if omitted. If the commit.Author is omitted, it will be filled in with // the authenticated user’s information and the current date. // -// GitHub API docs: https://developer.github.com/v3/git/commits/#create-a-commit -func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string, commit *Commit) (*Commit, *Response, error) { - if commit == nil { - return nil, nil, fmt.Errorf("commit must be provided") +// GitHub API docs: https://docs.github.com/rest/git/commits?apiVersion=2022-11-28#create-a-commit +// +//meta:operation POST /repos/{owner}/{repo}/git/commits +func (s *GitService) CreateCommit(ctx context.Context, owner, repo string, commit Commit, opts *CreateCommitOptions) (*Commit, *Response, error) { + if opts == nil { + opts = &CreateCommitOptions{} } u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo) @@ -126,24 +150,24 @@ func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string if commit.Tree != nil { body.Tree = commit.Tree.SHA } - if commit.SigningKey != nil { - signature, err := createSignature(commit.SigningKey, body) + switch { + case commit.Verification != nil: + body.Signature = commit.Verification.Signature + case opts.Signer != nil: + signature, err := createSignature(opts.Signer, body) if err != nil { return nil, nil, err } body.Signature = &signature } - if commit.Verification != nil { - body.Signature = commit.Verification.Signature - } - req, err := s.client.NewRequest("POST", u, body) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - c := new(Commit) - resp, err := s.client.Do(ctx, req, c) + var c *Commit + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -151,8 +175,8 @@ func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string return c, resp, nil } -func createSignature(signingKey *openpgp.Entity, commit *createCommit) (string, error) { - if signingKey == nil || commit == nil { +func createSignature(signer MessageSigner, commit *createCommit) (string, error) { + if signer == nil { return "", errors.New("createSignature: invalid parameters") } @@ -161,9 +185,9 @@ func createSignature(signingKey *openpgp.Entity, commit *createCommit) (string, return "", err } - writer := new(bytes.Buffer) - reader := bytes.NewReader([]byte(message)) - if err := openpgp.ArmoredDetachSign(writer, signingKey, reader, nil); err != nil { + var writer bytes.Buffer + err = signer.Sign(&writer, strings.NewReader(message)) + if err != nil { return "", err } @@ -178,14 +202,14 @@ func createSignatureMessage(commit *createCommit) (string, error) { var message []string if commit.Tree != nil { - message = append(message, fmt.Sprintf("tree %s", *commit.Tree)) + message = append(message, fmt.Sprintf("tree %v", *commit.Tree)) } for _, parent := range commit.Parents { - message = append(message, fmt.Sprintf("parent %s", parent)) + message = append(message, fmt.Sprintf("parent %v", parent)) } - message = append(message, fmt.Sprintf("author %s <%s> %d %s", commit.Author.GetName(), commit.Author.GetEmail(), commit.Author.GetDate().Unix(), commit.Author.GetDate().Format("-0700"))) + message = append(message, fmt.Sprintf("author %v <%v> %v %v", commit.Author.GetName(), commit.Author.GetEmail(), commit.Author.GetDate().Unix(), commit.Author.GetDate().Format("-0700"))) committer := commit.Committer if committer == nil { @@ -193,7 +217,7 @@ func createSignatureMessage(commit *createCommit) (string, error) { } // There needs to be a double newline after committer - message = append(message, fmt.Sprintf("committer %s <%s> %d %s\n", committer.GetName(), committer.GetEmail(), committer.GetDate().Unix(), committer.GetDate().Format("-0700"))) + message = append(message, fmt.Sprintf("committer %v <%v> %v %v\n", committer.GetName(), committer.GetEmail(), committer.GetDate().Unix(), committer.GetDate().Format("-0700"))) message = append(message, *commit.Message) return strings.Join(message, "\n"), nil diff --git a/github/git_commits_test.go b/github/git_commits_test.go index f316f01e0fd..f7febde7246 100644 --- a/github/git_commits_test.go +++ b/github/git_commits_test.go @@ -6,328 +6,351 @@ package github import ( - "context" - "encoding/json" + "errors" "fmt" + "io" "net/http" - "reflect" - "strings" "testing" "time" - "golang.org/x/crypto/openpgp" + "github.com/google/go-cmp/cmp" ) +func mockSigner(t *testing.T, signature string, emitErr error, wantMessage string) MessageSignerFunc { + return func(w io.Writer, r io.Reader) error { + t.Helper() + message, err := io.ReadAll(r) + assertNilError(t, err) + if wantMessage != "" && string(message) != wantMessage { + t.Errorf("MessageSignerFunc got %q, want %q", string(message), wantMessage) + } + assertWrite(t, w, []byte(signature)) + return emitErr + } +} + +func uncalledSigner(t *testing.T) MessageSignerFunc { + return func(io.Writer, io.Reader) error { + t.Error("MessageSignerFunc should not be called") + return nil + } +} + func TestGitService_GetCommit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/git/commits/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"sha":"s","message":"Commit Message.","author":{"name":"n"}}`) }) - commit, _, err := client.Git.GetCommit(context.Background(), "o", "r", "s") + ctx := t.Context() + commit, _, err := client.Git.GetCommit(ctx, "o", "r", "s") if err != nil { t.Errorf("Git.GetCommit returned error: %v", err) } - want := &Commit{SHA: String("s"), Message: String("Commit Message."), Author: &CommitAuthor{Name: String("n")}} - if !reflect.DeepEqual(commit, want) { + want := &Commit{SHA: Ptr("s"), Message: Ptr("Commit Message."), Author: &CommitAuthor{Name: Ptr("n")}} + if !cmp.Equal(commit, want) { t.Errorf("Git.GetCommit returned %+v, want %+v", commit, want) } + + const methodName = "GetCommit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetCommit(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetCommit(ctx, "o", "r", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_GetCommit_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Git.GetCommit(context.Background(), "%", "%", "%") + ctx := t.Context() + _, _, err := client.Git.GetCommit(ctx, "%", "%", "%") testURLParseError(t, err) } func TestGitService_CreateCommit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Commit{ - Message: String("Commit Message."), - Tree: &Tree{SHA: String("t")}, - Parents: []Commit{{SHA: String("p")}}, + input := Commit{ + Message: Ptr("Commit Message."), + Tree: &Tree{SHA: Ptr("t")}, + Parents: []*Commit{{SHA: Ptr("p")}}, } mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) { - v := new(createCommit) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - - want := &createCommit{ + want := createCommit{ Message: input.Message, - Tree: String("t"), - Parents: []string{"p"}, - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) + Tree: input.Tree.SHA, + Parents: []string{*input.Parents[0].SHA}, } + testJSONBody(t, r, want) fmt.Fprint(w, `{"sha":"s"}`) }) - commit, _, err := client.Git.CreateCommit(context.Background(), "o", "r", input) + ctx := t.Context() + commit, _, err := client.Git.CreateCommit(ctx, "o", "r", input, nil) if err != nil { t.Errorf("Git.CreateCommit returned error: %v", err) } - want := &Commit{SHA: String("s")} - if !reflect.DeepEqual(commit, want) { + want := &Commit{SHA: Ptr("s")} + if !cmp.Equal(commit, want) { t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want) } + + const methodName = "CreateCommit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateCommit(ctx, "\n", "\n", input, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateCommit(ctx, "o", "r", input, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateSignedCommit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - signature := "----- BEGIN PGP SIGNATURE -----\n\naaaa\naaaa\n----- END PGP SIGNATURE -----" - - input := &Commit{ - Message: String("Commit Message."), - Tree: &Tree{SHA: String("t")}, - Parents: []Commit{{SHA: String("p")}}, + input := Commit{ + Message: Ptr("Commit Message."), + Tree: &Tree{SHA: Ptr("t")}, + Parents: []*Commit{{SHA: Ptr("p")}}, Verification: &SignatureVerification{ - Signature: String(signature), + Signature: Ptr("----- BEGIN PGP SIGNATURE -----\n\naaaa\naaaa\n----- END PGP SIGNATURE -----"), }, } mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) { - v := new(createCommit) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - - want := &createCommit{ + want := struct { + Message *string `json:"message,omitempty"` + Tree *string `json:"tree,omitempty"` + Parents []string `json:"parents,omitempty"` + Signature *string `json:"signature,omitempty"` + }{ Message: input.Message, - Tree: String("t"), - Parents: []string{"p"}, - Signature: String(signature), - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) + Tree: input.Tree.SHA, + Parents: []string{*input.Parents[0].SHA}, + Signature: input.Verification.Signature, } + testJSONBody(t, r, want) fmt.Fprint(w, `{"sha":"commitSha"}`) }) - commit, _, err := client.Git.CreateCommit(context.Background(), "o", "r", input) + ctx := t.Context() + commit, _, err := client.Git.CreateCommit(ctx, "o", "r", input, nil) if err != nil { t.Errorf("Git.CreateCommit returned error: %v", err) } - want := &Commit{SHA: String("commitSha")} - if !reflect.DeepEqual(commit, want) { + want := &Commit{SHA: Ptr("commitSha")} + if !cmp.Equal(commit, want) { t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want) } + + const methodName = "CreateCommit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateCommit(ctx, "\n", "\n", input, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateCommit(ctx, "o", "r", input, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } + func TestGitService_CreateSignedCommitWithInvalidParams(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - input := &Commit{ - SigningKey: &openpgp.Entity{}, - } + input := Commit{} - _, _, err := client.Git.CreateCommit(context.Background(), "o", "r", input) + ctx := t.Context() + opts := CreateCommitOptions{Signer: uncalledSigner(t)} + _, _, err := client.Git.CreateCommit(ctx, "o", "r", input, &opts) if err == nil { - t.Errorf("Expected error to be returned because invalid params was passed") + t.Error("Expected error to be returned because invalid params were passed") } } -func TestGitService_CreateSignedCommitWithKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - s := strings.NewReader(testGPGKey) - keyring, err := openpgp.ReadArmoredKeyRing(s) - if err != nil { - t.Errorf("Error reading keyring: %+v", err) - } +func TestGitService_CreateCommit_WithSigner(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - date, _ := time.Parse("Mon Jan 02 15:04:05 2006 -0700", "Thu May 04 00:03:43 2017 +0200") + signature := "my voice is my password" + date := time.Date(2017, time.May, 4, 0, 3, 43, 0, time.FixedZone("CEST", 2*3600)) author := CommitAuthor{ - Name: String("go-github"), - Email: String("go-github@github.com"), - Date: &date, + Name: Ptr("go-github"), + Email: Ptr("go-github@github.com"), + Date: &Timestamp{date}, } - input := &Commit{ - Message: String("Commit Message."), - Tree: &Tree{SHA: String("t")}, - Parents: []Commit{{SHA: String("p")}}, - SigningKey: keyring[0], - Author: &author, - } - - messageReader := strings.NewReader(`tree t + wantMessage := `tree t parent p author go-github 1493849023 +0200 committer go-github 1493849023 +0200 -Commit Message.`) - +Commit Message.` + sha := "commitSha" + input := Commit{ + SHA: &sha, + Message: Ptr("Commit Message."), + Tree: &Tree{SHA: Ptr("t")}, + Parents: []*Commit{{SHA: Ptr("p")}}, + Author: &author, + } + wantBody := &createCommit{ + Message: input.Message, + Tree: Ptr("t"), + Parents: []string{"p"}, + Author: &author, + Signature: &signature, + } mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) { - v := new(createCommit) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - - want := &createCommit{ - Message: input.Message, - Tree: String("t"), - Parents: []string{"p"}, - Author: &author, - } - - sigReader := strings.NewReader(*v.Signature) - signer, err := openpgp.CheckArmoredDetachedSignature(keyring, messageReader, sigReader) - if err != nil { - t.Errorf("Error verifying signature: %+v", err) - } - if signer.Identities["go-github "].Name != "go-github " { - t.Errorf("Signer is incorrect. got: %+v, want %+v", signer.Identities["go-github "].Name, "go-github ") - } - // Nullify Signature since we checked it above - v.Signature = nil - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } - fmt.Fprint(w, `{"sha":"commitSha"}`) + testJSONBody(t, r, wantBody) + fmt.Fprintf(w, `{"sha":"%v"}`, sha) }) - - commit, _, err := client.Git.CreateCommit(context.Background(), "o", "r", input) - if err != nil { - t.Errorf("Git.CreateCommit returned error: %v", err) - } - - want := &Commit{SHA: String("commitSha")} - if !reflect.DeepEqual(commit, want) { - t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want) + ctx := t.Context() + wantCommit := &Commit{SHA: &sha} + opts := CreateCommitOptions{Signer: mockSigner(t, signature, nil, wantMessage)} + commit, _, err := client.Git.CreateCommit(ctx, "o", "r", input, &opts) + assertNilError(t, err) + if cmp.Diff(commit, wantCommit) != "" { + t.Errorf("Git.CreateCommit returned %+v, want %+v\n%v", commit, wantCommit, cmp.Diff(commit, wantCommit)) } } -func TestGitService_createSignature_nilSigningKey(t *testing.T) { +func TestGitService_createSignature_nilSigner(t *testing.T) { + t.Parallel() a := &createCommit{ - Message: String("Commit Message."), - Tree: String("t"), + Message: Ptr("Commit Message."), + Tree: Ptr("t"), Parents: []string{"p"}, } _, err := createSignature(nil, a) if err == nil { - t.Errorf("Expected error to be returned because no author was passed") + t.Error("Expected error to be returned because no author was passed") } } func TestGitService_createSignature_nilCommit(t *testing.T) { - _, err := createSignature(&openpgp.Entity{}, nil) + t.Parallel() + _, err := createSignature(uncalledSigner(t), nil) if err == nil { - t.Errorf("Expected error to be returned because no author was passed") + t.Error("Expected error to be returned because no author was passed") } } -func TestGitService_createSignature_noAuthor(t *testing.T) { +func TestGitService_createSignature_signerError(t *testing.T) { + t.Parallel() a := &createCommit{ - Message: String("Commit Message."), - Tree: String("t"), + Message: Ptr("Commit Message."), + Tree: Ptr("t"), Parents: []string{"p"}, + Author: &CommitAuthor{Name: Ptr("go-github")}, } - _, err := createSignature(&openpgp.Entity{}, a) - - if err == nil { - t.Errorf("Expected error to be returned because no author was passed") - } -} - -func TestGitService_createSignature_invalidKey(t *testing.T) { - date, _ := time.Parse("Mon Jan 02 15:04:05 2006 -0700", "Thu May 04 00:03:43 2017 +0200") - - _, err := createSignature(&openpgp.Entity{}, &createCommit{ - Message: String("Commit Message."), - Tree: String("t"), - Parents: []string{"p"}, - Author: &CommitAuthor{ - Name: String("go-github"), - Email: String("go-github@github.com"), - Date: &date, - }, - }) + signer := mockSigner(t, "", errors.New("signer error"), "") + _, err := createSignature(signer, a) if err == nil { - t.Errorf("Expected error to be returned due to invalid key") + t.Error("Expected error to be returned because signer returned an error") } } func TestGitService_createSignatureMessage_nilCommit(t *testing.T) { + t.Parallel() _, err := createSignatureMessage(nil) if err == nil { - t.Errorf("Expected error to be returned due to nil key") + t.Error("Expected error to be returned due to nil key") } } func TestGitService_createSignatureMessage_nilMessage(t *testing.T) { + t.Parallel() date, _ := time.Parse("Mon Jan 02 15:04:05 2006 -0700", "Thu May 04 00:03:43 2017 +0200") _, err := createSignatureMessage(&createCommit{ Message: nil, Parents: []string{"p"}, Author: &CommitAuthor{ - Name: String("go-github"), - Email: String("go-github@github.com"), - Date: &date, + Name: Ptr("go-github"), + Email: Ptr("go-github@github.com"), + Date: &Timestamp{date}, }, }) if err == nil { - t.Errorf("Expected error to be returned due to nil key") + t.Error("Expected error to be returned due to nil key") } } func TestGitService_createSignatureMessage_emptyMessage(t *testing.T) { + t.Parallel() date, _ := time.Parse("Mon Jan 02 15:04:05 2006 -0700", "Thu May 04 00:03:43 2017 +0200") - emptyString := "" _, err := createSignatureMessage(&createCommit{ - Message: &emptyString, + Message: Ptr(""), Parents: []string{"p"}, Author: &CommitAuthor{ - Name: String("go-github"), - Email: String("go-github@github.com"), - Date: &date, + Name: Ptr("go-github"), + Email: Ptr("go-github@github.com"), + Date: &Timestamp{date}, }, }) if err == nil { - t.Errorf("Expected error to be returned due to nil key") + t.Error("Expected error to be returned due to nil key") } } func TestGitService_createSignatureMessage_nilAuthor(t *testing.T) { + t.Parallel() _, err := createSignatureMessage(&createCommit{ - Message: String("Commit Message."), + Message: Ptr("Commit Message."), Parents: []string{"p"}, Author: nil, }) if err == nil { - t.Errorf("Expected error to be returned due to nil key") + t.Error("Expected error to be returned due to nil key") } } func TestGitService_createSignatureMessage_withoutTree(t *testing.T) { + t.Parallel() date, _ := time.Parse("Mon Jan 02 15:04:05 2006 -0700", "Thu May 04 00:03:43 2017 +0200") msg, _ := createSignatureMessage(&createCommit{ - Message: String("Commit Message."), + Message: Ptr("Commit Message."), Parents: []string{"p"}, Author: &CommitAuthor{ - Name: String("go-github"), - Email: String("go-github@github.com"), - Date: &date, + Name: Ptr("go-github"), + Email: Ptr("go-github@github.com"), + Date: &Timestamp{date}, }, }) expected := `parent p @@ -336,100 +359,43 @@ committer go-github 1493849023 +0200 Commit Message.` if msg != expected { - t.Errorf("Returned message incorrect. returned %s, want %s", msg, expected) + t.Errorf("Returned message incorrect. returned %v, want %v", msg, expected) } } func TestGitService_createSignatureMessage_withoutCommitter(t *testing.T) { + t.Parallel() date, _ := time.Parse("Mon Jan 02 15:04:05 2006 -0700", "Thu May 04 00:03:43 2017 +0200") msg, _ := createSignatureMessage(&createCommit{ - Message: String("Commit Message."), + Message: Ptr("Commit Message."), Parents: []string{"p"}, Author: &CommitAuthor{ - Name: String("go-github"), - Email: String("go-github@github.com"), - Date: &date, + Name: Ptr("go-github"), + Email: Ptr("go-github@github.com"), + Date: &Timestamp{date}, }, Committer: &CommitAuthor{ - Name: String("foo"), - Email: String("foo@bar.com"), - Date: &date, + Name: Ptr("foo"), + Email: Ptr("foo@example.com"), + Date: &Timestamp{date}, }, }) expected := `parent p author go-github 1493849023 +0200 -committer foo 1493849023 +0200 +committer foo 1493849023 +0200 Commit Message.` if msg != expected { - t.Errorf("Returned message incorrect. returned %s, want %s", msg, expected) + t.Errorf("Returned message incorrect. returned %v, want %v", msg, expected) } } func TestGitService_CreateCommit_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Git.CreateCommit(context.Background(), "%", "%", &Commit{}) + ctx := t.Context() + _, _, err := client.Git.CreateCommit(ctx, "%", "%", Commit{}, nil) testURLParseError(t, err) } - -const testGPGKey = ` ------BEGIN PGP PRIVATE KEY BLOCK----- - -lQOYBFyi1qYBCAD3EPfLJzIt4qkAceUKkhdvfaIvOsBwXbfr5sSu/lkMqL0Wq47+ -iv+SRwOC7zvN8SlB8nPUgs5dbTRCJJfG5MAqTRR7KZRbyq2jBpi4BtmO30Ul/qId -3A18cVUfgVbxH85K9bdnyOxep/Q2NjLjTKmWLkzgmgkfbUmSLuWW9HRXPjYy9B7i -dOFD6GdkN/HwPAaId8ym0TE1mIuSpw8UQHyxusAkK52Pn4h/PgJhLTzbSi1X2eDt -OgzjhbdxTPzKFQfs97dY8y9C7Bt+CqH6Bvr3785LeKdxiUnCjfUJ+WAoJy780ec+ -IVwSpPp1CaEtzu73w6GH5945GELHE8HRe25FABEBAAEAB/9dtx72/VAoXZCTbaBe -iRnAnZwWZCe4t6PbJHa4lhv7FEpdPggIf3r/5lXrpYk+zdpDfI75LgDPKWwoJq83 -r29A3GoHabcvtkp0yzzEmTyO2BvnlJWz09N9v5N1Vt8+qTzb7CZ8hJc8NGMK6TYW -R+8P21In4+XP+OluPMGzp9g1etHScLhQUtF/xcN3JQGkeq4CPX6jUSYlJNeEtuLm -xjBTLBdg8zK5mJ3tolvnS/VhSTdiBeUaYtVt/qxq+fPqdFGHrO5H9ORbt56ahU+f -Ne86sOjQfJZPsx9z8ffP+XhLZPT1ZUGJMI/Vysx9gwDiEnaxrCJ02fO0Dnqsj/o2 -T14lBAD55+KtaS0C0OpHpA/F+XhL3IDcYQOYgu8idBTshr4vv7M+jdZqpECOn72Q -8SZJ+gYMcA9Z07Afnin1DVdtxiMN/tbyOu7e1BE7y77eA+zQw4PjLJPZJMbco7z+ -q9ZnZF3GyRyil6HkKUTfrao8AMtb0allZnqXwpPb5Mza32VqtwQA/RdbG6OIS6og -OpP7zKu4GP4guBk8NrVpVuV5Xz4r8JlL+POt0TadlT93coW/SajLrN/eeUwk6jQw -wrabmIGMarG5mrC4tnXLze5LICJTpOuqCACyFwL6w/ag+c7Qt9t9hvLMDFifcZW/ -mylqY7Z1eVcnbOcFsQG+0LzJBU0qouMEAKkXmJcQ3lJM8yoJuYOvbwexVR+5Y+5v -FNEGPlp3H/fq6ETYWHjMxPOE5dvGbQL8oKWZgkkHEGAKAavEGebM/y/qIPOCAluT -tn1sfx//n6kTMhswpg/3+BciUaJFjwYbIwUH5XD0vFbe9O2VOfTVdo1p19wegVs5 -LMf8rWFWYXtqUgG0IGdvLWdpdGh1YiA8Z28tZ2l0aHViQGdpdGh1Yi5jb20+iQFU -BBMBCAA+FiEELZ6AMqOpBMVblK0uiKTQXVy+MAsFAlyi1qYCGwMFCQPCZwAFCwkI -BwIGFQoJCAsCBBYCAwECHgECF4AACgkQiKTQXVy+MAtEYggA0LRecz71HUjEKXJj -C5Wgds1hZ0q+g3ew7zms4fuascd/2PqT5lItHU3oezdzMOHetSPvPzJILjl7RYcY -pWvoyzEBC5MutlmuzfwUa7qYCiuRDkYRjke8a4o8ijsxc8ANXwulXcI3udjAZdV0 -CKjrjPTyrHFUnPyZyaZp8p2eX62iPYhaXkoBnEiarf0xKtJuT/8IlP5n/redlKYz -GIHG5Svg3uDq9E09BOjFsgemhPyqbf7yrh5aRwDOIdHtn9mNevFPfQ1jO8lI/wbe -4kC6zXM7te0/ZkM06DYRhcaeoYdeyY/gvE+w7wU/+f7Wzqt+LxOMIjKk0oDxZIv9 -praEM50DmARcotamAQgAsiO75WZvjt7BEAzdTvWekWXqBo4NOes2UgzSYToVs6xW -8iXnE+mpDS7GHtNQLU6oeC0vizUjCwBfU+qGqw1JjI3I1pwv7xRqBIlA6f5ancVK -KiMx+/HxasbBrbav8DmZT8E8VaJhYM614Kav91W8YoqK5YXmP/A+OwwhkVEGo8v3 -Iy7mnJPMSjNiNTpiDgc5wvRiTan+uf+AtNPUS0k0fbrTZWosbrSmBymhrEy8stMj -rG2wZX5aRY7AXrQXoIXedqvP3kW/nqd0wvuiD11ZZWvoawjZRRVsT27DED0x2+o6 -aAEKrSLj8LlWvGVkD/jP9lSkC81uwGgD5VIMeXv6EQARAQABAAf7BHef8SdJ+ee9 -KLVh4WaIdPX80fBDBaZP5OvcZMLLo4dZYNYxfs7XxfRb1I8RDinQUL81V4TcHZ0D -Rvv1J5n8M7GkjTk6fIDjDb0RayzNQfKeIwNh8AMHvllApyYTMG+JWDYs2KrrTT2x -0vHrLMUyJbh6tjnO5eCU9u8dcmL5Syc6DzGUvDl6ZdJxlHEEJOwMlVCwQn5LQDVI -t0KEXigqs7eDCpTduJeAI7oA96s/8LwdlG5t6q9vbkEjl1XpR5FfKvJcZbd7Kmk9 -6R0EdbH6Ffe8qAp8lGmjx+91gqeL7jyl500H4gK/ybzlxQczIsbQ7WcZTPEnROIX -tCFWh6puvwQAyV6ygcatz+1BfCfgxWNYFXyowwOGSP9Nma+/aDVdeRCjZ69Is0lz -GV0NNqh7hpaoVbXS9Vc3sFOwBr5ZyKQaf07BoCDW+XJtvPyyZNLb004smtB5uHCf -uWDBpQ9erlrpSkOLgifbzfkYHdSvhc2ws9Tgab7Mk7P/ExOZjnUJPOcEAOJ3q/2/ -0wqRnkSelgkWwUmZ+hFIBz6lgWS3KTJs6Qc5WBnXono+EOoqhFxsiRM4lewExxHM -kPIcxb+0hiNz8hJkWOHEdgkXNim9Q08J0HPz6owtlD/rtmOi2+7d5BukbY/3JEXs -r2bjqbXXIE7heytIn/dQv7aEDyDqexiJKnpHBACQItjuYlewLt94NMNdGcwxmKdJ -bfaoIQz1h8fX5uSGKU+hXatI6sltD9PrhwwhdqJNcQ0K1dRkm24olO4I/sJwactI -G3r1UTq6BMV94eIyS/zZH5xChlOUavy9PrgU3kAK21bdmAFuNwbHnN34BBUk9J6f -IIxEZUOxw2CrKhsubUOuiQE8BBgBCAAmFiEELZ6AMqOpBMVblK0uiKTQXVy+MAsF -Alyi1qYCGwwFCQPCZwAACgkQiKTQXVy+MAstJAf/Tm2hfagVjzgJ5pFHmpP+fYxp -8dIPZLonP5HW12iaSOXThtvWBY578Cb9RmU+WkHyPXg8SyshW7aco4HrUDk+Qmyi -f9BvHS5RsLbyPlhgCqNkn+3QS62fZiIlbHLrQ/6iHXkgLV04Fnj+F4v8YYpOI9nY -NFc5iWm0zZRcLiRKZk1up8SCngyolcjVuTuCXDKyAUX1jRqDu7tlN0qVH0CYDGch -BqTKXNkzAvV+CKOyaUILSBBWdef+cxVrDCJuuC3894x3G1FjJycOy0m9PArvGtSG -g7/0Bp9oLXwiHzFoUMDvx+WlPnPHQNcufmQXUNdZvg+Ad4/unEU81EGDBDz3Eg== -=VFSn ------END PGP PRIVATE KEY BLOCK-----` diff --git a/github/git_refs.go b/github/git_refs.go index b51bcbf1deb..81561a1f8a2 100644 --- a/github/git_refs.go +++ b/github/git_refs.go @@ -7,7 +7,6 @@ package github import ( "context" - "encoding/json" "errors" "fmt" "net/url" @@ -16,6 +15,7 @@ import ( // Reference represents a GitHub reference. type Reference struct { + // The name of the fully qualified reference, i.e.: `refs/heads/master`. Ref *string `json:"ref"` URL *string `json:"url"` Object *GitObject `json:"object"` @@ -37,123 +37,70 @@ func (o GitObject) String() string { return Stringify(o) } -// createRefRequest represents the payload for creating a reference. -type createRefRequest struct { - Ref *string `json:"ref"` - SHA *string `json:"sha"` +// CreateRef represents the payload for creating a reference. +type CreateRef struct { + Ref string `json:"ref"` + SHA string `json:"sha"` } -// updateRefRequest represents the payload for updating a reference. -type updateRefRequest struct { - SHA *string `json:"sha"` - Force *bool `json:"force"` +// UpdateRef represents the payload for updating a reference. +type UpdateRef struct { + SHA string `json:"sha"` + Force *bool `json:"force,omitempty"` } -// GetRef fetches a single Reference object for a given Git ref. -// If there is no exact match, GetRef will return an error. +// GetRef fetches a single reference in a repository. +// The ref must be formatted as `heads/` for branches and `tags/` for tags. // -// Note: The GitHub API can return multiple matches. -// If you wish to use this functionality please use the GetRefs() method. +// GitHub API docs: https://docs.github.com/rest/git/refs?apiVersion=2022-11-28#get-a-reference // -// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference -func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref string) (*Reference, *Response, error) { +//meta:operation GET /repos/{owner}/{repo}/git/ref/{ref} +func (s *GitService) GetRef(ctx context.Context, owner, repo, ref string) (*Reference, *Response, error) { ref = strings.TrimPrefix(ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref)) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/git/ref/%v", owner, repo, refURLEscape(ref)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - r := new(Reference) - resp, err := s.client.Do(ctx, req, r) - if _, ok := err.(*json.UnmarshalTypeError); ok { - // Multiple refs, means there wasn't an exact match. - return nil, resp, errors.New("multiple matches found for this ref") - } else if _, ok := err.(*ErrorResponse); ok && resp.StatusCode == 404 { - // No ref, there was no match for the ref - return nil, resp, errors.New("no match found for this ref") - } else if err != nil { + var r *Reference + resp, err := s.client.Do(req, &r) + if err != nil { return nil, resp, err } return r, resp, nil } -// GetRefs fetches a slice of Reference objects for a given Git ref. -// If there is an exact match, only that ref is returned. -// If there is no exact match, GitHub returns all refs that start with ref. -// If returned error is nil, there will be at least 1 ref returned. -// For example: -// -// "heads/featureA" -> ["refs/heads/featureA"] // Exact match, single ref is returned. -// "heads/feature" -> ["refs/heads/featureA", "refs/heads/featureB"] // All refs that start with ref. -// "heads/notexist" -> [] // Returns an error. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference -func (s *GitService) GetRefs(ctx context.Context, owner string, repo string, ref string) ([]*Reference, *Response, error) { - ref = strings.TrimPrefix(ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref)) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var rawJSON json.RawMessage - resp, err := s.client.Do(ctx, req, &rawJSON) - if err != nil { - return nil, resp, err - } - - // Prioritize the most common case: a single returned ref. - r := new(Reference) - singleUnmarshalError := json.Unmarshal(rawJSON, r) - if singleUnmarshalError == nil { - return []*Reference{r}, resp, nil +// refURLEscape escapes every path segment of the given ref. Those must +// not contain escaped "/" - as "%2F" - or github will not recognize it. +func refURLEscape(ref string) string { + parts := strings.Split(ref, "/") + for i, s := range parts { + parts[i] = url.PathEscape(s) } - - // Attempt to unmarshal multiple refs. - var rs []*Reference - multipleUnmarshalError := json.Unmarshal(rawJSON, &rs) - if multipleUnmarshalError == nil { - if len(rs) == 0 { - return nil, resp, fmt.Errorf("unexpected response from GitHub API: an array of refs with length 0") - } - return rs, resp, nil - } - - return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", singleUnmarshalError, multipleUnmarshalError) -} - -// ReferenceListOptions specifies optional parameters to the -// GitService.ListRefs method. -type ReferenceListOptions struct { - Type string `url:"-"` - - ListOptions + return strings.Join(parts, "/") } -// ListRefs lists all refs in a repository. +// ListMatchingRefs lists references in a repository that match a supplied ref. +// The ref in the URL must be formatted as `heads/` for branches and `tags/` for tags. +// If the ref doesn't exist in the repository, but existing refs start with ref, they will be returned as an array. +// Use an empty ref to list all references. // -// GitHub API docs: https://developer.github.com/v3/git/refs/#get-all-references -func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) { - var u string - if opt != nil && opt.Type != "" { - u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type) - } else { - u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } +// GitHub API docs: https://docs.github.com/rest/git/refs?apiVersion=2022-11-28#list-matching-references +// +//meta:operation GET /repos/{owner}/{repo}/git/matching-refs/{ref} +func (s *GitService) ListMatchingRefs(ctx context.Context, owner, repo, ref string) ([]*Reference, *Response, error) { + ref = strings.TrimPrefix(ref, "refs/") // API expects no "refs/" prefix + u := fmt.Sprintf("repos/%v/%v/git/matching-refs/%v", owner, repo, refURLEscape(ref)) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var rs []*Reference - resp, err := s.client.Do(ctx, req, &rs) + resp, err := s.client.Do(req, &rs) if err != nil { return nil, resp, err } @@ -163,20 +110,29 @@ func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *Refe // CreateRef creates a new ref in a repository. // -// GitHub API docs: https://developer.github.com/v3/git/refs/#create-a-reference -func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, ref *Reference) (*Reference, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/refs?apiVersion=2022-11-28#create-a-reference +// +//meta:operation POST /repos/{owner}/{repo}/git/refs +func (s *GitService) CreateRef(ctx context.Context, owner, repo string, body CreateRef) (*Reference, *Response, error) { + if body.Ref == "" { + return nil, nil, errors.New("ref must be provided") + } + + if body.SHA == "" { + return nil, nil, errors.New("sha must be provided") + } + + // ensure the 'refs/' prefix is present + body.Ref = "refs/" + strings.TrimPrefix(body.Ref, "refs/") + u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) - req, err := s.client.NewRequest("POST", u, &createRefRequest{ - // back-compat with previous behavior that didn't require 'refs/' prefix - Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")), - SHA: ref.Object.SHA, - }) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - r := new(Reference) - resp, err := s.client.Do(ctx, req, r) + var r *Reference + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -186,20 +142,27 @@ func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, r // UpdateRef updates an existing ref in a repository. // -// GitHub API docs: https://developer.github.com/v3/git/refs/#update-a-reference -func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) { - refPath := strings.TrimPrefix(*ref.Ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath) - req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{ - SHA: ref.Object.SHA, - Force: &force, - }) +// GitHub API docs: https://docs.github.com/rest/git/refs?apiVersion=2022-11-28#update-a-reference +// +//meta:operation PATCH /repos/{owner}/{repo}/git/refs/{ref} +func (s *GitService) UpdateRef(ctx context.Context, owner, repo, ref string, body UpdateRef) (*Reference, *Response, error) { + if ref == "" { + return nil, nil, errors.New("ref must be provided") + } + + if body.SHA == "" { + return nil, nil, errors.New("sha must be provided") + } + + refPath := strings.TrimPrefix(ref, "refs/") + u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refURLEscape(refPath)) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - r := new(Reference) - resp, err := s.client.Do(ctx, req, r) + var r *Reference + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -209,14 +172,16 @@ func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, r // DeleteRef deletes a ref from a repository. // -// GitHub API docs: https://developer.github.com/v3/git/refs/#delete-a-reference -func (s *GitService) DeleteRef(ctx context.Context, owner string, repo string, ref string) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/refs?apiVersion=2022-11-28#delete-a-reference +// +//meta:operation DELETE /repos/{owner}/{repo}/git/refs/{ref} +func (s *GitService) DeleteRef(ctx context.Context, owner, repo, ref string) (*Response, error) { ref = strings.TrimPrefix(ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref)) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refURLEscape(ref)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/git_refs_test.go b/github/git_refs_test.go index 6c8a1d87951..f3dedaba172 100644 --- a/github/git_refs_test.go +++ b/github/git_refs_test.go @@ -6,19 +6,19 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" + "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestGitService_GetRef_singleRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/git/ref/heads/b", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, ` { @@ -32,133 +32,141 @@ func TestGitService_GetRef_singleRef(t *testing.T) { }`) }) - ref, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b") + ctx := t.Context() + ref, _, err := client.Git.GetRef(ctx, "o", "r", "refs/heads/b") if err != nil { t.Fatalf("Git.GetRef returned error: %v", err) } want := &Reference{ - Ref: String("refs/heads/b"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Ref: Ptr("refs/heads/b"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/b"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, } - if !reflect.DeepEqual(ref, want) { + if !cmp.Equal(ref, want) { t.Errorf("Git.GetRef returned %+v, want %+v", ref, want) } // without 'refs/' prefix - if _, _, err := client.Git.GetRef(context.Background(), "o", "r", "heads/b"); err != nil { + if _, _, err := client.Git.GetRef(ctx, "o", "r", "heads/b"); err != nil { t.Errorf("Git.GetRef returned error: %v", err) } + + const methodName = "GetRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetRef(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetRef(ctx, "o", "r", "refs/heads/b") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_GetRef_noRefs(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) - fmt.Fprint(w, "[]") }) - _, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b") - want := "no match found for this ref" - if err.Error() != want { - t.Errorf("Git.GetRef returned %+v, want %+v", err, want) + ctx := t.Context() + ref, resp, err := client.Git.GetRef(ctx, "o", "r", "refs/heads/b") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Git.GetRef returned status %v, want %v", got, want) } + if ref != nil { + t.Errorf("Git.GetRef return %+v, want nil", ref) + } + + const methodName = "GetRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetRef(ctx, "o", "r", "refs/heads/b") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetRef(ctx, "o", "r", "refs/heads/b") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestGitService_GetRef_multipleRefs(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestGitService_ListMatchingRefs_singleRef(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/git/matching-refs/heads/b", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, ` [ { - "ref": "refs/heads/booger", - "url": "https://api.github.com/repos/o/r/git/refs/heads/booger", - "object": { - "type": "commit", - "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", - "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" - } - }, - { - "ref": "refs/heads/bandsaw", - "url": "https://api.github.com/repos/o/r/git/refs/heads/bandsaw", + "ref": "refs/heads/b", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b", "object": { "type": "commit", - "sha": "612077ae6dffb4d2fbd8ce0cccaa58893b07b5ac", - "url": "https://api.github.com/repos/o/r/git/commits/612077ae6dffb4d2fbd8ce0cccaa58893b07b5ac" + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" } } - ] - `) - }) - - _, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b") - want := "multiple matches found for this ref" - if err.Error() != want { - t.Errorf("Git.GetRef returned %+v, want %+v", err, want) - } - -} - -func TestGitService_GetRefs_singleRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, ` - { - "ref": "refs/heads/b", - "url": "https://api.github.com/repos/o/r/git/refs/heads/b", - "object": { - "type": "commit", - "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", - "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" - } - }`) + ]`) }) - refs, _, err := client.Git.GetRefs(context.Background(), "o", "r", "refs/heads/b") + ctx := t.Context() + refs, _, err := client.Git.ListMatchingRefs(ctx, "o", "r", "heads/b") if err != nil { - t.Fatalf("Git.GetRefs returned error: %v", err) + t.Fatalf("Git.ListMatchingRefs returned error: %v", err) } ref := refs[0] want := &Reference{ - Ref: String("refs/heads/b"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Ref: Ptr("refs/heads/b"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/b"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, } - if !reflect.DeepEqual(ref, want) { - t.Errorf("Git.GetRefs returned %+v, want %+v", ref, want) + if !cmp.Equal(ref, want) { + t.Errorf("Git.ListMatchingRefs returned %+v, want %+v", ref, want) } - // without 'refs/' prefix - if _, _, err := client.Git.GetRefs(context.Background(), "o", "r", "heads/b"); err != nil { - t.Errorf("Git.GetRefs returned error: %v", err) - } + const methodName = "ListMatchingRefs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.ListMatchingRefs(ctx, "\n", "\n", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.ListMatchingRefs(ctx, "o", "r", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestGitService_GetRefs_multipleRefs(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestGitService_ListMatchingRefs_multipleRefs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/git/matching-refs/heads/b", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, ` [ @@ -184,48 +192,79 @@ func TestGitService_GetRefs_multipleRefs(t *testing.T) { `) }) - refs, _, err := client.Git.GetRefs(context.Background(), "o", "r", "refs/heads/b") + ctx := t.Context() + refs, _, err := client.Git.ListMatchingRefs(ctx, "o", "r", "heads/b") if err != nil { - t.Errorf("Git.GetRefs returned error: %v", err) + t.Errorf("Git.ListMatchingRefs returned error: %v", err) } want := &Reference{ - Ref: String("refs/heads/booger"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/booger"), + Ref: Ptr("refs/heads/booger"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/booger"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, } - if !reflect.DeepEqual(refs[0], want) { - t.Errorf("Git.GetRefs returned %+v, want %+v", refs[0], want) + if !cmp.Equal(refs[0], want) { + t.Errorf("Git.ListMatchingRefs returned %+v, want %+v", refs[0], want) } + + const methodName = "ListMatchingRefs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.ListMatchingRefs(ctx, "\n", "\n", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.ListMatchingRefs(ctx, "o", "r", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -// TestGitService_GetRefs_noRefs tests for behaviour resulting from an unexpected GH response. This should never actually happen. -func TestGitService_GetRefs_noRefs(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestGitService_ListMatchingRefs_noRefs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/git/matching-refs/heads/b", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, "[]") }) - _, _, err := client.Git.GetRefs(context.Background(), "o", "r", "refs/heads/b") - want := "unexpected response from GitHub API: an array of refs with length 0" - if err.Error() != want { - t.Errorf("Git.GetRefs returned %+v, want %+v", err, want) + ctx := t.Context() + refs, _, err := client.Git.ListMatchingRefs(ctx, "o", "r", "heads/b") + if err != nil { + t.Errorf("Git.ListMatchingRefs returned error: %v", err) + } + + if len(refs) != 0 { + t.Errorf("Git.ListMatchingRefs returned %+v, want an empty slice", refs) } + const methodName = "ListMatchingRefs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.ListMatchingRefs(ctx, "\n", "\n", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.ListMatchingRefs(ctx, "o", "r", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestGitService_ListRefs(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestGitService_ListMatchingRefs_allRefs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/git/matching-refs/", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, ` [ @@ -233,92 +272,80 @@ func TestGitService_ListRefs(t *testing.T) { "ref": "refs/heads/branchA", "url": "https://api.github.com/repos/o/r/git/refs/heads/branchA", "object": { - "type": "commit", - "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", - "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" } }, { "ref": "refs/heads/branchB", "url": "https://api.github.com/repos/o/r/git/refs/heads/branchB", "object": { - "type": "commit", - "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", - "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" } } ]`) }) - refs, _, err := client.Git.ListRefs(context.Background(), "o", "r", nil) + ctx := t.Context() + refs, _, err := client.Git.ListMatchingRefs(ctx, "o", "r", "") if err != nil { - t.Errorf("Git.ListRefs returned error: %v", err) + t.Errorf("Git.ListMatchingRefs returned error: %v", err) } want := []*Reference{ { - Ref: String("refs/heads/branchA"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchA"), + Ref: Ptr("refs/heads/branchA"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/branchA"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, }, { - Ref: String("refs/heads/branchB"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchB"), + Ref: Ptr("refs/heads/branchB"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/branchB"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, }, } - if !reflect.DeepEqual(refs, want) { - t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) + if !cmp.Equal(refs, want) { + t.Errorf("Git.ListMatchingRefs returned %+v, want %+v", refs, want) } -} -func TestGitService_ListRefs_options(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/git/refs/t", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{"page": "2"}) - fmt.Fprint(w, `[{"ref": "r"}]`) + const methodName = "ListMatchingRefs" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.ListMatchingRefs(ctx, "\n", "\n", "") + return err }) - opt := &ReferenceListOptions{Type: "t", ListOptions: ListOptions{Page: 2}} - refs, _, err := client.Git.ListRefs(context.Background(), "o", "r", opt) - if err != nil { - t.Errorf("Git.ListRefs returned error: %v", err) - } - - want := []*Reference{{Ref: String("r")}} - if !reflect.DeepEqual(refs, want) { - t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.ListMatchingRefs(ctx, "o", "r", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - args := &createRefRequest{ - Ref: String("refs/heads/b"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), + args := CreateRef{ + Ref: "refs/heads/b", + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", } mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { - v := new(createRefRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, args) { - t.Errorf("Request body = %+v, want %+v", v, args) - } + testJSONBody(t, r, args) fmt.Fprint(w, ` { "ref": "refs/heads/b", @@ -331,58 +358,74 @@ func TestGitService_CreateRef(t *testing.T) { }`) }) - ref, _, err := client.Git.CreateRef(context.Background(), "o", "r", &Reference{ - Ref: String("refs/heads/b"), - Object: &GitObject{ - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - }, + ctx := t.Context() + ref, _, err := client.Git.CreateRef(ctx, "o", "r", CreateRef{ + Ref: "refs/heads/b", + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", }) if err != nil { t.Errorf("Git.CreateRef returned error: %v", err) } want := &Reference{ - Ref: String("refs/heads/b"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Ref: Ptr("refs/heads/b"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/b"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, } - if !reflect.DeepEqual(ref, want) { + if !cmp.Equal(ref, want) { t.Errorf("Git.CreateRef returned %+v, want %+v", ref, want) } // without 'refs/' prefix - _, _, err = client.Git.CreateRef(context.Background(), "o", "r", &Reference{ - Ref: String("heads/b"), - Object: &GitObject{ - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - }, + _, _, err = client.Git.CreateRef(ctx, "o", "r", CreateRef{ + Ref: "heads/b", + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", }) if err != nil { t.Errorf("Git.CreateRef returned error: %v", err) } + + const methodName = "CreateRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateRef(ctx, "o", "r", CreateRef{Ref: ""}) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateRef(ctx, "\n", "\n", CreateRef{ + Ref: "refs/heads/b", + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + }) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateRef(ctx, "o", "r", CreateRef{ + Ref: "refs/heads/b", + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + }) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_UpdateRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - args := &updateRefRequest{ - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - Force: Bool(true), + args := UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), } mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { - v := new(updateRefRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, args) { - t.Errorf("Request body = %+v, want %+v", v, args) - } + testJSONBody(t, r, args) fmt.Fprint(w, ` { "ref": "refs/heads/b", @@ -395,52 +438,181 @@ func TestGitService_UpdateRef(t *testing.T) { }`) }) - ref, _, err := client.Git.UpdateRef(context.Background(), "o", "r", &Reference{ - Ref: String("refs/heads/b"), - Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, - }, true) + ctx := t.Context() + ref, _, err := client.Git.UpdateRef(ctx, "o", "r", "refs/heads/b", UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), + }) if err != nil { t.Errorf("Git.UpdateRef returned error: %v", err) } want := &Reference{ - Ref: String("refs/heads/b"), - URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), + Ref: Ptr("refs/heads/b"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/b"), Object: &GitObject{ - Type: String("commit"), - SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), - URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), }, } - if !reflect.DeepEqual(ref, want) { + if !cmp.Equal(ref, want) { t.Errorf("Git.UpdateRef returned %+v, want %+v", ref, want) } // without 'refs/' prefix - _, _, err = client.Git.UpdateRef(context.Background(), "o", "r", &Reference{ - Ref: String("heads/b"), - Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, - }, true) + _, _, err = client.Git.UpdateRef(ctx, "o", "r", "heads/b", UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), + }) if err != nil { t.Errorf("Git.UpdateRef returned error: %v", err) } + + const methodName = "UpdateRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.UpdateRef(ctx, "o", "r", "", UpdateRef{SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd"}) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.UpdateRef(ctx, "o", "r", "refs/heads/b", UpdateRef{SHA: ""}) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.UpdateRef(ctx, "\n", "\n", "refs/heads/b", UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), + }) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.UpdateRef(ctx, "o", "r", "refs/heads/b", UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), + }) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_DeleteRef(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Git.DeleteRef(context.Background(), "o", "r", "refs/heads/b") + ctx := t.Context() + _, err := client.Git.DeleteRef(ctx, "o", "r", "refs/heads/b") if err != nil { t.Errorf("Git.DeleteRef returned error: %v", err) } // without 'refs/' prefix - if _, err := client.Git.DeleteRef(context.Background(), "o", "r", "heads/b"); err != nil { + if _, err := client.Git.DeleteRef(ctx, "o", "r", "heads/b"); err != nil { t.Errorf("Git.DeleteRef returned error: %v", err) } + + const methodName = "DeleteRef" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Git.DeleteRef(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Git.DeleteRef(ctx, "o", "r", "refs/heads/b") + }) +} + +func TestGitService_GetRef_pathEscape(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/git/ref/heads/b", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + if strings.Contains(r.URL.RawPath, "%2F") { + t.Errorf("RawPath still contains escaped / as %%2F: %v", r.URL.RawPath) + } + fmt.Fprint(w, ` + { + "ref": "refs/heads/b", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`) + }) + + ctx := t.Context() + _, _, err := client.Git.GetRef(ctx, "o", "r", "refs/heads/b") + if err != nil { + t.Fatalf("Git.GetRef returned error: %v", err) + } + + const methodName = "GetRef" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetRef(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetRef(ctx, "o", "r", "refs/heads/b") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestGitService_UpdateRef_pathEscape(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + args := UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), + } + + mux.HandleFunc("/repos/o/r/git/refs/heads/b#1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, args) + fmt.Fprint(w, ` + { + "ref": "refs/heads/b#1", + "url": "https://api.github.com/repos/o/r/git/refs/heads/b%231", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`) + }) + + ctx := t.Context() + ref, _, err := client.Git.UpdateRef(ctx, "o", "r", "refs/heads/b#1", UpdateRef{ + SHA: "aa218f56b14c9653891f9e74264a383fa43fefbd", + Force: Ptr(true), + }) + if err != nil { + t.Errorf("Git.UpdateRef returned error: %v", err) + } + + want := &Reference{ + Ref: Ptr("refs/heads/b#1"), + URL: Ptr("https://api.github.com/repos/o/r/git/refs/heads/b%231"), + Object: &GitObject{ + Type: Ptr("commit"), + SHA: Ptr("aa218f56b14c9653891f9e74264a383fa43fefbd"), + URL: Ptr("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), + }, + } + if !cmp.Equal(ref, want) { + t.Errorf("Git.UpdateRef returned %+v, want %+v", ref, want) + } } diff --git a/github/git_tags.go b/github/git_tags.go index abdbde6821b..8c90b7f9d82 100644 --- a/github/git_tags.go +++ b/github/git_tags.go @@ -22,55 +22,54 @@ type Tag struct { NodeID *string `json:"node_id,omitempty"` } -// createTagRequest represents the body of a CreateTag request. This is mostly -// identical to Tag with the exception that the object SHA and Type are -// top-level fields, rather than being nested inside a JSON object. -type createTagRequest struct { - Tag *string `json:"tag,omitempty"` - Message *string `json:"message,omitempty"` - Object *string `json:"object,omitempty"` - Type *string `json:"type,omitempty"` +// CreateTag represents the payload for creating a tag. +type CreateTag struct { + Tag string `json:"tag,omitempty"` + Message string `json:"message,omitempty"` + Object string `json:"object,omitempty"` + Type string `json:"type,omitempty"` Tagger *CommitAuthor `json:"tagger,omitempty"` } // GetTag fetches a tag from a repo given a SHA. // -// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag -func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/tags?apiVersion=2022-11-28#get-a-tag +// +//meta:operation GET /repos/{owner}/{repo}/git/tags/{tag_sha} +func (s *GitService) GetTag(ctx context.Context, owner, repo, sha string) (*Tag, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - tag := new(Tag) - resp, err := s.client.Do(ctx, req, tag) - return tag, resp, err + var tag *Tag + resp, err := s.client.Do(req, &tag) + if err != nil { + return nil, resp, err + } + + return tag, resp, nil } // CreateTag creates a tag object. // -// GitHub API docs: https://developer.github.com/v3/git/tags/#create-a-tag-object -func (s *GitService) CreateTag(ctx context.Context, owner string, repo string, tag *Tag) (*Tag, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/tags?apiVersion=2022-11-28#create-a-tag-object +// +//meta:operation POST /repos/{owner}/{repo}/git/tags +func (s *GitService) CreateTag(ctx context.Context, owner, repo string, body CreateTag) (*Tag, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo) - // convert Tag into a createTagRequest - tagRequest := &createTagRequest{ - Tag: tag.Tag, - Message: tag.Message, - Tagger: tag.Tagger, - } - if tag.Object != nil { - tagRequest.Object = tag.Object.SHA - tagRequest.Type = tag.Object.Type + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err } - req, err := s.client.NewRequest("POST", u, tagRequest) + var t *Tag + resp, err := s.client.Do(req, &t) if err != nil { - return nil, nil, err + return nil, resp, err } - t := new(Tag) - resp, err := s.client.Do(ctx, req, t) - return t, resp, err + return t, resp, nil } diff --git a/github/git_tags_test.go b/github/git_tags_test.go index 96239525bbc..2ff9de0dc27 100644 --- a/github/git_tags_test.go +++ b/github/git_tags_test.go @@ -6,62 +6,87 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestGitService_GetTag(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/git/tags/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"tag": "t"}`) }) - tag, _, err := client.Git.GetTag(context.Background(), "o", "r", "s") + ctx := t.Context() + tag, _, err := client.Git.GetTag(ctx, "o", "r", "s") if err != nil { t.Errorf("Git.GetTag returned error: %v", err) } - want := &Tag{Tag: String("t")} - if !reflect.DeepEqual(tag, want) { + want := &Tag{Tag: Ptr("t")} + if !cmp.Equal(tag, want) { t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) } + + const methodName = "GetTag" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetTag(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetTag(ctx, "o", "r", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateTag(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &createTagRequest{Tag: String("t"), Object: String("s")} + inputTag := CreateTag{ + Tag: "t", + Object: "s", + Type: "commit", + Message: "test message", + } mux.HandleFunc("/repos/o/r/git/tags", func(w http.ResponseWriter, r *http.Request) { - v := new(createTagRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, inputTag) fmt.Fprint(w, `{"tag": "t"}`) }) - tag, _, err := client.Git.CreateTag(context.Background(), "o", "r", &Tag{ - Tag: input.Tag, - Object: &GitObject{SHA: input.Object}, - }) + ctx := t.Context() + tag, _, err := client.Git.CreateTag(ctx, "o", "r", inputTag) if err != nil { t.Errorf("Git.CreateTag returned error: %v", err) } - want := &Tag{Tag: String("t")} - if !reflect.DeepEqual(tag, want) { - t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) + want := &Tag{Tag: Ptr("t")} + if !cmp.Equal(tag, want) { + t.Errorf("Git.CreateTag returned %+v, want %+v", tag, want) } + + const methodName = "CreateTag" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateTag(ctx, "\n", "\n", inputTag) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateTag(ctx, "o", "r", inputTag) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/git_trees.go b/github/git_trees.go index 4bc2913541a..2e4b9dc2103 100644 --- a/github/git_trees.go +++ b/github/git_trees.go @@ -7,13 +7,14 @@ package github import ( "context" + "encoding/json" "fmt" ) // Tree represents a GitHub tree. type Tree struct { - SHA *string `json:"sha,omitempty"` - Entries []TreeEntry `json:"tree,omitempty"` + SHA *string `json:"sha,omitempty"` + Entries []*TreeEntry `json:"tree,omitempty"` // Truncated is true if the number of items in the tree // exceeded GitHub's maximum limit and the Entries were truncated @@ -29,6 +30,13 @@ func (t Tree) String() string { // TreeEntry represents the contents of a tree structure. TreeEntry can // represent either a blob, a commit (in the case of a submodule), or another // tree. +// +// When used with [GitService.CreateTree], set Content for small text files, +// or set SHA to reference an existing blob (use [GitService.CreateBlob] for +// binary files or large content). To delete an entry, set both Content and SHA +// to nil; the entry will be serialized with `"sha": null` which the API interprets +// as a deletion. When deleting, the Type and Mode fields are ignored; only Path +// is required. type TreeEntry struct { SHA *string `json:"sha,omitempty"` Path *string `json:"path,omitempty"` @@ -43,22 +51,72 @@ func (t TreeEntry) String() string { return Stringify(t) } +// treeEntryWithFileDelete is used internally to delete a file whose +// Content and SHA fields are empty. It does this by removing the "omitempty" +// tag modifier on the SHA field which causes the GitHub API to receive +// {"sha":null} and thereby delete the file. +type treeEntryWithFileDelete struct { + SHA *string `json:"sha"` + Path *string `json:"path,omitempty"` + Mode *string `json:"mode,omitempty"` + Type *string `json:"type,omitempty"` + Size *int `json:"size,omitempty"` + Content *string `json:"content,omitempty"` + URL *string `json:"url,omitempty"` +} + +// MarshalJSON implements the json.Marshaler interface. +func (t TreeEntry) MarshalJSON() ([]byte, error) { + if t.SHA == nil && t.Content == nil { + return json.Marshal(struct { + SHA *string `json:"sha"` + Path *string `json:"path,omitempty"` + Mode *string `json:"mode,omitempty"` + Type *string `json:"type,omitempty"` + }{ + nil, + t.Path, + t.Mode, + t.Type, + }) + } + return json.Marshal(struct { + SHA *string `json:"sha,omitempty"` + Path *string `json:"path,omitempty"` + Mode *string `json:"mode,omitempty"` + Type *string `json:"type,omitempty"` + Size *int `json:"size,omitempty"` + Content *string `json:"content,omitempty"` + URL *string `json:"url,omitempty"` + }{ + SHA: t.SHA, + Path: t.Path, + Mode: t.Mode, + Type: t.Type, + Size: t.Size, + Content: t.Content, + URL: t.URL, + }) +} + // GetTree fetches the Tree object for a given sha hash from a repository. // -// GitHub API docs: https://developer.github.com/v3/git/trees/#get-a-tree -func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/git/trees?apiVersion=2022-11-28#get-a-tree +// +//meta:operation GET /repos/{owner}/{repo}/git/trees/{tree_sha} +func (s *GitService) GetTree(ctx context.Context, owner, repo, sha string, recursive bool) (*Tree, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha) if recursive { u += "?recursive=1" } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - t := new(Tree) - resp, err := s.client.Do(ctx, req, t) + var t *Tree + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -68,29 +126,52 @@ func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha // createTree represents the body of a CreateTree request. type createTree struct { - BaseTree string `json:"base_tree,omitempty"` - Entries []TreeEntry `json:"tree"` + BaseTree string `json:"base_tree,omitempty"` + Entries []any `json:"tree"` } // CreateTree creates a new tree in a repository. If both a tree and a nested // path modifying that tree are specified, it will overwrite the contents of // that tree with the new path contents and write a new tree out. // -// GitHub API docs: https://developer.github.com/v3/git/trees/#create-a-tree -func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { +// When baseTree is provided, entries are merged with that tree: paths not +// mentioned in entries are preserved from the base tree. If the same path +// appears multiple times in entries, the last entry wins. To delete an entry, +// include a [TreeEntry] with the path and both SHA and Content set to nil. +// Entire directories can be deleted this way. +// +// GitHub API docs: https://docs.github.com/rest/git/trees?apiVersion=2022-11-28#create-a-tree +// +//meta:operation POST /repos/{owner}/{repo}/git/trees +func (s *GitService) CreateTree(ctx context.Context, owner, repo, baseTree string, entries []*TreeEntry) (*Tree, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) + newEntries := make([]any, 0, len(entries)) + for _, entry := range entries { + if entry.Content == nil && entry.SHA == nil { + newEntries = append(newEntries, treeEntryWithFileDelete{ + Path: entry.Path, + Mode: entry.Mode, + Type: entry.Type, + Size: entry.Size, + URL: entry.URL, + }) + continue + } + newEntries = append(newEntries, entry) + } + body := &createTree{ BaseTree: baseTree, - Entries: entries, + Entries: newEntries, } - req, err := s.client.NewRequest("POST", u, body) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - t := new(Tree) - resp, err := s.client.Do(ctx, req, t) + var t *Tree + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } diff --git a/github/git_trees_test.go b/github/git_trees_test.go index 1f7024fc052..035c648e30c 100644 --- a/github/git_trees_test.go +++ b/github/git_trees_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestGitService_GetTree(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/git/trees/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,59 +26,72 @@ func TestGitService_GetTree(t *testing.T) { }`) }) - tree, _, err := client.Git.GetTree(context.Background(), "o", "r", "s", true) + ctx := t.Context() + tree, _, err := client.Git.GetTree(ctx, "o", "r", "s", true) if err != nil { t.Errorf("Git.GetTree returned error: %v", err) } want := Tree{ - SHA: String("s"), - Entries: []TreeEntry{ + SHA: Ptr("s"), + Entries: []*TreeEntry{ { - Type: String("blob"), + Type: Ptr("blob"), }, }, - Truncated: Bool(true), + Truncated: Ptr(true), } - if !reflect.DeepEqual(*tree, want) { - t.Errorf("Tree.Get returned %+v, want %+v", *tree, want) + if !cmp.Equal(*tree, want) { + t.Errorf("Git.GetTree returned %+v, want %+v", *tree, want) } + + const methodName = "GetTree" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.GetTree(ctx, "\n", "\n", "\n", true) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.GetTree(ctx, "o", "r", "s", true) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_GetTree_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Git.GetTree(context.Background(), "%", "%", "%", false) + ctx := t.Context() + _, _, err := client.Git.GetTree(ctx, "%", "%", "%", false) testURLParseError(t, err) } func TestGitService_CreateTree(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := []TreeEntry{ + input := []*TreeEntry{ { - Path: String("file.rb"), - Mode: String("100644"), - Type: String("blob"), - SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), + Path: Ptr("file.rb"), + Mode: Ptr("100644"), + Type: Ptr("blob"), + SHA: Ptr("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), }, } mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { - v := new(createTree) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - - want := &createTree{ - BaseTree: "b", - Entries: input, - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) - } + testJSONBody(t, r, createTree{BaseTree: "b", Entries: []any{ + map[string]any{ + "path": "file.rb", + "mode": "100644", + "type": "blob", + "sha": "7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b", + }, + }}) fmt.Fprint(w, `{ "sha": "cd8274d15fa3ae2ab983129fb037999f264ba9a7", @@ -95,55 +107,143 @@ func TestGitService_CreateTree(t *testing.T) { }`) }) - tree, _, err := client.Git.CreateTree(context.Background(), "o", "r", "b", input) + ctx := t.Context() + tree, _, err := client.Git.CreateTree(ctx, "o", "r", "b", input) if err != nil { t.Errorf("Git.CreateTree returned error: %v", err) } want := Tree{ - String("cd8274d15fa3ae2ab983129fb037999f264ba9a7"), - []TreeEntry{ + Ptr("cd8274d15fa3ae2ab983129fb037999f264ba9a7"), + []*TreeEntry{ { - Path: String("file.rb"), - Mode: String("100644"), - Type: String("blob"), - Size: Int(132), - SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), + Path: Ptr("file.rb"), + Mode: Ptr("100644"), + Type: Ptr("blob"), + Size: Ptr(132), + SHA: Ptr("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), }, }, nil, } - if !reflect.DeepEqual(*tree, want) { + if !cmp.Equal(*tree, want) { t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) } + + const methodName = "CreateTree" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateTree(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateTree(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateTree_Content(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := []TreeEntry{ + input := []*TreeEntry{ { - Path: String("content.md"), - Mode: String("100644"), - Content: String("file content"), + Path: Ptr("content.md"), + Mode: Ptr("100644"), + Content: Ptr("file content"), }, } mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { - v := new(createTree) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") + testJSONBody(t, r, createTree{BaseTree: "b", Entries: []any{ + map[string]any{ + "path": "content.md", + "mode": "100644", + "content": "file content", + }, + }}) - want := &createTree{ - BaseTree: "b", - Entries: input, - } - if !reflect.DeepEqual(v, want) { - t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) + fmt.Fprint(w, `{ + "sha": "5c6780ad2c68743383b740fd1dab6f6a33202b11", + "url": "https://api.github.com/repos/o/r/git/trees/5c6780ad2c68743383b740fd1dab6f6a33202b11", + "tree": [ + { + "mode": "100644", + "type": "blob", + "sha": "aad8feacf6f8063150476a7b2bd9770f2794c08b", + "path": "content.md", + "size": 12, + "url": "https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b" + } + ] + }`) + }) + + ctx := t.Context() + tree, _, err := client.Git.CreateTree(ctx, "o", "r", "b", input) + if err != nil { + t.Errorf("Git.CreateTree returned error: %v", err) + } + + want := Tree{ + Ptr("5c6780ad2c68743383b740fd1dab6f6a33202b11"), + []*TreeEntry{ + { + Path: Ptr("content.md"), + Mode: Ptr("100644"), + Type: Ptr("blob"), + Size: Ptr(12), + SHA: Ptr("aad8feacf6f8063150476a7b2bd9770f2794c08b"), + URL: Ptr("https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"), + }, + }, + nil, + } + + if !cmp.Equal(*tree, want) { + t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) + } + + const methodName = "CreateTree" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateTree(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateTree(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} + +func TestGitService_CreateTree_Delete(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []*TreeEntry{ + { + Path: Ptr("content.md"), + Mode: Ptr("100644"), + }, + } + + mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, createTree{BaseTree: "b", Entries: []any{ + map[string]any{ + "sha": nil, + "path": "content.md", + "mode": "100644", + }, + }}) fmt.Fprint(w, `{ "sha": "5c6780ad2c68743383b740fd1dab6f6a33202b11", @@ -152,7 +252,7 @@ func TestGitService_CreateTree_Content(t *testing.T) { { "mode": "100644", "type": "blob", - "sha": "aad8feacf6f8063150476a7b2bd9770f2794c08b", + "sha": null, "path": "content.md", "size": 12, "url": "https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b" @@ -161,35 +261,120 @@ func TestGitService_CreateTree_Content(t *testing.T) { }`) }) - tree, _, err := client.Git.CreateTree(context.Background(), "o", "r", "b", input) + ctx := t.Context() + tree, _, err := client.Git.CreateTree(ctx, "o", "r", "b", input) if err != nil { t.Errorf("Git.CreateTree returned error: %v", err) } want := Tree{ - String("5c6780ad2c68743383b740fd1dab6f6a33202b11"), - []TreeEntry{ + Ptr("5c6780ad2c68743383b740fd1dab6f6a33202b11"), + []*TreeEntry{ { - Path: String("content.md"), - Mode: String("100644"), - Type: String("blob"), - Size: Int(12), - SHA: String("aad8feacf6f8063150476a7b2bd9770f2794c08b"), - URL: String("https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"), + Path: Ptr("content.md"), + Mode: Ptr("100644"), + Type: Ptr("blob"), + Size: Ptr(12), + SHA: nil, + URL: Ptr("https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b"), }, }, nil, } - if !reflect.DeepEqual(*tree, want) { + if !cmp.Equal(*tree, want) { t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) } + + const methodName = "CreateTree" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Git.CreateTree(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Git.CreateTree(ctx, "o", "r", "b", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitService_CreateTree_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Git.CreateTree(context.Background(), "%", "%", "", nil) + ctx := t.Context() + _, _, err := client.Git.CreateTree(ctx, "%", "%", "", nil) testURLParseError(t, err) } + +func TestTreeEntry_MarshalJSON(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &TreeEntry{}, `{"sha": null}`) + + u := &TreeEntry{ + SHA: Ptr("sha"), + Path: Ptr("path"), + Mode: Ptr("mode"), + Type: Ptr("type"), + Size: Ptr(1), + Content: Ptr("content"), + URL: Ptr("url"), + } + + want := `{ + "sha": "sha", + "path": "path", + "mode": "mode", + "type": "type", + "size": 1, + "content": "content", + "url": "url" + }` + + testJSONMarshal(t, u, want) +} + +func TestTreeEntry_MarshalJSON_withNilContentAndSHA(t *testing.T) { + t.Parallel() + te := TreeEntry{ + Path: Ptr("path"), + Mode: Ptr("mode"), + Type: Ptr("type"), + Size: Ptr(1), + URL: Ptr("url"), + } + + want := `{"sha":null,"path":"path","mode":"mode","type":"type"}` + testJSONMarshalOnly(t, te, want) + testJSONMarshalOnly(t, &te, want) +} + +func TestTreeEntryWithFileDelete_MarshalJSON(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &treeEntryWithFileDelete{}, `{"sha": null}`) + + u := &treeEntryWithFileDelete{ + SHA: Ptr("sha"), + Path: Ptr("path"), + Mode: Ptr("mode"), + Type: Ptr("type"), + Size: Ptr(1), + Content: Ptr("content"), + URL: Ptr("url"), + } + + want := `{ + "sha": "sha", + "path": "path", + "mode": "mode", + "type": "type", + "size": 1, + "content": "content", + "url": "url" + }` + + testJSONMarshal(t, u, want) +} diff --git a/github/github-accessors.go b/github/github-accessors.go index 4873d98472f..ac6b242f8a0 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -1,10 +1,12 @@ +// Code generated by gen-accessors; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + // Copyright 2017 The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Code generated by gen-accessors; DO NOT EDIT. - package github import ( @@ -12,6 +14,14 @@ import ( "time" ) +// GetMessage returns the Message field. +func (a *AbuseRateLimitError) GetMessage() string { + if a == nil { + return "" + } + return a.Message +} + // GetRetryAfter returns the RetryAfter field if it's non-nil, zero value otherwise. func (a *AbuseRateLimitError) GetRetryAfter() time.Duration { if a == nil || a.RetryAfter == nil { @@ -20,12776 +30,45976 @@ func (a *AbuseRateLimitError) GetRetryAfter() time.Duration { return *a.RetryAfter } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (a *AdminEnforcement) GetURL() string { - if a == nil || a.URL == nil { +// GetAssignment returns the Assignment field. +func (a *AcceptedAssignment) GetAssignment() *ClassroomAssignment { + if a == nil { + return nil + } + return a.Assignment +} + +// GetCommitCount returns the CommitCount field if it's non-nil, zero value otherwise. +func (a *AcceptedAssignment) GetCommitCount() int { + if a == nil || a.CommitCount == nil { + return 0 + } + return *a.CommitCount +} + +// GetGrade returns the Grade field if it's non-nil, zero value otherwise. +func (a *AcceptedAssignment) GetGrade() string { + if a == nil || a.Grade == nil { return "" } - return *a.URL + return *a.Grade } -// GetComments returns the Comments field. -func (a *AdminStats) GetComments() *CommentStats { - if a == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *AcceptedAssignment) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return a.Comments + return *a.ID } -// GetGists returns the Gists field. -func (a *AdminStats) GetGists() *GistStats { - if a == nil { - return nil +// GetPassing returns the Passing field if it's non-nil, zero value otherwise. +func (a *AcceptedAssignment) GetPassing() bool { + if a == nil || a.Passing == nil { + return false } - return a.Gists + return *a.Passing } -// GetHooks returns the Hooks field. -func (a *AdminStats) GetHooks() *HookStats { +// GetRepository returns the Repository field. +func (a *AcceptedAssignment) GetRepository() *Repository { if a == nil { return nil } - return a.Hooks + return a.Repository } -// GetIssues returns the Issues field. -func (a *AdminStats) GetIssues() *IssueStats { - if a == nil { +// GetStudents returns the Students slice if it's non-nil, nil otherwise. +func (a *AcceptedAssignment) GetStudents() []*ClassroomUser { + if a == nil || a.Students == nil { return nil } - return a.Issues + return a.Students } -// GetMilestones returns the Milestones field. -func (a *AdminStats) GetMilestones() *MilestoneStats { - if a == nil { - return nil +// GetSubmitted returns the Submitted field if it's non-nil, zero value otherwise. +func (a *AcceptedAssignment) GetSubmitted() bool { + if a == nil || a.Submitted == nil { + return false } - return a.Milestones + return *a.Submitted } -// GetOrgs returns the Orgs field. -func (a *AdminStats) GetOrgs() *OrgStats { - if a == nil { +// GetRaw returns the Raw slice if it's non-nil, nil otherwise. +func (a *AcceptedError) GetRaw() []byte { + if a == nil || a.Raw == nil { return nil } - return a.Orgs + return a.Raw } -// GetPages returns the Pages field. -func (a *AdminStats) GetPages() *PageStats { +// GetFullName returns the FullName field. +func (a *AccessibleRepository) GetFullName() string { if a == nil { - return nil + return "" } - return a.Pages + return a.FullName } -// GetPulls returns the Pulls field. -func (a *AdminStats) GetPulls() *PullStats { +// GetID returns the ID field. +func (a *AccessibleRepository) GetID() int64 { if a == nil { - return nil + return 0 } - return a.Pulls + return a.ID } -// GetRepos returns the Repos field. -func (a *AdminStats) GetRepos() *RepoStats { +// GetName returns the Name field. +func (a *AccessibleRepository) GetName() string { if a == nil { - return nil + return "" } - return a.Repos + return a.Name } -// GetUsers returns the Users field. -func (a *AdminStats) GetUsers() *UserStats { - if a == nil { +// GetGithubOwnedAllowed returns the GithubOwnedAllowed field if it's non-nil, zero value otherwise. +func (a *ActionsAllowed) GetGithubOwnedAllowed() bool { + if a == nil || a.GithubOwnedAllowed == nil { + return false + } + return *a.GithubOwnedAllowed +} + +// GetPatternsAllowed returns the PatternsAllowed slice if it's non-nil, nil otherwise. +func (a *ActionsAllowed) GetPatternsAllowed() []string { + if a == nil || a.PatternsAllowed == nil { return nil } - return a.Users + return a.PatternsAllowed } -// GetVerifiablePasswordAuthentication returns the VerifiablePasswordAuthentication field if it's non-nil, zero value otherwise. -func (a *APIMeta) GetVerifiablePasswordAuthentication() bool { - if a == nil || a.VerifiablePasswordAuthentication == nil { +// GetVerifiedAllowed returns the VerifiedAllowed field if it's non-nil, zero value otherwise. +func (a *ActionsAllowed) GetVerifiedAllowed() bool { + if a == nil || a.VerifiedAllowed == nil { return false } - return *a.VerifiablePasswordAuthentication + return *a.VerifiedAllowed } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (a *App) GetCreatedAt() Timestamp { +func (a *ActionsCache) GetCreatedAt() Timestamp { if a == nil || a.CreatedAt == nil { return Timestamp{} } return *a.CreatedAt } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (a *App) GetDescription() string { - if a == nil || a.Description == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return *a.Description + return *a.ID } -// GetExternalURL returns the ExternalURL field if it's non-nil, zero value otherwise. -func (a *App) GetExternalURL() string { - if a == nil || a.ExternalURL == nil { +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetKey() string { + if a == nil || a.Key == nil { return "" } - return *a.ExternalURL + return *a.Key } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (a *App) GetHTMLURL() string { - if a == nil || a.HTMLURL == nil { +// GetLastAccessedAt returns the LastAccessedAt field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetLastAccessedAt() Timestamp { + if a == nil || a.LastAccessedAt == nil { + return Timestamp{} + } + return *a.LastAccessedAt +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetRef() string { + if a == nil || a.Ref == nil { return "" } - return *a.HTMLURL + return *a.Ref } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (a *App) GetID() int64 { - if a == nil || a.ID == nil { +// GetSizeInBytes returns the SizeInBytes field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetSizeInBytes() int64 { + if a == nil || a.SizeInBytes == nil { return 0 } - return *a.ID + return *a.SizeInBytes } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (a *App) GetName() string { - if a == nil || a.Name == nil { +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (a *ActionsCache) GetVersion() string { + if a == nil || a.Version == nil { return "" } - return *a.Name + return *a.Version } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (a *App) GetNodeID() string { - if a == nil || a.NodeID == nil { - return "" +// GetActionsCaches returns the ActionsCaches slice if it's non-nil, nil otherwise. +func (a *ActionsCacheList) GetActionsCaches() []*ActionsCache { + if a == nil || a.ActionsCaches == nil { + return nil } - return *a.NodeID + return a.ActionsCaches } -// GetOwner returns the Owner field. -func (a *App) GetOwner() *User { +// GetTotalCount returns the TotalCount field. +func (a *ActionsCacheList) GetTotalCount() int { if a == nil { - return nil + return 0 } - return a.Owner + return a.TotalCount } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (a *App) GetUpdatedAt() Timestamp { - if a == nil || a.UpdatedAt == nil { - return Timestamp{} +// GetDirection returns the Direction field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetDirection() string { + if a == nil || a.Direction == nil { + return "" } - return *a.UpdatedAt + return *a.Direction } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (a *Attachment) GetBody() string { - if a == nil || a.Body == nil { +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetKey() string { + if a == nil || a.Key == nil { return "" } - return *a.Body + return *a.Key } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (a *Attachment) GetID() int64 { - if a == nil || a.ID == nil { - return 0 +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetRef() string { + if a == nil || a.Ref == nil { + return "" } - return *a.ID + return *a.Ref } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (a *Attachment) GetTitle() string { - if a == nil || a.Title == nil { +// GetSort returns the Sort field if it's non-nil, zero value otherwise. +func (a *ActionsCacheListOptions) GetSort() string { + if a == nil || a.Sort == nil { return "" } - return *a.Title + return *a.Sort } -// GetApp returns the App field. -func (a *Authorization) GetApp() *AuthorizationApp { +// GetActiveCachesCount returns the ActiveCachesCount field. +func (a *ActionsCacheUsage) GetActiveCachesCount() int { if a == nil { - return nil + return 0 } - return a.App + return a.ActiveCachesCount } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (a *Authorization) GetCreatedAt() Timestamp { - if a == nil || a.CreatedAt == nil { - return Timestamp{} +// GetActiveCachesSizeInBytes returns the ActiveCachesSizeInBytes field. +func (a *ActionsCacheUsage) GetActiveCachesSizeInBytes() int64 { + if a == nil { + return 0 } - return *a.CreatedAt + return a.ActiveCachesSizeInBytes } -// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. -func (a *Authorization) GetFingerprint() string { - if a == nil || a.Fingerprint == nil { +// GetFullName returns the FullName field. +func (a *ActionsCacheUsage) GetFullName() string { + if a == nil { return "" } - return *a.Fingerprint + return a.FullName } -// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise. -func (a *Authorization) GetHashedToken() string { - if a == nil || a.HashedToken == nil { - return "" +// GetRepoCacheUsage returns the RepoCacheUsage slice if it's non-nil, nil otherwise. +func (a *ActionsCacheUsageList) GetRepoCacheUsage() []*ActionsCacheUsage { + if a == nil || a.RepoCacheUsage == nil { + return nil } - return *a.HashedToken + return a.RepoCacheUsage } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (a *Authorization) GetID() int64 { - if a == nil || a.ID == nil { +// GetTotalCount returns the TotalCount field. +func (a *ActionsCacheUsageList) GetTotalCount() int { + if a == nil { return 0 } - return *a.ID + return a.TotalCount } -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (a *Authorization) GetNote() string { - if a == nil || a.Note == nil { +// GetName returns the Name field. +func (a *ActionsCreateOrgVariableRequest) GetName() string { + if a == nil { return "" } - return *a.Note + return a.Name } -// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. -func (a *Authorization) GetNoteURL() string { - if a == nil || a.NoteURL == nil { - return "" +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (a *ActionsCreateOrgVariableRequest) GetSelectedRepositoryIDs() []int64 { + if a == nil || a.SelectedRepositoryIDs == nil { + return nil } - return *a.NoteURL + return a.SelectedRepositoryIDs } -// GetToken returns the Token field if it's non-nil, zero value otherwise. -func (a *Authorization) GetToken() string { - if a == nil || a.Token == nil { +// GetValue returns the Value field. +func (a *ActionsCreateOrgVariableRequest) GetValue() string { + if a == nil { return "" } - return *a.Token + return a.Value } -// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. -func (a *Authorization) GetTokenLastEight() string { - if a == nil || a.TokenLastEight == nil { +// GetVisibility returns the Visibility field. +func (a *ActionsCreateOrgVariableRequest) GetVisibility() string { + if a == nil { return "" } - return *a.TokenLastEight + return a.Visibility } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (a *Authorization) GetUpdatedAt() Timestamp { - if a == nil || a.UpdatedAt == nil { - return Timestamp{} +// GetName returns the Name field. +func (a *ActionsCreateVariableRequest) GetName() string { + if a == nil { + return "" } - return *a.UpdatedAt + return a.Name } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (a *Authorization) GetURL() string { - if a == nil || a.URL == nil { +// GetValue returns the Value field. +func (a *ActionsCreateVariableRequest) GetValue() string { + if a == nil { return "" } - return *a.URL + return a.Value } -// GetUser returns the User field. -func (a *Authorization) GetUser() *User { - if a == nil { +// GetOrganizations returns the Organizations slice if it's non-nil, nil otherwise. +func (a *ActionsEnabledOnEnterpriseRepos) GetOrganizations() []*Organization { + if a == nil || a.Organizations == nil { return nil } - return a.User + return a.Organizations } -// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. -func (a *AuthorizationApp) GetClientID() string { - if a == nil || a.ClientID == nil { - return "" +// GetTotalCount returns the TotalCount field. +func (a *ActionsEnabledOnEnterpriseRepos) GetTotalCount() int { + if a == nil { + return 0 } - return *a.ClientID + return a.TotalCount } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (a *AuthorizationApp) GetName() string { - if a == nil || a.Name == nil { - return "" +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (a *ActionsEnabledOnOrgRepos) GetRepositories() []*Repository { + if a == nil || a.Repositories == nil { + return nil } - return *a.Name + return a.Repositories } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (a *AuthorizationApp) GetURL() string { - if a == nil || a.URL == nil { - return "" +// GetTotalCount returns the TotalCount field. +func (a *ActionsEnabledOnOrgRepos) GetTotalCount() int { + if a == nil { + return 0 } - return *a.URL + return a.TotalCount } -// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetClientID() string { - if a == nil || a.ClientID == nil { - return "" +// GetFullDomains returns the FullDomains slice if it's non-nil, nil otherwise. +func (a *ActionsInboundDomains) GetFullDomains() []string { + if a == nil || a.FullDomains == nil { + return nil } - return *a.ClientID + return a.FullDomains } -// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetClientSecret() string { - if a == nil || a.ClientSecret == nil { +// GetWildcardDomains returns the WildcardDomains slice if it's non-nil, nil otherwise. +func (a *ActionsInboundDomains) GetWildcardDomains() []string { + if a == nil || a.WildcardDomains == nil { + return nil + } + return a.WildcardDomains +} + +// GetAllowedActions returns the AllowedActions field if it's non-nil, zero value otherwise. +func (a *ActionsPermissions) GetAllowedActions() string { + if a == nil || a.AllowedActions == nil { return "" } - return *a.ClientSecret + return *a.AllowedActions } -// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetFingerprint() string { - if a == nil || a.Fingerprint == nil { +// GetEnabledRepositories returns the EnabledRepositories field if it's non-nil, zero value otherwise. +func (a *ActionsPermissions) GetEnabledRepositories() string { + if a == nil || a.EnabledRepositories == nil { return "" } - return *a.Fingerprint + return *a.EnabledRepositories } -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetNote() string { - if a == nil || a.Note == nil { +// GetSelectedActionsURL returns the SelectedActionsURL field if it's non-nil, zero value otherwise. +func (a *ActionsPermissions) GetSelectedActionsURL() string { + if a == nil || a.SelectedActionsURL == nil { return "" } - return *a.Note + return *a.SelectedActionsURL } -// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetNoteURL() string { - if a == nil || a.NoteURL == nil { - return "" +// GetSHAPinningRequired returns the SHAPinningRequired field if it's non-nil, zero value otherwise. +func (a *ActionsPermissions) GetSHAPinningRequired() bool { + if a == nil || a.SHAPinningRequired == nil { + return false } - return *a.NoteURL + return *a.SHAPinningRequired } -// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. -func (a *AuthorizationUpdateRequest) GetFingerprint() string { - if a == nil || a.Fingerprint == nil { +// GetAllowedActions returns the AllowedActions field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsEnterprise) GetAllowedActions() string { + if a == nil || a.AllowedActions == nil { return "" } - return *a.Fingerprint + return *a.AllowedActions } -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (a *AuthorizationUpdateRequest) GetNote() string { - if a == nil || a.Note == nil { +// GetEnabledOrganizations returns the EnabledOrganizations field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsEnterprise) GetEnabledOrganizations() string { + if a == nil || a.EnabledOrganizations == nil { return "" } - return *a.Note + return *a.EnabledOrganizations } -// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. -func (a *AuthorizationUpdateRequest) GetNoteURL() string { - if a == nil || a.NoteURL == nil { +// GetSelectedActionsURL returns the SelectedActionsURL field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsEnterprise) GetSelectedActionsURL() string { + if a == nil || a.SelectedActionsURL == nil { return "" } - return *a.NoteURL + return *a.SelectedActionsURL } -// GetAppID returns the AppID field if it's non-nil, zero value otherwise. -func (a *AutoTriggerCheck) GetAppID() int64 { - if a == nil || a.AppID == nil { - return 0 +// GetAllowedActions returns the AllowedActions field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsRepository) GetAllowedActions() string { + if a == nil || a.AllowedActions == nil { + return "" } - return *a.AppID + return *a.AllowedActions } -// GetSetting returns the Setting field if it's non-nil, zero value otherwise. -func (a *AutoTriggerCheck) GetSetting() bool { - if a == nil || a.Setting == nil { +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsRepository) GetEnabled() bool { + if a == nil || a.Enabled == nil { return false } - return *a.Setting -} - -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (b *Blob) GetContent() string { - if b == nil || b.Content == nil { - return "" - } - return *b.Content + return *a.Enabled } -// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. -func (b *Blob) GetEncoding() string { - if b == nil || b.Encoding == nil { +// GetSelectedActionsURL returns the SelectedActionsURL field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsRepository) GetSelectedActionsURL() string { + if a == nil || a.SelectedActionsURL == nil { return "" } - return *b.Encoding + return *a.SelectedActionsURL } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (b *Blob) GetNodeID() string { - if b == nil || b.NodeID == nil { - return "" +// GetSHAPinningRequired returns the SHAPinningRequired field if it's non-nil, zero value otherwise. +func (a *ActionsPermissionsRepository) GetSHAPinningRequired() bool { + if a == nil || a.SHAPinningRequired == nil { + return false } - return *b.NodeID + return *a.SHAPinningRequired } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (b *Blob) GetSHA() string { - if b == nil || b.SHA == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *ActionsUpdateOrgVariableRequest) GetName() string { + if a == nil || a.Name == nil { return "" } - return *b.SHA + return *a.Name } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (b *Blob) GetSize() int { - if b == nil || b.Size == nil { - return 0 +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (a *ActionsUpdateOrgVariableRequest) GetSelectedRepositoryIDs() []int64 { + if a == nil || a.SelectedRepositoryIDs == nil { + return nil } - return *b.Size + return a.SelectedRepositoryIDs } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (b *Blob) GetURL() string { - if b == nil || b.URL == nil { +// GetValue returns the Value field if it's non-nil, zero value otherwise. +func (a *ActionsUpdateOrgVariableRequest) GetValue() string { + if a == nil || a.Value == nil { return "" } - return *b.URL + return *a.Value } -// GetCommit returns the Commit field. -func (b *Branch) GetCommit() *RepositoryCommit { - if b == nil { - return nil +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (a *ActionsUpdateOrgVariableRequest) GetVisibility() string { + if a == nil || a.Visibility == nil { + return "" } - return b.Commit + return *a.Visibility } // GetName returns the Name field if it's non-nil, zero value otherwise. -func (b *Branch) GetName() string { - if b == nil || b.Name == nil { +func (a *ActionsUpdateVariableRequest) GetName() string { + if a == nil || a.Name == nil { return "" } - return *b.Name + return *a.Name } -// GetProtected returns the Protected field if it's non-nil, zero value otherwise. -func (b *Branch) GetProtected() bool { - if b == nil || b.Protected == nil { - return false +// GetValue returns the Value field if it's non-nil, zero value otherwise. +func (a *ActionsUpdateVariableRequest) GetValue() string { + if a == nil || a.Value == nil { + return "" } - return *b.Protected + return *a.Value } -// GetCommit returns the Commit field. -func (b *BranchCommit) GetCommit() *Commit { - if b == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return b.Commit + return *a.CreatedAt } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (b *BranchCommit) GetName() string { - if b == nil || b.Name == nil { +// GetName returns the Name field. +func (a *ActionsVariable) GetName() string { + if a == nil { return "" } - return *b.Name + return a.Name } -// GetProtected returns the Protected field if it's non-nil, zero value otherwise. -func (b *BranchCommit) GetProtected() string { - if b == nil || b.Protected == nil { +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetSelectedRepositoriesURL() string { + if a == nil || a.SelectedRepositoriesURL == nil { return "" } - return *b.Protected + return *a.SelectedRepositoriesURL } -// GetApp returns the App field. -func (c *CheckRun) GetApp() *App { - if c == nil { - return nil +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} } - return c.App + return *a.UpdatedAt } -// GetCheckSuite returns the CheckSuite field. -func (c *CheckRun) GetCheckSuite() *CheckSuite { - if c == nil { - return nil +// GetValue returns the Value field. +func (a *ActionsVariable) GetValue() string { + if a == nil { + return "" } - return c.CheckSuite + return a.Value } -// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetCompletedAt() Timestamp { - if c == nil || c.CompletedAt == nil { - return Timestamp{} +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetVisibility() string { + if a == nil || a.Visibility == nil { + return "" } - return *c.CompletedAt + return *a.Visibility } -// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetConclusion() string { - if c == nil || c.Conclusion == nil { - return "" +// GetTotalCount returns the TotalCount field. +func (a *ActionsVariables) GetTotalCount() int { + if a == nil { + return 0 } - return *c.Conclusion + return a.TotalCount } -// GetDetailsURL returns the DetailsURL field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetDetailsURL() string { - if c == nil || c.DetailsURL == nil { - return "" +// GetVariables returns the Variables slice if it's non-nil, nil otherwise. +func (a *ActionsVariables) GetVariables() []*ActionsVariable { + if a == nil || a.Variables == nil { + return nil } - return *c.DetailsURL + return a.Variables } -// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetExternalID() string { - if c == nil || c.ExternalID == nil { - return "" +// GetMaximumAdvancedSecurityCommitters returns the MaximumAdvancedSecurityCommitters field if it's non-nil, zero value otherwise. +func (a *ActiveCommitters) GetMaximumAdvancedSecurityCommitters() int { + if a == nil || a.MaximumAdvancedSecurityCommitters == nil { + return 0 } - return *c.ExternalID + return *a.MaximumAdvancedSecurityCommitters } -// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetHeadSHA() string { - if c == nil || c.HeadSHA == nil { - return "" +// GetPurchasedAdvancedSecurityCommitters returns the PurchasedAdvancedSecurityCommitters field if it's non-nil, zero value otherwise. +func (a *ActiveCommitters) GetPurchasedAdvancedSecurityCommitters() int { + if a == nil || a.PurchasedAdvancedSecurityCommitters == nil { + return 0 } - return *c.HeadSHA + return *a.PurchasedAdvancedSecurityCommitters } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (a *ActiveCommitters) GetRepositories() []*RepositoryActiveCommitters { + if a == nil || a.Repositories == nil { + return nil } - return *c.HTMLURL + return a.Repositories } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetID() int64 { - if c == nil || c.ID == nil { +// GetTotalAdvancedSecurityCommitters returns the TotalAdvancedSecurityCommitters field if it's non-nil, zero value otherwise. +func (a *ActiveCommitters) GetTotalAdvancedSecurityCommitters() int { + if a == nil || a.TotalAdvancedSecurityCommitters == nil { return 0 } - return *c.ID + return *a.TotalAdvancedSecurityCommitters } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetName() string { - if c == nil || c.Name == nil { - return "" +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (a *ActiveCommitters) GetTotalCount() int { + if a == nil || a.TotalCount == nil { + return 0 } - return *c.Name + return *a.TotalCount } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetNodeID() string { - if c == nil || c.NodeID == nil { +// GetAdvancedSecurityProduct returns the AdvancedSecurityProduct field if it's non-nil, zero value otherwise. +func (a *ActiveCommittersListOptions) GetAdvancedSecurityProduct() string { + if a == nil || a.AdvancedSecurityProduct == nil { return "" } - return *c.NodeID + return *a.AdvancedSecurityProduct } -// GetOutput returns the Output field. -func (c *CheckRun) GetOutput() *CheckRunOutput { - if c == nil { - return nil +// GetDirection returns the Direction field. +func (a *ActivityListStarredOptions) GetDirection() string { + if a == nil { + return "" } - return c.Output + return a.Direction } -// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetStartedAt() Timestamp { - if c == nil || c.StartedAt == nil { - return Timestamp{} +// GetSort returns the Sort field. +func (a *ActivityListStarredOptions) GetSort() string { + if a == nil { + return "" } - return *c.StartedAt + return a.Sort } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetStatus() string { - if c == nil || c.Status == nil { +// GetCountryCode returns the CountryCode field if it's non-nil, zero value otherwise. +func (a *ActorLocation) GetCountryCode() string { + if a == nil || a.CountryCode == nil { return "" } - return *c.Status + return *a.CountryCode } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CheckRun) GetURL() string { - if c == nil || c.URL == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *AddProjectItemOptions) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return *c.URL + return *a.ID } -// GetAnnotationLevel returns the AnnotationLevel field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetAnnotationLevel() string { - if c == nil || c.AnnotationLevel == nil { - return "" +// GetType returns the Type field. +func (a *AddProjectItemOptions) GetType() *ProjectV2ItemContentType { + if a == nil { + return nil } - return *c.AnnotationLevel + return a.Type } -// GetBlobHRef returns the BlobHRef field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetBlobHRef() string { - if c == nil || c.BlobHRef == nil { +// GetDataType returns the DataType field if it's non-nil, zero value otherwise. +func (a *AddProjectV2FieldRequest) GetDataType() string { + if a == nil || a.DataType == nil { return "" } - return *c.BlobHRef + return *a.DataType } -// GetEndColumn returns the EndColumn field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetEndColumn() int { - if c == nil || c.EndColumn == nil { +// GetIssueFieldID returns the IssueFieldID field if it's non-nil, zero value otherwise. +func (a *AddProjectV2FieldRequest) GetIssueFieldID() int64 { + if a == nil || a.IssueFieldID == nil { return 0 } - return *c.EndColumn + return *a.IssueFieldID } -// GetEndLine returns the EndLine field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetEndLine() int { - if c == nil || c.EndLine == nil { - return 0 +// GetIterationConfiguration returns the IterationConfiguration field. +func (a *AddProjectV2FieldRequest) GetIterationConfiguration() *ProjectV2FieldIterationConfiguration { + if a == nil { + return nil } - return *c.EndLine + return a.IterationConfiguration } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetMessage() string { - if c == nil || c.Message == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *AddProjectV2FieldRequest) GetName() string { + if a == nil || a.Name == nil { return "" } - return *c.Message + return *a.Name } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetPath() string { - if c == nil || c.Path == nil { - return "" +// GetSingleSelectOptions returns the SingleSelectOptions slice if it's non-nil, nil otherwise. +func (a *AddProjectV2FieldRequest) GetSingleSelectOptions() []*ProjectV2FieldSingleSelectOption { + if a == nil || a.SingleSelectOptions == nil { + return nil } - return *c.Path + return a.SingleSelectOptions } -// GetRawDetails returns the RawDetails field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetRawDetails() string { - if c == nil || c.RawDetails == nil { +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (a *AddResourcesToCostCenterResponse) GetMessage() string { + if a == nil || a.Message == nil { return "" } - return *c.RawDetails + return *a.Message } -// GetStartColumn returns the StartColumn field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetStartColumn() int { - if c == nil || c.StartColumn == nil { - return 0 +// GetReassignedResources returns the ReassignedResources slice if it's non-nil, nil otherwise. +func (a *AddResourcesToCostCenterResponse) GetReassignedResources() []*ReassignedResource { + if a == nil || a.ReassignedResources == nil { + return nil } - return *c.StartColumn + return a.ReassignedResources } -// GetStartLine returns the StartLine field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetStartLine() int { - if c == nil || c.StartLine == nil { - return 0 +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (a *AdminEnforcedChanges) GetFrom() bool { + if a == nil || a.From == nil { + return false } - return *c.StartLine + return *a.From } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (c *CheckRunAnnotation) GetTitle() string { - if c == nil || c.Title == nil { - return "" +// GetEnabled returns the Enabled field. +func (a *AdminEnforcement) GetEnabled() bool { + if a == nil { + return false } - return *c.Title + return a.Enabled } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (c *CheckRunEvent) GetAction() string { - if c == nil || c.Action == nil { - return "" - } - return *c.Action +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *AdminEnforcement) GetURL() string { + if a == nil || a.URL == nil { + return "" + } + return *a.URL } -// GetCheckRun returns the CheckRun field. -func (c *CheckRunEvent) GetCheckRun() *CheckRun { - if c == nil { +// GetComments returns the Comments field. +func (a *AdminStats) GetComments() *CommentStats { + if a == nil { return nil } - return c.CheckRun + return a.Comments } -// GetInstallation returns the Installation field. -func (c *CheckRunEvent) GetInstallation() *Installation { - if c == nil { +// GetGists returns the Gists field. +func (a *AdminStats) GetGists() *GistStats { + if a == nil { return nil } - return c.Installation + return a.Gists } -// GetOrg returns the Org field. -func (c *CheckRunEvent) GetOrg() *Organization { - if c == nil { +// GetHooks returns the Hooks field. +func (a *AdminStats) GetHooks() *HookStats { + if a == nil { return nil } - return c.Org + return a.Hooks } -// GetRepo returns the Repo field. -func (c *CheckRunEvent) GetRepo() *Repository { - if c == nil { +// GetIssues returns the Issues field. +func (a *AdminStats) GetIssues() *IssueStats { + if a == nil { return nil } - return c.Repo + return a.Issues } -// GetRequestedAction returns the RequestedAction field. -func (c *CheckRunEvent) GetRequestedAction() *RequestedAction { - if c == nil { +// GetMilestones returns the Milestones field. +func (a *AdminStats) GetMilestones() *MilestoneStats { + if a == nil { return nil } - return c.RequestedAction + return a.Milestones } -// GetSender returns the Sender field. -func (c *CheckRunEvent) GetSender() *User { - if c == nil { +// GetOrgs returns the Orgs field. +func (a *AdminStats) GetOrgs() *OrgStats { + if a == nil { return nil } - return c.Sender + return a.Orgs } -// GetAlt returns the Alt field if it's non-nil, zero value otherwise. -func (c *CheckRunImage) GetAlt() string { - if c == nil || c.Alt == nil { - return "" +// GetPages returns the Pages field. +func (a *AdminStats) GetPages() *PageStats { + if a == nil { + return nil } - return *c.Alt + return a.Pages } -// GetCaption returns the Caption field if it's non-nil, zero value otherwise. -func (c *CheckRunImage) GetCaption() string { - if c == nil || c.Caption == nil { - return "" +// GetPulls returns the Pulls field. +func (a *AdminStats) GetPulls() *PullStats { + if a == nil { + return nil } - return *c.Caption + return a.Pulls } -// GetImageURL returns the ImageURL field if it's non-nil, zero value otherwise. -func (c *CheckRunImage) GetImageURL() string { - if c == nil || c.ImageURL == nil { - return "" +// GetRepos returns the Repos field. +func (a *AdminStats) GetRepos() *RepoStats { + if a == nil { + return nil } - return *c.ImageURL + return a.Repos } -// GetAnnotationsCount returns the AnnotationsCount field if it's non-nil, zero value otherwise. -func (c *CheckRunOutput) GetAnnotationsCount() int { - if c == nil || c.AnnotationsCount == nil { - return 0 +// GetUsers returns the Users field. +func (a *AdminStats) GetUsers() *UserStats { + if a == nil { + return nil } - return *c.AnnotationsCount + return a.Users } -// GetAnnotationsURL returns the AnnotationsURL field if it's non-nil, zero value otherwise. -func (c *CheckRunOutput) GetAnnotationsURL() string { - if c == nil || c.AnnotationsURL == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (a *AdvancedSecurity) GetStatus() string { + if a == nil || a.Status == nil { return "" } - return *c.AnnotationsURL + return *a.Status } -// GetSummary returns the Summary field if it's non-nil, zero value otherwise. -func (c *CheckRunOutput) GetSummary() string { - if c == nil || c.Summary == nil { +// GetLastPushedDate returns the LastPushedDate field. +func (a *AdvancedSecurityCommittersBreakdown) GetLastPushedDate() string { + if a == nil { return "" } - return *c.Summary + return a.LastPushedDate } -// GetText returns the Text field if it's non-nil, zero value otherwise. -func (c *CheckRunOutput) GetText() string { - if c == nil || c.Text == nil { +// GetLastPushedEmail returns the LastPushedEmail field. +func (a *AdvancedSecurityCommittersBreakdown) GetLastPushedEmail() string { + if a == nil { return "" } - return *c.Text + return a.LastPushedEmail } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (c *CheckRunOutput) GetTitle() string { - if c == nil || c.Title == nil { +// GetUserLogin returns the UserLogin field. +func (a *AdvancedSecurityCommittersBreakdown) GetUserLogin() string { + if a == nil { return "" } - return *c.Title + return a.UserLogin } -// GetAfterSHA returns the AfterSHA field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetAfterSHA() string { - if c == nil || c.AfterSHA == nil { +// GetScore returns the Score field if it's non-nil, zero value otherwise. +func (a *AdvisoryCVSS) GetScore() float64 { + if a == nil || a.Score == nil { + return 0 + } + return *a.Score +} + +// GetVectorString returns the VectorString field if it's non-nil, zero value otherwise. +func (a *AdvisoryCVSS) GetVectorString() string { + if a == nil || a.VectorString == nil { return "" } - return *c.AfterSHA + return *a.VectorString } -// GetApp returns the App field. -func (c *CheckSuite) GetApp() *App { - if c == nil { +// GetCVSSV3 returns the CVSSV3 field. +func (a *AdvisoryCVSSSeverities) GetCVSSV3() *AdvisoryCVSS { + if a == nil { return nil } - return c.App + return a.CVSSV3 } -// GetBeforeSHA returns the BeforeSHA field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetBeforeSHA() string { - if c == nil || c.BeforeSHA == nil { - return "" +// GetCVSSV4 returns the CVSSV4 field. +func (a *AdvisoryCVSSSeverities) GetCVSSV4() *AdvisoryCVSS { + if a == nil { + return nil } - return *c.BeforeSHA + return a.CVSSV4 } -// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetConclusion() string { - if c == nil || c.Conclusion == nil { +// GetCWEID returns the CWEID field if it's non-nil, zero value otherwise. +func (a *AdvisoryCWEs) GetCWEID() string { + if a == nil || a.CWEID == nil { return "" } - return *c.Conclusion + return *a.CWEID } -// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetHeadBranch() string { - if c == nil || c.HeadBranch == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *AdvisoryCWEs) GetName() string { + if a == nil || a.Name == nil { return "" } - return *c.HeadBranch + return *a.Name } -// GetHeadCommit returns the HeadCommit field. -func (c *CheckSuite) GetHeadCommit() *Commit { - if c == nil { - return nil +// GetPercentage returns the Percentage field. +func (a *AdvisoryEPSS) GetPercentage() float64 { + if a == nil { + return 0 } - return c.HeadCommit + return a.Percentage } -// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetHeadSHA() string { - if c == nil || c.HeadSHA == nil { +// GetPercentile returns the Percentile field. +func (a *AdvisoryEPSS) GetPercentile() float64 { + if a == nil { + return 0 + } + return a.Percentile +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (a *AdvisoryIdentifier) GetType() string { + if a == nil || a.Type == nil { return "" } - return *c.HeadSHA + return *a.Type } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetID() int64 { - if c == nil || c.ID == nil { - return 0 +// GetValue returns the Value field if it's non-nil, zero value otherwise. +func (a *AdvisoryIdentifier) GetValue() string { + if a == nil || a.Value == nil { + return "" } - return *c.ID + return *a.Value } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetNodeID() string { - if c == nil || c.NodeID == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *AdvisoryReference) GetURL() string { + if a == nil || a.URL == nil { return "" } - return *c.NodeID + return *a.URL } -// GetRepository returns the Repository field. -func (c *CheckSuite) GetRepository() *Repository { - if c == nil { +// GetFirstPatchedVersion returns the FirstPatchedVersion field. +func (a *AdvisoryVulnerability) GetFirstPatchedVersion() *FirstPatchedVersion { + if a == nil { return nil } - return c.Repository + return a.FirstPatchedVersion } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetStatus() string { - if c == nil || c.Status == nil { - return "" +// GetPackage returns the Package field. +func (a *AdvisoryVulnerability) GetPackage() *VulnerabilityPackage { + if a == nil { + return nil } - return *c.Status + return a.Package } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetURL() string { - if c == nil || c.URL == nil { +// GetPatchedVersions returns the PatchedVersions field if it's non-nil, zero value otherwise. +func (a *AdvisoryVulnerability) GetPatchedVersions() string { + if a == nil || a.PatchedVersions == nil { return "" } - return *c.URL + return *a.PatchedVersions } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (c *CheckSuiteEvent) GetAction() string { - if c == nil || c.Action == nil { +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (a *AdvisoryVulnerability) GetSeverity() string { + if a == nil || a.Severity == nil { return "" } - return *c.Action + return *a.Severity } -// GetCheckSuite returns the CheckSuite field. -func (c *CheckSuiteEvent) GetCheckSuite() *CheckSuite { - if c == nil { +// GetVulnerableFunctions returns the VulnerableFunctions slice if it's non-nil, nil otherwise. +func (a *AdvisoryVulnerability) GetVulnerableFunctions() []string { + if a == nil || a.VulnerableFunctions == nil { return nil } - return c.CheckSuite + return a.VulnerableFunctions } -// GetInstallation returns the Installation field. -func (c *CheckSuiteEvent) GetInstallation() *Installation { - if c == nil { - return nil +// GetVulnerableVersionRange returns the VulnerableVersionRange field if it's non-nil, zero value otherwise. +func (a *AdvisoryVulnerability) GetVulnerableVersionRange() string { + if a == nil || a.VulnerableVersionRange == nil { + return "" } - return c.Installation + return *a.VulnerableVersionRange } -// GetOrg returns the Org field. -func (c *CheckSuiteEvent) GetOrg() *Organization { - if c == nil { - return nil +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (a *Alert) GetClosedAt() Timestamp { + if a == nil || a.ClosedAt == nil { + return Timestamp{} } - return c.Org + return *a.ClosedAt } -// GetRepo returns the Repo field. -func (c *CheckSuiteEvent) GetRepo() *Repository { - if c == nil { +// GetClosedBy returns the ClosedBy field. +func (a *Alert) GetClosedBy() *User { + if a == nil { return nil } - return c.Repo + return a.ClosedBy } -// GetSender returns the Sender field. -func (c *CheckSuiteEvent) GetSender() *User { - if c == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *Alert) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return c.Sender + return *a.CreatedAt } -// GetPreferences returns the Preferences field. -func (c *CheckSuitePreferenceResults) GetPreferences() *PreferenceList { - if c == nil { - return nil +// GetDismissedAt returns the DismissedAt field if it's non-nil, zero value otherwise. +func (a *Alert) GetDismissedAt() Timestamp { + if a == nil || a.DismissedAt == nil { + return Timestamp{} } - return c.Preferences + return *a.DismissedAt } -// GetRepository returns the Repository field. -func (c *CheckSuitePreferenceResults) GetRepository() *Repository { - if c == nil { +// GetDismissedBy returns the DismissedBy field. +func (a *Alert) GetDismissedBy() *User { + if a == nil { return nil } - return c.Repository + return a.DismissedBy } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetBody() string { - if c == nil || c.Body == nil { +// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise. +func (a *Alert) GetDismissedComment() string { + if a == nil || a.DismissedComment == nil { return "" } - return *c.Body + return *a.DismissedComment } -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetKey() string { - if c == nil || c.Key == nil { +// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise. +func (a *Alert) GetDismissedReason() string { + if a == nil || a.DismissedReason == nil { return "" } - return *c.Key + return *a.DismissedReason } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetName() string { - if c == nil || c.Name == nil { - return "" +// GetFixedAt returns the FixedAt field if it's non-nil, zero value otherwise. +func (a *Alert) GetFixedAt() Timestamp { + if a == nil || a.FixedAt == nil { + return Timestamp{} } - return *c.Name + return *a.FixedAt } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetURL() string { - if c == nil || c.URL == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (a *Alert) GetHTMLURL() string { + if a == nil || a.HTMLURL == nil { return "" } - return *c.URL + return *a.HTMLURL } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" +// GetInstances returns the Instances slice if it's non-nil, nil otherwise. +func (a *Alert) GetInstances() []*MostRecentInstance { + if a == nil || a.Instances == nil { + return nil } - return *c.HTMLURL + return a.Instances } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetName() string { - if c == nil || c.Name == nil { +// GetInstancesURL returns the InstancesURL field if it's non-nil, zero value otherwise. +func (a *Alert) GetInstancesURL() string { + if a == nil || a.InstancesURL == nil { return "" } - return *c.Name + return *a.InstancesURL } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetPath() string { - if c == nil || c.Path == nil { - return "" +// GetMostRecentInstance returns the MostRecentInstance field. +func (a *Alert) GetMostRecentInstance() *MostRecentInstance { + if a == nil { + return nil } - return *c.Path + return a.MostRecentInstance +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (a *Alert) GetNumber() int { + if a == nil || a.Number == nil { + return 0 + } + return *a.Number } // GetRepository returns the Repository field. -func (c *CodeResult) GetRepository() *Repository { - if c == nil { +func (a *Alert) GetRepository() *Repository { + if a == nil { return nil } - return c.Repository + return a.Repository } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetSHA() string { - if c == nil || c.SHA == nil { +// GetRule returns the Rule field. +func (a *Alert) GetRule() *Rule { + if a == nil { + return nil + } + return a.Rule +} + +// GetRuleDescription returns the RuleDescription field if it's non-nil, zero value otherwise. +func (a *Alert) GetRuleDescription() string { + if a == nil || a.RuleDescription == nil { return "" } - return *c.SHA + return *a.RuleDescription } -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (c *CodeSearchResult) GetIncompleteResults() bool { - if c == nil || c.IncompleteResults == nil { - return false +// GetRuleID returns the RuleID field if it's non-nil, zero value otherwise. +func (a *Alert) GetRuleID() string { + if a == nil || a.RuleID == nil { + return "" } - return *c.IncompleteResults + return *a.RuleID } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *CodeSearchResult) GetTotal() int { - if c == nil || c.Total == nil { - return 0 +// GetRuleSeverity returns the RuleSeverity field if it's non-nil, zero value otherwise. +func (a *Alert) GetRuleSeverity() string { + if a == nil || a.RuleSeverity == nil { + return "" } - return *c.Total + return *a.RuleSeverity } -// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetCommitURL() string { - if c == nil || c.CommitURL == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (a *Alert) GetState() string { + if a == nil || a.State == nil { return "" } - return *c.CommitURL + return *a.State } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetName() string { - if c == nil || c.Name == nil { +// GetTool returns the Tool field. +func (a *Alert) GetTool() *Tool { + if a == nil { + return nil + } + return a.Tool +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *Alert) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} + } + return *a.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *Alert) GetURL() string { + if a == nil || a.URL == nil { return "" } - return *c.Name + return *a.URL } -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetRepositoryURL() string { - if c == nil || c.RepositoryURL == nil { +// GetRef returns the Ref field. +func (a *AlertInstancesListOptions) GetRef() string { + if a == nil { return "" } - return *c.RepositoryURL + return a.Ref } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetSHA() string { - if c == nil || c.SHA == nil { +// GetDirection returns the Direction field. +func (a *AlertListOptions) GetDirection() string { + if a == nil { return "" } - return *c.SHA + return a.Direction } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetState() string { - if c == nil || c.State == nil { +// GetRef returns the Ref field. +func (a *AlertListOptions) GetRef() string { + if a == nil { return "" } - return *c.State + return a.Ref } -// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetTotalCount() int { - if c == nil || c.TotalCount == nil { - return 0 +// GetSeverity returns the Severity field. +func (a *AlertListOptions) GetSeverity() string { + if a == nil { + return "" } - return *c.TotalCount + return a.Severity } -// GetTotalCommitComments returns the TotalCommitComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalCommitComments() int { - if c == nil || c.TotalCommitComments == nil { - return 0 +// GetSort returns the Sort field. +func (a *AlertListOptions) GetSort() string { + if a == nil { + return "" } - return *c.TotalCommitComments + return a.Sort } -// GetTotalGistComments returns the TotalGistComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalGistComments() int { - if c == nil || c.TotalGistComments == nil { - return 0 +// GetState returns the State field. +func (a *AlertListOptions) GetState() string { + if a == nil { + return "" } - return *c.TotalGistComments + return a.State } -// GetTotalIssueComments returns the TotalIssueComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalIssueComments() int { - if c == nil || c.TotalIssueComments == nil { - return 0 +// GetToolGUID returns the ToolGUID field. +func (a *AlertListOptions) GetToolGUID() string { + if a == nil { + return "" } - return *c.TotalIssueComments + return a.ToolGUID } -// GetTotalPullRequestComments returns the TotalPullRequestComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalPullRequestComments() int { - if c == nil || c.TotalPullRequestComments == nil { - return 0 +// GetToolName returns the ToolName field. +func (a *AlertListOptions) GetToolName() string { + if a == nil { + return "" } - return *c.TotalPullRequestComments + return a.ToolName } -// GetAuthor returns the Author field. -func (c *Commit) GetAuthor() *CommitAuthor { - if c == nil { - return nil +// GetEnabled returns the Enabled field. +func (a *AllowDeletions) GetEnabled() bool { + if a == nil { + return false } - return c.Author + return a.Enabled } -// GetCommentCount returns the CommentCount field if it's non-nil, zero value otherwise. -func (c *Commit) GetCommentCount() int { - if c == nil || c.CommentCount == nil { - return 0 +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (a *AllowDeletionsEnforcementLevelChanges) GetFrom() string { + if a == nil || a.From == nil { + return "" } - return *c.CommentCount + return *a.From } -// GetCommitter returns the Committer field. -func (c *Commit) GetCommitter() *CommitAuthor { - if c == nil { - return nil +// GetEnabled returns the Enabled field. +func (a *AllowForcePushes) GetEnabled() bool { + if a == nil { + return false } - return c.Committer + return a.Enabled } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *Commit) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (a *AllowForkSyncing) GetEnabled() bool { + if a == nil || a.Enabled == nil { + return false + } + return *a.Enabled +} + +// GetAuthenticationType returns the AuthenticationType field. +func (a *AmazonS3AccessKeysConfig) GetAuthenticationType() string { + if a == nil { return "" } - return *c.HTMLURL + return a.AuthenticationType } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (c *Commit) GetMessage() string { - if c == nil || c.Message == nil { +// GetBucket returns the Bucket field. +func (a *AmazonS3AccessKeysConfig) GetBucket() string { + if a == nil { return "" } - return *c.Message + return a.Bucket } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (c *Commit) GetNodeID() string { - if c == nil || c.NodeID == nil { +// GetEncryptedAccessKeyID returns the EncryptedAccessKeyID field. +func (a *AmazonS3AccessKeysConfig) GetEncryptedAccessKeyID() string { + if a == nil { return "" } - return *c.NodeID + return a.EncryptedAccessKeyID } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *Commit) GetSHA() string { - if c == nil || c.SHA == nil { +// GetEncryptedSecretKey returns the EncryptedSecretKey field. +func (a *AmazonS3AccessKeysConfig) GetEncryptedSecretKey() string { + if a == nil { return "" } - return *c.SHA + return a.EncryptedSecretKey } -// GetStats returns the Stats field. -func (c *Commit) GetStats() *CommitStats { - if c == nil { - return nil +// GetKeyID returns the KeyID field. +func (a *AmazonS3AccessKeysConfig) GetKeyID() string { + if a == nil { + return "" } - return c.Stats + return a.KeyID } -// GetTree returns the Tree field. -func (c *Commit) GetTree() *Tree { - if c == nil { - return nil +// GetRegion returns the Region field. +func (a *AmazonS3AccessKeysConfig) GetRegion() string { + if a == nil { + return "" } - return c.Tree + return a.Region } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *Commit) GetURL() string { - if c == nil || c.URL == nil { +// GetArnRole returns the ArnRole field. +func (a *AmazonS3OIDCConfig) GetArnRole() string { + if a == nil { return "" } - return *c.URL + return a.ArnRole } -// GetVerification returns the Verification field. -func (c *Commit) GetVerification() *SignatureVerification { - if c == nil { - return nil +// GetAuthenticationType returns the AuthenticationType field. +func (a *AmazonS3OIDCConfig) GetAuthenticationType() string { + if a == nil { + return "" } - return c.Verification + return a.AuthenticationType } -// GetDate returns the Date field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetDate() time.Time { - if c == nil || c.Date == nil { - return time.Time{} +// GetBucket returns the Bucket field. +func (a *AmazonS3OIDCConfig) GetBucket() string { + if a == nil { + return "" } - return *c.Date + return a.Bucket } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetEmail() string { - if c == nil || c.Email == nil { +// GetKeyID returns the KeyID field. +func (a *AmazonS3OIDCConfig) GetKeyID() string { + if a == nil { return "" } - return *c.Email + return a.KeyID } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetLogin() string { - if c == nil || c.Login == nil { +// GetRegion returns the Region field. +func (a *AmazonS3OIDCConfig) GetRegion() string { + if a == nil { return "" } - return *c.Login + return a.Region } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetName() string { - if c == nil || c.Name == nil { +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (a *AnalysesListOptions) GetRef() string { + if a == nil || a.Ref == nil { return "" } - return *c.Name + return *a.Ref } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (c *CommitCommentEvent) GetAction() string { - if c == nil || c.Action == nil { +// GetSarifID returns the SarifID field if it's non-nil, zero value otherwise. +func (a *AnalysesListOptions) GetSarifID() string { + if a == nil || a.SarifID == nil { return "" } - return *c.Action + return *a.SarifID } -// GetComment returns the Comment field. -func (c *CommitCommentEvent) GetComment() *RepositoryComment { - if c == nil { +// GetActions returns the Actions slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetActions() []string { + if a == nil || a.Actions == nil { return nil } - return c.Comment + return a.Actions } -// GetInstallation returns the Installation field. -func (c *CommitCommentEvent) GetInstallation() *Installation { - if c == nil { +// GetActionsMacos returns the ActionsMacos slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetActionsMacos() []string { + if a == nil || a.ActionsMacos == nil { return nil } - return c.Installation + return a.ActionsMacos } -// GetRepo returns the Repo field. -func (c *CommitCommentEvent) GetRepo() *Repository { - if c == nil { +// GetAPI returns the API slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetAPI() []string { + if a == nil || a.API == nil { return nil } - return c.Repo + return a.API } -// GetSender returns the Sender field. -func (c *CommitCommentEvent) GetSender() *User { - if c == nil { +// GetCodespaces returns the Codespaces slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetCodespaces() []string { + if a == nil || a.Codespaces == nil { return nil } - return c.Sender + return a.Codespaces } -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetAdditions() int { - if c == nil || c.Additions == nil { - return 0 +// GetCopilot returns the Copilot slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetCopilot() []string { + if a == nil || a.Copilot == nil { + return nil } - return *c.Additions + return a.Copilot } -// GetBlobURL returns the BlobURL field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetBlobURL() string { - if c == nil || c.BlobURL == nil { - return "" +// GetDependabot returns the Dependabot slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetDependabot() []string { + if a == nil || a.Dependabot == nil { + return nil } - return *c.BlobURL + return a.Dependabot } -// GetChanges returns the Changes field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetChanges() int { - if c == nil || c.Changes == nil { - return 0 +// GetDomains returns the Domains field. +func (a *APIMeta) GetDomains() *APIMetaDomains { + if a == nil { + return nil } - return *c.Changes + return a.Domains } -// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetContentsURL() string { - if c == nil || c.ContentsURL == nil { - return "" - } - return *c.ContentsURL -} - -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetDeletions() int { - if c == nil || c.Deletions == nil { - return 0 +// GetGit returns the Git slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetGit() []string { + if a == nil || a.Git == nil { + return nil } - return *c.Deletions + return a.Git } -// GetFilename returns the Filename field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetFilename() string { - if c == nil || c.Filename == nil { - return "" +// GetGithubEnterpriseImporter returns the GithubEnterpriseImporter slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetGithubEnterpriseImporter() []string { + if a == nil || a.GithubEnterpriseImporter == nil { + return nil } - return *c.Filename + return a.GithubEnterpriseImporter } -// GetPatch returns the Patch field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetPatch() string { - if c == nil || c.Patch == nil { - return "" +// GetHooks returns the Hooks slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetHooks() []string { + if a == nil || a.Hooks == nil { + return nil } - return *c.Patch + return a.Hooks } -// GetPreviousFilename returns the PreviousFilename field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetPreviousFilename() string { - if c == nil || c.PreviousFilename == nil { - return "" +// GetImporter returns the Importer slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetImporter() []string { + if a == nil || a.Importer == nil { + return nil } - return *c.PreviousFilename + return a.Importer } -// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetRawURL() string { - if c == nil || c.RawURL == nil { - return "" +// GetPackages returns the Packages slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetPackages() []string { + if a == nil || a.Packages == nil { + return nil } - return *c.RawURL + return a.Packages } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetSHA() string { - if c == nil || c.SHA == nil { - return "" +// GetPages returns the Pages slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetPages() []string { + if a == nil || a.Pages == nil { + return nil } - return *c.SHA + return a.Pages } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetStatus() string { - if c == nil || c.Status == nil { - return "" +// GetSSHKeyFingerprints returns the SSHKeyFingerprints map if it's non-nil, an empty map otherwise. +func (a *APIMeta) GetSSHKeyFingerprints() map[string]string { + if a == nil || a.SSHKeyFingerprints == nil { + return map[string]string{} } - return *c.Status + return a.SSHKeyFingerprints } -// GetAuthor returns the Author field. -func (c *CommitResult) GetAuthor() *User { - if c == nil { +// GetSSHKeys returns the SSHKeys slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetSSHKeys() []string { + if a == nil || a.SSHKeys == nil { return nil } - return c.Author + return a.SSHKeys } -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetCommentsURL() string { - if c == nil || c.CommentsURL == nil { - return "" +// GetVerifiablePasswordAuthentication returns the VerifiablePasswordAuthentication field if it's non-nil, zero value otherwise. +func (a *APIMeta) GetVerifiablePasswordAuthentication() bool { + if a == nil || a.VerifiablePasswordAuthentication == nil { + return false } - return *c.CommentsURL + return *a.VerifiablePasswordAuthentication } -// GetCommit returns the Commit field. -func (c *CommitResult) GetCommit() *Commit { - if c == nil { +// GetWeb returns the Web slice if it's non-nil, nil otherwise. +func (a *APIMeta) GetWeb() []string { + if a == nil || a.Web == nil { return nil } - return c.Commit + return a.Web } -// GetCommitter returns the Committer field. -func (c *CommitResult) GetCommitter() *User { - if c == nil { +// GetServices returns the Services slice if it's non-nil, nil otherwise. +func (a *APIMetaArtifactAttestations) GetServices() []string { + if a == nil || a.Services == nil { return nil } - return c.Committer + return a.Services } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { +// GetTrustDomain returns the TrustDomain field. +func (a *APIMetaArtifactAttestations) GetTrustDomain() string { + if a == nil { return "" } - return *c.HTMLURL + return a.TrustDomain } -// GetRepository returns the Repository field. -func (c *CommitResult) GetRepository() *Repository { - if c == nil { +// GetActions returns the Actions slice if it's non-nil, nil otherwise. +func (a *APIMetaDomains) GetActions() []string { + if a == nil || a.Actions == nil { return nil } - return c.Repository + return a.Actions } -// GetScore returns the Score field. -func (c *CommitResult) GetScore() *float64 { - if c == nil { +// GetActionsInbound returns the ActionsInbound field. +func (a *APIMetaDomains) GetActionsInbound() *ActionsInboundDomains { + if a == nil { return nil } - return c.Score + return a.ActionsInbound } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetSHA() string { - if c == nil || c.SHA == nil { - return "" +// GetArtifactAttestations returns the ArtifactAttestations field. +func (a *APIMetaDomains) GetArtifactAttestations() *APIMetaArtifactAttestations { + if a == nil { + return nil } - return *c.SHA + return a.ArtifactAttestations } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetURL() string { - if c == nil || c.URL == nil { - return "" +// GetCodespaces returns the Codespaces slice if it's non-nil, nil otherwise. +func (a *APIMetaDomains) GetCodespaces() []string { + if a == nil || a.Codespaces == nil { + return nil } - return *c.URL + return a.Codespaces } -// GetAheadBy returns the AheadBy field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetAheadBy() int { - if c == nil || c.AheadBy == nil { - return 0 +// GetCopilot returns the Copilot slice if it's non-nil, nil otherwise. +func (a *APIMetaDomains) GetCopilot() []string { + if a == nil || a.Copilot == nil { + return nil } - return *c.AheadBy + return a.Copilot } -// GetBaseCommit returns the BaseCommit field. -func (c *CommitsComparison) GetBaseCommit() *RepositoryCommit { - if c == nil { +// GetPackages returns the Packages slice if it's non-nil, nil otherwise. +func (a *APIMetaDomains) GetPackages() []string { + if a == nil || a.Packages == nil { return nil } - return c.BaseCommit + return a.Packages } -// GetBehindBy returns the BehindBy field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetBehindBy() int { - if c == nil || c.BehindBy == nil { - return 0 +// GetWebsite returns the Website slice if it's non-nil, nil otherwise. +func (a *APIMetaDomains) GetWebsite() []string { + if a == nil || a.Website == nil { + return nil } - return *c.BehindBy + return a.Website } -// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetDiffURL() string { - if c == nil || c.DiffURL == nil { +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (a *App) GetClientID() string { + if a == nil || a.ClientID == nil { return "" } - return *c.DiffURL + return *a.ClientID } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *App) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} + } + return *a.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (a *App) GetDescription() string { + if a == nil || a.Description == nil { return "" } - return *c.HTMLURL + return *a.Description } -// GetMergeBaseCommit returns the MergeBaseCommit field. -func (c *CommitsComparison) GetMergeBaseCommit() *RepositoryCommit { - if c == nil { +// GetEvents returns the Events slice if it's non-nil, nil otherwise. +func (a *App) GetEvents() []string { + if a == nil || a.Events == nil { return nil } - return c.MergeBaseCommit + return a.Events } -// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetPatchURL() string { - if c == nil || c.PatchURL == nil { +// GetExternalURL returns the ExternalURL field if it's non-nil, zero value otherwise. +func (a *App) GetExternalURL() string { + if a == nil || a.ExternalURL == nil { return "" } - return *c.PatchURL + return *a.ExternalURL } -// GetPermalinkURL returns the PermalinkURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetPermalinkURL() string { - if c == nil || c.PermalinkURL == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (a *App) GetHTMLURL() string { + if a == nil || a.HTMLURL == nil { return "" } - return *c.PermalinkURL + return *a.HTMLURL } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetStatus() string { - if c == nil || c.Status == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *App) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return *c.Status + return *a.ID } -// GetTotalCommits returns the TotalCommits field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetTotalCommits() int { - if c == nil || c.TotalCommits == nil { +// GetInstallationsCount returns the InstallationsCount field if it's non-nil, zero value otherwise. +func (a *App) GetInstallationsCount() int { + if a == nil || a.InstallationsCount == nil { return 0 } - return *c.TotalCommits + return *a.InstallationsCount } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetURL() string { - if c == nil || c.URL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *App) GetName() string { + if a == nil || a.Name == nil { return "" } - return *c.URL + return *a.Name } -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (c *CommitsSearchResult) GetIncompleteResults() bool { - if c == nil || c.IncompleteResults == nil { - return false +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (a *App) GetNodeID() string { + if a == nil || a.NodeID == nil { + return "" } - return *c.IncompleteResults + return *a.NodeID } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *CommitsSearchResult) GetTotal() int { - if c == nil || c.Total == nil { - return 0 +// GetOwner returns the Owner field. +func (a *App) GetOwner() *User { + if a == nil { + return nil } - return *c.Total + return a.Owner } -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (c *CommitStats) GetAdditions() int { - if c == nil || c.Additions == nil { - return 0 +// GetPermissions returns the Permissions field. +func (a *App) GetPermissions() *InstallationPermissions { + if a == nil { + return nil } - return *c.Additions + return a.Permissions } -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (c *CommitStats) GetDeletions() int { - if c == nil || c.Deletions == nil { - return 0 +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (a *App) GetSlug() string { + if a == nil || a.Slug == nil { + return "" } - return *c.Deletions + return *a.Slug } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *CommitStats) GetTotal() int { - if c == nil || c.Total == nil { - return 0 +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *App) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} } - return *c.Total + return *a.UpdatedAt } -// GetCodeOfConduct returns the CodeOfConduct field. -func (c *CommunityHealthFiles) GetCodeOfConduct() *Metric { - if c == nil { - return nil +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetClientID() string { + if a == nil || a.ClientID == nil { + return "" } - return c.CodeOfConduct + return *a.ClientID } -// GetContributing returns the Contributing field. -func (c *CommunityHealthFiles) GetContributing() *Metric { - if c == nil { - return nil +// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetClientSecret() string { + if a == nil || a.ClientSecret == nil { + return "" } - return c.Contributing + return *a.ClientSecret } -// GetIssueTemplate returns the IssueTemplate field. -func (c *CommunityHealthFiles) GetIssueTemplate() *Metric { - if c == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return c.IssueTemplate + return *a.CreatedAt } -// GetLicense returns the License field. -func (c *CommunityHealthFiles) GetLicense() *Metric { - if c == nil { - return nil +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetDescription() string { + if a == nil || a.Description == nil { + return "" } - return c.License -} - -// GetPullRequestTemplate returns the PullRequestTemplate field. -func (c *CommunityHealthFiles) GetPullRequestTemplate() *Metric { - if c == nil { - return nil - } - return c.PullRequestTemplate + return *a.Description } -// GetReadme returns the Readme field. -func (c *CommunityHealthFiles) GetReadme() *Metric { - if c == nil { - return nil +// GetExternalURL returns the ExternalURL field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetExternalURL() string { + if a == nil || a.ExternalURL == nil { + return "" } - return c.Readme + return *a.ExternalURL } -// GetFiles returns the Files field. -func (c *CommunityHealthMetrics) GetFiles() *CommunityHealthFiles { - if c == nil { - return nil +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetHTMLURL() string { + if a == nil || a.HTMLURL == nil { + return "" } - return c.Files + return *a.HTMLURL } -// GetHealthPercentage returns the HealthPercentage field if it's non-nil, zero value otherwise. -func (c *CommunityHealthMetrics) GetHealthPercentage() int { - if c == nil || c.HealthPercentage == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetID() int64 { + if a == nil || a.ID == nil { return 0 } - return *c.HealthPercentage + return *a.ID } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (c *CommunityHealthMetrics) GetUpdatedAt() time.Time { - if c == nil || c.UpdatedAt == nil { - return time.Time{} +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetName() string { + if a == nil || a.Name == nil { + return "" } - return *c.UpdatedAt + return *a.Name } -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetAvatarURL() string { - if c == nil || c.AvatarURL == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetNodeID() string { + if a == nil || a.NodeID == nil { return "" } - return *c.AvatarURL + return *a.NodeID } -// GetContributions returns the Contributions field if it's non-nil, zero value otherwise. -func (c *Contributor) GetContributions() int { - if c == nil || c.Contributions == nil { - return 0 +// GetOwner returns the Owner field. +func (a *AppConfig) GetOwner() *User { + if a == nil { + return nil } - return *c.Contributions + return a.Owner } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetEventsURL() string { - if c == nil || c.EventsURL == nil { +// GetPEM returns the PEM field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetPEM() string { + if a == nil || a.PEM == nil { return "" } - return *c.EventsURL + return *a.PEM } -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetFollowersURL() string { - if c == nil || c.FollowersURL == nil { +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetSlug() string { + if a == nil || a.Slug == nil { return "" } - return *c.FollowersURL + return *a.Slug } -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetFollowingURL() string { - if c == nil || c.FollowingURL == nil { - return "" +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} } - return *c.FollowingURL + return *a.UpdatedAt } -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetGistsURL() string { - if c == nil || c.GistsURL == nil { +// GetWebhookSecret returns the WebhookSecret field if it's non-nil, zero value otherwise. +func (a *AppConfig) GetWebhookSecret() string { + if a == nil || a.WebhookSecret == nil { return "" } - return *c.GistsURL + return *a.WebhookSecret } -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetGravatarID() string { - if c == nil || c.GravatarID == nil { - return "" +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (a *AppInstallationRepositoriesRequest) GetRepositories() []string { + if a == nil || a.Repositories == nil { + return nil } - return *c.GravatarID + return a.Repositories } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (a *ArchivedAt) GetFrom() Timestamp { + if a == nil || a.From == nil { + return Timestamp{} } - return *c.HTMLURL + return *a.From } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetID() int64 { - if c == nil || c.ID == nil { - return 0 +// GetTo returns the To field if it's non-nil, zero value otherwise. +func (a *ArchivedAt) GetTo() Timestamp { + if a == nil || a.To == nil { + return Timestamp{} } - return *c.ID + return *a.To } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (c *Contributor) GetLogin() string { - if c == nil || c.Login == nil { +// GetArchiveDownloadURL returns the ArchiveDownloadURL field if it's non-nil, zero value otherwise. +func (a *Artifact) GetArchiveDownloadURL() string { + if a == nil || a.ArchiveDownloadURL == nil { return "" } - return *c.Login + return *a.ArchiveDownloadURL } -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetOrganizationsURL() string { - if c == nil || c.OrganizationsURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *Artifact) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return *c.OrganizationsURL + return *a.CreatedAt } -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetReceivedEventsURL() string { - if c == nil || c.ReceivedEventsURL == nil { +// GetDigest returns the Digest field if it's non-nil, zero value otherwise. +func (a *Artifact) GetDigest() string { + if a == nil || a.Digest == nil { return "" } - return *c.ReceivedEventsURL + return *a.Digest } -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetReposURL() string { - if c == nil || c.ReposURL == nil { - return "" +// GetExpired returns the Expired field if it's non-nil, zero value otherwise. +func (a *Artifact) GetExpired() bool { + if a == nil || a.Expired == nil { + return false } - return *c.ReposURL + return *a.Expired } -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (c *Contributor) GetSiteAdmin() bool { - if c == nil || c.SiteAdmin == nil { - return false +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (a *Artifact) GetExpiresAt() Timestamp { + if a == nil || a.ExpiresAt == nil { + return Timestamp{} } - return *c.SiteAdmin + return *a.ExpiresAt } -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetStarredURL() string { - if c == nil || c.StarredURL == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *Artifact) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return *c.StarredURL + return *a.ID } -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetSubscriptionsURL() string { - if c == nil || c.SubscriptionsURL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *Artifact) GetName() string { + if a == nil || a.Name == nil { return "" } - return *c.SubscriptionsURL + return *a.Name } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (c *Contributor) GetType() string { - if c == nil || c.Type == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (a *Artifact) GetNodeID() string { + if a == nil || a.NodeID == nil { return "" } - return *c.Type + return *a.NodeID +} + +// GetSizeInBytes returns the SizeInBytes field if it's non-nil, zero value otherwise. +func (a *Artifact) GetSizeInBytes() int64 { + if a == nil || a.SizeInBytes == nil { + return 0 + } + return *a.SizeInBytes +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *Artifact) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} + } + return *a.UpdatedAt } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetURL() string { - if c == nil || c.URL == nil { +func (a *Artifact) GetURL() string { + if a == nil || a.URL == nil { return "" } - return *c.URL + return *a.URL } -// GetAuthor returns the Author field. -func (c *ContributorStats) GetAuthor() *Contributor { - if c == nil { +// GetWorkflowRun returns the WorkflowRun field. +func (a *Artifact) GetWorkflowRun() *ArtifactWorkflowRun { + if a == nil { return nil } - return c.Author + return a.WorkflowRun } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *ContributorStats) GetTotal() int { - if c == nil || c.Total == nil { +// GetAttestationID returns the AttestationID field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetAttestationID() int64 { + if a == nil || a.AttestationID == nil { return 0 } - return *c.Total + return *a.AttestationID } -// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. -func (c *CreateCheckRunOptions) GetCompletedAt() Timestamp { - if c == nil || c.CompletedAt == nil { - return Timestamp{} +// GetCluster returns the Cluster field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetCluster() string { + if a == nil || a.Cluster == nil { + return "" } - return *c.CompletedAt + return *a.Cluster } -// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. -func (c *CreateCheckRunOptions) GetConclusion() string { - if c == nil || c.Conclusion == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return *c.Conclusion + return *a.CreatedAt } -// GetDetailsURL returns the DetailsURL field if it's non-nil, zero value otherwise. -func (c *CreateCheckRunOptions) GetDetailsURL() string { - if c == nil || c.DetailsURL == nil { +// GetDeploymentName returns the DeploymentName field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetDeploymentName() string { + if a == nil || a.DeploymentName == nil { return "" } - return *c.DetailsURL + return *a.DeploymentName } -// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. -func (c *CreateCheckRunOptions) GetExternalID() string { - if c == nil || c.ExternalID == nil { +// GetDigest returns the Digest field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetDigest() string { + if a == nil || a.Digest == nil { return "" } - return *c.ExternalID + return *a.Digest } -// GetOutput returns the Output field. -func (c *CreateCheckRunOptions) GetOutput() *CheckRunOutput { - if c == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return c.Output + return *a.ID } -// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. -func (c *CreateCheckRunOptions) GetStartedAt() Timestamp { - if c == nil || c.StartedAt == nil { - return Timestamp{} +// GetLogicalEnvironment returns the LogicalEnvironment field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetLogicalEnvironment() string { + if a == nil || a.LogicalEnvironment == nil { + return "" } - return *c.StartedAt + return *a.LogicalEnvironment } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CreateCheckRunOptions) GetStatus() string { - if c == nil || c.Status == nil { +// GetPhysicalEnvironment returns the PhysicalEnvironment field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetPhysicalEnvironment() string { + if a == nil || a.PhysicalEnvironment == nil { return "" } - return *c.Status + return *a.PhysicalEnvironment } -// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. -func (c *CreateCheckSuiteOptions) GetHeadBranch() string { - if c == nil || c.HeadBranch == nil { - return "" +// GetRuntimeRisks returns the RuntimeRisks slice if it's non-nil, nil otherwise. +func (a *ArtifactDeploymentRecord) GetRuntimeRisks() []DeploymentRuntimeRisk { + if a == nil || a.RuntimeRisks == nil { + return nil } - return *c.HeadBranch + return a.RuntimeRisks } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetDescription() string { - if c == nil || c.Description == nil { - return "" +// GetTags returns the Tags map if it's non-nil, an empty map otherwise. +func (a *ArtifactDeploymentRecord) GetTags() map[string]string { + if a == nil || a.Tags == nil { + return map[string]string{} } - return *c.Description + return a.Tags } -// GetInstallation returns the Installation field. -func (c *CreateEvent) GetInstallation() *Installation { - if c == nil { +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentRecord) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} + } + return *a.UpdatedAt +} + +// GetDeploymentRecords returns the DeploymentRecords slice if it's non-nil, nil otherwise. +func (a *ArtifactDeploymentResponse) GetDeploymentRecords() []*ArtifactDeploymentRecord { + if a == nil || a.DeploymentRecords == nil { return nil } - return c.Installation + return a.DeploymentRecords } -// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetMasterBranch() string { - if c == nil || c.MasterBranch == nil { - return "" +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (a *ArtifactDeploymentResponse) GetTotalCount() int { + if a == nil || a.TotalCount == nil { + return 0 } - return *c.MasterBranch + return *a.TotalCount } -// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetPusherType() string { - if c == nil || c.PusherType == nil { - return "" +// GetArtifacts returns the Artifacts slice if it's non-nil, nil otherwise. +func (a *ArtifactList) GetArtifacts() []*Artifact { + if a == nil || a.Artifacts == nil { + return nil } - return *c.PusherType + return a.Artifacts } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetRef() string { - if c == nil || c.Ref == nil { - return "" +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (a *ArtifactList) GetTotalCount() int64 { + if a == nil || a.TotalCount == nil { + return 0 } - return *c.Ref + return *a.TotalCount } -// GetRefType returns the RefType field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetRefType() string { - if c == nil || c.RefType == nil { - return "" +// GetDays returns the Days field if it's non-nil, zero value otherwise. +func (a *ArtifactPeriod) GetDays() int { + if a == nil || a.Days == nil { + return 0 } - return *c.RefType + return *a.Days } -// GetRepo returns the Repo field. -func (c *CreateEvent) GetRepo() *Repository { - if c == nil { - return nil +// GetMaximumAllowedDays returns the MaximumAllowedDays field if it's non-nil, zero value otherwise. +func (a *ArtifactPeriod) GetMaximumAllowedDays() int { + if a == nil || a.MaximumAllowedDays == nil { + return 0 } - return c.Repo + return *a.MaximumAllowedDays } -// GetSender returns the Sender field. -func (c *CreateEvent) GetSender() *User { - if c == nil { - return nil +// GetDays returns the Days field if it's non-nil, zero value otherwise. +func (a *ArtifactPeriodOpt) GetDays() int { + if a == nil || a.Days == nil { + return 0 } - return c.Sender + return *a.Days } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (c *CreateOrgInvitationOptions) GetEmail() string { - if c == nil || c.Email == nil { +// GetArtifactURL returns the ArtifactURL field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetArtifactURL() string { + if a == nil || a.ArtifactURL == nil { return "" } - return *c.Email + return *a.ArtifactURL } -// GetInviteeID returns the InviteeID field if it's non-nil, zero value otherwise. -func (c *CreateOrgInvitationOptions) GetInviteeID() int64 { - if c == nil || c.InviteeID == nil { - return 0 +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return *c.InviteeID + return *a.CreatedAt } -// GetRole returns the Role field if it's non-nil, zero value otherwise. -func (c *CreateOrgInvitationOptions) GetRole() string { - if c == nil || c.Role == nil { +// GetDigest returns the Digest field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetDigest() string { + if a == nil || a.Digest == nil { return "" } - return *c.Role + return *a.Digest } -// GetInstallation returns the Installation field. -func (d *DeleteEvent) GetInstallation() *Installation { - if d == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return d.Installation + return *a.ID } -// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. -func (d *DeleteEvent) GetPusherType() string { - if d == nil || d.PusherType == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetName() string { + if a == nil || a.Name == nil { return "" } - return *d.PusherType + return *a.Name } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (d *DeleteEvent) GetRef() string { - if d == nil || d.Ref == nil { +// GetRegistryURL returns the RegistryURL field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetRegistryURL() string { + if a == nil || a.RegistryURL == nil { return "" } - return *d.Ref + return *a.RegistryURL } -// GetRefType returns the RefType field if it's non-nil, zero value otherwise. -func (d *DeleteEvent) GetRefType() string { - if d == nil || d.RefType == nil { +// GetRepository returns the Repository field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetRepository() string { + if a == nil || a.Repository == nil { return "" } - return *d.RefType -} - -// GetRepo returns the Repo field. -func (d *DeleteEvent) GetRepo() *Repository { - if d == nil { - return nil - } - return d.Repo + return *a.Repository } -// GetSender returns the Sender field. -func (d *DeleteEvent) GetSender() *User { - if d == nil { - return nil +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetStatus() string { + if a == nil || a.Status == nil { + return "" } - return d.Sender + return *a.Status } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (d *DeployKeyEvent) GetAction() string { - if d == nil || d.Action == nil { - return "" +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageRecord) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} } - return *d.Action + return *a.UpdatedAt } -// GetKey returns the Key field. -func (d *DeployKeyEvent) GetKey() *Key { - if d == nil { +// GetStorageRecords returns the StorageRecords slice if it's non-nil, nil otherwise. +func (a *ArtifactStorageResponse) GetStorageRecords() []*ArtifactStorageRecord { + if a == nil || a.StorageRecords == nil { return nil } - return d.Key + return a.StorageRecords } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (d *Deployment) GetCreatedAt() Timestamp { - if d == nil || d.CreatedAt == nil { - return Timestamp{} +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (a *ArtifactStorageResponse) GetTotalCount() int { + if a == nil || a.TotalCount == nil { + return 0 } - return *d.CreatedAt + return *a.TotalCount } -// GetCreator returns the Creator field. -func (d *Deployment) GetCreator() *User { - if d == nil { - return nil +// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. +func (a *ArtifactWorkflowRun) GetHeadBranch() string { + if a == nil || a.HeadBranch == nil { + return "" } - return d.Creator + return *a.HeadBranch } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *Deployment) GetDescription() string { - if d == nil || d.Description == nil { - return "" +// GetHeadRepositoryID returns the HeadRepositoryID field if it's non-nil, zero value otherwise. +func (a *ArtifactWorkflowRun) GetHeadRepositoryID() int64 { + if a == nil || a.HeadRepositoryID == nil { + return 0 } - return *d.Description + return *a.HeadRepositoryID } -// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. -func (d *Deployment) GetEnvironment() string { - if d == nil || d.Environment == nil { +// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. +func (a *ArtifactWorkflowRun) GetHeadSHA() string { + if a == nil || a.HeadSHA == nil { return "" } - return *d.Environment + return *a.HeadSHA } // GetID returns the ID field if it's non-nil, zero value otherwise. -func (d *Deployment) GetID() int64 { - if d == nil || d.ID == nil { +func (a *ArtifactWorkflowRun) GetID() int64 { + if a == nil || a.ID == nil { return 0 } - return *d.ID + return *a.ID } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (d *Deployment) GetNodeID() string { - if d == nil || d.NodeID == nil { - return "" +// GetRepositoryID returns the RepositoryID field if it's non-nil, zero value otherwise. +func (a *ArtifactWorkflowRun) GetRepositoryID() int64 { + if a == nil || a.RepositoryID == nil { + return 0 } - return *d.NodeID + return *a.RepositoryID } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (d *Deployment) GetRef() string { - if d == nil || d.Ref == nil { +// GetAssignmentName returns the AssignmentName field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetAssignmentName() string { + if a == nil || a.AssignmentName == nil { return "" } - return *d.Ref + return *a.AssignmentName } -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (d *Deployment) GetRepositoryURL() string { - if d == nil || d.RepositoryURL == nil { +// GetAssignmentURL returns the AssignmentURL field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetAssignmentURL() string { + if a == nil || a.AssignmentURL == nil { return "" } - return *d.RepositoryURL + return *a.AssignmentURL } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (d *Deployment) GetSHA() string { - if d == nil || d.SHA == nil { +// GetGithubUsername returns the GithubUsername field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetGithubUsername() string { + if a == nil || a.GithubUsername == nil { return "" } - return *d.SHA + return *a.GithubUsername } -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (d *Deployment) GetStatusesURL() string { - if d == nil || d.StatusesURL == nil { +// GetGroupName returns the GroupName field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetGroupName() string { + if a == nil || a.GroupName == nil { return "" } - return *d.StatusesURL + return *a.GroupName } -// GetTask returns the Task field if it's non-nil, zero value otherwise. -func (d *Deployment) GetTask() string { - if d == nil || d.Task == nil { - return "" +// GetPointsAvailable returns the PointsAvailable field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetPointsAvailable() int { + if a == nil || a.PointsAvailable == nil { + return 0 } - return *d.Task + return *a.PointsAvailable } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (d *Deployment) GetUpdatedAt() Timestamp { - if d == nil || d.UpdatedAt == nil { - return Timestamp{} +// GetPointsAwarded returns the PointsAwarded field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetPointsAwarded() int { + if a == nil || a.PointsAwarded == nil { + return 0 } - return *d.UpdatedAt + return *a.PointsAwarded } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (d *Deployment) GetURL() string { - if d == nil || d.URL == nil { +// GetRosterIdentifier returns the RosterIdentifier field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetRosterIdentifier() string { + if a == nil || a.RosterIdentifier == nil { return "" } - return *d.URL -} - -// GetDeployment returns the Deployment field. -func (d *DeploymentEvent) GetDeployment() *Deployment { - if d == nil { - return nil - } - return d.Deployment + return *a.RosterIdentifier } -// GetInstallation returns the Installation field. -func (d *DeploymentEvent) GetInstallation() *Installation { - if d == nil { - return nil +// GetStarterCodeURL returns the StarterCodeURL field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetStarterCodeURL() string { + if a == nil || a.StarterCodeURL == nil { + return "" } - return d.Installation + return *a.StarterCodeURL } -// GetRepo returns the Repo field. -func (d *DeploymentEvent) GetRepo() *Repository { - if d == nil { - return nil +// GetStudentRepositoryName returns the StudentRepositoryName field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetStudentRepositoryName() string { + if a == nil || a.StudentRepositoryName == nil { + return "" } - return d.Repo + return *a.StudentRepositoryName } -// GetSender returns the Sender field. -func (d *DeploymentEvent) GetSender() *User { - if d == nil { - return nil +// GetStudentRepositoryURL returns the StudentRepositoryURL field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetStudentRepositoryURL() string { + if a == nil || a.StudentRepositoryURL == nil { + return "" } - return d.Sender + return *a.StudentRepositoryURL } -// GetAutoMerge returns the AutoMerge field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetAutoMerge() bool { - if d == nil || d.AutoMerge == nil { - return false +// GetSubmissionTimestamp returns the SubmissionTimestamp field if it's non-nil, zero value otherwise. +func (a *AssignmentGrade) GetSubmissionTimestamp() Timestamp { + if a == nil || a.SubmissionTimestamp == nil { + return Timestamp{} } - return *d.AutoMerge + return *a.SubmissionTimestamp } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetDescription() string { - if d == nil || d.Description == nil { +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (a *Attachment) GetBody() string { + if a == nil || a.Body == nil { return "" } - return *d.Description + return *a.Body } -// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetEnvironment() string { - if d == nil || d.Environment == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *Attachment) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return *d.Environment + return *a.ID } -// GetPayload returns the Payload field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetPayload() string { - if d == nil || d.Payload == nil { +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (a *Attachment) GetTitle() string { + if a == nil || a.Title == nil { return "" } - return *d.Payload + return *a.Title } -// GetProductionEnvironment returns the ProductionEnvironment field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetProductionEnvironment() bool { - if d == nil || d.ProductionEnvironment == nil { - return false +// GetBundle returns the Bundle field. +func (a *Attestation) GetBundle() json.RawMessage { + if a == nil { + return json.RawMessage{} } - return *d.ProductionEnvironment + return a.Bundle } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetRef() string { - if d == nil || d.Ref == nil { - return "" +// GetRepositoryID returns the RepositoryID field. +func (a *Attestation) GetRepositoryID() int64 { + if a == nil { + return 0 } - return *d.Ref + return a.RepositoryID } -// GetRequiredContexts returns the RequiredContexts field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetRequiredContexts() []string { - if d == nil || d.RequiredContexts == nil { +// GetAttestations returns the Attestations slice if it's non-nil, nil otherwise. +func (a *AttestationsResponse) GetAttestations() []*Attestation { + if a == nil || a.Attestations == nil { return nil } - return *d.RequiredContexts + return a.Attestations } -// GetTask returns the Task field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetTask() string { - if d == nil || d.Task == nil { +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetAction() string { + if a == nil || a.Action == nil { return "" } - return *d.Task + return *a.Action } -// GetTransientEnvironment returns the TransientEnvironment field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetTransientEnvironment() bool { - if d == nil || d.TransientEnvironment == nil { - return false +// GetActor returns the Actor field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetActor() string { + if a == nil || a.Actor == nil { + return "" } - return *d.TransientEnvironment + return *a.Actor } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetCreatedAt() Timestamp { - if d == nil || d.CreatedAt == nil { - return Timestamp{} +// GetActorID returns the ActorID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetActorID() int64 { + if a == nil || a.ActorID == nil { + return 0 } - return *d.CreatedAt + return *a.ActorID } -// GetCreator returns the Creator field. -func (d *DeploymentStatus) GetCreator() *User { - if d == nil { +// GetActorLocation returns the ActorLocation field. +func (a *AuditEntry) GetActorLocation() *ActorLocation { + if a == nil { return nil } - return d.Creator + return a.ActorLocation } -// GetDeploymentURL returns the DeploymentURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetDeploymentURL() string { - if d == nil || d.DeploymentURL == nil { - return "" +// GetAdditionalFields returns the AdditionalFields map if it's non-nil, an empty map otherwise. +func (a *AuditEntry) GetAdditionalFields() map[string]any { + if a == nil || a.AdditionalFields == nil { + return map[string]any{} } - return *d.DeploymentURL + return a.AdditionalFields } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetDescription() string { - if d == nil || d.Description == nil { +// GetBusiness returns the Business field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetBusiness() string { + if a == nil || a.Business == nil { return "" } - return *d.Description + return *a.Business } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetID() int64 { - if d == nil || d.ID == nil { +// GetBusinessID returns the BusinessID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetBusinessID() int64 { + if a == nil || a.BusinessID == nil { return 0 } - return *d.ID + return *a.BusinessID } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetNodeID() string { - if d == nil || d.NodeID == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} } - return *d.NodeID + return *a.CreatedAt } -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetRepositoryURL() string { - if d == nil || d.RepositoryURL == nil { - return "" +// GetData returns the Data map if it's non-nil, an empty map otherwise. +func (a *AuditEntry) GetData() map[string]any { + if a == nil || a.Data == nil { + return map[string]any{} } - return *d.RepositoryURL + return a.Data } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetState() string { - if d == nil || d.State == nil { +// GetDocumentID returns the DocumentID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetDocumentID() string { + if a == nil || a.DocumentID == nil { return "" } - return *d.State + return *a.DocumentID } -// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetTargetURL() string { - if d == nil || d.TargetURL == nil { +// GetExternalIdentityNameID returns the ExternalIdentityNameID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetExternalIdentityNameID() string { + if a == nil || a.ExternalIdentityNameID == nil { return "" } - return *d.TargetURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetUpdatedAt() Timestamp { - if d == nil || d.UpdatedAt == nil { - return Timestamp{} - } - return *d.UpdatedAt + return *a.ExternalIdentityNameID } -// GetDeployment returns the Deployment field. -func (d *DeploymentStatusEvent) GetDeployment() *Deployment { - if d == nil { - return nil +// GetExternalIdentityUsername returns the ExternalIdentityUsername field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetExternalIdentityUsername() string { + if a == nil || a.ExternalIdentityUsername == nil { + return "" } - return d.Deployment + return *a.ExternalIdentityUsername } -// GetDeploymentStatus returns the DeploymentStatus field. -func (d *DeploymentStatusEvent) GetDeploymentStatus() *DeploymentStatus { - if d == nil { - return nil +// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetHashedToken() string { + if a == nil || a.HashedToken == nil { + return "" } - return d.DeploymentStatus + return *a.HashedToken } -// GetInstallation returns the Installation field. -func (d *DeploymentStatusEvent) GetInstallation() *Installation { - if d == nil { - return nil +// GetOrg returns the Org field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetOrg() string { + if a == nil || a.Org == nil { + return "" } - return d.Installation + return *a.Org } -// GetRepo returns the Repo field. -func (d *DeploymentStatusEvent) GetRepo() *Repository { - if d == nil { - return nil +// GetOrgID returns the OrgID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetOrgID() int64 { + if a == nil || a.OrgID == nil { + return 0 } - return d.Repo + return *a.OrgID } -// GetSender returns the Sender field. -func (d *DeploymentStatusEvent) GetSender() *User { - if d == nil { - return nil +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetTimestamp() Timestamp { + if a == nil || a.Timestamp == nil { + return Timestamp{} } - return d.Sender + return *a.Timestamp } -// GetAutoInactive returns the AutoInactive field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetAutoInactive() bool { - if d == nil || d.AutoInactive == nil { - return false +// GetTokenID returns the TokenID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetTokenID() int64 { + if a == nil || a.TokenID == nil { + return 0 } - return *d.AutoInactive + return *a.TokenID } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetDescription() string { - if d == nil || d.Description == nil { +// GetTokenScopes returns the TokenScopes field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetTokenScopes() string { + if a == nil || a.TokenScopes == nil { return "" } - return *d.Description + return *a.TokenScopes } -// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetEnvironment() string { - if d == nil || d.Environment == nil { +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetUser() string { + if a == nil || a.User == nil { return "" } - return *d.Environment + return *a.User } -// GetEnvironmentURL returns the EnvironmentURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetEnvironmentURL() string { - if d == nil || d.EnvironmentURL == nil { - return "" +// GetUserID returns the UserID field if it's non-nil, zero value otherwise. +func (a *AuditEntry) GetUserID() int64 { + if a == nil || a.UserID == nil { + return 0 } - return *d.EnvironmentURL + return *a.UserID } -// GetLogURL returns the LogURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetLogURL() string { - if d == nil || d.LogURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field. +func (a *AuditLogStream) GetCreatedAt() Timestamp { + if a == nil { + return Timestamp{} } - return *d.LogURL + return a.CreatedAt } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetState() string { - if d == nil || d.State == nil { - return "" +// GetEnabled returns the Enabled field. +func (a *AuditLogStream) GetEnabled() bool { + if a == nil { + return false } - return *d.State + return a.Enabled } -// GetAuthor returns the Author field. -func (d *DiscussionComment) GetAuthor() *User { - if d == nil { - return nil +// GetID returns the ID field. +func (a *AuditLogStream) GetID() int64 { + if a == nil { + return 0 } - return d.Author + return a.ID } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetBody() string { - if d == nil || d.Body == nil { - return "" +// GetPausedAt returns the PausedAt field if it's non-nil, zero value otherwise. +func (a *AuditLogStream) GetPausedAt() Timestamp { + if a == nil || a.PausedAt == nil { + return Timestamp{} } - return *d.Body + return *a.PausedAt } -// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetBodyHTML() string { - if d == nil || d.BodyHTML == nil { +// GetStreamDetails returns the StreamDetails field. +func (a *AuditLogStream) GetStreamDetails() string { + if a == nil { return "" } - return *d.BodyHTML + return a.StreamDetails } -// GetBodyVersion returns the BodyVersion field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetBodyVersion() string { - if d == nil || d.BodyVersion == nil { +// GetStreamType returns the StreamType field. +func (a *AuditLogStream) GetStreamType() string { + if a == nil { return "" } - return *d.BodyVersion + return a.StreamType } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetCreatedAt() Timestamp { - if d == nil || d.CreatedAt == nil { +// GetUpdatedAt returns the UpdatedAt field. +func (a *AuditLogStream) GetUpdatedAt() Timestamp { + if a == nil { return Timestamp{} } - return *d.CreatedAt + return a.UpdatedAt } -// GetDiscussionURL returns the DiscussionURL field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetDiscussionURL() string { - if d == nil || d.DiscussionURL == nil { - return "" +// GetEnabled returns the Enabled field. +func (a *AuditLogStreamConfig) GetEnabled() bool { + if a == nil { + return false } - return *d.DiscussionURL + return a.Enabled } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetHTMLURL() string { - if d == nil || d.HTMLURL == nil { +// GetStreamType returns the StreamType field. +func (a *AuditLogStreamConfig) GetStreamType() string { + if a == nil { return "" } - return *d.HTMLURL + return a.StreamType } -// GetLastEditedAt returns the LastEditedAt field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetLastEditedAt() Timestamp { - if d == nil || d.LastEditedAt == nil { - return Timestamp{} +// GetVendorSpecific returns the VendorSpecific field. +func (a *AuditLogStreamConfig) GetVendorSpecific() AuditLogStreamVendorConfig { + if a == nil { + return nil } - return *d.LastEditedAt + return a.VendorSpecific } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetNodeID() string { - if d == nil || d.NodeID == nil { +// GetKey returns the Key field. +func (a *AuditLogStreamKey) GetKey() string { + if a == nil { return "" } - return *d.NodeID + return a.Key } -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetNumber() int { - if d == nil || d.Number == nil { - return 0 +// GetKeyID returns the KeyID field. +func (a *AuditLogStreamKey) GetKeyID() string { + if a == nil { + return "" } - return *d.Number + return a.KeyID } -// GetReactions returns the Reactions field. -func (d *DiscussionComment) GetReactions() *Reactions { - if d == nil { +// GetApp returns the App field. +func (a *Authorization) GetApp() *AuthorizationApp { + if a == nil { return nil } - return d.Reactions + return a.App } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetUpdatedAt() Timestamp { - if d == nil || d.UpdatedAt == nil { +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *Authorization) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { return Timestamp{} } - return *d.UpdatedAt + return *a.CreatedAt } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (d *DiscussionComment) GetURL() string { - if d == nil || d.URL == nil { +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (a *Authorization) GetFingerprint() string { + if a == nil || a.Fingerprint == nil { return "" } - return *d.URL + return *a.Fingerprint } -// GetTeams returns the Teams field if it's non-nil, zero value otherwise. -func (d *DismissalRestrictionsRequest) GetTeams() []string { - if d == nil || d.Teams == nil { - return nil +// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise. +func (a *Authorization) GetHashedToken() string { + if a == nil || a.HashedToken == nil { + return "" } - return *d.Teams + return *a.HashedToken } -// GetUsers returns the Users field if it's non-nil, zero value otherwise. -func (d *DismissalRestrictionsRequest) GetUsers() []string { - if d == nil || d.Users == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *Authorization) GetID() int64 { + if a == nil || a.ID == nil { + return 0 } - return *d.Users + return *a.ID } -// GetDismissalCommitID returns the DismissalCommitID field if it's non-nil, zero value otherwise. -func (d *DismissedReview) GetDismissalCommitID() string { - if d == nil || d.DismissalCommitID == nil { +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (a *Authorization) GetNote() string { + if a == nil || a.Note == nil { return "" } - return *d.DismissalCommitID + return *a.Note } -// GetDismissalMessage returns the DismissalMessage field if it's non-nil, zero value otherwise. -func (d *DismissedReview) GetDismissalMessage() string { - if d == nil || d.DismissalMessage == nil { +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (a *Authorization) GetNoteURL() string { + if a == nil || a.NoteURL == nil { return "" } - return *d.DismissalMessage + return *a.NoteURL } -// GetReviewID returns the ReviewID field if it's non-nil, zero value otherwise. -func (d *DismissedReview) GetReviewID() int64 { - if d == nil || d.ReviewID == nil { - return 0 +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (a *Authorization) GetScopes() []Scope { + if a == nil || a.Scopes == nil { + return nil } - return *d.ReviewID + return a.Scopes } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (d *DismissedReview) GetState() string { - if d == nil || d.State == nil { +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (a *Authorization) GetToken() string { + if a == nil || a.Token == nil { return "" } - return *d.State + return *a.Token } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (d *DraftReviewComment) GetBody() string { - if d == nil || d.Body == nil { +// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. +func (a *Authorization) GetTokenLastEight() string { + if a == nil || a.TokenLastEight == nil { return "" } - return *d.Body + return *a.TokenLastEight } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (d *DraftReviewComment) GetPath() string { - if d == nil || d.Path == nil { - return "" +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *Authorization) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} } - return *d.Path + return *a.UpdatedAt } -// GetPosition returns the Position field if it's non-nil, zero value otherwise. -func (d *DraftReviewComment) GetPosition() int { - if d == nil || d.Position == nil { - return 0 +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *Authorization) GetURL() string { + if a == nil || a.URL == nil { + return "" } - return *d.Position + return *a.URL } -// GetActor returns the Actor field. -func (e *Event) GetActor() *User { - if e == nil { +// GetUser returns the User field. +func (a *Authorization) GetUser() *User { + if a == nil { return nil } - return e.Actor -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (e *Event) GetCreatedAt() time.Time { - if e == nil || e.CreatedAt == nil { - return time.Time{} - } - return *e.CreatedAt + return a.User } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (e *Event) GetID() string { - if e == nil || e.ID == nil { +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (a *AuthorizationApp) GetClientID() string { + if a == nil || a.ClientID == nil { return "" } - return *e.ID + return *a.ClientID } -// GetOrg returns the Org field. -func (e *Event) GetOrg() *Organization { - if e == nil { - return nil +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (a *AuthorizationApp) GetName() string { + if a == nil || a.Name == nil { + return "" } - return e.Org + return *a.Name } -// GetPublic returns the Public field if it's non-nil, zero value otherwise. -func (e *Event) GetPublic() bool { - if e == nil || e.Public == nil { - return false +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (a *AuthorizationApp) GetURL() string { + if a == nil || a.URL == nil { + return "" } - return *e.Public + return *a.URL } -// GetRawPayload returns the RawPayload field if it's non-nil, zero value otherwise. -func (e *Event) GetRawPayload() json.RawMessage { - if e == nil || e.RawPayload == nil { - return json.RawMessage{} +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetClientID() string { + if a == nil || a.ClientID == nil { + return "" } - return *e.RawPayload + return *a.ClientID } -// GetRepo returns the Repo field. -func (e *Event) GetRepo() *Repository { - if e == nil { - return nil +// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetClientSecret() string { + if a == nil || a.ClientSecret == nil { + return "" } - return e.Repo + return *a.ClientSecret } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (e *Event) GetType() string { - if e == nil || e.Type == nil { +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetFingerprint() string { + if a == nil || a.Fingerprint == nil { return "" } - return *e.Type + return *a.Fingerprint } -// GetHRef returns the HRef field if it's non-nil, zero value otherwise. -func (f *FeedLink) GetHRef() string { - if f == nil || f.HRef == nil { +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetNote() string { + if a == nil || a.Note == nil { return "" } - return *f.HRef + return *a.Note } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (f *FeedLink) GetType() string { - if f == nil || f.Type == nil { +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (a *AuthorizationRequest) GetNoteURL() string { + if a == nil || a.NoteURL == nil { return "" } - return *f.Type + return *a.NoteURL } -// GetCurrentUserActorURL returns the CurrentUserActorURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserActorURL() string { - if f == nil || f.CurrentUserActorURL == nil { - return "" +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (a *AuthorizationRequest) GetScopes() []Scope { + if a == nil || a.Scopes == nil { + return nil } - return *f.CurrentUserActorURL + return a.Scopes } -// GetCurrentUserOrganizationURL returns the CurrentUserOrganizationURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserOrganizationURL() string { - if f == nil || f.CurrentUserOrganizationURL == nil { - return "" +// GetAddScopes returns the AddScopes slice if it's non-nil, nil otherwise. +func (a *AuthorizationUpdateRequest) GetAddScopes() []string { + if a == nil || a.AddScopes == nil { + return nil } - return *f.CurrentUserOrganizationURL + return a.AddScopes } -// GetCurrentUserPublicURL returns the CurrentUserPublicURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserPublicURL() string { - if f == nil || f.CurrentUserPublicURL == nil { +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (a *AuthorizationUpdateRequest) GetFingerprint() string { + if a == nil || a.Fingerprint == nil { return "" } - return *f.CurrentUserPublicURL + return *a.Fingerprint } -// GetCurrentUserURL returns the CurrentUserURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserURL() string { - if f == nil || f.CurrentUserURL == nil { +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (a *AuthorizationUpdateRequest) GetNote() string { + if a == nil || a.Note == nil { return "" } - return *f.CurrentUserURL + return *a.Note } -// GetTimelineURL returns the TimelineURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetTimelineURL() string { - if f == nil || f.TimelineURL == nil { +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (a *AuthorizationUpdateRequest) GetNoteURL() string { + if a == nil || a.NoteURL == nil { return "" } - return *f.TimelineURL + return *a.NoteURL } -// GetUserURL returns the UserURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetUserURL() string { - if f == nil || f.UserURL == nil { - return "" +// GetRemoveScopes returns the RemoveScopes slice if it's non-nil, nil otherwise. +func (a *AuthorizationUpdateRequest) GetRemoveScopes() []string { + if a == nil || a.RemoveScopes == nil { + return nil } - return *f.UserURL + return a.RemoveScopes } -// GetForkee returns the Forkee field. -func (f *ForkEvent) GetForkee() *Repository { - if f == nil { +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (a *AuthorizationUpdateRequest) GetScopes() []string { + if a == nil || a.Scopes == nil { return nil } - return f.Forkee + return a.Scopes } -// GetInstallation returns the Installation field. -func (f *ForkEvent) GetInstallation() *Installation { - if f == nil { +// GetFrom returns the From slice if it's non-nil, nil otherwise. +func (a *AuthorizedActorNames) GetFrom() []string { + if a == nil || a.From == nil { return nil } - return f.Installation + return a.From } -// GetRepo returns the Repo field. -func (f *ForkEvent) GetRepo() *Repository { - if f == nil { - return nil +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (a *AuthorizedActorsOnly) GetFrom() bool { + if a == nil || a.From == nil { + return false } - return f.Repo + return *a.From } -// GetSender returns the Sender field. -func (f *ForkEvent) GetSender() *User { - if f == nil { - return nil +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (a *AuthorizedDismissalActorsOnlyChanges) GetFrom() bool { + if a == nil || a.From == nil { + return false } - return f.Sender + return *a.From } -// GetComments returns the Comments field if it's non-nil, zero value otherwise. -func (g *Gist) GetComments() int { - if g == nil || g.Comments == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (a *Autolink) GetID() int64 { + if a == nil || a.ID == nil { return 0 } - return *g.Comments + return *a.ID } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *Gist) GetCreatedAt() time.Time { - if g == nil || g.CreatedAt == nil { - return time.Time{} +// GetIsAlphanumeric returns the IsAlphanumeric field if it's non-nil, zero value otherwise. +func (a *Autolink) GetIsAlphanumeric() bool { + if a == nil || a.IsAlphanumeric == nil { + return false } - return *g.CreatedAt + return *a.IsAlphanumeric } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (g *Gist) GetDescription() string { - if g == nil || g.Description == nil { +// GetKeyPrefix returns the KeyPrefix field if it's non-nil, zero value otherwise. +func (a *Autolink) GetKeyPrefix() string { + if a == nil || a.KeyPrefix == nil { return "" } - return *g.Description + return *a.KeyPrefix } -// GetGitPullURL returns the GitPullURL field if it's non-nil, zero value otherwise. -func (g *Gist) GetGitPullURL() string { - if g == nil || g.GitPullURL == nil { +// GetURLTemplate returns the URLTemplate field if it's non-nil, zero value otherwise. +func (a *Autolink) GetURLTemplate() string { + if a == nil || a.URLTemplate == nil { return "" } - return *g.GitPullURL + return *a.URLTemplate } -// GetGitPushURL returns the GitPushURL field if it's non-nil, zero value otherwise. -func (g *Gist) GetGitPushURL() string { - if g == nil || g.GitPushURL == nil { - return "" +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (a *AutomatedSecurityFixes) GetEnabled() bool { + if a == nil || a.Enabled == nil { + return false } - return *g.GitPushURL + return *a.Enabled } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (g *Gist) GetHTMLURL() string { - if g == nil || g.HTMLURL == nil { - return "" +// GetPaused returns the Paused field if it's non-nil, zero value otherwise. +func (a *AutomatedSecurityFixes) GetPaused() bool { + if a == nil || a.Paused == nil { + return false } - return *g.HTMLURL + return *a.Paused } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *Gist) GetID() string { - if g == nil || g.ID == nil { - return "" +// GetAppID returns the AppID field if it's non-nil, zero value otherwise. +func (a *AutoTriggerCheck) GetAppID() int64 { + if a == nil || a.AppID == nil { + return 0 } - return *g.ID + return *a.AppID } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (g *Gist) GetNodeID() string { - if g == nil || g.NodeID == nil { +// GetSetting returns the Setting field if it's non-nil, zero value otherwise. +func (a *AutoTriggerCheck) GetSetting() bool { + if a == nil || a.Setting == nil { + return false + } + return *a.Setting +} + +// GetContainer returns the Container field. +func (a *AzureBlobConfig) GetContainer() string { + if a == nil { return "" } - return *g.NodeID + return a.Container } -// GetOwner returns the Owner field. -func (g *Gist) GetOwner() *User { - if g == nil { - return nil +// GetEncryptedSASURL returns the EncryptedSASURL field. +func (a *AzureBlobConfig) GetEncryptedSASURL() string { + if a == nil { + return "" } - return g.Owner + return a.EncryptedSASURL } -// GetPublic returns the Public field if it's non-nil, zero value otherwise. -func (g *Gist) GetPublic() bool { - if g == nil || g.Public == nil { - return false +// GetKeyID returns the KeyID field. +func (a *AzureBlobConfig) GetKeyID() string { + if a == nil { + return "" } - return *g.Public + return a.KeyID } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *Gist) GetUpdatedAt() time.Time { - if g == nil || g.UpdatedAt == nil { - return time.Time{} +// GetEncryptedConnstring returns the EncryptedConnstring field. +func (a *AzureHubConfig) GetEncryptedConnstring() string { + if a == nil { + return "" } - return *g.UpdatedAt + return a.EncryptedConnstring } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (g *GistComment) GetBody() string { - if g == nil || g.Body == nil { +// GetKeyID returns the KeyID field. +func (a *AzureHubConfig) GetKeyID() string { + if a == nil { return "" } - return *g.Body + return a.KeyID } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GistComment) GetCreatedAt() time.Time { - if g == nil || g.CreatedAt == nil { - return time.Time{} +// GetName returns the Name field. +func (a *AzureHubConfig) GetName() string { + if a == nil { + return "" } - return *g.CreatedAt + return a.Name } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *GistComment) GetID() int64 { - if g == nil || g.ID == nil { - return 0 - } - return *g.ID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GistComment) GetURL() string { - if g == nil || g.URL == nil { +// GetOTP returns the OTP field. +func (b *BasicAuthTransport) GetOTP() string { + if b == nil { return "" } - return *g.URL + return b.OTP } -// GetUser returns the User field. -func (g *GistComment) GetUser() *User { - if g == nil { - return nil +// GetPassword returns the Password field. +func (b *BasicAuthTransport) GetPassword() string { + if b == nil { + return "" } - return g.User + return b.Password } -// GetChangeStatus returns the ChangeStatus field. -func (g *GistCommit) GetChangeStatus() *CommitStats { - if g == nil { - return nil +// GetUsername returns the Username field. +func (b *BasicAuthTransport) GetUsername() string { + if b == nil { + return "" } - return g.ChangeStatus + return b.Username } -// GetCommittedAt returns the CommittedAt field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetCommittedAt() Timestamp { - if g == nil || g.CommittedAt == nil { - return Timestamp{} +// GetID returns the ID field. +func (b *BillingCostCenter) GetID() string { + if b == nil { + return "" } - return *g.CommittedAt + return b.ID } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetNodeID() string { - if g == nil || g.NodeID == nil { +// GetName returns the Name field. +func (b *BillingCostCenter) GetName() string { + if b == nil { return "" } - return *g.NodeID + return b.Name } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetURL() string { - if g == nil || g.URL == nil { +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (b *Blob) GetContent() string { + if b == nil || b.Content == nil { return "" } - return *g.URL + return *b.Content } -// GetUser returns the User field. -func (g *GistCommit) GetUser() *User { - if g == nil { - return nil +// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. +func (b *Blob) GetEncoding() string { + if b == nil || b.Encoding == nil { + return "" } - return g.User + return *b.Encoding } -// GetVersion returns the Version field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetVersion() string { - if g == nil || g.Version == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (b *Blob) GetNodeID() string { + if b == nil || b.NodeID == nil { return "" } - return *g.Version + return *b.NodeID } -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (g *GistFile) GetContent() string { - if g == nil || g.Content == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (b *Blob) GetSHA() string { + if b == nil || b.SHA == nil { return "" } - return *g.Content + return *b.SHA } -// GetFilename returns the Filename field if it's non-nil, zero value otherwise. -func (g *GistFile) GetFilename() string { - if g == nil || g.Filename == nil { - return "" +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (b *Blob) GetSize() int { + if b == nil || b.Size == nil { + return 0 } - return *g.Filename + return *b.Size } -// GetLanguage returns the Language field if it's non-nil, zero value otherwise. -func (g *GistFile) GetLanguage() string { - if g == nil || g.Language == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (b *Blob) GetURL() string { + if b == nil || b.URL == nil { return "" } - return *g.Language + return *b.URL } -// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. -func (g *GistFile) GetRawURL() string { - if g == nil || g.RawURL == nil { - return "" +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (b *BlockCreations) GetEnabled() bool { + if b == nil || b.Enabled == nil { + return false } - return *g.RawURL + return *b.Enabled } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (g *GistFile) GetSize() int { - if g == nil || g.Size == nil { - return 0 +// GetCommit returns the Commit field. +func (b *Branch) GetCommit() *RepositoryCommit { + if b == nil { + return nil } - return *g.Size + return b.Commit } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (g *GistFile) GetType() string { - if g == nil || g.Type == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (b *Branch) GetName() string { + if b == nil || b.Name == nil { return "" } - return *g.Type + return *b.Name } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GistFork) GetCreatedAt() Timestamp { - if g == nil || g.CreatedAt == nil { - return Timestamp{} +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (b *Branch) GetProtected() bool { + if b == nil || b.Protected == nil { + return false } - return *g.CreatedAt + return *b.Protected } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *GistFork) GetID() string { - if g == nil || g.ID == nil { - return "" +// GetProtection returns the Protection field. +func (b *Branch) GetProtection() *Protection { + if b == nil { + return nil } - return *g.ID + return b.Protection } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (g *GistFork) GetNodeID() string { - if g == nil || g.NodeID == nil { +// GetProtectionURL returns the ProtectionURL field if it's non-nil, zero value otherwise. +func (b *Branch) GetProtectionURL() string { + if b == nil || b.ProtectionURL == nil { return "" } - return *g.NodeID + return *b.ProtectionURL } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *GistFork) GetUpdatedAt() Timestamp { - if g == nil || g.UpdatedAt == nil { - return Timestamp{} +// GetCommit returns the Commit field. +func (b *BranchCommit) GetCommit() *Commit { + if b == nil { + return nil } - return *g.UpdatedAt + return b.Commit } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GistFork) GetURL() string { - if g == nil || g.URL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (b *BranchCommit) GetName() string { + if b == nil || b.Name == nil { return "" } - return *g.URL + return *b.Name } -// GetUser returns the User field. -func (g *GistFork) GetUser() *User { - if g == nil { - return nil +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (b *BranchCommit) GetProtected() bool { + if b == nil || b.Protected == nil { + return false } - return g.User + return *b.Protected } -// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. -func (g *GistStats) GetPrivateGists() int { - if g == nil || g.PrivateGists == nil { - return 0 +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (b *BranchListOptions) GetProtected() bool { + if b == nil || b.Protected == nil { + return false } - return *g.PrivateGists + return *b.Protected } -// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. -func (g *GistStats) GetPublicGists() int { - if g == nil || g.PublicGists == nil { - return 0 +// GetCustomBranchPolicies returns the CustomBranchPolicies field if it's non-nil, zero value otherwise. +func (b *BranchPolicy) GetCustomBranchPolicies() bool { + if b == nil || b.CustomBranchPolicies == nil { + return false } - return *g.PublicGists + return *b.CustomBranchPolicies } -// GetTotalGists returns the TotalGists field if it's non-nil, zero value otherwise. -func (g *GistStats) GetTotalGists() int { - if g == nil || g.TotalGists == nil { - return 0 +// GetProtectedBranches returns the ProtectedBranches field if it's non-nil, zero value otherwise. +func (b *BranchPolicy) GetProtectedBranches() bool { + if b == nil || b.ProtectedBranches == nil { + return false } - return *g.TotalGists + return *b.ProtectedBranches } // GetAction returns the Action field if it's non-nil, zero value otherwise. -func (g *GitHubAppAuthorizationEvent) GetAction() string { - if g == nil || g.Action == nil { +func (b *BranchProtectionConfigurationEvent) GetAction() string { + if b == nil || b.Action == nil { return "" } - return *g.Action + return *b.Action } -// GetSender returns the Sender field. -func (g *GitHubAppAuthorizationEvent) GetSender() *User { - if g == nil { +// GetEnterprise returns the Enterprise field. +func (b *BranchProtectionConfigurationEvent) GetEnterprise() *Enterprise { + if b == nil { return nil } - return g.Sender + return b.Enterprise } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (g *Gitignore) GetName() string { - if g == nil || g.Name == nil { - return "" +// GetInstallation returns the Installation field. +func (b *BranchProtectionConfigurationEvent) GetInstallation() *Installation { + if b == nil { + return nil } - return *g.Name + return b.Installation } -// GetSource returns the Source field if it's non-nil, zero value otherwise. -func (g *Gitignore) GetSource() string { - if g == nil || g.Source == nil { - return "" +// GetOrg returns the Org field. +func (b *BranchProtectionConfigurationEvent) GetOrg() *Organization { + if b == nil { + return nil } - return *g.Source + return b.Org } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (g *GitObject) GetSHA() string { - if g == nil || g.SHA == nil { - return "" +// GetRepo returns the Repo field. +func (b *BranchProtectionConfigurationEvent) GetRepo() *Repository { + if b == nil { + return nil } - return *g.SHA + return b.Repo } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (g *GitObject) GetType() string { - if g == nil || g.Type == nil { - return "" +// GetSender returns the Sender field. +func (b *BranchProtectionConfigurationEvent) GetSender() *User { + if b == nil { + return nil } - return *g.Type + return b.Sender } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GitObject) GetURL() string { - if g == nil || g.URL == nil { - return "" +// GetAdminEnforced returns the AdminEnforced field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetAdminEnforced() bool { + if b == nil || b.AdminEnforced == nil { + return false } - return *g.URL + return *b.AdminEnforced } -// GetInstallation returns the Installation field. -func (g *GollumEvent) GetInstallation() *Installation { - if g == nil { - return nil +// GetAllowDeletionsEnforcementLevel returns the AllowDeletionsEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetAllowDeletionsEnforcementLevel() string { + if b == nil || b.AllowDeletionsEnforcementLevel == nil { + return "" } - return g.Installation + return *b.AllowDeletionsEnforcementLevel } -// GetRepo returns the Repo field. -func (g *GollumEvent) GetRepo() *Repository { - if g == nil { - return nil +// GetAllowForcePushesEnforcementLevel returns the AllowForcePushesEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetAllowForcePushesEnforcementLevel() string { + if b == nil || b.AllowForcePushesEnforcementLevel == nil { + return "" } - return g.Repo + return *b.AllowForcePushesEnforcementLevel } -// GetSender returns the Sender field. -func (g *GollumEvent) GetSender() *User { - if g == nil { +// GetAuthorizedActorNames returns the AuthorizedActorNames slice if it's non-nil, nil otherwise. +func (b *BranchProtectionRule) GetAuthorizedActorNames() []string { + if b == nil || b.AuthorizedActorNames == nil { return nil } - return g.Sender + return b.AuthorizedActorNames } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (g *GPGEmail) GetEmail() string { - if g == nil || g.Email == nil { - return "" +// GetAuthorizedActorsOnly returns the AuthorizedActorsOnly field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetAuthorizedActorsOnly() bool { + if b == nil || b.AuthorizedActorsOnly == nil { + return false } - return *g.Email + return *b.AuthorizedActorsOnly } -// GetVerified returns the Verified field if it's non-nil, zero value otherwise. -func (g *GPGEmail) GetVerified() bool { - if g == nil || g.Verified == nil { +// GetAuthorizedDismissalActorsOnly returns the AuthorizedDismissalActorsOnly field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetAuthorizedDismissalActorsOnly() bool { + if b == nil || b.AuthorizedDismissalActorsOnly == nil { return false } - return *g.Verified + return *b.AuthorizedDismissalActorsOnly } -// GetCanCertify returns the CanCertify field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanCertify() bool { - if g == nil || g.CanCertify == nil { - return false +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetCreatedAt() Timestamp { + if b == nil || b.CreatedAt == nil { + return Timestamp{} } - return *g.CanCertify + return *b.CreatedAt } -// GetCanEncryptComms returns the CanEncryptComms field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanEncryptComms() bool { - if g == nil || g.CanEncryptComms == nil { +// GetDismissStaleReviewsOnPush returns the DismissStaleReviewsOnPush field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetDismissStaleReviewsOnPush() bool { + if b == nil || b.DismissStaleReviewsOnPush == nil { return false } - return *g.CanEncryptComms + return *b.DismissStaleReviewsOnPush } -// GetCanEncryptStorage returns the CanEncryptStorage field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanEncryptStorage() bool { - if g == nil || g.CanEncryptStorage == nil { - return false +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetID() int64 { + if b == nil || b.ID == nil { + return 0 } - return *g.CanEncryptStorage + return *b.ID } -// GetCanSign returns the CanSign field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanSign() bool { - if g == nil || g.CanSign == nil { +// GetIgnoreApprovalsFromContributors returns the IgnoreApprovalsFromContributors field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetIgnoreApprovalsFromContributors() bool { + if b == nil || b.IgnoreApprovalsFromContributors == nil { return false } - return *g.CanSign + return *b.IgnoreApprovalsFromContributors } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCreatedAt() time.Time { - if g == nil || g.CreatedAt == nil { - return time.Time{} +// GetLinearHistoryRequirementEnforcementLevel returns the LinearHistoryRequirementEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetLinearHistoryRequirementEnforcementLevel() string { + if b == nil || b.LinearHistoryRequirementEnforcementLevel == nil { + return "" } - return *g.CreatedAt + return *b.LinearHistoryRequirementEnforcementLevel } -// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetExpiresAt() time.Time { - if g == nil || g.ExpiresAt == nil { - return time.Time{} +// GetMergeQueueEnforcementLevel returns the MergeQueueEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetMergeQueueEnforcementLevel() string { + if b == nil || b.MergeQueueEnforcementLevel == nil { + return "" } - return *g.ExpiresAt + return *b.MergeQueueEnforcementLevel } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetID() int64 { - if g == nil || g.ID == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetName() string { + if b == nil || b.Name == nil { + return "" } - return *g.ID + return *b.Name } -// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetKeyID() string { - if g == nil || g.KeyID == nil { +// GetPullRequestReviewsEnforcementLevel returns the PullRequestReviewsEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetPullRequestReviewsEnforcementLevel() string { + if b == nil || b.PullRequestReviewsEnforcementLevel == nil { return "" } - return *g.KeyID + return *b.PullRequestReviewsEnforcementLevel } -// GetPrimaryKeyID returns the PrimaryKeyID field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetPrimaryKeyID() int64 { - if g == nil || g.PrimaryKeyID == nil { +// GetRepositoryID returns the RepositoryID field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRepositoryID() int64 { + if b == nil || b.RepositoryID == nil { return 0 } - return *g.PrimaryKeyID + return *b.RepositoryID } -// GetPublicKey returns the PublicKey field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetPublicKey() string { - if g == nil || g.PublicKey == nil { - return "" +// GetRequireCodeOwnerReview returns the RequireCodeOwnerReview field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRequireCodeOwnerReview() bool { + if b == nil || b.RequireCodeOwnerReview == nil { + return false } - return *g.PublicKey + return *b.RequireCodeOwnerReview } -// GetApp returns the App field. -func (g *Grant) GetApp() *AuthorizationApp { - if g == nil { - return nil +// GetRequiredApprovingReviewCount returns the RequiredApprovingReviewCount field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRequiredApprovingReviewCount() int { + if b == nil || b.RequiredApprovingReviewCount == nil { + return 0 } - return g.App + return *b.RequiredApprovingReviewCount } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *Grant) GetCreatedAt() Timestamp { - if g == nil || g.CreatedAt == nil { - return Timestamp{} +// GetRequiredConversationResolutionLevel returns the RequiredConversationResolutionLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRequiredConversationResolutionLevel() string { + if b == nil || b.RequiredConversationResolutionLevel == nil { + return "" } - return *g.CreatedAt + return *b.RequiredConversationResolutionLevel } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *Grant) GetID() int64 { - if g == nil || g.ID == nil { - return 0 +// GetRequiredDeploymentsEnforcementLevel returns the RequiredDeploymentsEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRequiredDeploymentsEnforcementLevel() string { + if b == nil || b.RequiredDeploymentsEnforcementLevel == nil { + return "" } - return *g.ID + return *b.RequiredDeploymentsEnforcementLevel } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *Grant) GetUpdatedAt() Timestamp { - if g == nil || g.UpdatedAt == nil { - return Timestamp{} +// GetRequiredStatusChecks returns the RequiredStatusChecks slice if it's non-nil, nil otherwise. +func (b *BranchProtectionRule) GetRequiredStatusChecks() []string { + if b == nil || b.RequiredStatusChecks == nil { + return nil } - return *g.UpdatedAt + return b.RequiredStatusChecks } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *Grant) GetURL() string { - if g == nil || g.URL == nil { +// GetRequiredStatusChecksEnforcementLevel returns the RequiredStatusChecksEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRequiredStatusChecksEnforcementLevel() string { + if b == nil || b.RequiredStatusChecksEnforcementLevel == nil { return "" } - return *g.URL + return *b.RequiredStatusChecksEnforcementLevel } -// GetActive returns the Active field if it's non-nil, zero value otherwise. -func (h *Hook) GetActive() bool { - if h == nil || h.Active == nil { +// GetRequireLastPushApproval returns the RequireLastPushApproval field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetRequireLastPushApproval() bool { + if b == nil || b.RequireLastPushApproval == nil { return false } - return *h.Active + return *b.RequireLastPushApproval } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (h *Hook) GetCreatedAt() time.Time { - if h == nil || h.CreatedAt == nil { - return time.Time{} +// GetSignatureRequirementEnforcementLevel returns the SignatureRequirementEnforcementLevel field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetSignatureRequirementEnforcementLevel() string { + if b == nil || b.SignatureRequirementEnforcementLevel == nil { + return "" } - return *h.CreatedAt + return *b.SignatureRequirementEnforcementLevel } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (h *Hook) GetID() int64 { - if h == nil || h.ID == nil { - return 0 +// GetStrictRequiredStatusChecksPolicy returns the StrictRequiredStatusChecksPolicy field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRule) GetStrictRequiredStatusChecksPolicy() bool { + if b == nil || b.StrictRequiredStatusChecksPolicy == nil { + return false } - return *h.ID + return *b.StrictRequiredStatusChecksPolicy } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (h *Hook) GetUpdatedAt() time.Time { - if h == nil || h.UpdatedAt == nil { - return time.Time{} +func (b *BranchProtectionRule) GetUpdatedAt() Timestamp { + if b == nil || b.UpdatedAt == nil { + return Timestamp{} } - return *h.UpdatedAt + return *b.UpdatedAt } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (h *Hook) GetURL() string { - if h == nil || h.URL == nil { +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (b *BranchProtectionRuleEvent) GetAction() string { + if b == nil || b.Action == nil { return "" } - return *h.URL + return *b.Action } -// GetActiveHooks returns the ActiveHooks field if it's non-nil, zero value otherwise. -func (h *HookStats) GetActiveHooks() int { - if h == nil || h.ActiveHooks == nil { - return 0 +// GetChanges returns the Changes field. +func (b *BranchProtectionRuleEvent) GetChanges() *ProtectionChanges { + if b == nil { + return nil } - return *h.ActiveHooks + return b.Changes } -// GetInactiveHooks returns the InactiveHooks field if it's non-nil, zero value otherwise. -func (h *HookStats) GetInactiveHooks() int { - if h == nil || h.InactiveHooks == nil { - return 0 +// GetInstallation returns the Installation field. +func (b *BranchProtectionRuleEvent) GetInstallation() *Installation { + if b == nil { + return nil } - return *h.InactiveHooks + return b.Installation } -// GetTotalHooks returns the TotalHooks field if it's non-nil, zero value otherwise. -func (h *HookStats) GetTotalHooks() int { - if h == nil || h.TotalHooks == nil { - return 0 +// GetOrg returns the Org field. +func (b *BranchProtectionRuleEvent) GetOrg() *Organization { + if b == nil { + return nil } - return *h.TotalHooks + return b.Org } -// GetGroupDescription returns the GroupDescription field if it's non-nil, zero value otherwise. -func (i *IDPGroup) GetGroupDescription() string { - if i == nil || i.GroupDescription == nil { - return "" +// GetRepo returns the Repo field. +func (b *BranchProtectionRuleEvent) GetRepo() *Repository { + if b == nil { + return nil } - return *i.GroupDescription + return b.Repo } -// GetGroupID returns the GroupID field if it's non-nil, zero value otherwise. -func (i *IDPGroup) GetGroupID() string { - if i == nil || i.GroupID == nil { - return "" +// GetRule returns the Rule field. +func (b *BranchProtectionRuleEvent) GetRule() *BranchProtectionRule { + if b == nil { + return nil } - return *i.GroupID + return b.Rule } -// GetGroupName returns the GroupName field if it's non-nil, zero value otherwise. -func (i *IDPGroup) GetGroupName() string { - if i == nil || i.GroupName == nil { - return "" +// GetSender returns the Sender field. +func (b *BranchProtectionRuleEvent) GetSender() *User { + if b == nil { + return nil } - return *i.GroupName + return b.Sender } -// GetAuthorsCount returns the AuthorsCount field if it's non-nil, zero value otherwise. -func (i *Import) GetAuthorsCount() int { - if i == nil || i.AuthorsCount == nil { - return 0 +// GetApps returns the Apps slice if it's non-nil, nil otherwise. +func (b *BranchRestrictions) GetApps() []*App { + if b == nil || b.Apps == nil { + return nil } - return *i.AuthorsCount + return b.Apps } -// GetAuthorsURL returns the AuthorsURL field if it's non-nil, zero value otherwise. -func (i *Import) GetAuthorsURL() string { - if i == nil || i.AuthorsURL == nil { - return "" +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (b *BranchRestrictions) GetTeams() []*Team { + if b == nil || b.Teams == nil { + return nil } - return *i.AuthorsURL + return b.Teams } -// GetCommitCount returns the CommitCount field if it's non-nil, zero value otherwise. -func (i *Import) GetCommitCount() int { - if i == nil || i.CommitCount == nil { - return 0 +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (b *BranchRestrictions) GetUsers() []*User { + if b == nil || b.Users == nil { + return nil } - return *i.CommitCount + return b.Users } -// GetFailedStep returns the FailedStep field if it's non-nil, zero value otherwise. -func (i *Import) GetFailedStep() string { - if i == nil || i.FailedStep == nil { - return "" +// GetApps returns the Apps slice if it's non-nil, nil otherwise. +func (b *BranchRestrictionsRequest) GetApps() []string { + if b == nil || b.Apps == nil { + return nil } - return *i.FailedStep + return b.Apps } -// GetHasLargeFiles returns the HasLargeFiles field if it's non-nil, zero value otherwise. -func (i *Import) GetHasLargeFiles() bool { - if i == nil || i.HasLargeFiles == nil { - return false +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (b *BranchRestrictionsRequest) GetTeams() []string { + if b == nil || b.Teams == nil { + return nil } - return *i.HasLargeFiles + return b.Teams } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *Import) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (b *BranchRestrictionsRequest) GetUsers() []string { + if b == nil || b.Users == nil { + return nil } - return *i.HTMLURL + return b.Users } -// GetHumanName returns the HumanName field if it's non-nil, zero value otherwise. -func (i *Import) GetHumanName() string { - if i == nil || i.HumanName == nil { +// GetRulesetID returns the RulesetID field. +func (b *BranchRuleMetadata) GetRulesetID() int64 { + if b == nil { + return 0 + } + return b.RulesetID +} + +// GetRulesetSource returns the RulesetSource field. +func (b *BranchRuleMetadata) GetRulesetSource() string { + if b == nil { return "" } - return *i.HumanName + return b.RulesetSource } -// GetLargeFilesCount returns the LargeFilesCount field if it's non-nil, zero value otherwise. -func (i *Import) GetLargeFilesCount() int { - if i == nil || i.LargeFilesCount == nil { - return 0 +// GetRulesetSourceType returns the RulesetSourceType field. +func (b *BranchRuleMetadata) GetRulesetSourceType() RulesetSourceType { + if b == nil { + return "" } - return *i.LargeFilesCount + return b.RulesetSourceType } -// GetLargeFilesSize returns the LargeFilesSize field if it's non-nil, zero value otherwise. -func (i *Import) GetLargeFilesSize() int { - if i == nil || i.LargeFilesSize == nil { - return 0 +// GetBranchNamePattern returns the BranchNamePattern slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetBranchNamePattern() []*PatternBranchRule { + if b == nil || b.BranchNamePattern == nil { + return nil } - return *i.LargeFilesSize + return b.BranchNamePattern } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (i *Import) GetMessage() string { - if i == nil || i.Message == nil { - return "" +// GetCodeScanning returns the CodeScanning slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetCodeScanning() []*CodeScanningBranchRule { + if b == nil || b.CodeScanning == nil { + return nil } - return *i.Message + return b.CodeScanning } -// GetPercent returns the Percent field if it's non-nil, zero value otherwise. -func (i *Import) GetPercent() int { - if i == nil || i.Percent == nil { - return 0 +// GetCommitAuthorEmailPattern returns the CommitAuthorEmailPattern slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetCommitAuthorEmailPattern() []*PatternBranchRule { + if b == nil || b.CommitAuthorEmailPattern == nil { + return nil } - return *i.Percent + return b.CommitAuthorEmailPattern } -// GetPushPercent returns the PushPercent field if it's non-nil, zero value otherwise. -func (i *Import) GetPushPercent() int { - if i == nil || i.PushPercent == nil { - return 0 +// GetCommitMessagePattern returns the CommitMessagePattern slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetCommitMessagePattern() []*PatternBranchRule { + if b == nil || b.CommitMessagePattern == nil { + return nil } - return *i.PushPercent + return b.CommitMessagePattern } -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (i *Import) GetRepositoryURL() string { - if i == nil || i.RepositoryURL == nil { - return "" +// GetCommitterEmailPattern returns the CommitterEmailPattern slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetCommitterEmailPattern() []*PatternBranchRule { + if b == nil || b.CommitterEmailPattern == nil { + return nil } - return *i.RepositoryURL + return b.CommitterEmailPattern } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (i *Import) GetStatus() string { - if i == nil || i.Status == nil { - return "" +// GetCopilotCodeReview returns the CopilotCodeReview slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetCopilotCodeReview() []*CopilotCodeReviewBranchRule { + if b == nil || b.CopilotCodeReview == nil { + return nil } - return *i.Status + return b.CopilotCodeReview } -// GetStatusText returns the StatusText field if it's non-nil, zero value otherwise. -func (i *Import) GetStatusText() string { - if i == nil || i.StatusText == nil { - return "" +// GetCreation returns the Creation slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetCreation() []*BranchRuleMetadata { + if b == nil || b.Creation == nil { + return nil } - return *i.StatusText + return b.Creation } -// GetTFVCProject returns the TFVCProject field if it's non-nil, zero value otherwise. -func (i *Import) GetTFVCProject() string { - if i == nil || i.TFVCProject == nil { - return "" +// GetDeletion returns the Deletion slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetDeletion() []*BranchRuleMetadata { + if b == nil || b.Deletion == nil { + return nil } - return *i.TFVCProject + return b.Deletion } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *Import) GetURL() string { - if i == nil || i.URL == nil { - return "" +// GetFileExtensionRestriction returns the FileExtensionRestriction slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetFileExtensionRestriction() []*FileExtensionRestrictionBranchRule { + if b == nil || b.FileExtensionRestriction == nil { + return nil } - return *i.URL + return b.FileExtensionRestriction } -// GetUseLFS returns the UseLFS field if it's non-nil, zero value otherwise. -func (i *Import) GetUseLFS() string { - if i == nil || i.UseLFS == nil { - return "" +// GetFilePathRestriction returns the FilePathRestriction slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetFilePathRestriction() []*FilePathRestrictionBranchRule { + if b == nil || b.FilePathRestriction == nil { + return nil } - return *i.UseLFS + return b.FilePathRestriction } -// GetVCS returns the VCS field if it's non-nil, zero value otherwise. -func (i *Import) GetVCS() string { - if i == nil || i.VCS == nil { - return "" +// GetMaxFilePathLength returns the MaxFilePathLength slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetMaxFilePathLength() []*MaxFilePathLengthBranchRule { + if b == nil || b.MaxFilePathLength == nil { + return nil } - return *i.VCS + return b.MaxFilePathLength } -// GetVCSPassword returns the VCSPassword field if it's non-nil, zero value otherwise. -func (i *Import) GetVCSPassword() string { - if i == nil || i.VCSPassword == nil { - return "" +// GetMaxFileSize returns the MaxFileSize slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetMaxFileSize() []*MaxFileSizeBranchRule { + if b == nil || b.MaxFileSize == nil { + return nil } - return *i.VCSPassword + return b.MaxFileSize } -// GetVCSURL returns the VCSURL field if it's non-nil, zero value otherwise. -func (i *Import) GetVCSURL() string { - if i == nil || i.VCSURL == nil { - return "" +// GetMergeQueue returns the MergeQueue slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetMergeQueue() []*MergeQueueBranchRule { + if b == nil || b.MergeQueue == nil { + return nil } - return *i.VCSURL + return b.MergeQueue } -// GetVCSUsername returns the VCSUsername field if it's non-nil, zero value otherwise. -func (i *Import) GetVCSUsername() string { - if i == nil || i.VCSUsername == nil { - return "" +// GetNonFastForward returns the NonFastForward slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetNonFastForward() []*BranchRuleMetadata { + if b == nil || b.NonFastForward == nil { + return nil } - return *i.VCSUsername + return b.NonFastForward } -// GetAccessTokensURL returns the AccessTokensURL field if it's non-nil, zero value otherwise. -func (i *Installation) GetAccessTokensURL() string { - if i == nil || i.AccessTokensURL == nil { - return "" +// GetPullRequest returns the PullRequest slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetPullRequest() []*PullRequestBranchRule { + if b == nil || b.PullRequest == nil { + return nil } - return *i.AccessTokensURL + return b.PullRequest } -// GetAccount returns the Account field. -func (i *Installation) GetAccount() *User { - if i == nil { +// GetRequiredDeployments returns the RequiredDeployments slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetRequiredDeployments() []*RequiredDeploymentsBranchRule { + if b == nil || b.RequiredDeployments == nil { return nil } - return i.Account + return b.RequiredDeployments } -// GetAppID returns the AppID field if it's non-nil, zero value otherwise. -func (i *Installation) GetAppID() int64 { - if i == nil || i.AppID == nil { - return 0 +// GetRequiredLinearHistory returns the RequiredLinearHistory slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetRequiredLinearHistory() []*BranchRuleMetadata { + if b == nil || b.RequiredLinearHistory == nil { + return nil } - return *i.AppID + return b.RequiredLinearHistory } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Installation) GetCreatedAt() Timestamp { - if i == nil || i.CreatedAt == nil { - return Timestamp{} +// GetRequiredSignatures returns the RequiredSignatures slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetRequiredSignatures() []*BranchRuleMetadata { + if b == nil || b.RequiredSignatures == nil { + return nil } - return *i.CreatedAt + return b.RequiredSignatures } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *Installation) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" +// GetRequiredStatusChecks returns the RequiredStatusChecks slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetRequiredStatusChecks() []*RequiredStatusChecksBranchRule { + if b == nil || b.RequiredStatusChecks == nil { + return nil } - return *i.HTMLURL + return b.RequiredStatusChecks } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *Installation) GetID() int64 { - if i == nil || i.ID == nil { - return 0 +// GetTagNamePattern returns the TagNamePattern slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetTagNamePattern() []*PatternBranchRule { + if b == nil || b.TagNamePattern == nil { + return nil } - return *i.ID + return b.TagNamePattern } -// GetPermissions returns the Permissions field. -func (i *Installation) GetPermissions() *InstallationPermissions { - if i == nil { +// GetUpdate returns the Update slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetUpdate() []*UpdateBranchRule { + if b == nil || b.Update == nil { return nil } - return i.Permissions + return b.Update } -// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. -func (i *Installation) GetRepositoriesURL() string { - if i == nil || i.RepositoriesURL == nil { - return "" +// GetWorkflows returns the Workflows slice if it's non-nil, nil otherwise. +func (b *BranchRules) GetWorkflows() []*WorkflowsBranchRule { + if b == nil || b.Workflows == nil { + return nil } - return *i.RepositoriesURL + return b.Workflows } -// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. -func (i *Installation) GetRepositorySelection() string { - if i == nil || i.RepositorySelection == nil { - return "" +// GetActorID returns the ActorID field if it's non-nil, zero value otherwise. +func (b *BypassActor) GetActorID() int64 { + if b == nil || b.ActorID == nil { + return 0 } - return *i.RepositorySelection + return *b.ActorID } -// GetSingleFileName returns the SingleFileName field if it's non-nil, zero value otherwise. -func (i *Installation) GetSingleFileName() string { - if i == nil || i.SingleFileName == nil { - return "" +// GetActorType returns the ActorType field. +func (b *BypassActor) GetActorType() *BypassActorType { + if b == nil { + return nil } - return *i.SingleFileName + return b.ActorType } -// GetTargetID returns the TargetID field if it's non-nil, zero value otherwise. -func (i *Installation) GetTargetID() int64 { - if i == nil || i.TargetID == nil { - return 0 +// GetBypassMode returns the BypassMode field. +func (b *BypassActor) GetBypassMode() *BypassMode { + if b == nil { + return nil } - return *i.TargetID + return b.BypassMode } -// GetTargetType returns the TargetType field if it's non-nil, zero value otherwise. -func (i *Installation) GetTargetType() string { - if i == nil || i.TargetType == nil { - return "" +// GetApps returns the Apps slice if it's non-nil, nil otherwise. +func (b *BypassPullRequestAllowances) GetApps() []*App { + if b == nil || b.Apps == nil { + return nil } - return *i.TargetType + return b.Apps } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *Installation) GetUpdatedAt() Timestamp { - if i == nil || i.UpdatedAt == nil { - return Timestamp{} +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (b *BypassPullRequestAllowances) GetTeams() []*Team { + if b == nil || b.Teams == nil { + return nil } - return *i.UpdatedAt + return b.Teams } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *InstallationEvent) GetAction() string { - if i == nil || i.Action == nil { - return "" +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (b *BypassPullRequestAllowances) GetUsers() []*User { + if b == nil || b.Users == nil { + return nil } - return *i.Action + return b.Users } -// GetInstallation returns the Installation field. -func (i *InstallationEvent) GetInstallation() *Installation { - if i == nil { +// GetApps returns the Apps slice if it's non-nil, nil otherwise. +func (b *BypassPullRequestAllowancesRequest) GetApps() []string { + if b == nil || b.Apps == nil { return nil } - return i.Installation + return b.Apps } -// GetSender returns the Sender field. -func (i *InstallationEvent) GetSender() *User { - if i == nil { +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (b *BypassPullRequestAllowancesRequest) GetTeams() []string { + if b == nil || b.Teams == nil { return nil } - return i.Sender + return b.Teams } -// GetAdministration returns the Administration field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetAdministration() string { - if i == nil || i.Administration == nil { - return "" +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (b *BypassPullRequestAllowancesRequest) GetUsers() []string { + if b == nil || b.Users == nil { + return nil } - return *i.Administration + return b.Users } -// GetChecks returns the Checks field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetChecks() string { - if i == nil || i.Checks == nil { - return "" +// GetReviewerID returns the ReviewerID field. +func (b *BypassReviewer) GetReviewerID() int64 { + if b == nil { + return 0 } - return *i.Checks + return b.ReviewerID } -// GetContentReferences returns the ContentReferences field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetContentReferences() string { - if i == nil || i.ContentReferences == nil { +// GetReviewerType returns the ReviewerType field. +func (b *BypassReviewer) GetReviewerType() string { + if b == nil { return "" } - return *i.ContentReferences + return b.ReviewerType } -// GetContents returns the Contents field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetContents() string { - if i == nil || i.Contents == nil { - return "" +// GetSecurityConfigurationID returns the SecurityConfigurationID field if it's non-nil, zero value otherwise. +func (b *BypassReviewer) GetSecurityConfigurationID() int64 { + if b == nil || b.SecurityConfigurationID == nil { + return 0 } - return *i.Contents + return *b.SecurityConfigurationID } -// GetDeployments returns the Deployments field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetDeployments() string { - if i == nil || i.Deployments == nil { - return "" +// GetApp returns the App field. +func (c *CheckRun) GetApp() *App { + if c == nil { + return nil } - return *i.Deployments + return c.App } -// GetIssues returns the Issues field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetIssues() string { - if i == nil || i.Issues == nil { - return "" +// GetCheckSuite returns the CheckSuite field. +func (c *CheckRun) GetCheckSuite() *CheckSuite { + if c == nil { + return nil } - return *i.Issues + return c.CheckSuite } -// GetMembers returns the Members field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetMembers() string { - if i == nil || i.Members == nil { - return "" +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetCompletedAt() Timestamp { + if c == nil || c.CompletedAt == nil { + return Timestamp{} } - return *i.Members + return *c.CompletedAt } -// GetMetadata returns the Metadata field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetMetadata() string { - if i == nil || i.Metadata == nil { +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetConclusion() string { + if c == nil || c.Conclusion == nil { return "" } - return *i.Metadata + return *c.Conclusion } -// GetOrganizationAdministration returns the OrganizationAdministration field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetOrganizationAdministration() string { - if i == nil || i.OrganizationAdministration == nil { +// GetDetailsURL returns the DetailsURL field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetDetailsURL() string { + if c == nil || c.DetailsURL == nil { return "" } - return *i.OrganizationAdministration + return *c.DetailsURL } -// GetOrganizationHooks returns the OrganizationHooks field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetOrganizationHooks() string { - if i == nil || i.OrganizationHooks == nil { +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetExternalID() string { + if c == nil || c.ExternalID == nil { return "" } - return *i.OrganizationHooks + return *c.ExternalID } -// GetOrganizationPlan returns the OrganizationPlan field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetOrganizationPlan() string { - if i == nil || i.OrganizationPlan == nil { +// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetHeadSHA() string { + if c == nil || c.HeadSHA == nil { return "" } - return *i.OrganizationPlan + return *c.HeadSHA } -// GetOrganizationPreReceiveHooks returns the OrganizationPreReceiveHooks field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetOrganizationPreReceiveHooks() string { - if i == nil || i.OrganizationPreReceiveHooks == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { return "" } - return *i.OrganizationPreReceiveHooks + return *c.HTMLURL } -// GetOrganizationProjects returns the OrganizationProjects field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetOrganizationProjects() string { - if i == nil || i.OrganizationProjects == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return *i.OrganizationProjects + return *c.ID } -// GetOrganizationUserBlocking returns the OrganizationUserBlocking field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetOrganizationUserBlocking() string { - if i == nil || i.OrganizationUserBlocking == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetName() string { + if c == nil || c.Name == nil { return "" } - return *i.OrganizationUserBlocking + return *c.Name } -// GetPackages returns the Packages field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetPackages() string { - if i == nil || i.Packages == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetNodeID() string { + if c == nil || c.NodeID == nil { return "" } - return *i.Packages + return *c.NodeID } -// GetPages returns the Pages field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetPages() string { - if i == nil || i.Pages == nil { - return "" +// GetOutput returns the Output field. +func (c *CheckRun) GetOutput() *CheckRunOutput { + if c == nil { + return nil } - return *i.Pages + return c.Output } -// GetPullRequests returns the PullRequests field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetPullRequests() string { - if i == nil || i.PullRequests == nil { - return "" +// GetPullRequests returns the PullRequests slice if it's non-nil, nil otherwise. +func (c *CheckRun) GetPullRequests() []*PullRequest { + if c == nil || c.PullRequests == nil { + return nil } - return *i.PullRequests + return c.PullRequests } -// GetRepositoryHooks returns the RepositoryHooks field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetRepositoryHooks() string { - if i == nil || i.RepositoryHooks == nil { - return "" +// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetStartedAt() Timestamp { + if c == nil || c.StartedAt == nil { + return Timestamp{} } - return *i.RepositoryHooks + return *c.StartedAt } -// GetRepositoryPreReceiveHooks returns the RepositoryPreReceiveHooks field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetRepositoryPreReceiveHooks() string { - if i == nil || i.RepositoryPreReceiveHooks == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetStatus() string { + if c == nil || c.Status == nil { return "" } - return *i.RepositoryPreReceiveHooks + return *c.Status } -// GetRepositoryProjects returns the RepositoryProjects field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetRepositoryProjects() string { - if i == nil || i.RepositoryProjects == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CheckRun) GetURL() string { + if c == nil || c.URL == nil { return "" } - return *i.RepositoryProjects + return *c.URL } -// GetSingleFile returns the SingleFile field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetSingleFile() string { - if i == nil || i.SingleFile == nil { +// GetDescription returns the Description field. +func (c *CheckRunAction) GetDescription() string { + if c == nil { return "" } - return *i.SingleFile + return c.Description } -// GetStatuses returns the Statuses field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetStatuses() string { - if i == nil || i.Statuses == nil { +// GetIdentifier returns the Identifier field. +func (c *CheckRunAction) GetIdentifier() string { + if c == nil { return "" } - return *i.Statuses + return c.Identifier } -// GetTeamDiscussions returns the TeamDiscussions field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetTeamDiscussions() string { - if i == nil || i.TeamDiscussions == nil { +// GetLabel returns the Label field. +func (c *CheckRunAction) GetLabel() string { + if c == nil { return "" } - return *i.TeamDiscussions + return c.Label } -// GetVulnerabilityAlerts returns the VulnerabilityAlerts field if it's non-nil, zero value otherwise. -func (i *InstallationPermissions) GetVulnerabilityAlerts() string { - if i == nil || i.VulnerabilityAlerts == nil { +// GetAnnotationLevel returns the AnnotationLevel field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetAnnotationLevel() string { + if c == nil || c.AnnotationLevel == nil { return "" } - return *i.VulnerabilityAlerts + return *c.AnnotationLevel } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *InstallationRepositoriesEvent) GetAction() string { - if i == nil || i.Action == nil { - return "" +// GetEndColumn returns the EndColumn field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetEndColumn() int { + if c == nil || c.EndColumn == nil { + return 0 } - return *i.Action + return *c.EndColumn } -// GetInstallation returns the Installation field. -func (i *InstallationRepositoriesEvent) GetInstallation() *Installation { - if i == nil { - return nil +// GetEndLine returns the EndLine field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetEndLine() int { + if c == nil || c.EndLine == nil { + return 0 } - return i.Installation + return *c.EndLine } -// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. -func (i *InstallationRepositoriesEvent) GetRepositorySelection() string { - if i == nil || i.RepositorySelection == nil { +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetMessage() string { + if c == nil || c.Message == nil { return "" } - return *i.RepositorySelection -} - -// GetSender returns the Sender field. -func (i *InstallationRepositoriesEvent) GetSender() *User { - if i == nil { - return nil - } - return i.Sender + return *c.Message } -// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (i *InstallationToken) GetExpiresAt() time.Time { - if i == nil || i.ExpiresAt == nil { - return time.Time{} +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetPath() string { + if c == nil || c.Path == nil { + return "" } - return *i.ExpiresAt + return *c.Path } -// GetPermissions returns the Permissions field. -func (i *InstallationToken) GetPermissions() *InstallationPermissions { - if i == nil { - return nil +// GetRawDetails returns the RawDetails field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetRawDetails() string { + if c == nil || c.RawDetails == nil { + return "" } - return i.Permissions + return *c.RawDetails } -// GetToken returns the Token field if it's non-nil, zero value otherwise. -func (i *InstallationToken) GetToken() string { - if i == nil || i.Token == nil { - return "" +// GetStartColumn returns the StartColumn field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetStartColumn() int { + if c == nil || c.StartColumn == nil { + return 0 } - return *i.Token + return *c.StartColumn } -// GetPermissions returns the Permissions field. -func (i *InstallationTokenOptions) GetPermissions() *InstallationPermissions { - if i == nil { - return nil +// GetStartLine returns the StartLine field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetStartLine() int { + if c == nil || c.StartLine == nil { + return 0 } - return i.Permissions + return *c.StartLine } -// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (i *InteractionRestriction) GetExpiresAt() Timestamp { - if i == nil || i.ExpiresAt == nil { - return Timestamp{} +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (c *CheckRunAnnotation) GetTitle() string { + if c == nil || c.Title == nil { + return "" } - return *i.ExpiresAt + return *c.Title } -// GetLimit returns the Limit field if it's non-nil, zero value otherwise. -func (i *InteractionRestriction) GetLimit() string { - if i == nil || i.Limit == nil { +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CheckRunEvent) GetAction() string { + if c == nil || c.Action == nil { return "" } - return *i.Limit + return *c.Action } -// GetOrigin returns the Origin field if it's non-nil, zero value otherwise. -func (i *InteractionRestriction) GetOrigin() string { - if i == nil || i.Origin == nil { - return "" +// GetCheckRun returns the CheckRun field. +func (c *CheckRunEvent) GetCheckRun() *CheckRun { + if c == nil { + return nil } - return *i.Origin + return c.CheckRun } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Invitation) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} +// GetInstallation returns the Installation field. +func (c *CheckRunEvent) GetInstallation() *Installation { + if c == nil { + return nil } - return *i.CreatedAt + return c.Installation } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (i *Invitation) GetEmail() string { - if i == nil || i.Email == nil { - return "" +// GetOrg returns the Org field. +func (c *CheckRunEvent) GetOrg() *Organization { + if c == nil { + return nil } - return *i.Email + return c.Org } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *Invitation) GetID() int64 { - if i == nil || i.ID == nil { - return 0 +// GetRepo returns the Repo field. +func (c *CheckRunEvent) GetRepo() *Repository { + if c == nil { + return nil } - return *i.ID + return c.Repo } -// GetInvitationTeamURL returns the InvitationTeamURL field if it's non-nil, zero value otherwise. -func (i *Invitation) GetInvitationTeamURL() string { - if i == nil || i.InvitationTeamURL == nil { - return "" +// GetRequestedAction returns the RequestedAction field. +func (c *CheckRunEvent) GetRequestedAction() *RequestedAction { + if c == nil { + return nil } - return *i.InvitationTeamURL + return c.RequestedAction } -// GetInviter returns the Inviter field. -func (i *Invitation) GetInviter() *User { - if i == nil { +// GetSender returns the Sender field. +func (c *CheckRunEvent) GetSender() *User { + if c == nil { return nil } - return i.Inviter + return c.Sender } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (i *Invitation) GetLogin() string { - if i == nil || i.Login == nil { +// GetAlt returns the Alt field if it's non-nil, zero value otherwise. +func (c *CheckRunImage) GetAlt() string { + if c == nil || c.Alt == nil { return "" } - return *i.Login + return *c.Alt } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (i *Invitation) GetNodeID() string { - if i == nil || i.NodeID == nil { +// GetCaption returns the Caption field if it's non-nil, zero value otherwise. +func (c *CheckRunImage) GetCaption() string { + if c == nil || c.Caption == nil { return "" } - return *i.NodeID + return *c.Caption } -// GetRole returns the Role field if it's non-nil, zero value otherwise. -func (i *Invitation) GetRole() string { - if i == nil || i.Role == nil { +// GetImageURL returns the ImageURL field if it's non-nil, zero value otherwise. +func (c *CheckRunImage) GetImageURL() string { + if c == nil || c.ImageURL == nil { return "" } - return *i.Role + return *c.ImageURL } -// GetTeamCount returns the TeamCount field if it's non-nil, zero value otherwise. -func (i *Invitation) GetTeamCount() int { - if i == nil || i.TeamCount == nil { +// GetAnnotations returns the Annotations slice if it's non-nil, nil otherwise. +func (c *CheckRunOutput) GetAnnotations() []*CheckRunAnnotation { + if c == nil || c.Annotations == nil { + return nil + } + return c.Annotations +} + +// GetAnnotationsCount returns the AnnotationsCount field if it's non-nil, zero value otherwise. +func (c *CheckRunOutput) GetAnnotationsCount() int { + if c == nil || c.AnnotationsCount == nil { return 0 } - return *i.TeamCount + return *c.AnnotationsCount } -// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. -func (i *Issue) GetActiveLockReason() string { - if i == nil || i.ActiveLockReason == nil { +// GetAnnotationsURL returns the AnnotationsURL field if it's non-nil, zero value otherwise. +func (c *CheckRunOutput) GetAnnotationsURL() string { + if c == nil || c.AnnotationsURL == nil { return "" } - return *i.ActiveLockReason + return *c.AnnotationsURL } -// GetAssignee returns the Assignee field. -func (i *Issue) GetAssignee() *User { - if i == nil { +// GetImages returns the Images slice if it's non-nil, nil otherwise. +func (c *CheckRunOutput) GetImages() []*CheckRunImage { + if c == nil || c.Images == nil { return nil } - return i.Assignee + return c.Images } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (i *Issue) GetBody() string { - if i == nil || i.Body == nil { +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (c *CheckRunOutput) GetSummary() string { + if c == nil || c.Summary == nil { return "" } - return *i.Body -} - -// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetClosedAt() time.Time { - if i == nil || i.ClosedAt == nil { - return time.Time{} - } - return *i.ClosedAt + return *c.Summary } -// GetClosedBy returns the ClosedBy field. -func (i *Issue) GetClosedBy() *User { - if i == nil { - return nil +// GetText returns the Text field if it's non-nil, zero value otherwise. +func (c *CheckRunOutput) GetText() string { + if c == nil || c.Text == nil { + return "" } - return i.ClosedBy + return *c.Text } -// GetComments returns the Comments field if it's non-nil, zero value otherwise. -func (i *Issue) GetComments() int { - if i == nil || i.Comments == nil { - return 0 +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (c *CheckRunOutput) GetTitle() string { + if c == nil || c.Title == nil { + return "" } - return *i.Comments + return *c.Title } -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetCommentsURL() string { - if i == nil || i.CommentsURL == nil { +// GetAfterSHA returns the AfterSHA field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetAfterSHA() string { + if c == nil || c.AfterSHA == nil { return "" } - return *i.CommentsURL + return *c.AfterSHA } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} +// GetApp returns the App field. +func (c *CheckSuite) GetApp() *App { + if c == nil { + return nil } - return *i.CreatedAt + return c.App } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetEventsURL() string { - if i == nil || i.EventsURL == nil { +// GetBeforeSHA returns the BeforeSHA field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetBeforeSHA() string { + if c == nil || c.BeforeSHA == nil { return "" } - return *i.EventsURL + return *c.BeforeSHA } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetConclusion() string { + if c == nil || c.Conclusion == nil { return "" } - return *i.HTMLURL + return *c.Conclusion } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *Issue) GetID() int64 { - if i == nil || i.ID == nil { - return 0 +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} } - return *i.ID + return *c.CreatedAt } -// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetLabelsURL() string { - if i == nil || i.LabelsURL == nil { +// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetHeadBranch() string { + if c == nil || c.HeadBranch == nil { return "" } - return *i.LabelsURL + return *c.HeadBranch } -// GetLocked returns the Locked field if it's non-nil, zero value otherwise. -func (i *Issue) GetLocked() bool { - if i == nil || i.Locked == nil { - return false +// GetHeadCommit returns the HeadCommit field. +func (c *CheckSuite) GetHeadCommit() *Commit { + if c == nil { + return nil } - return *i.Locked + return c.HeadCommit } -// GetMilestone returns the Milestone field. -func (i *Issue) GetMilestone() *Milestone { - if i == nil { - return nil +// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetHeadSHA() string { + if c == nil || c.HeadSHA == nil { + return "" } - return i.Milestone + return *c.HeadSHA } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (i *Issue) GetNodeID() string { - if i == nil || i.NodeID == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return *i.NodeID + return *c.ID } -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (i *Issue) GetNumber() int { - if i == nil || i.Number == nil { +// GetLatestCheckRunsCount returns the LatestCheckRunsCount field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetLatestCheckRunsCount() int64 { + if c == nil || c.LatestCheckRunsCount == nil { return 0 } - return *i.Number + return *c.LatestCheckRunsCount } -// GetPullRequestLinks returns the PullRequestLinks field. -func (i *Issue) GetPullRequestLinks() *PullRequestLinks { - if i == nil { - return nil +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" } - return i.PullRequestLinks + return *c.NodeID } -// GetReactions returns the Reactions field. -func (i *Issue) GetReactions() *Reactions { - if i == nil { +// GetPullRequests returns the PullRequests slice if it's non-nil, nil otherwise. +func (c *CheckSuite) GetPullRequests() []*PullRequest { + if c == nil || c.PullRequests == nil { return nil } - return i.Reactions + return c.PullRequests } // GetRepository returns the Repository field. -func (i *Issue) GetRepository() *Repository { - if i == nil { +func (c *CheckSuite) GetRepository() *Repository { + if c == nil { return nil } - return i.Repository + return c.Repository } -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetRepositoryURL() string { - if i == nil || i.RepositoryURL == nil { - return "" +// GetRerequestable returns the Rerequestable field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetRerequestable() bool { + if c == nil || c.Rerequestable == nil { + return false } - return *i.RepositoryURL + return *c.Rerequestable } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (i *Issue) GetState() string { - if i == nil || i.State == nil { - return "" +// GetRunsRerequestable returns the RunsRerequestable field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetRunsRerequestable() bool { + if c == nil || c.RunsRerequestable == nil { + return false } - return *i.State + return *c.RunsRerequestable } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (i *Issue) GetTitle() string { - if i == nil || i.Title == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetStatus() string { + if c == nil || c.Status == nil { return "" } - return *i.Title + return *c.Status } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetUpdatedAt() time.Time { - if i == nil || i.UpdatedAt == nil { - return time.Time{} +func (c *CheckSuite) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} } - return *i.UpdatedAt + return *c.UpdatedAt } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *Issue) GetURL() string { - if i == nil || i.URL == nil { +func (c *CheckSuite) GetURL() string { + if c == nil || c.URL == nil { return "" } - return *i.URL + return *c.URL } -// GetUser returns the User field. -func (i *Issue) GetUser() *User { - if i == nil { - return nil +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CheckSuiteEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" } - return i.User + return *c.Action } -// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetAuthorAssociation() string { - if i == nil || i.AuthorAssociation == nil { - return "" +// GetCheckSuite returns the CheckSuite field. +func (c *CheckSuiteEvent) GetCheckSuite() *CheckSuite { + if c == nil { + return nil } - return *i.AuthorAssociation + return c.CheckSuite } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetBody() string { - if i == nil || i.Body == nil { - return "" +// GetInstallation returns the Installation field. +func (c *CheckSuiteEvent) GetInstallation() *Installation { + if c == nil { + return nil } - return *i.Body + return c.Installation } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} +// GetOrg returns the Org field. +func (c *CheckSuiteEvent) GetOrg() *Organization { + if c == nil { + return nil } - return *i.CreatedAt + return c.Org } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" +// GetRepo returns the Repo field. +func (c *CheckSuiteEvent) GetRepo() *Repository { + if c == nil { + return nil } - return *i.HTMLURL + return c.Repo } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetID() int64 { - if i == nil || i.ID == nil { - return 0 +// GetSender returns the Sender field. +func (c *CheckSuiteEvent) GetSender() *User { + if c == nil { + return nil } - return *i.ID + return c.Sender } -// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetIssueURL() string { - if i == nil || i.IssueURL == nil { - return "" +// GetAutoTriggerChecks returns the AutoTriggerChecks slice if it's non-nil, nil otherwise. +func (c *CheckSuitePreferenceOptions) GetAutoTriggerChecks() []*AutoTriggerCheck { + if c == nil || c.AutoTriggerChecks == nil { + return nil } - return *i.IssueURL + return c.AutoTriggerChecks } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetNodeID() string { - if i == nil || i.NodeID == nil { - return "" +// GetPreferences returns the Preferences field. +func (c *CheckSuitePreferenceResults) GetPreferences() *PreferenceList { + if c == nil { + return nil } - return *i.NodeID + return c.Preferences } -// GetReactions returns the Reactions field. -func (i *IssueComment) GetReactions() *Reactions { - if i == nil { +// GetRepository returns the Repository field. +func (c *CheckSuitePreferenceResults) GetRepository() *Repository { + if c == nil { return nil } - return i.Reactions + return c.Repository } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetUpdatedAt() time.Time { - if i == nil || i.UpdatedAt == nil { - return time.Time{} +// GetArchived returns the Archived field if it's non-nil, zero value otherwise. +func (c *Classroom) GetArchived() bool { + if c == nil || c.Archived == nil { + return false } - return *i.UpdatedAt + return *c.Archived } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetURL() string { - if i == nil || i.URL == nil { - return "" - } - return *i.URL -} - -// GetUser returns the User field. -func (i *IssueComment) GetUser() *User { - if i == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *Classroom) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return i.User + return *c.ID } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *IssueCommentEvent) GetAction() string { - if i == nil || i.Action == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *Classroom) GetName() string { + if c == nil || c.Name == nil { return "" } - return *i.Action + return *c.Name } -// GetChanges returns the Changes field. -func (i *IssueCommentEvent) GetChanges() *EditChange { - if i == nil { +// GetOrganization returns the Organization field. +func (c *Classroom) GetOrganization() *Organization { + if c == nil { return nil } - return i.Changes + return c.Organization } -// GetComment returns the Comment field. -func (i *IssueCommentEvent) GetComment() *IssueComment { - if i == nil { - return nil +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Classroom) GetURL() string { + if c == nil || c.URL == nil { + return "" } - return i.Comment + return *c.URL } -// GetInstallation returns the Installation field. -func (i *IssueCommentEvent) GetInstallation() *Installation { - if i == nil { - return nil +// GetAccepted returns the Accepted field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetAccepted() int { + if c == nil || c.Accepted == nil { + return 0 } - return i.Installation + return *c.Accepted } -// GetIssue returns the Issue field. -func (i *IssueCommentEvent) GetIssue() *Issue { - if i == nil { +// GetClassroom returns the Classroom field. +func (c *ClassroomAssignment) GetClassroom() *Classroom { + if c == nil { return nil } - return i.Issue + return c.Classroom } -// GetRepo returns the Repo field. -func (i *IssueCommentEvent) GetRepo() *Repository { - if i == nil { - return nil +// GetDeadline returns the Deadline field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetDeadline() Timestamp { + if c == nil || c.Deadline == nil { + return Timestamp{} } - return i.Repo + return *c.Deadline } -// GetSender returns the Sender field. -func (i *IssueCommentEvent) GetSender() *User { - if i == nil { - return nil +// GetEditor returns the Editor field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetEditor() string { + if c == nil || c.Editor == nil { + return "" } - return i.Sender + return *c.Editor } -// GetActor returns the Actor field. -func (i *IssueEvent) GetActor() *User { - if i == nil { - return nil +// GetFeedbackPullRequestsEnabled returns the FeedbackPullRequestsEnabled field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetFeedbackPullRequestsEnabled() bool { + if c == nil || c.FeedbackPullRequestsEnabled == nil { + return false } - return i.Actor + return *c.FeedbackPullRequestsEnabled } -// GetAssignee returns the Assignee field. -func (i *IssueEvent) GetAssignee() *User { - if i == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return i.Assignee + return *c.ID } -// GetAssigner returns the Assigner field. -func (i *IssueEvent) GetAssigner() *User { - if i == nil { - return nil +// GetInvitationsEnabled returns the InvitationsEnabled field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetInvitationsEnabled() bool { + if c == nil || c.InvitationsEnabled == nil { + return false } - return i.Assigner + return *c.InvitationsEnabled } -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetCommitID() string { - if i == nil || i.CommitID == nil { +// GetInviteLink returns the InviteLink field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetInviteLink() string { + if c == nil || c.InviteLink == nil { return "" } - return *i.CommitID -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} - } - return *i.CreatedAt + return *c.InviteLink } -// GetDismissedReview returns the DismissedReview field. -func (i *IssueEvent) GetDismissedReview() *DismissedReview { - if i == nil { - return nil +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetLanguage() string { + if c == nil || c.Language == nil { + return "" } - return i.DismissedReview + return *c.Language } -// GetEvent returns the Event field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetEvent() string { - if i == nil || i.Event == nil { - return "" +// GetMaxMembers returns the MaxMembers field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetMaxMembers() int { + if c == nil || c.MaxMembers == nil { + return 0 } - return *i.Event + return *c.MaxMembers } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetID() int64 { - if i == nil || i.ID == nil { +// GetMaxTeams returns the MaxTeams field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetMaxTeams() int { + if c == nil || c.MaxTeams == nil { return 0 } - return *i.ID + return *c.MaxTeams } -// GetIssue returns the Issue field. -func (i *IssueEvent) GetIssue() *Issue { - if i == nil { - return nil +// GetPassing returns the Passing field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetPassing() int { + if c == nil || c.Passing == nil { + return 0 } - return i.Issue + return *c.Passing } -// GetLabel returns the Label field. -func (i *IssueEvent) GetLabel() *Label { - if i == nil { - return nil +// GetPublicRepo returns the PublicRepo field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetPublicRepo() bool { + if c == nil || c.PublicRepo == nil { + return false } - return i.Label + return *c.PublicRepo } -// GetLockReason returns the LockReason field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetLockReason() string { - if i == nil || i.LockReason == nil { +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetSlug() string { + if c == nil || c.Slug == nil { return "" } - return *i.LockReason + return *c.Slug } -// GetMilestone returns the Milestone field. -func (i *IssueEvent) GetMilestone() *Milestone { - if i == nil { +// GetStarterCodeRepository returns the StarterCodeRepository field. +func (c *ClassroomAssignment) GetStarterCodeRepository() *Repository { + if c == nil { return nil } - return i.Milestone + return c.StarterCodeRepository } -// GetProjectCard returns the ProjectCard field. -func (i *IssueEvent) GetProjectCard() *ProjectCard { - if i == nil { - return nil +// GetStudentsAreRepoAdmins returns the StudentsAreRepoAdmins field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetStudentsAreRepoAdmins() bool { + if c == nil || c.StudentsAreRepoAdmins == nil { + return false } - return i.ProjectCard + return *c.StudentsAreRepoAdmins } -// GetRename returns the Rename field. -func (i *IssueEvent) GetRename() *Rename { - if i == nil { - return nil +// GetSubmitted returns the Submitted field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetSubmitted() int { + if c == nil || c.Submitted == nil { + return 0 } - return i.Rename + return *c.Submitted } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetURL() string { - if i == nil || i.URL == nil { +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetTitle() string { + if c == nil || c.Title == nil { return "" } - return *i.URL + return *c.Title } -// GetAssignee returns the Assignee field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetAssignee() string { - if i == nil || i.Assignee == nil { +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *ClassroomAssignment) GetType() string { + if c == nil || c.Type == nil { return "" } - return *i.Assignee + return *c.Type } -// GetAssignees returns the Assignees field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetAssignees() []string { - if i == nil || i.Assignees == nil { - return nil +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (c *ClassroomUser) GetAvatarURL() string { + if c == nil || c.AvatarURL == nil { + return "" } - return *i.Assignees + return *c.AvatarURL } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetBody() string { - if i == nil || i.Body == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *ClassroomUser) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { return "" } - return *i.Body + return *c.HTMLURL } -// GetLabels returns the Labels field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetLabels() []string { - if i == nil || i.Labels == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *ClassroomUser) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return *i.Labels + return *c.ID } -// GetMilestone returns the Milestone field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetMilestone() int { - if i == nil || i.Milestone == nil { - return 0 +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *ClassroomUser) GetLogin() string { + if c == nil || c.Login == nil { + return "" } - return *i.Milestone + return *c.Login } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetState() string { - if i == nil || i.State == nil { +// GetDeploymentName returns the DeploymentName field. +func (c *ClusterArtifactDeployment) GetDeploymentName() string { + if c == nil { return "" } - return *i.State + return c.DeploymentName } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetTitle() string { - if i == nil || i.Title == nil { +// GetDigest returns the Digest field. +func (c *ClusterArtifactDeployment) GetDigest() string { + if c == nil { return "" } - return *i.Title + return c.Digest } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *IssuesEvent) GetAction() string { - if i == nil || i.Action == nil { +// GetGithubRepository returns the GithubRepository field if it's non-nil, zero value otherwise. +func (c *ClusterArtifactDeployment) GetGithubRepository() string { + if c == nil || c.GithubRepository == nil { return "" } - return *i.Action + return *c.GithubRepository } -// GetAssignee returns the Assignee field. -func (i *IssuesEvent) GetAssignee() *User { - if i == nil { - return nil +// GetName returns the Name field. +func (c *ClusterArtifactDeployment) GetName() string { + if c == nil { + return "" } - return i.Assignee + return c.Name } -// GetChanges returns the Changes field. -func (i *IssuesEvent) GetChanges() *EditChange { - if i == nil { +// GetRuntimeRisks returns the RuntimeRisks slice if it's non-nil, nil otherwise. +func (c *ClusterArtifactDeployment) GetRuntimeRisks() []DeploymentRuntimeRisk { + if c == nil || c.RuntimeRisks == nil { return nil } - return i.Changes + return c.RuntimeRisks } -// GetInstallation returns the Installation field. -func (i *IssuesEvent) GetInstallation() *Installation { - if i == nil { - return nil +// GetStatus returns the Status field. +func (c *ClusterArtifactDeployment) GetStatus() string { + if c == nil { + return "" } - return i.Installation + return c.Status } -// GetIssue returns the Issue field. -func (i *IssuesEvent) GetIssue() *Issue { - if i == nil { - return nil +// GetTags returns the Tags map if it's non-nil, an empty map otherwise. +func (c *ClusterArtifactDeployment) GetTags() map[string]string { + if c == nil || c.Tags == nil { + return map[string]string{} } - return i.Issue + return c.Tags } -// GetLabel returns the Label field. -func (i *IssuesEvent) GetLabel() *Label { - if i == nil { - return nil +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (c *ClusterArtifactDeployment) GetVersion() string { + if c == nil || c.Version == nil { + return "" } - return i.Label + return *c.Version } -// GetRepo returns the Repo field. -func (i *IssuesEvent) GetRepo() *Repository { - if i == nil { +// GetDeployments returns the Deployments slice if it's non-nil, nil otherwise. +func (c *ClusterDeploymentRecordsRequest) GetDeployments() []*ClusterArtifactDeployment { + if c == nil || c.Deployments == nil { return nil } - return i.Repo + return c.Deployments } -// GetSender returns the Sender field. -func (i *IssuesEvent) GetSender() *User { - if i == nil { - return nil +// GetLogicalEnvironment returns the LogicalEnvironment field. +func (c *ClusterDeploymentRecordsRequest) GetLogicalEnvironment() string { + if c == nil { + return "" } - return i.Sender + return c.LogicalEnvironment } -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (i *IssuesSearchResult) GetIncompleteResults() bool { - if i == nil || i.IncompleteResults == nil { - return false +// GetPhysicalEnvironment returns the PhysicalEnvironment field if it's non-nil, zero value otherwise. +func (c *ClusterDeploymentRecordsRequest) GetPhysicalEnvironment() string { + if c == nil || c.PhysicalEnvironment == nil { + return "" } - return *i.IncompleteResults + return *c.PhysicalEnvironment } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (i *IssuesSearchResult) GetTotal() int { - if i == nil || i.Total == nil { - return 0 +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (c *ClusterSSHKey) GetFingerprint() string { + if c == nil || c.Fingerprint == nil { + return "" } - return *i.Total + return *c.Fingerprint } -// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetClosedIssues() int { - if i == nil || i.ClosedIssues == nil { - return 0 +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *ClusterSSHKey) GetKey() string { + if c == nil || c.Key == nil { + return "" } - return *i.ClosedIssues + return *c.Key } -// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetOpenIssues() int { - if i == nil || i.OpenIssues == nil { - return 0 +// GetNodes returns the Nodes slice if it's non-nil, nil otherwise. +func (c *ClusterStatus) GetNodes() []*ClusterStatusNode { + if c == nil || c.Nodes == nil { + return nil } - return *i.OpenIssues + return c.Nodes } -// GetTotalIssues returns the TotalIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetTotalIssues() int { - if i == nil || i.TotalIssues == nil { - return 0 +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *ClusterStatus) GetStatus() string { + if c == nil || c.Status == nil { + return "" } - return *i.TotalIssues + return *c.Status } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (k *Key) GetCreatedAt() Timestamp { - if k == nil || k.CreatedAt == nil { - return Timestamp{} +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNode) GetHostname() string { + if c == nil || c.Hostname == nil { + return "" } - return *k.CreatedAt + return *c.Hostname } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (k *Key) GetID() int64 { - if k == nil || k.ID == nil { - return 0 +// GetServices returns the Services slice if it's non-nil, nil otherwise. +func (c *ClusterStatusNode) GetServices() []*ClusterStatusNodeServiceItem { + if c == nil || c.Services == nil { + return nil } - return *k.ID + return c.Services } -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (k *Key) GetKey() string { - if k == nil || k.Key == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNode) GetStatus() string { + if c == nil || c.Status == nil { return "" } - return *k.Key + return *c.Status } -// GetReadOnly returns the ReadOnly field if it's non-nil, zero value otherwise. -func (k *Key) GetReadOnly() bool { - if k == nil || k.ReadOnly == nil { - return false +// GetDetails returns the Details field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNodeServiceItem) GetDetails() string { + if c == nil || c.Details == nil { + return "" } - return *k.ReadOnly + return *c.Details } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (k *Key) GetTitle() string { - if k == nil || k.Title == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNodeServiceItem) GetName() string { + if c == nil || c.Name == nil { return "" } - return *k.Title + return *c.Name } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (k *Key) GetURL() string { - if k == nil || k.URL == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNodeServiceItem) GetStatus() string { + if c == nil || c.Status == nil { return "" } - return *k.URL + return *c.Status } -// GetColor returns the Color field if it's non-nil, zero value otherwise. -func (l *Label) GetColor() string { - if l == nil || l.Color == nil { +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetBody() string { + if c == nil || c.Body == nil { return "" } - return *l.Color + return *c.Body } -// GetDefault returns the Default field if it's non-nil, zero value otherwise. -func (l *Label) GetDefault() bool { - if l == nil || l.Default == nil { - return false +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetKey() string { + if c == nil || c.Key == nil { + return "" } - return *l.Default + return *c.Key } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (l *Label) GetDescription() string { - if l == nil || l.Description == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetName() string { + if c == nil || c.Name == nil { return "" } - return *l.Description + return *c.Name } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (l *Label) GetID() int64 { - if l == nil || l.ID == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CodeOfConduct) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetColumn returns the Column field. +func (c *CodeownersError) GetColumn() int { + if c == nil { return 0 } - return *l.ID + return c.Column } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (l *Label) GetName() string { - if l == nil || l.Name == nil { +// GetKind returns the Kind field. +func (c *CodeownersError) GetKind() string { + if c == nil { return "" } - return *l.Name + return c.Kind } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (l *Label) GetNodeID() string { - if l == nil || l.NodeID == nil { - return "" +// GetLine returns the Line field. +func (c *CodeownersError) GetLine() int { + if c == nil { + return 0 } - return *l.NodeID + return c.Line } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (l *Label) GetURL() string { - if l == nil || l.URL == nil { +// GetMessage returns the Message field. +func (c *CodeownersError) GetMessage() string { + if c == nil { return "" } - return *l.URL + return c.Message } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (l *LabelEvent) GetAction() string { - if l == nil || l.Action == nil { +// GetPath returns the Path field. +func (c *CodeownersError) GetPath() string { + if c == nil { return "" } - return *l.Action + return c.Path } -// GetChanges returns the Changes field. -func (l *LabelEvent) GetChanges() *EditChange { - if l == nil { - return nil +// GetSource returns the Source field. +func (c *CodeownersError) GetSource() string { + if c == nil { + return "" } - return l.Changes + return c.Source } -// GetInstallation returns the Installation field. -func (l *LabelEvent) GetInstallation() *Installation { - if l == nil { - return nil +// GetSuggestion returns the Suggestion field if it's non-nil, zero value otherwise. +func (c *CodeownersError) GetSuggestion() string { + if c == nil || c.Suggestion == nil { + return "" } - return l.Installation + return *c.Suggestion } -// GetLabel returns the Label field. -func (l *LabelEvent) GetLabel() *Label { - if l == nil { +// GetErrors returns the Errors slice if it's non-nil, nil otherwise. +func (c *CodeownersErrors) GetErrors() []*CodeownersError { + if c == nil || c.Errors == nil { return nil } - return l.Label + return c.Errors } -// GetOrg returns the Org field. -func (l *LabelEvent) GetOrg() *Organization { - if l == nil { - return nil +// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetContentType() string { + if c == nil || c.ContentType == nil { + return "" } - return l.Org + return *c.ContentType } -// GetRepo returns the Repo field. -func (l *LabelEvent) GetRepo() *Repository { - if l == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} } - return l.Repo + return *c.CreatedAt } -// GetColor returns the Color field if it's non-nil, zero value otherwise. -func (l *LabelResult) GetColor() string { - if l == nil || l.Color == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return *l.Color + return *c.ID } -// GetDefault returns the Default field if it's non-nil, zero value otherwise. -func (l *LabelResult) GetDefault() bool { - if l == nil || l.Default == nil { - return false +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetLanguage() string { + if c == nil || c.Language == nil { + return "" } - return *l.Default + return *c.Language } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (l *LabelResult) GetDescription() string { - if l == nil || l.Description == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetName() string { + if c == nil || c.Name == nil { return "" } - return *l.Description + return *c.Name } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (l *LabelResult) GetID() int64 { - if l == nil || l.ID == nil { +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetSize() int64 { + if c == nil || c.Size == nil { return 0 } - return *l.ID + return *c.Size } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (l *LabelResult) GetName() string { - if l == nil || l.Name == nil { - return "" +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CodeQLDatabase) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} } - return *l.Name + return *c.UpdatedAt } -// GetScore returns the Score field. -func (l *LabelResult) GetScore() *float64 { - if l == nil { +// GetUploader returns the Uploader field. +func (c *CodeQLDatabase) GetUploader() *User { + if c == nil { return nil } - return l.Score + return c.Uploader } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (l *LabelResult) GetURL() string { - if l == nil || l.URL == nil { +func (c *CodeQLDatabase) GetURL() string { + if c == nil || c.URL == nil { return "" } - return *l.URL + return *c.URL } -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (l *LabelsSearchResult) GetIncompleteResults() bool { - if l == nil || l.IncompleteResults == nil { - return false +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CodeQualityFinding) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} } - return *l.IncompleteResults + return *c.CreatedAt } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (l *LabelsSearchResult) GetTotal() int { - if l == nil || l.Total == nil { - return 0 +// GetLocation returns the Location field. +func (c *CodeQualityFinding) GetLocation() CodeQualityFindingLocation { + if c == nil { + return CodeQualityFindingLocation{} } - return *l.Total + return c.Location } -// GetOID returns the OID field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetOID() string { - if l == nil || l.OID == nil { - return "" +// GetMessage returns the Message field. +func (c *CodeQualityFinding) GetMessage() CodeQualityFindingMessage { + if c == nil { + return CodeQualityFindingMessage{} } - return *l.OID + return c.Message } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetPath() string { - if l == nil || l.Path == nil { - return "" +// GetNumber returns the Number field. +func (c *CodeQualityFinding) GetNumber() int { + if c == nil { + return 0 } - return *l.Path + return c.Number } -// GetRefName returns the RefName field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetRefName() string { - if l == nil || l.RefName == nil { - return "" +// GetRule returns the Rule field. +func (c *CodeQualityFinding) GetRule() CodeQualityFindingRule { + if c == nil { + return CodeQualityFindingRule{} } - return *l.RefName + return c.Rule } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetSize() int { - if l == nil || l.Size == nil { - return 0 +// GetState returns the State field. +func (c *CodeQualityFinding) GetState() string { + if c == nil { + return "" } - return *l.Size + return c.State } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (l *License) GetBody() string { - if l == nil || l.Body == nil { +// GetURL returns the URL field. +func (c *CodeQualityFinding) GetURL() string { + if c == nil { return "" } - return *l.Body + return c.URL } -// GetConditions returns the Conditions field if it's non-nil, zero value otherwise. -func (l *License) GetConditions() []string { - if l == nil || l.Conditions == nil { - return nil +// GetEndColumn returns the EndColumn field if it's non-nil, zero value otherwise. +func (c *CodeQualityFindingLocation) GetEndColumn() int { + if c == nil || c.EndColumn == nil { + return 0 } - return *l.Conditions + return *c.EndColumn } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (l *License) GetDescription() string { - if l == nil || l.Description == nil { +// GetEndLine returns the EndLine field if it's non-nil, zero value otherwise. +func (c *CodeQualityFindingLocation) GetEndLine() int { + if c == nil || c.EndLine == nil { + return 0 + } + return *c.EndLine +} + +// GetPath returns the Path field. +func (c *CodeQualityFindingLocation) GetPath() string { + if c == nil { return "" } - return *l.Description + return c.Path } -// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. -func (l *License) GetFeatured() bool { - if l == nil || l.Featured == nil { - return false +// GetStartColumn returns the StartColumn field if it's non-nil, zero value otherwise. +func (c *CodeQualityFindingLocation) GetStartColumn() int { + if c == nil || c.StartColumn == nil { + return 0 } - return *l.Featured + return *c.StartColumn } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (l *License) GetHTMLURL() string { - if l == nil || l.HTMLURL == nil { - return "" +// GetStartLine returns the StartLine field if it's non-nil, zero value otherwise. +func (c *CodeQualityFindingLocation) GetStartLine() int { + if c == nil || c.StartLine == nil { + return 0 } - return *l.HTMLURL + return *c.StartLine } -// GetImplementation returns the Implementation field if it's non-nil, zero value otherwise. -func (l *License) GetImplementation() string { - if l == nil || l.Implementation == nil { +// GetMarkdown returns the Markdown field. +func (c *CodeQualityFindingMessage) GetMarkdown() string { + if c == nil { return "" } - return *l.Implementation + return c.Markdown } -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (l *License) GetKey() string { - if l == nil || l.Key == nil { +// GetText returns the Text field. +func (c *CodeQualityFindingMessage) GetText() string { + if c == nil { return "" } - return *l.Key -} - -// GetLimitations returns the Limitations field if it's non-nil, zero value otherwise. -func (l *License) GetLimitations() []string { - if l == nil || l.Limitations == nil { - return nil - } - return *l.Limitations + return c.Text } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (l *License) GetName() string { - if l == nil || l.Name == nil { +// GetCategory returns the Category field. +func (c *CodeQualityFindingRule) GetCategory() string { + if c == nil { return "" } - return *l.Name -} - -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (l *License) GetPermissions() []string { - if l == nil || l.Permissions == nil { - return nil - } - return *l.Permissions + return c.Category } -// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. -func (l *License) GetSPDXID() string { - if l == nil || l.SPDXID == nil { +// GetDescription returns the Description field. +func (c *CodeQualityFindingRule) GetDescription() string { + if c == nil { return "" } - return *l.SPDXID + return c.Description } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (l *License) GetURL() string { - if l == nil || l.URL == nil { +// GetHelp returns the Help field if it's non-nil, zero value otherwise. +func (c *CodeQualityFindingRule) GetHelp() string { + if c == nil || c.Help == nil { return "" } - return *l.URL + return *c.Help } -// GetCheckName returns the CheckName field if it's non-nil, zero value otherwise. -func (l *ListCheckRunsOptions) GetCheckName() string { - if l == nil || l.CheckName == nil { +// GetID returns the ID field. +func (c *CodeQualityFindingRule) GetID() string { + if c == nil { return "" } - return *l.CheckName + return c.ID } -// GetFilter returns the Filter field if it's non-nil, zero value otherwise. -func (l *ListCheckRunsOptions) GetFilter() string { - if l == nil || l.Filter == nil { +// GetSeverity returns the Severity field. +func (c *CodeQualityFindingRule) GetSeverity() string { + if c == nil { return "" } - return *l.Filter + return c.Severity } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (l *ListCheckRunsOptions) GetStatus() string { - if l == nil || l.Status == nil { +// GetTitle returns the Title field. +func (c *CodeQualityFindingRule) GetTitle() string { + if c == nil { return "" } - return *l.Status + return c.Title } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (l *ListCheckRunsResults) GetTotal() int { - if l == nil || l.Total == nil { - return 0 +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (c *CodeQualitySetupConfiguration) GetLanguages() []string { + if c == nil || c.Languages == nil { + return nil } - return *l.Total + return c.Languages } -// GetAppID returns the AppID field if it's non-nil, zero value otherwise. -func (l *ListCheckSuiteOptions) GetAppID() int { - if l == nil || l.AppID == nil { - return 0 +// GetRunnerLabel returns the RunnerLabel field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetRunnerLabel() string { + if c == nil || c.RunnerLabel == nil { + return "" } - return *l.AppID + return *c.RunnerLabel } -// GetCheckName returns the CheckName field if it's non-nil, zero value otherwise. -func (l *ListCheckSuiteOptions) GetCheckName() string { - if l == nil || l.CheckName == nil { +// GetRunnerType returns the RunnerType field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetRunnerType() string { + if c == nil || c.RunnerType == nil { return "" } - return *l.CheckName + return *c.RunnerType } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (l *ListCheckSuiteResults) GetTotal() int { - if l == nil || l.Total == nil { - return 0 +// GetSchedule returns the Schedule field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetSchedule() string { + if c == nil || c.Schedule == nil { + return "" } - return *l.Total + return *c.Schedule } -// GetAffiliation returns the Affiliation field if it's non-nil, zero value otherwise. -func (l *ListCollaboratorOptions) GetAffiliation() string { - if l == nil || l.Affiliation == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetState() string { + if c == nil || c.State == nil { return "" } - return *l.Affiliation + return *c.State } -// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. -func (m *MarketplacePendingChange) GetEffectiveDate() Timestamp { - if m == nil || m.EffectiveDate == nil { +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CodeQualitySetupConfiguration) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { return Timestamp{} } - return *m.EffectiveDate + return *c.UpdatedAt } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *MarketplacePendingChange) GetID() int64 { - if m == nil || m.ID == nil { - return 0 +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (c *CodeQualityUpdateSetupRequest) GetLanguages() []string { + if c == nil || c.Languages == nil { + return nil } - return *m.ID + return c.Languages } -// GetPlan returns the Plan field. -func (m *MarketplacePendingChange) GetPlan() *MarketplacePlan { - if m == nil { - return nil +// GetRunnerLabel returns the RunnerLabel field if it's non-nil, zero value otherwise. +func (c *CodeQualityUpdateSetupRequest) GetRunnerLabel() string { + if c == nil || c.RunnerLabel == nil { + return "" } - return m.Plan + return *c.RunnerLabel } -// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. -func (m *MarketplacePendingChange) GetUnitCount() int { - if m == nil || m.UnitCount == nil { - return 0 +// GetRunnerType returns the RunnerType field if it's non-nil, zero value otherwise. +func (c *CodeQualityUpdateSetupRequest) GetRunnerType() string { + if c == nil || c.RunnerType == nil { + return "" } - return *m.UnitCount + return *c.RunnerType } -// GetAccountsURL returns the AccountsURL field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetAccountsURL() string { - if m == nil || m.AccountsURL == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CodeQualityUpdateSetupRequest) GetState() string { + if c == nil || c.State == nil { return "" } - return *m.AccountsURL + return *c.State } -// GetBullets returns the Bullets field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetBullets() []string { - if m == nil || m.Bullets == nil { - return nil +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (c *CodeQualityUpdateSetupResponse) GetRunID() int64 { + if c == nil || c.RunID == nil { + return 0 } - return *m.Bullets + return *c.RunID } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetDescription() string { - if m == nil || m.Description == nil { +// GetRunURL returns the RunURL field if it's non-nil, zero value otherwise. +func (c *CodeQualityUpdateSetupResponse) GetRunURL() string { + if c == nil || c.RunURL == nil { return "" } - return *m.Description + return *c.RunURL } -// GetHasFreeTrial returns the HasFreeTrial field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetHasFreeTrial() bool { - if m == nil || m.HasFreeTrial == nil { - return false +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" } - return *m.HasFreeTrial + return *c.HTMLURL } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetID() int64 { - if m == nil || m.ID == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetName() string { + if c == nil || c.Name == nil { + return "" } - return *m.ID + return *c.Name } -// GetMonthlyPriceInCents returns the MonthlyPriceInCents field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetMonthlyPriceInCents() int { - if m == nil || m.MonthlyPriceInCents == nil { - return 0 +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetPath() string { + if c == nil || c.Path == nil { + return "" } - return *m.MonthlyPriceInCents + return *c.Path } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetName() string { - if m == nil || m.Name == nil { - return "" +// GetRepository returns the Repository field. +func (c *CodeResult) GetRepository() *Repository { + if c == nil { + return nil } - return *m.Name + return c.Repository } -// GetPriceModel returns the PriceModel field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetPriceModel() string { - if m == nil || m.PriceModel == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CodeResult) GetSHA() string { + if c == nil || c.SHA == nil { return "" } - return *m.PriceModel + return *c.SHA } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetState() string { - if m == nil || m.State == nil { - return "" +// GetTextMatches returns the TextMatches slice if it's non-nil, nil otherwise. +func (c *CodeResult) GetTextMatches() []*TextMatch { + if c == nil || c.TextMatches == nil { + return nil } - return *m.State + return c.TextMatches } -// GetUnitName returns the UnitName field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetUnitName() string { - if m == nil || m.UnitName == nil { +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertEvent) GetAction() string { + if c == nil || c.Action == nil { return "" } - return *m.UnitName + return *c.Action } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetURL() string { - if m == nil || m.URL == nil { - return "" +// GetAlert returns the Alert field. +func (c *CodeScanningAlertEvent) GetAlert() *Alert { + if c == nil { + return nil } - return *m.URL + return c.Alert } -// GetYearlyPriceInCents returns the YearlyPriceInCents field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetYearlyPriceInCents() int { - if m == nil || m.YearlyPriceInCents == nil { - return 0 +// GetCommitOID returns the CommitOID field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertEvent) GetCommitOID() string { + if c == nil || c.CommitOID == nil { + return "" } - return *m.YearlyPriceInCents + return *c.CommitOID } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetEmail() string { - if m == nil || m.Email == nil { - return "" +// GetInstallation returns the Installation field. +func (c *CodeScanningAlertEvent) GetInstallation() *Installation { + if c == nil { + return nil } - return *m.Email + return c.Installation } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetID() int64 { - if m == nil || m.ID == nil { - return 0 +// GetOrg returns the Org field. +func (c *CodeScanningAlertEvent) GetOrg() *Organization { + if c == nil { + return nil } - return *m.ID + return c.Org } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetLogin() string { - if m == nil || m.Login == nil { +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertEvent) GetRef() string { + if c == nil || c.Ref == nil { return "" } - return *m.Login + return *c.Ref } -// GetMarketplacePendingChange returns the MarketplacePendingChange field. -func (m *MarketplacePlanAccount) GetMarketplacePendingChange() *MarketplacePendingChange { - if m == nil { +// GetRepo returns the Repo field. +func (c *CodeScanningAlertEvent) GetRepo() *Repository { + if c == nil { return nil } - return m.MarketplacePendingChange + return c.Repo } -// GetMarketplacePurchase returns the MarketplacePurchase field. -func (m *MarketplacePlanAccount) GetMarketplacePurchase() *MarketplacePurchase { - if m == nil { +// GetSender returns the Sender field. +func (c *CodeScanningAlertEvent) GetSender() *User { + if c == nil { return nil } - return m.MarketplacePurchase + return c.Sender } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetNodeID() string { - if m == nil || m.NodeID == nil { +// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertState) GetDismissedComment() string { + if c == nil || c.DismissedComment == nil { return "" } - return *m.NodeID + return *c.DismissedComment } -// GetOrganizationBillingEmail returns the OrganizationBillingEmail field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetOrganizationBillingEmail() string { - if m == nil || m.OrganizationBillingEmail == nil { +// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise. +func (c *CodeScanningAlertState) GetDismissedReason() string { + if c == nil || c.DismissedReason == nil { return "" } - return *m.OrganizationBillingEmail + return *c.DismissedReason } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetType() string { - if m == nil || m.Type == nil { +// GetState returns the State field. +func (c *CodeScanningAlertState) GetState() string { + if c == nil { return "" } - return *m.Type + return c.State } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetURL() string { - if m == nil || m.URL == nil { - return "" +// GetParameters returns the Parameters field. +func (c *CodeScanningBranchRule) GetParameters() CodeScanningRuleParameters { + if c == nil { + return CodeScanningRuleParameters{} } - return *m.URL + return c.Parameters } -// GetAccount returns the Account field. -func (m *MarketplacePurchase) GetAccount() *MarketplacePlanAccount { - if m == nil { - return nil +// GetRunnerLabel returns the RunnerLabel field if it's non-nil, zero value otherwise. +func (c *CodeScanningDefaultSetupOptions) GetRunnerLabel() string { + if c == nil || c.RunnerLabel == nil { + return "" } - return m.Account + return *c.RunnerLabel } -// GetBillingCycle returns the BillingCycle field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetBillingCycle() string { - if m == nil || m.BillingCycle == nil { +// GetRunnerType returns the RunnerType field. +func (c *CodeScanningDefaultSetupOptions) GetRunnerType() string { + if c == nil { return "" } - return *m.BillingCycle + return c.RunnerType } -// GetFreeTrialEndsOn returns the FreeTrialEndsOn field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetFreeTrialEndsOn() Timestamp { - if m == nil || m.FreeTrialEndsOn == nil { - return Timestamp{} +// GetAllowAdvanced returns the AllowAdvanced field if it's non-nil, zero value otherwise. +func (c *CodeScanningOptions) GetAllowAdvanced() bool { + if c == nil || c.AllowAdvanced == nil { + return false } - return *m.FreeTrialEndsOn + return *c.AllowAdvanced } -// GetNextBillingDate returns the NextBillingDate field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetNextBillingDate() Timestamp { - if m == nil || m.NextBillingDate == nil { - return Timestamp{} +// GetCodeScanningTools returns the CodeScanningTools slice if it's non-nil, nil otherwise. +func (c *CodeScanningRuleParameters) GetCodeScanningTools() []*RuleCodeScanningTool { + if c == nil || c.CodeScanningTools == nil { + return nil } - return *m.NextBillingDate + return c.CodeScanningTools } -// GetOnFreeTrial returns the OnFreeTrial field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetOnFreeTrial() bool { - if m == nil || m.OnFreeTrial == nil { - return false +// GetCodeResults returns the CodeResults slice if it's non-nil, nil otherwise. +func (c *CodeSearchResult) GetCodeResults() []*CodeResult { + if c == nil || c.CodeResults == nil { + return nil } - return *m.OnFreeTrial + return c.CodeResults } -// GetPlan returns the Plan field. -func (m *MarketplacePurchase) GetPlan() *MarketplacePlan { - if m == nil { - return nil +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (c *CodeSearchResult) GetIncompleteResults() bool { + if c == nil || c.IncompleteResults == nil { + return false } - return m.Plan + return *c.IncompleteResults } -// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetUnitCount() int { - if m == nil || m.UnitCount == nil { +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *CodeSearchResult) GetTotal() int { + if c == nil || c.Total == nil { return 0 } - return *m.UnitCount + return *c.Total } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchaseEvent) GetAction() string { - if m == nil || m.Action == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CodeSecurity) GetStatus() string { + if c == nil || c.Status == nil { return "" } - return *m.Action + return *c.Status } -// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchaseEvent) GetEffectiveDate() Timestamp { - if m == nil || m.EffectiveDate == nil { - return Timestamp{} +// GetAdvancedSecurity returns the AdvancedSecurity field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetAdvancedSecurity() string { + if c == nil || c.AdvancedSecurity == nil { + return "" } - return *m.EffectiveDate + return *c.AdvancedSecurity } -// GetInstallation returns the Installation field. -func (m *MarketplacePurchaseEvent) GetInstallation() *Installation { - if m == nil { - return nil +// GetCodeScanningDefaultSetup returns the CodeScanningDefaultSetup field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetCodeScanningDefaultSetup() string { + if c == nil || c.CodeScanningDefaultSetup == nil { + return "" } - return m.Installation + return *c.CodeScanningDefaultSetup } -// GetMarketplacePurchase returns the MarketplacePurchase field. -func (m *MarketplacePurchaseEvent) GetMarketplacePurchase() *MarketplacePurchase { - if m == nil { +// GetCodeScanningDefaultSetupOptions returns the CodeScanningDefaultSetupOptions field. +func (c *CodeSecurityConfiguration) GetCodeScanningDefaultSetupOptions() *CodeScanningDefaultSetupOptions { + if c == nil { return nil } - return m.MarketplacePurchase + return c.CodeScanningDefaultSetupOptions } -// GetPreviousMarketplacePurchase returns the PreviousMarketplacePurchase field. -func (m *MarketplacePurchaseEvent) GetPreviousMarketplacePurchase() *MarketplacePurchase { - if m == nil { - return nil +// GetCodeScanningDelegatedAlertDismissal returns the CodeScanningDelegatedAlertDismissal field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetCodeScanningDelegatedAlertDismissal() string { + if c == nil || c.CodeScanningDelegatedAlertDismissal == nil { + return "" } - return m.PreviousMarketplacePurchase + return *c.CodeScanningDelegatedAlertDismissal } -// GetSender returns the Sender field. -func (m *MarketplacePurchaseEvent) GetSender() *User { - if m == nil { +// GetCodeScanningOptions returns the CodeScanningOptions field. +func (c *CodeSecurityConfiguration) GetCodeScanningOptions() *CodeScanningOptions { + if c == nil { return nil } - return m.Sender + return c.CodeScanningOptions } -// GetText returns the Text field if it's non-nil, zero value otherwise. -func (m *Match) GetText() string { - if m == nil || m.Text == nil { +// GetCodeSecurity returns the CodeSecurity field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetCodeSecurity() string { + if c == nil || c.CodeSecurity == nil { return "" } - return *m.Text + return *c.CodeSecurity } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MemberEvent) GetAction() string { - if m == nil || m.Action == nil { +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} + } + return *c.CreatedAt +} + +// GetDependabotAlerts returns the DependabotAlerts field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetDependabotAlerts() string { + if c == nil || c.DependabotAlerts == nil { return "" } - return *m.Action + return *c.DependabotAlerts } -// GetInstallation returns the Installation field. -func (m *MemberEvent) GetInstallation() *Installation { - if m == nil { - return nil +// GetDependabotDelegatedAlertDismissal returns the DependabotDelegatedAlertDismissal field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetDependabotDelegatedAlertDismissal() string { + if c == nil || c.DependabotDelegatedAlertDismissal == nil { + return "" } - return m.Installation + return *c.DependabotDelegatedAlertDismissal } -// GetMember returns the Member field. -func (m *MemberEvent) GetMember() *User { - if m == nil { - return nil +// GetDependabotSecurityUpdates returns the DependabotSecurityUpdates field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetDependabotSecurityUpdates() string { + if c == nil || c.DependabotSecurityUpdates == nil { + return "" } - return m.Member + return *c.DependabotSecurityUpdates } -// GetRepo returns the Repo field. -func (m *MemberEvent) GetRepo() *Repository { - if m == nil { - return nil +// GetDependencyGraph returns the DependencyGraph field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetDependencyGraph() string { + if c == nil || c.DependencyGraph == nil { + return "" } - return m.Repo + return *c.DependencyGraph } -// GetSender returns the Sender field. -func (m *MemberEvent) GetSender() *User { - if m == nil { - return nil +// GetDependencyGraphAutosubmitAction returns the DependencyGraphAutosubmitAction field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetDependencyGraphAutosubmitAction() string { + if c == nil || c.DependencyGraphAutosubmitAction == nil { + return "" } - return m.Sender + return *c.DependencyGraphAutosubmitAction } -// GetOrganization returns the Organization field. -func (m *Membership) GetOrganization() *Organization { - if m == nil { +// GetDependencyGraphAutosubmitActionOptions returns the DependencyGraphAutosubmitActionOptions field. +func (c *CodeSecurityConfiguration) GetDependencyGraphAutosubmitActionOptions() *DependencyGraphAutosubmitActionOptions { + if c == nil { return nil } - return m.Organization + return c.DependencyGraphAutosubmitActionOptions } -// GetOrganizationURL returns the OrganizationURL field if it's non-nil, zero value otherwise. -func (m *Membership) GetOrganizationURL() string { - if m == nil || m.OrganizationURL == nil { +// GetDescription returns the Description field. +func (c *CodeSecurityConfiguration) GetDescription() string { + if c == nil { return "" } - return *m.OrganizationURL + return c.Description } -// GetRole returns the Role field if it's non-nil, zero value otherwise. -func (m *Membership) GetRole() string { - if m == nil || m.Role == nil { +// GetEnforcement returns the Enforcement field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetEnforcement() string { + if c == nil || c.Enforcement == nil { return "" } - return *m.Role + return *c.Enforcement } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *Membership) GetState() string { - if m == nil || m.State == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { return "" } - return *m.State + return *c.HTMLURL } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Membership) GetURL() string { - if m == nil || m.URL == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return *m.URL + return *c.ID } -// GetUser returns the User field. -func (m *Membership) GetUser() *User { - if m == nil { - return nil +// GetName returns the Name field. +func (c *CodeSecurityConfiguration) GetName() string { + if c == nil { + return "" } - return m.User + return c.Name } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MembershipEvent) GetAction() string { - if m == nil || m.Action == nil { +// GetPrivateVulnerabilityReporting returns the PrivateVulnerabilityReporting field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetPrivateVulnerabilityReporting() string { + if c == nil || c.PrivateVulnerabilityReporting == nil { return "" } - return *m.Action + return *c.PrivateVulnerabilityReporting } -// GetInstallation returns the Installation field. -func (m *MembershipEvent) GetInstallation() *Installation { - if m == nil { - return nil +// GetSecretProtection returns the SecretProtection field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretProtection() string { + if c == nil || c.SecretProtection == nil { + return "" } - return m.Installation + return *c.SecretProtection } -// GetMember returns the Member field. -func (m *MembershipEvent) GetMember() *User { - if m == nil { - return nil +// GetSecretScanning returns the SecretScanning field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanning() string { + if c == nil || c.SecretScanning == nil { + return "" } - return m.Member + return *c.SecretScanning } -// GetOrg returns the Org field. -func (m *MembershipEvent) GetOrg() *Organization { - if m == nil { - return nil +// GetSecretScanningDelegatedAlertDismissal returns the SecretScanningDelegatedAlertDismissal field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningDelegatedAlertDismissal() string { + if c == nil || c.SecretScanningDelegatedAlertDismissal == nil { + return "" } - return m.Org + return *c.SecretScanningDelegatedAlertDismissal } -// GetScope returns the Scope field if it's non-nil, zero value otherwise. -func (m *MembershipEvent) GetScope() string { - if m == nil || m.Scope == nil { +// GetSecretScanningDelegatedBypass returns the SecretScanningDelegatedBypass field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningDelegatedBypass() string { + if c == nil || c.SecretScanningDelegatedBypass == nil { return "" } - return *m.Scope + return *c.SecretScanningDelegatedBypass } -// GetSender returns the Sender field. -func (m *MembershipEvent) GetSender() *User { - if m == nil { +// GetSecretScanningDelegatedBypassOptions returns the SecretScanningDelegatedBypassOptions field. +func (c *CodeSecurityConfiguration) GetSecretScanningDelegatedBypassOptions() *SecretScanningDelegatedBypassOptions { + if c == nil { return nil } - return m.Sender + return c.SecretScanningDelegatedBypassOptions } -// GetTeam returns the Team field. -func (m *MembershipEvent) GetTeam() *Team { - if m == nil { - return nil +// GetSecretScanningExtendedMetadata returns the SecretScanningExtendedMetadata field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningExtendedMetadata() string { + if c == nil || c.SecretScanningExtendedMetadata == nil { + return "" } - return m.Team + return *c.SecretScanningExtendedMetadata } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MetaEvent) GetAction() string { - if m == nil || m.Action == nil { +// GetSecretScanningGenericSecrets returns the SecretScanningGenericSecrets field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningGenericSecrets() string { + if c == nil || c.SecretScanningGenericSecrets == nil { return "" } - return *m.Action + return *c.SecretScanningGenericSecrets } -// GetHook returns the Hook field. -func (m *MetaEvent) GetHook() *Hook { - if m == nil { - return nil +// GetSecretScanningNonProviderPatterns returns the SecretScanningNonProviderPatterns field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningNonProviderPatterns() string { + if c == nil || c.SecretScanningNonProviderPatterns == nil { + return "" } - return m.Hook + return *c.SecretScanningNonProviderPatterns } -// GetHookID returns the HookID field if it's non-nil, zero value otherwise. -func (m *MetaEvent) GetHookID() int64 { - if m == nil || m.HookID == nil { - return 0 +// GetSecretScanningPushProtection returns the SecretScanningPushProtection field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningPushProtection() string { + if c == nil || c.SecretScanningPushProtection == nil { + return "" } - return *m.HookID + return *c.SecretScanningPushProtection } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (m *Metric) GetHTMLURL() string { - if m == nil || m.HTMLURL == nil { +// GetSecretScanningValidityChecks returns the SecretScanningValidityChecks field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetSecretScanningValidityChecks() string { + if c == nil || c.SecretScanningValidityChecks == nil { return "" } - return *m.HTMLURL + return *c.SecretScanningValidityChecks } -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (m *Metric) GetKey() string { - if m == nil || m.Key == nil { +// GetTargetType returns the TargetType field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetTargetType() string { + if c == nil || c.TargetType == nil { return "" } - return *m.Key + return *c.TargetType } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (m *Metric) GetName() string { - if m == nil || m.Name == nil { - return "" +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfiguration) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} } - return *m.Name + return *c.UpdatedAt } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Metric) GetURL() string { - if m == nil || m.URL == nil { +func (c *CodeSecurityConfiguration) GetURL() string { + if c == nil || c.URL == nil { return "" } - return *m.URL + return *c.URL } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (m *Migration) GetCreatedAt() string { - if m == nil || m.CreatedAt == nil { - return "" +// GetConfiguration returns the Configuration field. +func (c *CodeSecurityConfigurationWithDefaultForNewRepos) GetConfiguration() *CodeSecurityConfiguration { + if c == nil { + return nil } - return *m.CreatedAt + return c.Configuration } -// GetExcludeAttachments returns the ExcludeAttachments field if it's non-nil, zero value otherwise. -func (m *Migration) GetExcludeAttachments() bool { - if m == nil || m.ExcludeAttachments == nil { - return false +// GetDefaultForNewRepos returns the DefaultForNewRepos field if it's non-nil, zero value otherwise. +func (c *CodeSecurityConfigurationWithDefaultForNewRepos) GetDefaultForNewRepos() string { + if c == nil || c.DefaultForNewRepos == nil { + return "" } - return *m.ExcludeAttachments + return *c.DefaultForNewRepos } -// GetGUID returns the GUID field if it's non-nil, zero value otherwise. -func (m *Migration) GetGUID() string { - if m == nil || m.GUID == nil { - return "" - } - return *m.GUID -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *Migration) GetID() int64 { - if m == nil || m.ID == nil { - return 0 +// GetBillableOwner returns the BillableOwner field. +func (c *Codespace) GetBillableOwner() *User { + if c == nil { + return nil } - return *m.ID + return c.BillableOwner } -// GetLockRepositories returns the LockRepositories field if it's non-nil, zero value otherwise. -func (m *Migration) GetLockRepositories() bool { - if m == nil || m.LockRepositories == nil { - return false +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *Codespace) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} } - return *m.LockRepositories + return *c.CreatedAt } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *Migration) GetState() string { - if m == nil || m.State == nil { +// GetDevcontainerPath returns the DevcontainerPath field if it's non-nil, zero value otherwise. +func (c *Codespace) GetDevcontainerPath() string { + if c == nil || c.DevcontainerPath == nil { return "" } - return *m.State + return *c.DevcontainerPath } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (m *Migration) GetUpdatedAt() string { - if m == nil || m.UpdatedAt == nil { +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (c *Codespace) GetDisplayName() string { + if c == nil || c.DisplayName == nil { return "" } - return *m.UpdatedAt + return *c.DisplayName } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Migration) GetURL() string { - if m == nil || m.URL == nil { +// GetEnvironmentID returns the EnvironmentID field if it's non-nil, zero value otherwise. +func (c *Codespace) GetEnvironmentID() string { + if c == nil || c.EnvironmentID == nil { return "" } - return *m.URL + return *c.EnvironmentID } -// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetClosedAt() time.Time { - if m == nil || m.ClosedAt == nil { - return time.Time{} +// GetGitStatus returns the GitStatus field. +func (c *Codespace) GetGitStatus() *CodespacesGitStatus { + if c == nil { + return nil } - return *m.ClosedAt + return c.GitStatus } -// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. -func (m *Milestone) GetClosedIssues() int { - if m == nil || m.ClosedIssues == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *Codespace) GetID() int64 { + if c == nil || c.ID == nil { return 0 } - return *m.ClosedIssues + return *c.ID } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetCreatedAt() time.Time { - if m == nil || m.CreatedAt == nil { - return time.Time{} +// GetIdleTimeoutMinutes returns the IdleTimeoutMinutes field if it's non-nil, zero value otherwise. +func (c *Codespace) GetIdleTimeoutMinutes() int { + if c == nil || c.IdleTimeoutMinutes == nil { + return 0 } - return *m.CreatedAt + return *c.IdleTimeoutMinutes } -// GetCreator returns the Creator field. -func (m *Milestone) GetCreator() *User { - if m == nil { - return nil +// GetIdleTimeoutNotice returns the IdleTimeoutNotice field if it's non-nil, zero value otherwise. +func (c *Codespace) GetIdleTimeoutNotice() string { + if c == nil || c.IdleTimeoutNotice == nil { + return "" } - return m.Creator + return *c.IdleTimeoutNotice } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (m *Milestone) GetDescription() string { - if m == nil || m.Description == nil { +// GetLastKnownStopNotice returns the LastKnownStopNotice field if it's non-nil, zero value otherwise. +func (c *Codespace) GetLastKnownStopNotice() string { + if c == nil || c.LastKnownStopNotice == nil { return "" } - return *m.Description + return *c.LastKnownStopNotice } -// GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. -func (m *Milestone) GetDueOn() time.Time { - if m == nil || m.DueOn == nil { - return time.Time{} +// GetLastUsedAt returns the LastUsedAt field if it's non-nil, zero value otherwise. +func (c *Codespace) GetLastUsedAt() Timestamp { + if c == nil || c.LastUsedAt == nil { + return Timestamp{} } - return *m.DueOn + return *c.LastUsedAt } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (m *Milestone) GetHTMLURL() string { - if m == nil || m.HTMLURL == nil { +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (c *Codespace) GetLocation() string { + if c == nil || c.Location == nil { return "" } - return *m.HTMLURL + return *c.Location } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *Milestone) GetID() int64 { - if m == nil || m.ID == nil { - return 0 +// GetMachine returns the Machine field. +func (c *Codespace) GetMachine() *CodespacesMachine { + if c == nil { + return nil } - return *m.ID + return c.Machine } -// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. -func (m *Milestone) GetLabelsURL() string { - if m == nil || m.LabelsURL == nil { +// GetMachinesURL returns the MachinesURL field if it's non-nil, zero value otherwise. +func (c *Codespace) GetMachinesURL() string { + if c == nil || c.MachinesURL == nil { return "" } - return *m.LabelsURL + return *c.MachinesURL } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (m *Milestone) GetNodeID() string { - if m == nil || m.NodeID == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *Codespace) GetName() string { + if c == nil || c.Name == nil { return "" } - return *m.NodeID -} - -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (m *Milestone) GetNumber() int { - if m == nil || m.Number == nil { - return 0 - } - return *m.Number + return *c.Name } -// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. -func (m *Milestone) GetOpenIssues() int { - if m == nil || m.OpenIssues == nil { - return 0 +// GetOwner returns the Owner field. +func (c *Codespace) GetOwner() *User { + if c == nil { + return nil } - return *m.OpenIssues + return c.Owner } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *Milestone) GetState() string { - if m == nil || m.State == nil { - return "" +// GetPendingOperation returns the PendingOperation field if it's non-nil, zero value otherwise. +func (c *Codespace) GetPendingOperation() bool { + if c == nil || c.PendingOperation == nil { + return false } - return *m.State + return *c.PendingOperation } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (m *Milestone) GetTitle() string { - if m == nil || m.Title == nil { +// GetPendingOperationDisabledReason returns the PendingOperationDisabledReason field if it's non-nil, zero value otherwise. +func (c *Codespace) GetPendingOperationDisabledReason() string { + if c == nil || c.PendingOperationDisabledReason == nil { return "" } - return *m.Title -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetUpdatedAt() time.Time { - if m == nil || m.UpdatedAt == nil { - return time.Time{} - } - return *m.UpdatedAt + return *c.PendingOperationDisabledReason } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Milestone) GetURL() string { - if m == nil || m.URL == nil { - return "" +// GetPrebuild returns the Prebuild field if it's non-nil, zero value otherwise. +func (c *Codespace) GetPrebuild() bool { + if c == nil || c.Prebuild == nil { + return false } - return *m.URL + return *c.Prebuild } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MilestoneEvent) GetAction() string { - if m == nil || m.Action == nil { +// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. +func (c *Codespace) GetPullsURL() string { + if c == nil || c.PullsURL == nil { return "" } - return *m.Action + return *c.PullsURL } -// GetChanges returns the Changes field. -func (m *MilestoneEvent) GetChanges() *EditChange { - if m == nil { +// GetRecentFolders returns the RecentFolders slice if it's non-nil, nil otherwise. +func (c *Codespace) GetRecentFolders() []string { + if c == nil || c.RecentFolders == nil { return nil } - return m.Changes + return c.RecentFolders } -// GetInstallation returns the Installation field. -func (m *MilestoneEvent) GetInstallation() *Installation { - if m == nil { +// GetRepository returns the Repository field. +func (c *Codespace) GetRepository() *Repository { + if c == nil { return nil } - return m.Installation + return c.Repository } -// GetMilestone returns the Milestone field. -func (m *MilestoneEvent) GetMilestone() *Milestone { - if m == nil { - return nil +// GetRetentionExpiresAt returns the RetentionExpiresAt field if it's non-nil, zero value otherwise. +func (c *Codespace) GetRetentionExpiresAt() Timestamp { + if c == nil || c.RetentionExpiresAt == nil { + return Timestamp{} } - return m.Milestone + return *c.RetentionExpiresAt } -// GetOrg returns the Org field. -func (m *MilestoneEvent) GetOrg() *Organization { - if m == nil { - return nil +// GetRetentionPeriodMinutes returns the RetentionPeriodMinutes field if it's non-nil, zero value otherwise. +func (c *Codespace) GetRetentionPeriodMinutes() int { + if c == nil || c.RetentionPeriodMinutes == nil { + return 0 } - return m.Org + return *c.RetentionPeriodMinutes } -// GetRepo returns the Repo field. -func (m *MilestoneEvent) GetRepo() *Repository { - if m == nil { +// GetRuntimeConstraints returns the RuntimeConstraints field. +func (c *Codespace) GetRuntimeConstraints() *CodespacesRuntimeConstraints { + if c == nil { return nil } - return m.Repo + return c.RuntimeConstraints } -// GetSender returns the Sender field. -func (m *MilestoneEvent) GetSender() *User { - if m == nil { - return nil +// GetStartURL returns the StartURL field if it's non-nil, zero value otherwise. +func (c *Codespace) GetStartURL() string { + if c == nil || c.StartURL == nil { + return "" } - return m.Sender + return *c.StartURL } -// GetClosedMilestones returns the ClosedMilestones field if it's non-nil, zero value otherwise. -func (m *MilestoneStats) GetClosedMilestones() int { - if m == nil || m.ClosedMilestones == nil { - return 0 +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *Codespace) GetState() string { + if c == nil || c.State == nil { + return "" } - return *m.ClosedMilestones + return *c.State } -// GetOpenMilestones returns the OpenMilestones field if it's non-nil, zero value otherwise. -func (m *MilestoneStats) GetOpenMilestones() int { - if m == nil || m.OpenMilestones == nil { - return 0 +// GetStopURL returns the StopURL field if it's non-nil, zero value otherwise. +func (c *Codespace) GetStopURL() string { + if c == nil || c.StopURL == nil { + return "" } - return *m.OpenMilestones + return *c.StopURL } -// GetTotalMilestones returns the TotalMilestones field if it's non-nil, zero value otherwise. -func (m *MilestoneStats) GetTotalMilestones() int { - if m == nil || m.TotalMilestones == nil { - return 0 +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *Codespace) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} } - return *m.TotalMilestones + return *c.UpdatedAt } -// GetBase returns the Base field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetBase() string { - if n == nil || n.Base == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Codespace) GetURL() string { + if c == nil || c.URL == nil { return "" } - return *n.Base + return *c.URL } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetBody() string { - if n == nil || n.Body == nil { +// GetWebURL returns the WebURL field if it's non-nil, zero value otherwise. +func (c *Codespace) GetWebURL() string { + if c == nil || c.WebURL == nil { return "" } - return *n.Body + return *c.WebURL } -// GetDraft returns the Draft field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetDraft() bool { - if n == nil || n.Draft == nil { - return false +// GetClientIP returns the ClientIP field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetClientIP() string { + if c == nil || c.ClientIP == nil { + return "" } - return *n.Draft + return *c.ClientIP } -// GetHead returns the Head field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetHead() string { - if n == nil || n.Head == nil { +// GetDevcontainerPath returns the DevcontainerPath field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetDevcontainerPath() string { + if c == nil || c.DevcontainerPath == nil { return "" } - return *n.Head + return *c.DevcontainerPath } -// GetIssue returns the Issue field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetIssue() int { - if n == nil || n.Issue == nil { - return 0 +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetDisplayName() string { + if c == nil || c.DisplayName == nil { + return "" } - return *n.Issue + return *c.DisplayName } -// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetMaintainerCanModify() bool { - if n == nil || n.MaintainerCanModify == nil { - return false +// GetGeo returns the Geo field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetGeo() string { + if c == nil || c.Geo == nil { + return "" } - return *n.MaintainerCanModify + return *c.Geo } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetTitle() string { - if n == nil || n.Title == nil { - return "" +// GetIdleTimeoutMinutes returns the IdleTimeoutMinutes field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetIdleTimeoutMinutes() int { + if c == nil || c.IdleTimeoutMinutes == nil { + return 0 } - return *n.Title + return *c.IdleTimeoutMinutes } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetDescription() string { - if n == nil || n.Description == nil { +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetLocation() string { + if c == nil || c.Location == nil { return "" } - return *n.Description + return *c.Location } -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetLDAPDN() string { - if n == nil || n.LDAPDN == nil { +// GetMachine returns the Machine field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetMachine() string { + if c == nil || c.Machine == nil { return "" } - return *n.LDAPDN + return *c.Machine } -// GetParentTeamID returns the ParentTeamID field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetParentTeamID() int64 { - if n == nil || n.ParentTeamID == nil { - return 0 +// GetMultiRepoPermissionsOptOut returns the MultiRepoPermissionsOptOut field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetMultiRepoPermissionsOptOut() bool { + if c == nil || c.MultiRepoPermissionsOptOut == nil { + return false } - return *n.ParentTeamID + return *c.MultiRepoPermissionsOptOut } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetPermission() string { - if n == nil || n.Permission == nil { - return "" +// GetPullRequest returns the PullRequest field. +func (c *CodespaceCreateForUserOptions) GetPullRequest() *CodespacePullRequestOptions { + if c == nil { + return nil } - return *n.Permission + return c.PullRequest } -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetPrivacy() string { - if n == nil || n.Privacy == nil { +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetRef() string { + if c == nil || c.Ref == nil { return "" } - return *n.Privacy + return *c.Ref } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (n *Notification) GetID() string { - if n == nil || n.ID == nil { - return "" +// GetRepositoryID returns the RepositoryID field. +func (c *CodespaceCreateForUserOptions) GetRepositoryID() int64 { + if c == nil { + return 0 } - return *n.ID + return c.RepositoryID } -// GetLastReadAt returns the LastReadAt field if it's non-nil, zero value otherwise. -func (n *Notification) GetLastReadAt() time.Time { - if n == nil || n.LastReadAt == nil { - return time.Time{} +// GetRetentionPeriodMinutes returns the RetentionPeriodMinutes field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetRetentionPeriodMinutes() int { + if c == nil || c.RetentionPeriodMinutes == nil { + return 0 } - return *n.LastReadAt + return *c.RetentionPeriodMinutes } -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (n *Notification) GetReason() string { - if n == nil || n.Reason == nil { +// GetWorkingDirectory returns the WorkingDirectory field if it's non-nil, zero value otherwise. +func (c *CodespaceCreateForUserOptions) GetWorkingDirectory() string { + if c == nil || c.WorkingDirectory == nil { return "" } - return *n.Reason + return *c.WorkingDirectory } -// GetRepository returns the Repository field. -func (n *Notification) GetRepository() *Repository { - if n == nil { +// GetBillableOwner returns the BillableOwner field. +func (c *CodespaceDefaultAttributes) GetBillableOwner() *User { + if c == nil { return nil } - return n.Repository + return c.BillableOwner } -// GetSubject returns the Subject field. -func (n *Notification) GetSubject() *NotificationSubject { - if n == nil { +// GetDefaults returns the Defaults field. +func (c *CodespaceDefaultAttributes) GetDefaults() *CodespaceDefaults { + if c == nil { return nil } - return n.Subject + return c.Defaults } -// GetUnread returns the Unread field if it's non-nil, zero value otherwise. -func (n *Notification) GetUnread() bool { - if n == nil || n.Unread == nil { - return false +// GetDevcontainerPath returns the DevcontainerPath field if it's non-nil, zero value otherwise. +func (c *CodespaceDefaults) GetDevcontainerPath() string { + if c == nil || c.DevcontainerPath == nil { + return "" } - return *n.Unread + return *c.DevcontainerPath } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (n *Notification) GetUpdatedAt() time.Time { - if n == nil || n.UpdatedAt == nil { - return time.Time{} +// GetLocation returns the Location field. +func (c *CodespaceDefaults) GetLocation() string { + if c == nil { + return "" } - return *n.UpdatedAt + return c.Location } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (n *Notification) GetURL() string { - if n == nil || n.URL == nil { +// GetBranch returns the Branch field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetBranch() string { + if c == nil || c.Branch == nil { return "" } - return *n.URL + return *c.Branch } -// GetLatestCommentURL returns the LatestCommentURL field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetLatestCommentURL() string { - if n == nil || n.LatestCommentURL == nil { +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetCompletedAt() Timestamp { + if c == nil || c.CompletedAt == nil { + return Timestamp{} + } + return *c.CompletedAt +} + +// GetExportURL returns the ExportURL field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetExportURL() string { + if c == nil || c.ExportURL == nil { return "" } - return *n.LatestCommentURL + return *c.ExportURL } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetTitle() string { - if n == nil || n.Title == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { return "" } - return *n.Title + return *c.HTMLURL } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetType() string { - if n == nil || n.Type == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetID() string { + if c == nil || c.ID == nil { return "" } - return *n.Type + return *c.ID } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetURL() string { - if n == nil || n.URL == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetSHA() string { + if c == nil || c.SHA == nil { return "" } - return *n.URL + return *c.SHA } -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetAvatarURL() string { - if o == nil || o.AvatarURL == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CodespaceExport) GetState() string { + if c == nil || c.State == nil { return "" } - return *o.AvatarURL + return *c.State } -// GetBillingEmail returns the BillingEmail field if it's non-nil, zero value otherwise. -func (o *Organization) GetBillingEmail() string { - if o == nil || o.BillingEmail == nil { +// GetClientIP returns the ClientIP field if it's non-nil, zero value otherwise. +func (c *CodespaceGetDefaultAttributesOptions) GetClientIP() string { + if c == nil || c.ClientIP == nil { return "" } - return *o.BillingEmail + return *c.ClientIP } -// GetBlog returns the Blog field if it's non-nil, zero value otherwise. -func (o *Organization) GetBlog() string { - if o == nil || o.Blog == nil { +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CodespaceGetDefaultAttributesOptions) GetRef() string { + if c == nil || c.Ref == nil { return "" } - return *o.Blog + return *c.Ref } -// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. -func (o *Organization) GetCollaborators() int { - if o == nil || o.Collaborators == nil { - return 0 +// GetAccepted returns the Accepted field. +func (c *CodespacePermissions) GetAccepted() bool { + if c == nil { + return false } - return *o.Collaborators + return c.Accepted } -// GetCompany returns the Company field if it's non-nil, zero value otherwise. -func (o *Organization) GetCompany() string { - if o == nil || o.Company == nil { - return "" +// GetPullRequestNumber returns the PullRequestNumber field. +func (c *CodespacePullRequestOptions) GetPullRequestNumber() int64 { + if c == nil { + return 0 } - return *o.Company + return c.PullRequestNumber } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (o *Organization) GetCreatedAt() time.Time { - if o == nil || o.CreatedAt == nil { - return time.Time{} +// GetRepositoryID returns the RepositoryID field. +func (c *CodespacePullRequestOptions) GetRepositoryID() int64 { + if c == nil { + return 0 } - return *o.CreatedAt + return c.RepositoryID } -// GetDefaultRepoPermission returns the DefaultRepoPermission field if it's non-nil, zero value otherwise. -func (o *Organization) GetDefaultRepoPermission() string { - if o == nil || o.DefaultRepoPermission == nil { - return "" +// GetAhead returns the Ahead field if it's non-nil, zero value otherwise. +func (c *CodespacesGitStatus) GetAhead() int { + if c == nil || c.Ahead == nil { + return 0 } - return *o.DefaultRepoPermission + return *c.Ahead } -// GetDefaultRepoSettings returns the DefaultRepoSettings field if it's non-nil, zero value otherwise. -func (o *Organization) GetDefaultRepoSettings() string { - if o == nil || o.DefaultRepoSettings == nil { - return "" +// GetBehind returns the Behind field if it's non-nil, zero value otherwise. +func (c *CodespacesGitStatus) GetBehind() int { + if c == nil || c.Behind == nil { + return 0 } - return *o.DefaultRepoSettings + return *c.Behind } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (o *Organization) GetDescription() string { - if o == nil || o.Description == nil { - return "" +// GetHasUncommittedChanges returns the HasUncommittedChanges field if it's non-nil, zero value otherwise. +func (c *CodespacesGitStatus) GetHasUncommittedChanges() bool { + if c == nil || c.HasUncommittedChanges == nil { + return false } - return *o.Description + return *c.HasUncommittedChanges } -// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. -func (o *Organization) GetDiskUsage() int { - if o == nil || o.DiskUsage == nil { - return 0 +// GetHasUnpushedChanges returns the HasUnpushedChanges field if it's non-nil, zero value otherwise. +func (c *CodespacesGitStatus) GetHasUnpushedChanges() bool { + if c == nil || c.HasUnpushedChanges == nil { + return false } - return *o.DiskUsage + return *c.HasUnpushedChanges } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (o *Organization) GetEmail() string { - if o == nil || o.Email == nil { +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CodespacesGitStatus) GetRef() string { + if c == nil || c.Ref == nil { return "" } - return *o.Email + return *c.Ref } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetEventsURL() string { - if o == nil || o.EventsURL == nil { +// GetCPUs returns the CPUs field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetCPUs() int { + if c == nil || c.CPUs == nil { + return 0 + } + return *c.CPUs +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetDisplayName() string { + if c == nil || c.DisplayName == nil { return "" } - return *o.EventsURL + return *c.DisplayName } -// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. -func (o *Organization) GetFollowers() int { - if o == nil || o.Followers == nil { +// GetMemoryInBytes returns the MemoryInBytes field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetMemoryInBytes() int64 { + if c == nil || c.MemoryInBytes == nil { return 0 } - return *o.Followers + return *c.MemoryInBytes } -// GetFollowing returns the Following field if it's non-nil, zero value otherwise. -func (o *Organization) GetFollowing() int { - if o == nil || o.Following == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetName() string { + if c == nil || c.Name == nil { + return "" } - return *o.Following + return *c.Name } -// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetHooksURL() string { - if o == nil || o.HooksURL == nil { +// GetOperatingSystem returns the OperatingSystem field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetOperatingSystem() string { + if c == nil || c.OperatingSystem == nil { return "" } - return *o.HooksURL + return *c.OperatingSystem } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetHTMLURL() string { - if o == nil || o.HTMLURL == nil { +// GetPrebuildAvailability returns the PrebuildAvailability field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetPrebuildAvailability() string { + if c == nil || c.PrebuildAvailability == nil { return "" } - return *o.HTMLURL + return *c.PrebuildAvailability } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (o *Organization) GetID() int64 { - if o == nil || o.ID == nil { +// GetStorageInBytes returns the StorageInBytes field if it's non-nil, zero value otherwise. +func (c *CodespacesMachine) GetStorageInBytes() int64 { + if c == nil || c.StorageInBytes == nil { return 0 } - return *o.ID + return *c.StorageInBytes } -// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetIssuesURL() string { - if o == nil || o.IssuesURL == nil { - return "" +// GetMachines returns the Machines slice if it's non-nil, nil otherwise. +func (c *CodespacesMachines) GetMachines() []*CodespacesMachine { + if c == nil || c.Machines == nil { + return nil } - return *o.IssuesURL + return c.Machines } -// GetLocation returns the Location field if it's non-nil, zero value otherwise. -func (o *Organization) GetLocation() string { - if o == nil || o.Location == nil { - return "" +// GetTotalCount returns the TotalCount field. +func (c *CodespacesMachines) GetTotalCount() int64 { + if c == nil { + return 0 } - return *o.Location + return c.TotalCount } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (o *Organization) GetLogin() string { - if o == nil || o.Login == nil { - return "" +// GetSelectedUsernames returns the SelectedUsernames slice if it's non-nil, nil otherwise. +func (c *CodespacesOrgAccessControlRequest) GetSelectedUsernames() []string { + if c == nil || c.SelectedUsernames == nil { + return nil } - return *o.Login + return c.SelectedUsernames } -// GetMembersAllowedRepositoryCreationType returns the MembersAllowedRepositoryCreationType field if it's non-nil, zero value otherwise. -func (o *Organization) GetMembersAllowedRepositoryCreationType() string { - if o == nil || o.MembersAllowedRepositoryCreationType == nil { +// GetVisibility returns the Visibility field. +func (c *CodespacesOrgAccessControlRequest) GetVisibility() string { + if c == nil { return "" } - return *o.MembersAllowedRepositoryCreationType + return c.Visibility } -// GetMembersCanCreateRepos returns the MembersCanCreateRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetMembersCanCreateRepos() bool { - if o == nil || o.MembersCanCreateRepos == nil { - return false +// GetAllowedPortPrivacySettings returns the AllowedPortPrivacySettings slice if it's non-nil, nil otherwise. +func (c *CodespacesRuntimeConstraints) GetAllowedPortPrivacySettings() []string { + if c == nil || c.AllowedPortPrivacySettings == nil { + return nil } - return *o.MembersCanCreateRepos + return c.AllowedPortPrivacySettings } -// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetMembersURL() string { - if o == nil || o.MembersURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CollaboratorInvitation) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} } - return *o.MembersURL + return *c.CreatedAt } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (o *Organization) GetName() string { - if o == nil || o.Name == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CollaboratorInvitation) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { return "" } - return *o.Name + return *c.HTMLURL } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (o *Organization) GetNodeID() string { - if o == nil || o.NodeID == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CollaboratorInvitation) GetID() int64 { + if c == nil || c.ID == nil { + return 0 } - return *o.NodeID + return *c.ID } -// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetOwnedPrivateRepos() int { - if o == nil || o.OwnedPrivateRepos == nil { - return 0 +// GetInvitee returns the Invitee field. +func (c *CollaboratorInvitation) GetInvitee() *User { + if c == nil { + return nil } - return *o.OwnedPrivateRepos + return c.Invitee } -// GetPlan returns the Plan field. -func (o *Organization) GetPlan() *Plan { - if o == nil { +// GetInviter returns the Inviter field. +func (c *CollaboratorInvitation) GetInviter() *User { + if c == nil { return nil } - return o.Plan + return c.Inviter } -// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. -func (o *Organization) GetPrivateGists() int { - if o == nil || o.PrivateGists == nil { - return 0 +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (c *CollaboratorInvitation) GetPermissions() string { + if c == nil || c.Permissions == nil { + return "" } - return *o.PrivateGists + return *c.Permissions } -// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. -func (o *Organization) GetPublicGists() int { - if o == nil || o.PublicGists == nil { - return 0 +// GetRepo returns the Repo field. +func (c *CollaboratorInvitation) GetRepo() *Repository { + if c == nil { + return nil } - return *o.PublicGists + return c.Repo } -// GetPublicMembersURL returns the PublicMembersURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetPublicMembersURL() string { - if o == nil || o.PublicMembersURL == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CollaboratorInvitation) GetURL() string { + if c == nil || c.URL == nil { return "" } - return *o.PublicMembersURL + return *c.URL } -// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetPublicRepos() int { - if o == nil || o.PublicRepos == nil { - return 0 +// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetCommitURL() string { + if c == nil || c.CommitURL == nil { + return "" } - return *o.PublicRepos + return *c.CommitURL } -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetReposURL() string { - if o == nil || o.ReposURL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetName() string { + if c == nil || c.Name == nil { return "" } - return *o.ReposURL + return *c.Name } -// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetTotalPrivateRepos() int { - if o == nil || o.TotalPrivateRepos == nil { - return 0 +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetRepositoryURL() string { + if c == nil || c.RepositoryURL == nil { + return "" } - return *o.TotalPrivateRepos + return *c.RepositoryURL } -// GetTwoFactorRequirementEnabled returns the TwoFactorRequirementEnabled field if it's non-nil, zero value otherwise. -func (o *Organization) GetTwoFactorRequirementEnabled() bool { - if o == nil || o.TwoFactorRequirementEnabled == nil { - return false +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetSHA() string { + if c == nil || c.SHA == nil { + return "" } - return *o.TwoFactorRequirementEnabled + return *c.SHA } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (o *Organization) GetType() string { - if o == nil || o.Type == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetState() string { + if c == nil || c.State == nil { return "" } - return *o.Type + return *c.State } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (o *Organization) GetUpdatedAt() time.Time { - if o == nil || o.UpdatedAt == nil { - return time.Time{} +// GetStatuses returns the Statuses slice if it's non-nil, nil otherwise. +func (c *CombinedStatus) GetStatuses() []*RepoStatus { + if c == nil || c.Statuses == nil { + return nil } - return *o.UpdatedAt + return c.Statuses } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (o *Organization) GetURL() string { - if o == nil || o.URL == nil { +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (c *CombinedStatus) GetTotalCount() int { + if c == nil || c.TotalCount == nil { + return 0 + } + return *c.TotalCount +} + +// GetBody returns the Body field. +func (c *Comment) GetBody() string { + if c == nil { return "" } - return *o.URL + return c.Body } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (o *OrganizationEvent) GetAction() string { - if o == nil || o.Action == nil { +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *Comment) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} + } + return *c.CreatedAt +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetAuthorAssociation() string { + if c == nil || c.AuthorAssociation == nil { return "" } - return *o.Action + return *c.AuthorAssociation } -// GetInstallation returns the Installation field. -func (o *OrganizationEvent) GetInstallation() *Installation { - if o == nil { - return nil +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetBody() string { + if c == nil || c.Body == nil { + return "" } - return o.Installation + return *c.Body } -// GetInvitation returns the Invitation field. -func (o *OrganizationEvent) GetInvitation() *Invitation { - if o == nil { - return nil +// GetChildCommentCount returns the ChildCommentCount field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetChildCommentCount() int { + if c == nil || c.ChildCommentCount == nil { + return 0 } - return o.Invitation + return *c.ChildCommentCount } -// GetMembership returns the Membership field. -func (o *OrganizationEvent) GetMembership() *Membership { - if o == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} } - return o.Membership + return *c.CreatedAt } -// GetOrganization returns the Organization field. -func (o *OrganizationEvent) GetOrganization() *Organization { - if o == nil { - return nil +// GetDiscussionID returns the DiscussionID field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetDiscussionID() int64 { + if c == nil || c.DiscussionID == nil { + return 0 } - return o.Organization + return *c.DiscussionID } -// GetSender returns the Sender field. -func (o *OrganizationEvent) GetSender() *User { - if o == nil { - return nil +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" } - return o.Sender + return *c.HTMLURL } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (o *OrgBlockEvent) GetAction() string { - if o == nil || o.Action == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetNodeID() string { + if c == nil || c.NodeID == nil { return "" } - return *o.Action + return *c.NodeID } -// GetBlockedUser returns the BlockedUser field. -func (o *OrgBlockEvent) GetBlockedUser() *User { - if o == nil { - return nil +// GetParentID returns the ParentID field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetParentID() int64 { + if c == nil || c.ParentID == nil { + return 0 } - return o.BlockedUser + return *c.ParentID } -// GetInstallation returns the Installation field. -func (o *OrgBlockEvent) GetInstallation() *Installation { - if o == nil { +// GetReactions returns the Reactions field. +func (c *CommentDiscussion) GetReactions() *Reactions { + if c == nil { return nil } - return o.Installation + return c.Reactions } -// GetOrganization returns the Organization field. -func (o *OrgBlockEvent) GetOrganization() *Organization { - if o == nil { - return nil +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetRepositoryURL() string { + if c == nil || c.RepositoryURL == nil { + return "" } - return o.Organization + return *c.RepositoryURL } -// GetSender returns the Sender field. -func (o *OrgBlockEvent) GetSender() *User { - if o == nil { +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CommentDiscussion) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} + } + return *c.UpdatedAt +} + +// GetUser returns the User field. +func (c *CommentDiscussion) GetUser() *User { + if c == nil { return nil } - return o.Sender + return c.User } -// GetDisabledOrgs returns the DisabledOrgs field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetDisabledOrgs() int { - if o == nil || o.DisabledOrgs == nil { +// GetTotalCommitComments returns the TotalCommitComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalCommitComments() int { + if c == nil || c.TotalCommitComments == nil { return 0 } - return *o.DisabledOrgs + return *c.TotalCommitComments } -// GetTotalOrgs returns the TotalOrgs field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetTotalOrgs() int { - if o == nil || o.TotalOrgs == nil { +// GetTotalGistComments returns the TotalGistComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalGistComments() int { + if c == nil || c.TotalGistComments == nil { return 0 } - return *o.TotalOrgs + return *c.TotalGistComments } -// GetTotalTeamMembers returns the TotalTeamMembers field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetTotalTeamMembers() int { - if o == nil || o.TotalTeamMembers == nil { +// GetTotalIssueComments returns the TotalIssueComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalIssueComments() int { + if c == nil || c.TotalIssueComments == nil { return 0 } - return *o.TotalTeamMembers + return *c.TotalIssueComments } -// GetTotalTeams returns the TotalTeams field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetTotalTeams() int { - if o == nil || o.TotalTeams == nil { +// GetTotalPullRequestComments returns the TotalPullRequestComments field if it's non-nil, zero value otherwise. +func (c *CommentStats) GetTotalPullRequestComments() int { + if c == nil || c.TotalPullRequestComments == nil { return 0 } - return *o.TotalTeams + return *c.TotalPullRequestComments } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *Page) GetAction() string { - if p == nil || p.Action == nil { - return "" +// GetAuthor returns the Author field. +func (c *Commit) GetAuthor() *CommitAuthor { + if c == nil { + return nil } - return *p.Action + return c.Author } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *Page) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" +// GetCommentCount returns the CommentCount field if it's non-nil, zero value otherwise. +func (c *Commit) GetCommentCount() int { + if c == nil || c.CommentCount == nil { + return 0 } - return *p.HTMLURL + return *c.CommentCount } -// GetPageName returns the PageName field if it's non-nil, zero value otherwise. -func (p *Page) GetPageName() string { - if p == nil || p.PageName == nil { - return "" +// GetCommitter returns the Committer field. +func (c *Commit) GetCommitter() *CommitAuthor { + if c == nil { + return nil } - return *p.PageName + return c.Committer } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *Page) GetSHA() string { - if p == nil || p.SHA == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *Commit) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { return "" } - return *p.SHA + return *c.HTMLURL } -// GetSummary returns the Summary field if it's non-nil, zero value otherwise. -func (p *Page) GetSummary() string { - if p == nil || p.Summary == nil { +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (c *Commit) GetMessage() string { + if c == nil || c.Message == nil { return "" } - return *p.Summary + return *c.Message } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (p *Page) GetTitle() string { - if p == nil || p.Title == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *Commit) GetNodeID() string { + if c == nil || c.NodeID == nil { return "" } - return *p.Title + return *c.NodeID } -// GetBuild returns the Build field. -func (p *PageBuildEvent) GetBuild() *PagesBuild { - if p == nil { +// GetParents returns the Parents slice if it's non-nil, nil otherwise. +func (c *Commit) GetParents() []*Commit { + if c == nil || c.Parents == nil { return nil } - return p.Build + return c.Parents } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PageBuildEvent) GetID() int64 { - if p == nil || p.ID == nil { - return 0 +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *Commit) GetSHA() string { + if c == nil || c.SHA == nil { + return "" } - return *p.ID + return *c.SHA } -// GetInstallation returns the Installation field. -func (p *PageBuildEvent) GetInstallation() *Installation { - if p == nil { +// GetTree returns the Tree field. +func (c *Commit) GetTree() *Tree { + if c == nil { return nil } - return p.Installation + return c.Tree } -// GetRepo returns the Repo field. -func (p *PageBuildEvent) GetRepo() *Repository { - if p == nil { - return nil +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Commit) GetURL() string { + if c == nil || c.URL == nil { + return "" } - return p.Repo + return *c.URL +} + +// GetVerification returns the Verification field. +func (c *Commit) GetVerification() *SignatureVerification { + if c == nil { + return nil + } + return c.Verification +} + +// GetDate returns the Date field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetDate() Timestamp { + if c == nil || c.Date == nil { + return Timestamp{} + } + return *c.Date +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CommitAuthor) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CommitCommentEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" + } + return *c.Action +} + +// GetComment returns the Comment field. +func (c *CommitCommentEvent) GetComment() *RepositoryComment { + if c == nil { + return nil + } + return c.Comment +} + +// GetInstallation returns the Installation field. +func (c *CommitCommentEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetOrg returns the Org field. +func (c *CommitCommentEvent) GetOrg() *Organization { + if c == nil { + return nil + } + return c.Org +} + +// GetRepo returns the Repo field. +func (c *CommitCommentEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *CommitCommentEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetAdditions() int { + if c == nil || c.Additions == nil { + return 0 + } + return *c.Additions +} + +// GetBlobURL returns the BlobURL field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetBlobURL() string { + if c == nil || c.BlobURL == nil { + return "" + } + return *c.BlobURL +} + +// GetChanges returns the Changes field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetChanges() int { + if c == nil || c.Changes == nil { + return 0 + } + return *c.Changes +} + +// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetContentsURL() string { + if c == nil || c.ContentsURL == nil { + return "" + } + return *c.ContentsURL +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetDeletions() int { + if c == nil || c.Deletions == nil { + return 0 + } + return *c.Deletions +} + +// GetFilename returns the Filename field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetFilename() string { + if c == nil || c.Filename == nil { + return "" + } + return *c.Filename +} + +// GetPatch returns the Patch field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetPatch() string { + if c == nil || c.Patch == nil { + return "" + } + return *c.Patch +} + +// GetPreviousFilename returns the PreviousFilename field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetPreviousFilename() string { + if c == nil || c.PreviousFilename == nil { + return "" + } + return *c.PreviousFilename +} + +// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetRawURL() string { + if c == nil || c.RawURL == nil { + return "" + } + return *c.RawURL +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CommitFile) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetAuthor returns the Author field. +func (c *CommitResult) GetAuthor() *User { + if c == nil { + return nil + } + return c.Author +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetCommentsURL() string { + if c == nil || c.CommentsURL == nil { + return "" + } + return *c.CommentsURL +} + +// GetCommit returns the Commit field. +func (c *CommitResult) GetCommit() *Commit { + if c == nil { + return nil + } + return c.Commit +} + +// GetCommitter returns the Committer field. +func (c *CommitResult) GetCommitter() *User { + if c == nil { + return nil + } + return c.Committer +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetParents returns the Parents slice if it's non-nil, nil otherwise. +func (c *CommitResult) GetParents() []*Commit { + if c == nil || c.Parents == nil { + return nil + } + return c.Parents +} + +// GetRepository returns the Repository field. +func (c *CommitResult) GetRepository() *Repository { + if c == nil { + return nil + } + return c.Repository +} + +// GetScore returns the Score field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetScore() float64 { + if c == nil || c.Score == nil { + return 0 + } + return *c.Score +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetSHA() string { + if c == nil || c.SHA == nil { + return "" + } + return *c.SHA +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CommitResult) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetAheadBy returns the AheadBy field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetAheadBy() int { + if c == nil || c.AheadBy == nil { + return 0 + } + return *c.AheadBy +} + +// GetBaseCommit returns the BaseCommit field. +func (c *CommitsComparison) GetBaseCommit() *RepositoryCommit { + if c == nil { + return nil + } + return c.BaseCommit +} + +// GetBehindBy returns the BehindBy field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetBehindBy() int { + if c == nil || c.BehindBy == nil { + return 0 + } + return *c.BehindBy +} + +// GetCommits returns the Commits slice if it's non-nil, nil otherwise. +func (c *CommitsComparison) GetCommits() []*RepositoryCommit { + if c == nil || c.Commits == nil { + return nil + } + return c.Commits +} + +// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetDiffURL() string { + if c == nil || c.DiffURL == nil { + return "" + } + return *c.DiffURL +} + +// GetFiles returns the Files slice if it's non-nil, nil otherwise. +func (c *CommitsComparison) GetFiles() []*CommitFile { + if c == nil || c.Files == nil { + return nil + } + return c.Files +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetMergeBaseCommit returns the MergeBaseCommit field. +func (c *CommitsComparison) GetMergeBaseCommit() *RepositoryCommit { + if c == nil { + return nil + } + return c.MergeBaseCommit +} + +// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetPatchURL() string { + if c == nil || c.PatchURL == nil { + return "" + } + return *c.PatchURL +} + +// GetPermalinkURL returns the PermalinkURL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetPermalinkURL() string { + if c == nil || c.PermalinkURL == nil { + return "" + } + return *c.PermalinkURL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetTotalCommits returns the TotalCommits field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetTotalCommits() int { + if c == nil || c.TotalCommits == nil { + return 0 + } + return *c.TotalCommits +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CommitsComparison) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetAuthor returns the Author field. +func (c *CommitsListOptions) GetAuthor() string { + if c == nil { + return "" + } + return c.Author +} + +// GetPath returns the Path field. +func (c *CommitsListOptions) GetPath() string { + if c == nil { + return "" + } + return c.Path +} + +// GetSHA returns the SHA field. +func (c *CommitsListOptions) GetSHA() string { + if c == nil { + return "" + } + return c.SHA +} + +// GetSince returns the Since field. +func (c *CommitsListOptions) GetSince() time.Time { + if c == nil { + return time.Time{} + } + return c.Since +} + +// GetUntil returns the Until field. +func (c *CommitsListOptions) GetUntil() time.Time { + if c == nil { + return time.Time{} + } + return c.Until +} + +// GetCommits returns the Commits slice if it's non-nil, nil otherwise. +func (c *CommitsSearchResult) GetCommits() []*CommitResult { + if c == nil || c.Commits == nil { + return nil + } + return c.Commits +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (c *CommitsSearchResult) GetIncompleteResults() bool { + if c == nil || c.IncompleteResults == nil { + return false + } + return *c.IncompleteResults +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *CommitsSearchResult) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (c *CommitStats) GetAdditions() int { + if c == nil || c.Additions == nil { + return 0 + } + return *c.Additions +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (c *CommitStats) GetDeletions() int { + if c == nil || c.Deletions == nil { + return 0 + } + return *c.Deletions +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *CommitStats) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetCodeOfConduct returns the CodeOfConduct field. +func (c *CommunityHealthFiles) GetCodeOfConduct() *Metric { + if c == nil { + return nil + } + return c.CodeOfConduct +} + +// GetCodeOfConductFile returns the CodeOfConductFile field. +func (c *CommunityHealthFiles) GetCodeOfConductFile() *Metric { + if c == nil { + return nil + } + return c.CodeOfConductFile +} + +// GetContributing returns the Contributing field. +func (c *CommunityHealthFiles) GetContributing() *Metric { + if c == nil { + return nil + } + return c.Contributing +} + +// GetIssueTemplate returns the IssueTemplate field. +func (c *CommunityHealthFiles) GetIssueTemplate() *Metric { + if c == nil { + return nil + } + return c.IssueTemplate +} + +// GetLicense returns the License field. +func (c *CommunityHealthFiles) GetLicense() *Metric { + if c == nil { + return nil + } + return c.License +} + +// GetPullRequestTemplate returns the PullRequestTemplate field. +func (c *CommunityHealthFiles) GetPullRequestTemplate() *Metric { + if c == nil { + return nil + } + return c.PullRequestTemplate +} + +// GetReadme returns the Readme field. +func (c *CommunityHealthFiles) GetReadme() *Metric { + if c == nil { + return nil + } + return c.Readme +} + +// GetContentReportsEnabled returns the ContentReportsEnabled field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetContentReportsEnabled() bool { + if c == nil || c.ContentReportsEnabled == nil { + return false + } + return *c.ContentReportsEnabled +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetDocumentation returns the Documentation field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetDocumentation() string { + if c == nil || c.Documentation == nil { + return "" + } + return *c.Documentation +} + +// GetFiles returns the Files field. +func (c *CommunityHealthMetrics) GetFiles() *CommunityHealthFiles { + if c == nil { + return nil + } + return c.Files +} + +// GetHealthPercentage returns the HealthPercentage field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetHealthPercentage() int { + if c == nil || c.HealthPercentage == nil { + return 0 + } + return *c.HealthPercentage +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CommunityHealthMetrics) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} + } + return *c.UpdatedAt +} + +// GetNodes returns the Nodes slice if it's non-nil, nil otherwise. +func (c *ConfigApplyEvents) GetNodes() []*ConfigApplyEventsNode { + if c == nil || c.Nodes == nil { + return nil + } + return c.Nodes +} + +// GetEvents returns the Events slice if it's non-nil, nil otherwise. +func (c *ConfigApplyEventsNode) GetEvents() []*ConfigApplyEventsNodeEvent { + if c == nil || c.Events == nil { + return nil + } + return c.Events +} + +// GetLastRequestID returns the LastRequestID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNode) GetLastRequestID() string { + if c == nil || c.LastRequestID == nil { + return "" + } + return *c.LastRequestID +} + +// GetNode returns the Node field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNode) GetNode() string { + if c == nil || c.Node == nil { + return "" + } + return *c.Node +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetBody() string { + if c == nil || c.Body == nil { + return "" + } + return *c.Body +} + +// GetConfigRunID returns the ConfigRunID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetConfigRunID() string { + if c == nil || c.ConfigRunID == nil { + return "" + } + return *c.ConfigRunID +} + +// GetEventName returns the EventName field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetEventName() string { + if c == nil || c.EventName == nil { + return "" + } + return *c.EventName +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetHostname() string { + if c == nil || c.Hostname == nil { + return "" + } + return *c.Hostname +} + +// GetSeverityText returns the SeverityText field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSeverityText() string { + if c == nil || c.SeverityText == nil { + return "" + } + return *c.SeverityText +} + +// GetSpanDepth returns the SpanDepth field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSpanDepth() int { + if c == nil || c.SpanDepth == nil { + return 0 + } + return *c.SpanDepth +} + +// GetSpanID returns the SpanID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSpanID() string { + if c == nil || c.SpanID == nil { + return "" + } + return *c.SpanID +} + +// GetSpanParentID returns the SpanParentID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSpanParentID() int64 { + if c == nil || c.SpanParentID == nil { + return 0 + } + return *c.SpanParentID +} + +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetTimestamp() Timestamp { + if c == nil || c.Timestamp == nil { + return Timestamp{} + } + return *c.Timestamp +} + +// GetTopology returns the Topology field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetTopology() string { + if c == nil || c.Topology == nil { + return "" + } + return *c.Topology +} + +// GetTraceID returns the TraceID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetTraceID() string { + if c == nil || c.TraceID == nil { + return "" + } + return *c.TraceID +} + +// GetLastRequestID returns the LastRequestID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsOptions) GetLastRequestID() string { + if c == nil || c.LastRequestID == nil { + return "" + } + return *c.LastRequestID +} + +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyOptions) GetRunID() string { + if c == nil || c.RunID == nil { + return "" + } + return *c.RunID +} + +// GetNodes returns the Nodes slice if it's non-nil, nil otherwise. +func (c *ConfigApplyStatus) GetNodes() []*ConfigApplyStatusNode { + if c == nil || c.Nodes == nil { + return nil + } + return c.Nodes +} + +// GetRunning returns the Running field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatus) GetRunning() bool { + if c == nil || c.Running == nil { + return false + } + return *c.Running +} + +// GetSuccessful returns the Successful field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatus) GetSuccessful() bool { + if c == nil || c.Successful == nil { + return false + } + return *c.Successful +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetHostname() string { + if c == nil || c.Hostname == nil { + return "" + } + return *c.Hostname +} + +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetRunID() string { + if c == nil || c.RunID == nil { + return "" + } + return *c.RunID +} + +// GetRunning returns the Running field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetRunning() bool { + if c == nil || c.Running == nil { + return false + } + return *c.Running +} + +// GetSuccessful returns the Successful field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetSuccessful() bool { + if c == nil || c.Successful == nil { + return false + } + return *c.Successful +} + +// GetAdminPassword returns the AdminPassword field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetAdminPassword() string { + if c == nil || c.AdminPassword == nil { + return "" + } + return *c.AdminPassword +} + +// GetAssets returns the Assets field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetAssets() string { + if c == nil || c.Assets == nil { + return "" + } + return *c.Assets +} + +// GetAuthMode returns the AuthMode field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetAuthMode() string { + if c == nil || c.AuthMode == nil { + return "" + } + return *c.AuthMode +} + +// GetAvatar returns the Avatar field. +func (c *ConfigSettings) GetAvatar() *ConfigSettingsAvatar { + if c == nil { + return nil + } + return c.Avatar +} + +// GetCAS returns the CAS field. +func (c *ConfigSettings) GetCAS() *ConfigSettingsCAS { + if c == nil { + return nil + } + return c.CAS +} + +// GetCollectd returns the Collectd field. +func (c *ConfigSettings) GetCollectd() *ConfigSettingsCollectd { + if c == nil { + return nil + } + return c.Collectd +} + +// GetConfigurationID returns the ConfigurationID field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetConfigurationID() int64 { + if c == nil || c.ConfigurationID == nil { + return 0 + } + return *c.ConfigurationID +} + +// GetConfigurationRunCount returns the ConfigurationRunCount field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetConfigurationRunCount() int { + if c == nil || c.ConfigurationRunCount == nil { + return 0 + } + return *c.ConfigurationRunCount +} + +// GetCustomer returns the Customer field. +func (c *ConfigSettings) GetCustomer() *ConfigSettingsCustomer { + if c == nil { + return nil + } + return c.Customer +} + +// GetExpireSessions returns the ExpireSessions field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetExpireSessions() bool { + if c == nil || c.ExpireSessions == nil { + return false + } + return *c.ExpireSessions +} + +// GetGithubHostname returns the GithubHostname field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetGithubHostname() string { + if c == nil || c.GithubHostname == nil { + return "" + } + return *c.GithubHostname +} + +// GetGithubOAuth returns the GithubOAuth field. +func (c *ConfigSettings) GetGithubOAuth() *ConfigSettingsGithubOAuth { + if c == nil { + return nil + } + return c.GithubOAuth +} + +// GetGithubSSL returns the GithubSSL field. +func (c *ConfigSettings) GetGithubSSL() *ConfigSettingsGithubSSL { + if c == nil { + return nil + } + return c.GithubSSL +} + +// GetHTTPProxy returns the HTTPProxy field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetHTTPProxy() string { + if c == nil || c.HTTPProxy == nil { + return "" + } + return *c.HTTPProxy +} + +// GetIdenticonsHost returns the IdenticonsHost field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetIdenticonsHost() string { + if c == nil || c.IdenticonsHost == nil { + return "" + } + return *c.IdenticonsHost +} + +// GetLDAP returns the LDAP field. +func (c *ConfigSettings) GetLDAP() *ConfigSettingsLDAP { + if c == nil { + return nil + } + return c.LDAP +} + +// GetLicense returns the License field. +func (c *ConfigSettings) GetLicense() *ConfigSettingsLicenseSettings { + if c == nil { + return nil + } + return c.License +} + +// GetLoadBalancer returns the LoadBalancer field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetLoadBalancer() string { + if c == nil || c.LoadBalancer == nil { + return "" + } + return *c.LoadBalancer +} + +// GetMapping returns the Mapping field. +func (c *ConfigSettings) GetMapping() *ConfigSettingsMapping { + if c == nil { + return nil + } + return c.Mapping +} + +// GetNTP returns the NTP field. +func (c *ConfigSettings) GetNTP() *ConfigSettingsNTP { + if c == nil { + return nil + } + return c.NTP +} + +// GetPages returns the Pages field. +func (c *ConfigSettings) GetPages() *ConfigSettingsPagesSettings { + if c == nil { + return nil + } + return c.Pages +} + +// GetPrivateMode returns the PrivateMode field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetPrivateMode() bool { + if c == nil || c.PrivateMode == nil { + return false + } + return *c.PrivateMode +} + +// GetPublicPages returns the PublicPages field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetPublicPages() bool { + if c == nil || c.PublicPages == nil { + return false + } + return *c.PublicPages +} + +// GetSAML returns the SAML field. +func (c *ConfigSettings) GetSAML() *ConfigSettingsSAML { + if c == nil { + return nil + } + return c.SAML +} + +// GetSignupEnabled returns the SignupEnabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetSignupEnabled() bool { + if c == nil || c.SignupEnabled == nil { + return false + } + return *c.SignupEnabled +} + +// GetSMTP returns the SMTP field. +func (c *ConfigSettings) GetSMTP() *ConfigSettingsSMTP { + if c == nil { + return nil + } + return c.SMTP +} + +// GetSNMP returns the SNMP field. +func (c *ConfigSettings) GetSNMP() *ConfigSettingsSNMP { + if c == nil { + return nil + } + return c.SNMP +} + +// GetSubdomainIsolation returns the SubdomainIsolation field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetSubdomainIsolation() bool { + if c == nil || c.SubdomainIsolation == nil { + return false + } + return *c.SubdomainIsolation +} + +// GetSyslog returns the Syslog field. +func (c *ConfigSettings) GetSyslog() *ConfigSettingsSyslog { + if c == nil { + return nil + } + return c.Syslog +} + +// GetTimezone returns the Timezone field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetTimezone() string { + if c == nil || c.Timezone == nil { + return "" + } + return *c.Timezone +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsAvatar) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetURI returns the URI field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsAvatar) GetURI() string { + if c == nil || c.URI == nil { + return "" + } + return *c.URI +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCAS) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetEncryption returns the Encryption field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetEncryption() string { + if c == nil || c.Encryption == nil { + return "" + } + return *c.Encryption +} + +// GetPassword returns the Password field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetPassword() string { + if c == nil || c.Password == nil { + return "" + } + return *c.Password +} + +// GetPort returns the Port field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetPort() int { + if c == nil || c.Port == nil { + return 0 + } + return *c.Port +} + +// GetServer returns the Server field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetServer() string { + if c == nil || c.Server == nil { + return "" + } + return *c.Server +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetUsername() string { + if c == nil || c.Username == nil { + return "" + } + return *c.Username +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetPublicKeyData returns the PublicKeyData field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetPublicKeyData() string { + if c == nil || c.PublicKeyData == nil { + return "" + } + return *c.PublicKeyData +} + +// GetSecret returns the Secret field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetSecret() string { + if c == nil || c.Secret == nil { + return "" + } + return *c.Secret +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetUUID() string { + if c == nil || c.UUID == nil { + return "" + } + return *c.UUID +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetClientID() string { + if c == nil || c.ClientID == nil { + return "" + } + return *c.ClientID +} + +// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetClientSecret() string { + if c == nil || c.ClientSecret == nil { + return "" + } + return *c.ClientSecret +} + +// GetOrganizationName returns the OrganizationName field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetOrganizationName() string { + if c == nil || c.OrganizationName == nil { + return "" + } + return *c.OrganizationName +} + +// GetOrganizationTeam returns the OrganizationTeam field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetOrganizationTeam() string { + if c == nil || c.OrganizationTeam == nil { + return "" + } + return *c.OrganizationTeam +} + +// GetCert returns the Cert field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubSSL) GetCert() string { + if c == nil || c.Cert == nil { + return "" + } + return *c.Cert +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubSSL) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubSSL) GetKey() string { + if c == nil || c.Key == nil { + return "" + } + return *c.Key +} + +// GetAdminGroup returns the AdminGroup field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetAdminGroup() string { + if c == nil || c.AdminGroup == nil { + return "" + } + return *c.AdminGroup +} + +// GetBase returns the Base slice if it's non-nil, nil otherwise. +func (c *ConfigSettingsLDAP) GetBase() []string { + if c == nil || c.Base == nil { + return nil + } + return c.Base +} + +// GetBindDN returns the BindDN field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetBindDN() string { + if c == nil || c.BindDN == nil { + return "" + } + return *c.BindDN +} + +// GetHost returns the Host field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetHost() string { + if c == nil || c.Host == nil { + return "" + } + return *c.Host +} + +// GetMethod returns the Method field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetMethod() string { + if c == nil || c.Method == nil { + return "" + } + return *c.Method +} + +// GetPassword returns the Password field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetPassword() string { + if c == nil || c.Password == nil { + return "" + } + return *c.Password +} + +// GetPort returns the Port field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetPort() int { + if c == nil || c.Port == nil { + return 0 + } + return *c.Port +} + +// GetPosixSupport returns the PosixSupport field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetPosixSupport() bool { + if c == nil || c.PosixSupport == nil { + return false + } + return *c.PosixSupport +} + +// GetProfile returns the Profile field. +func (c *ConfigSettingsLDAP) GetProfile() *ConfigSettingsLDAPProfile { + if c == nil { + return nil + } + return c.Profile +} + +// GetReconciliation returns the Reconciliation field. +func (c *ConfigSettingsLDAP) GetReconciliation() *ConfigSettingsLDAPReconciliation { + if c == nil { + return nil + } + return c.Reconciliation +} + +// GetRecursiveGroupSearch returns the RecursiveGroupSearch field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetRecursiveGroupSearch() bool { + if c == nil || c.RecursiveGroupSearch == nil { + return false + } + return *c.RecursiveGroupSearch +} + +// GetSearchStrategy returns the SearchStrategy field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetSearchStrategy() string { + if c == nil || c.SearchStrategy == nil { + return "" + } + return *c.SearchStrategy +} + +// GetSyncEnabled returns the SyncEnabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetSyncEnabled() bool { + if c == nil || c.SyncEnabled == nil { + return false + } + return *c.SyncEnabled +} + +// GetTeamSyncInterval returns the TeamSyncInterval field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetTeamSyncInterval() int { + if c == nil || c.TeamSyncInterval == nil { + return 0 + } + return *c.TeamSyncInterval +} + +// GetUID returns the UID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUID() string { + if c == nil || c.UID == nil { + return "" + } + return *c.UID +} + +// GetUserGroups returns the UserGroups slice if it's non-nil, nil otherwise. +func (c *ConfigSettingsLDAP) GetUserGroups() []string { + if c == nil || c.UserGroups == nil { + return nil + } + return c.UserGroups +} + +// GetUserSyncEmails returns the UserSyncEmails field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUserSyncEmails() bool { + if c == nil || c.UserSyncEmails == nil { + return false + } + return *c.UserSyncEmails +} + +// GetUserSyncInterval returns the UserSyncInterval field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUserSyncInterval() int { + if c == nil || c.UserSyncInterval == nil { + return 0 + } + return *c.UserSyncInterval +} + +// GetUserSyncKeys returns the UserSyncKeys field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUserSyncKeys() bool { + if c == nil || c.UserSyncKeys == nil { + return false + } + return *c.UserSyncKeys +} + +// GetVirtualAttributeEnabled returns the VirtualAttributeEnabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetVirtualAttributeEnabled() bool { + if c == nil || c.VirtualAttributeEnabled == nil { + return false + } + return *c.VirtualAttributeEnabled +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetKey() string { + if c == nil || c.Key == nil { + return "" + } + return *c.Key +} + +// GetMail returns the Mail field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetMail() string { + if c == nil || c.Mail == nil { + return "" + } + return *c.Mail +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetUID returns the UID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetUID() string { + if c == nil || c.UID == nil { + return "" + } + return *c.UID +} + +// GetOrg returns the Org field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPReconciliation) GetOrg() string { + if c == nil || c.Org == nil { + return "" + } + return *c.Org +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPReconciliation) GetUser() string { + if c == nil || c.User == nil { + return "" + } + return *c.User +} + +// GetClusterSupport returns the ClusterSupport field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetClusterSupport() bool { + if c == nil || c.ClusterSupport == nil { + return false + } + return *c.ClusterSupport +} + +// GetEvaluation returns the Evaluation field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetEvaluation() bool { + if c == nil || c.Evaluation == nil { + return false + } + return *c.Evaluation +} + +// GetExpireAt returns the ExpireAt field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetExpireAt() Timestamp { + if c == nil || c.ExpireAt == nil { + return Timestamp{} + } + return *c.ExpireAt +} + +// GetPerpetual returns the Perpetual field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetPerpetual() bool { + if c == nil || c.Perpetual == nil { + return false + } + return *c.Perpetual +} + +// GetSeats returns the Seats field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetSeats() int { + if c == nil || c.Seats == nil { + return 0 + } + return *c.Seats +} + +// GetSSHAllowed returns the SSHAllowed field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetSSHAllowed() bool { + if c == nil || c.SSHAllowed == nil { + return false + } + return *c.SSHAllowed +} + +// GetSupportKey returns the SupportKey field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetSupportKey() string { + if c == nil || c.SupportKey == nil { + return "" + } + return *c.SupportKey +} + +// GetUnlimitedSeating returns the UnlimitedSeating field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetUnlimitedSeating() bool { + if c == nil || c.UnlimitedSeating == nil { + return false + } + return *c.UnlimitedSeating +} + +// GetBasemap returns the Basemap field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetBasemap() string { + if c == nil || c.Basemap == nil { + return "" + } + return *c.Basemap +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetTileserver returns the Tileserver field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetTileserver() string { + if c == nil || c.Tileserver == nil { + return "" + } + return *c.Tileserver +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetToken() string { + if c == nil || c.Token == nil { + return "" + } + return *c.Token +} + +// GetPrimaryServer returns the PrimaryServer field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsNTP) GetPrimaryServer() string { + if c == nil || c.PrimaryServer == nil { + return "" + } + return *c.PrimaryServer +} + +// GetSecondaryServer returns the SecondaryServer field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsNTP) GetSecondaryServer() string { + if c == nil || c.SecondaryServer == nil { + return "" + } + return *c.SecondaryServer +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsPagesSettings) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetCertificate returns the Certificate field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetCertificate() string { + if c == nil || c.Certificate == nil { + return "" + } + return *c.Certificate +} + +// GetCertificatePath returns the CertificatePath field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetCertificatePath() string { + if c == nil || c.CertificatePath == nil { + return "" + } + return *c.CertificatePath +} + +// GetDisableAdminDemote returns the DisableAdminDemote field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetDisableAdminDemote() bool { + if c == nil || c.DisableAdminDemote == nil { + return false + } + return *c.DisableAdminDemote +} + +// GetIDPInitiatedSSO returns the IDPInitiatedSSO field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetIDPInitiatedSSO() bool { + if c == nil || c.IDPInitiatedSSO == nil { + return false + } + return *c.IDPInitiatedSSO +} + +// GetIssuer returns the Issuer field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetIssuer() string { + if c == nil || c.Issuer == nil { + return "" + } + return *c.Issuer +} + +// GetSSOURL returns the SSOURL field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetSSOURL() string { + if c == nil || c.SSOURL == nil { + return "" + } + return *c.SSOURL +} + +// GetAddress returns the Address field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetAddress() string { + if c == nil || c.Address == nil { + return "" + } + return *c.Address +} + +// GetAuthentication returns the Authentication field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetAuthentication() string { + if c == nil || c.Authentication == nil { + return "" + } + return *c.Authentication +} + +// GetDiscardToNoreplyAddress returns the DiscardToNoreplyAddress field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetDiscardToNoreplyAddress() bool { + if c == nil || c.DiscardToNoreplyAddress == nil { + return false + } + return *c.DiscardToNoreplyAddress +} + +// GetDomain returns the Domain field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetDomain() string { + if c == nil || c.Domain == nil { + return "" + } + return *c.Domain +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetEnableStarttlsAuto returns the EnableStarttlsAuto field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetEnableStarttlsAuto() bool { + if c == nil || c.EnableStarttlsAuto == nil { + return false + } + return *c.EnableStarttlsAuto +} + +// GetNoreplyAddress returns the NoreplyAddress field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetNoreplyAddress() string { + if c == nil || c.NoreplyAddress == nil { + return "" + } + return *c.NoreplyAddress +} + +// GetPassword returns the Password field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetPassword() string { + if c == nil || c.Password == nil { + return "" + } + return *c.Password +} + +// GetPort returns the Port field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetPort() string { + if c == nil || c.Port == nil { + return "" + } + return *c.Port +} + +// GetSupportAddress returns the SupportAddress field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetSupportAddress() string { + if c == nil || c.SupportAddress == nil { + return "" + } + return *c.SupportAddress +} + +// GetSupportAddressType returns the SupportAddressType field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetSupportAddressType() string { + if c == nil || c.SupportAddressType == nil { + return "" + } + return *c.SupportAddressType +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetUsername() string { + if c == nil || c.Username == nil { + return "" + } + return *c.Username +} + +// GetUserName returns the UserName field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetUserName() string { + if c == nil || c.UserName == nil { + return "" + } + return *c.UserName +} + +// GetCommunity returns the Community field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSNMP) GetCommunity() string { + if c == nil || c.Community == nil { + return "" + } + return *c.Community +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSNMP) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSyslog) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetProtocolName returns the ProtocolName field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSyslog) GetProtocolName() string { + if c == nil || c.ProtocolName == nil { + return "" + } + return *c.ProtocolName +} + +// GetServer returns the Server field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSyslog) GetServer() string { + if c == nil || c.Server == nil { + return "" + } + return *c.Server +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ConnectionServiceItem) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (c *ConnectionServiceItem) GetNumber() int { + if c == nil || c.Number == nil { + return 0 + } + return *c.Number +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *ContentReference) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *ContentReference) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetReference returns the Reference field if it's non-nil, zero value otherwise. +func (c *ContentReference) GetReference() string { + if c == nil || c.Reference == nil { + return "" + } + return *c.Reference +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *ContentReferenceEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" + } + return *c.Action +} + +// GetContentReference returns the ContentReference field. +func (c *ContentReferenceEvent) GetContentReference() *ContentReference { + if c == nil { + return nil + } + return c.ContentReference +} + +// GetInstallation returns the Installation field. +func (c *ContentReferenceEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetRepo returns the Repo field. +func (c *ContentReferenceEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *ContentReferenceEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetAvatarURL() string { + if c == nil || c.AvatarURL == nil { + return "" + } + return *c.AvatarURL +} + +// GetContributions returns the Contributions field if it's non-nil, zero value otherwise. +func (c *Contributor) GetContributions() int { + if c == nil || c.Contributions == nil { + return 0 + } + return *c.Contributions +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *Contributor) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetEventsURL() string { + if c == nil || c.EventsURL == nil { + return "" + } + return *c.EventsURL +} + +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetFollowersURL() string { + if c == nil || c.FollowersURL == nil { + return "" + } + return *c.FollowersURL +} + +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetFollowingURL() string { + if c == nil || c.FollowingURL == nil { + return "" + } + return *c.FollowingURL +} + +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetGistsURL() string { + if c == nil || c.GistsURL == nil { + return "" + } + return *c.GistsURL +} + +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetGravatarID() string { + if c == nil || c.GravatarID == nil { + return "" + } + return *c.GravatarID +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *Contributor) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *Contributor) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetOrganizationsURL() string { + if c == nil || c.OrganizationsURL == nil { + return "" + } + return *c.OrganizationsURL +} + +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetReceivedEventsURL() string { + if c == nil || c.ReceivedEventsURL == nil { + return "" + } + return *c.ReceivedEventsURL +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetReposURL() string { + if c == nil || c.ReposURL == nil { + return "" + } + return *c.ReposURL +} + +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (c *Contributor) GetSiteAdmin() bool { + if c == nil || c.SiteAdmin == nil { + return false + } + return *c.SiteAdmin +} + +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetStarredURL() string { + if c == nil || c.StarredURL == nil { + return "" + } + return *c.StarredURL +} + +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetSubscriptionsURL() string { + if c == nil || c.SubscriptionsURL == nil { + return "" + } + return *c.SubscriptionsURL +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *Contributor) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetApprovalPolicy returns the ApprovalPolicy field. +func (c *ContributorApprovalPermissions) GetApprovalPolicy() string { + if c == nil { + return "" + } + return c.ApprovalPolicy +} + +// GetAuthor returns the Author field. +func (c *ContributorStats) GetAuthor() *Contributor { + if c == nil { + return nil + } + return c.Author +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *ContributorStats) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetWeeks returns the Weeks slice if it's non-nil, nil otherwise. +func (c *ContributorStats) GetWeeks() []*WeeklyStats { + if c == nil || c.Weeks == nil { + return nil + } + return c.Weeks +} + +// GetCustomAllowlist returns the CustomAllowlist slice if it's non-nil, nil otherwise. +func (c *CopilotCloudAgentConfiguration) GetCustomAllowlist() []string { + if c == nil || c.CustomAllowlist == nil { + return nil + } + return c.CustomAllowlist +} + +// GetEnabledTools returns the EnabledTools field. +func (c *CopilotCloudAgentConfiguration) GetEnabledTools() *CopilotCloudAgentEnabledTools { + if c == nil { + return nil + } + return c.EnabledTools +} + +// GetIsFirewallEnabled returns the IsFirewallEnabled field. +func (c *CopilotCloudAgentConfiguration) GetIsFirewallEnabled() bool { + if c == nil { + return false + } + return c.IsFirewallEnabled +} + +// GetIsFirewallRecommendedAllowlistEnabled returns the IsFirewallRecommendedAllowlistEnabled field. +func (c *CopilotCloudAgentConfiguration) GetIsFirewallRecommendedAllowlistEnabled() bool { + if c == nil { + return false + } + return c.IsFirewallRecommendedAllowlistEnabled +} + +// GetMCPConfiguration returns the MCPConfiguration field. +func (c *CopilotCloudAgentConfiguration) GetMCPConfiguration() any { + if c == nil { + return nil + } + return c.MCPConfiguration +} + +// GetRequireActionsWorkflowApproval returns the RequireActionsWorkflowApproval field. +func (c *CopilotCloudAgentConfiguration) GetRequireActionsWorkflowApproval() bool { + if c == nil { + return false + } + return c.RequireActionsWorkflowApproval +} + +// GetCodeql returns the Codeql field. +func (c *CopilotCloudAgentEnabledTools) GetCodeql() bool { + if c == nil { + return false + } + return c.Codeql +} + +// GetCopilotCodeReview returns the CopilotCodeReview field. +func (c *CopilotCloudAgentEnabledTools) GetCopilotCodeReview() bool { + if c == nil { + return false + } + return c.CopilotCodeReview +} + +// GetDependencyVulnerabilityChecks returns the DependencyVulnerabilityChecks field. +func (c *CopilotCloudAgentEnabledTools) GetDependencyVulnerabilityChecks() bool { + if c == nil { + return false + } + return c.DependencyVulnerabilityChecks +} + +// GetSecretScanning returns the SecretScanning field. +func (c *CopilotCloudAgentEnabledTools) GetSecretScanning() bool { + if c == nil { + return false + } + return c.SecretScanning +} + +// GetParameters returns the Parameters field. +func (c *CopilotCodeReviewBranchRule) GetParameters() CopilotCodeReviewRuleParameters { + if c == nil { + return CopilotCodeReviewRuleParameters{} + } + return c.Parameters +} + +// GetReviewDraftPullRequests returns the ReviewDraftPullRequests field. +func (c *CopilotCodeReviewRuleParameters) GetReviewDraftPullRequests() bool { + if c == nil { + return false + } + return c.ReviewDraftPullRequests +} + +// GetReviewOnPush returns the ReviewOnPush field. +func (c *CopilotCodeReviewRuleParameters) GetReviewOnPush() bool { + if c == nil { + return false + } + return c.ReviewOnPush +} + +// GetCodeAcceptanceActivityCount returns the CodeAcceptanceActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetCodeAcceptanceActivityCount() int { + if c == nil || c.CodeAcceptanceActivityCount == nil { + return 0 + } + return *c.CodeAcceptanceActivityCount +} + +// GetCodeGenerationActivityCount returns the CodeGenerationActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetCodeGenerationActivityCount() int { + if c == nil || c.CodeGenerationActivityCount == nil { + return 0 + } + return *c.CodeGenerationActivityCount +} + +// GetDailyActiveCLIUsers returns the DailyActiveCLIUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetDailyActiveCLIUsers() int { + if c == nil || c.DailyActiveCLIUsers == nil { + return 0 + } + return *c.DailyActiveCLIUsers +} + +// GetDailyActiveCopilotCloudAgentUsers returns the DailyActiveCopilotCloudAgentUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetDailyActiveCopilotCloudAgentUsers() int { + if c == nil || c.DailyActiveCopilotCloudAgentUsers == nil { + return 0 + } + return *c.DailyActiveCopilotCloudAgentUsers +} + +// GetDailyActiveUsers returns the DailyActiveUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetDailyActiveUsers() int { + if c == nil || c.DailyActiveUsers == nil { + return 0 + } + return *c.DailyActiveUsers +} + +// GetDay returns the Day field. +func (c *CopilotDailyMetrics) GetDay() string { + if c == nil { + return "" + } + return c.Day +} + +// GetEnterpriseID returns the EnterpriseID field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetEnterpriseID() string { + if c == nil || c.EnterpriseID == nil { + return "" + } + return *c.EnterpriseID +} + +// GetLOCAddedSum returns the LOCAddedSum field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetLOCAddedSum() int { + if c == nil || c.LOCAddedSum == nil { + return 0 + } + return *c.LOCAddedSum +} + +// GetLOCDeletedSum returns the LOCDeletedSum field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetLOCDeletedSum() int { + if c == nil || c.LOCDeletedSum == nil { + return 0 + } + return *c.LOCDeletedSum +} + +// GetLOCSuggestedToAddSum returns the LOCSuggestedToAddSum field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetLOCSuggestedToAddSum() int { + if c == nil || c.LOCSuggestedToAddSum == nil { + return 0 + } + return *c.LOCSuggestedToAddSum +} + +// GetLOCSuggestedToDeleteSum returns the LOCSuggestedToDeleteSum field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetLOCSuggestedToDeleteSum() int { + if c == nil || c.LOCSuggestedToDeleteSum == nil { + return 0 + } + return *c.LOCSuggestedToDeleteSum +} + +// GetMonthlyActiveAgentUsers returns the MonthlyActiveAgentUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetMonthlyActiveAgentUsers() int { + if c == nil || c.MonthlyActiveAgentUsers == nil { + return 0 + } + return *c.MonthlyActiveAgentUsers +} + +// GetMonthlyActiveChatUsers returns the MonthlyActiveChatUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetMonthlyActiveChatUsers() int { + if c == nil || c.MonthlyActiveChatUsers == nil { + return 0 + } + return *c.MonthlyActiveChatUsers +} + +// GetMonthlyActiveCopilotCloudAgentUsers returns the MonthlyActiveCopilotCloudAgentUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetMonthlyActiveCopilotCloudAgentUsers() int { + if c == nil || c.MonthlyActiveCopilotCloudAgentUsers == nil { + return 0 + } + return *c.MonthlyActiveCopilotCloudAgentUsers +} + +// GetMonthlyActiveUsers returns the MonthlyActiveUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetMonthlyActiveUsers() int { + if c == nil || c.MonthlyActiveUsers == nil { + return 0 + } + return *c.MonthlyActiveUsers +} + +// GetOrganizationID returns the OrganizationID field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetOrganizationID() string { + if c == nil || c.OrganizationID == nil { + return "" + } + return *c.OrganizationID +} + +// GetPullRequests returns the PullRequests field. +func (c *CopilotDailyMetrics) GetPullRequests() *CopilotMetricsPullRequests { + if c == nil { + return nil + } + return c.PullRequests +} + +// GetTotalsByCLI returns the TotalsByCLI field. +func (c *CopilotDailyMetrics) GetTotalsByCLI() *CopilotMetricsCLI { + if c == nil { + return nil + } + return c.TotalsByCLI +} + +// GetTotalsByFeature returns the TotalsByFeature slice if it's non-nil, nil otherwise. +func (c *CopilotDailyMetrics) GetTotalsByFeature() []*CopilotMetricsFeature { + if c == nil || c.TotalsByFeature == nil { + return nil + } + return c.TotalsByFeature +} + +// GetTotalsByIDE returns the TotalsByIDE slice if it's non-nil, nil otherwise. +func (c *CopilotDailyMetrics) GetTotalsByIDE() []*CopilotMetricsIDE { + if c == nil || c.TotalsByIDE == nil { + return nil + } + return c.TotalsByIDE +} + +// GetTotalsByLanguageFeature returns the TotalsByLanguageFeature slice if it's non-nil, nil otherwise. +func (c *CopilotDailyMetrics) GetTotalsByLanguageFeature() []*CopilotMetricsLanguageFeature { + if c == nil || c.TotalsByLanguageFeature == nil { + return nil + } + return c.TotalsByLanguageFeature +} + +// GetTotalsByLanguageModel returns the TotalsByLanguageModel slice if it's non-nil, nil otherwise. +func (c *CopilotDailyMetrics) GetTotalsByLanguageModel() []*CopilotMetricsLanguageModel { + if c == nil || c.TotalsByLanguageModel == nil { + return nil + } + return c.TotalsByLanguageModel +} + +// GetTotalsByModelFeature returns the TotalsByModelFeature slice if it's non-nil, nil otherwise. +func (c *CopilotDailyMetrics) GetTotalsByModelFeature() []*CopilotMetricsModelFeature { + if c == nil || c.TotalsByModelFeature == nil { + return nil + } + return c.TotalsByModelFeature +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetWeeklyActiveCopilotCloudAgentUsers returns the WeeklyActiveCopilotCloudAgentUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetWeeklyActiveCopilotCloudAgentUsers() int { + if c == nil || c.WeeklyActiveCopilotCloudAgentUsers == nil { + return 0 + } + return *c.WeeklyActiveCopilotCloudAgentUsers +} + +// GetWeeklyActiveUsers returns the WeeklyActiveUsers field if it's non-nil, zero value otherwise. +func (c *CopilotDailyMetrics) GetWeeklyActiveUsers() int { + if c == nil || c.WeeklyActiveUsers == nil { + return 0 + } + return *c.WeeklyActiveUsers +} + +// GetDownloadLinks returns the DownloadLinks slice if it's non-nil, nil otherwise. +func (c *CopilotDailyMetricsReport) GetDownloadLinks() []string { + if c == nil || c.DownloadLinks == nil { + return nil + } + return c.DownloadLinks +} + +// GetReportDay returns the ReportDay field. +func (c *CopilotDailyMetricsReport) GetReportDay() string { + if c == nil { + return "" + } + return c.ReportDay +} + +// GetModels returns the Models slice if it's non-nil, nil otherwise. +func (c *CopilotDotcomChat) GetModels() []*CopilotDotcomChatModel { + if c == nil || c.Models == nil { + return nil + } + return c.Models +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotDotcomChat) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotDotcomChatModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetIsCustomModel returns the IsCustomModel field. +func (c *CopilotDotcomChatModel) GetIsCustomModel() bool { + if c == nil { + return false + } + return c.IsCustomModel +} + +// GetName returns the Name field. +func (c *CopilotDotcomChatModel) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalChats returns the TotalChats field. +func (c *CopilotDotcomChatModel) GetTotalChats() int { + if c == nil { + return 0 + } + return c.TotalChats +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotDotcomChatModel) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (c *CopilotDotcomPullRequests) GetRepositories() []*CopilotDotcomPullRequestsRepository { + if c == nil || c.Repositories == nil { + return nil + } + return c.Repositories +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotDotcomPullRequests) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotDotcomPullRequestsModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetIsCustomModel returns the IsCustomModel field. +func (c *CopilotDotcomPullRequestsModel) GetIsCustomModel() bool { + if c == nil { + return false + } + return c.IsCustomModel +} + +// GetName returns the Name field. +func (c *CopilotDotcomPullRequestsModel) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotDotcomPullRequestsModel) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetTotalPRSummariesCreated returns the TotalPRSummariesCreated field. +func (c *CopilotDotcomPullRequestsModel) GetTotalPRSummariesCreated() int { + if c == nil { + return 0 + } + return c.TotalPRSummariesCreated +} + +// GetModels returns the Models slice if it's non-nil, nil otherwise. +func (c *CopilotDotcomPullRequestsRepository) GetModels() []*CopilotDotcomPullRequestsModel { + if c == nil || c.Models == nil { + return nil + } + return c.Models +} + +// GetName returns the Name field. +func (c *CopilotDotcomPullRequestsRepository) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotDotcomPullRequestsRepository) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetEditors returns the Editors slice if it's non-nil, nil otherwise. +func (c *CopilotIDEChat) GetEditors() []*CopilotIDEChatEditor { + if c == nil || c.Editors == nil { + return nil + } + return c.Editors +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDEChat) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetModels returns the Models slice if it's non-nil, nil otherwise. +func (c *CopilotIDEChatEditor) GetModels() []*CopilotIDEChatModel { + if c == nil || c.Models == nil { + return nil + } + return c.Models +} + +// GetName returns the Name field. +func (c *CopilotIDEChatEditor) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDEChatEditor) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotIDEChatModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetIsCustomModel returns the IsCustomModel field. +func (c *CopilotIDEChatModel) GetIsCustomModel() bool { + if c == nil { + return false + } + return c.IsCustomModel +} + +// GetName returns the Name field. +func (c *CopilotIDEChatModel) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalChatCopyEvents returns the TotalChatCopyEvents field. +func (c *CopilotIDEChatModel) GetTotalChatCopyEvents() int { + if c == nil { + return 0 + } + return c.TotalChatCopyEvents +} + +// GetTotalChatInsertionEvents returns the TotalChatInsertionEvents field. +func (c *CopilotIDEChatModel) GetTotalChatInsertionEvents() int { + if c == nil { + return 0 + } + return c.TotalChatInsertionEvents +} + +// GetTotalChats returns the TotalChats field. +func (c *CopilotIDEChatModel) GetTotalChats() int { + if c == nil { + return 0 + } + return c.TotalChats +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDEChatModel) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetEditors returns the Editors slice if it's non-nil, nil otherwise. +func (c *CopilotIDECodeCompletions) GetEditors() []*CopilotIDECodeCompletionsEditor { + if c == nil || c.Editors == nil { + return nil + } + return c.Editors +} + +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (c *CopilotIDECodeCompletions) GetLanguages() []*CopilotIDECodeCompletionsLanguage { + if c == nil || c.Languages == nil { + return nil + } + return c.Languages +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDECodeCompletions) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetModels returns the Models slice if it's non-nil, nil otherwise. +func (c *CopilotIDECodeCompletionsEditor) GetModels() []*CopilotIDECodeCompletionsModel { + if c == nil || c.Models == nil { + return nil + } + return c.Models +} + +// GetName returns the Name field. +func (c *CopilotIDECodeCompletionsEditor) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDECodeCompletionsEditor) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetName returns the Name field. +func (c *CopilotIDECodeCompletionsLanguage) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDECodeCompletionsLanguage) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotIDECodeCompletionsModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetIsCustomModel returns the IsCustomModel field. +func (c *CopilotIDECodeCompletionsModel) GetIsCustomModel() bool { + if c == nil { + return false + } + return c.IsCustomModel +} + +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (c *CopilotIDECodeCompletionsModel) GetLanguages() []*CopilotIDECodeCompletionsModelLanguage { + if c == nil || c.Languages == nil { + return nil + } + return c.Languages +} + +// GetName returns the Name field. +func (c *CopilotIDECodeCompletionsModel) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDECodeCompletionsModel) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetName returns the Name field. +func (c *CopilotIDECodeCompletionsModelLanguage) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetTotalCodeAcceptances returns the TotalCodeAcceptances field. +func (c *CopilotIDECodeCompletionsModelLanguage) GetTotalCodeAcceptances() int { + if c == nil { + return 0 + } + return c.TotalCodeAcceptances +} + +// GetTotalCodeLinesAccepted returns the TotalCodeLinesAccepted field. +func (c *CopilotIDECodeCompletionsModelLanguage) GetTotalCodeLinesAccepted() int { + if c == nil { + return 0 + } + return c.TotalCodeLinesAccepted +} + +// GetTotalCodeLinesSuggested returns the TotalCodeLinesSuggested field. +func (c *CopilotIDECodeCompletionsModelLanguage) GetTotalCodeLinesSuggested() int { + if c == nil { + return 0 + } + return c.TotalCodeLinesSuggested +} + +// GetTotalCodeSuggestions returns the TotalCodeSuggestions field. +func (c *CopilotIDECodeCompletionsModelLanguage) GetTotalCodeSuggestions() int { + if c == nil { + return 0 + } + return c.TotalCodeSuggestions +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field. +func (c *CopilotIDECodeCompletionsModelLanguage) GetTotalEngagedUsers() int { + if c == nil { + return 0 + } + return c.TotalEngagedUsers +} + +// GetCopilotDotcomChat returns the CopilotDotcomChat field. +func (c *CopilotMetrics) GetCopilotDotcomChat() *CopilotDotcomChat { + if c == nil { + return nil + } + return c.CopilotDotcomChat +} + +// GetCopilotDotcomPullRequests returns the CopilotDotcomPullRequests field. +func (c *CopilotMetrics) GetCopilotDotcomPullRequests() *CopilotDotcomPullRequests { + if c == nil { + return nil + } + return c.CopilotDotcomPullRequests +} + +// GetCopilotIDEChat returns the CopilotIDEChat field. +func (c *CopilotMetrics) GetCopilotIDEChat() *CopilotIDEChat { + if c == nil { + return nil + } + return c.CopilotIDEChat +} + +// GetCopilotIDECodeCompletions returns the CopilotIDECodeCompletions field. +func (c *CopilotMetrics) GetCopilotIDECodeCompletions() *CopilotIDECodeCompletions { + if c == nil { + return nil + } + return c.CopilotIDECodeCompletions +} + +// GetDate returns the Date field. +func (c *CopilotMetrics) GetDate() string { + if c == nil { + return "" + } + return c.Date +} + +// GetTotalActiveUsers returns the TotalActiveUsers field if it's non-nil, zero value otherwise. +func (c *CopilotMetrics) GetTotalActiveUsers() int { + if c == nil || c.TotalActiveUsers == nil { + return 0 + } + return *c.TotalActiveUsers +} + +// GetTotalEngagedUsers returns the TotalEngagedUsers field if it's non-nil, zero value otherwise. +func (c *CopilotMetrics) GetTotalEngagedUsers() int { + if c == nil || c.TotalEngagedUsers == nil { + return 0 + } + return *c.TotalEngagedUsers +} + +// GetChatPanelAgentMode returns the ChatPanelAgentMode field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsChatPanel) GetChatPanelAgentMode() int { + if c == nil || c.ChatPanelAgentMode == nil { + return 0 + } + return *c.ChatPanelAgentMode +} + +// GetChatPanelAskMode returns the ChatPanelAskMode field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsChatPanel) GetChatPanelAskMode() int { + if c == nil || c.ChatPanelAskMode == nil { + return 0 + } + return *c.ChatPanelAskMode +} + +// GetChatPanelCustomMode returns the ChatPanelCustomMode field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsChatPanel) GetChatPanelCustomMode() int { + if c == nil || c.ChatPanelCustomMode == nil { + return 0 + } + return *c.ChatPanelCustomMode +} + +// GetChatPanelEditMode returns the ChatPanelEditMode field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsChatPanel) GetChatPanelEditMode() int { + if c == nil || c.ChatPanelEditMode == nil { + return 0 + } + return *c.ChatPanelEditMode +} + +// GetChatPanelUnknownMode returns the ChatPanelUnknownMode field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsChatPanel) GetChatPanelUnknownMode() int { + if c == nil || c.ChatPanelUnknownMode == nil { + return 0 + } + return *c.ChatPanelUnknownMode +} + +// GetLastKnownCLIVersion returns the LastKnownCLIVersion field. +func (c *CopilotMetricsCLI) GetLastKnownCLIVersion() *CopilotMetricsCLIVersion { + if c == nil { + return nil + } + return c.LastKnownCLIVersion +} + +// GetPromptCount returns the PromptCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLI) GetPromptCount() int { + if c == nil || c.PromptCount == nil { + return 0 + } + return *c.PromptCount +} + +// GetRequestCount returns the RequestCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLI) GetRequestCount() int { + if c == nil || c.RequestCount == nil { + return 0 + } + return *c.RequestCount +} + +// GetSessionCount returns the SessionCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLI) GetSessionCount() int { + if c == nil || c.SessionCount == nil { + return 0 + } + return *c.SessionCount +} + +// GetTokenUsage returns the TokenUsage field. +func (c *CopilotMetricsCLI) GetTokenUsage() *CopilotMetricsCLITokenUsage { + if c == nil { + return nil + } + return c.TokenUsage +} + +// GetAvgTokensPerRequest returns the AvgTokensPerRequest field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLITokenUsage) GetAvgTokensPerRequest() float64 { + if c == nil || c.AvgTokensPerRequest == nil { + return 0 + } + return *c.AvgTokensPerRequest +} + +// GetOutputTokensSum returns the OutputTokensSum field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLITokenUsage) GetOutputTokensSum() int { + if c == nil || c.OutputTokensSum == nil { + return 0 + } + return *c.OutputTokensSum +} + +// GetPromptTokensSum returns the PromptTokensSum field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLITokenUsage) GetPromptTokensSum() int { + if c == nil || c.PromptTokensSum == nil { + return 0 + } + return *c.PromptTokensSum +} + +// GetCLIVersion returns the CLIVersion field. +func (c *CopilotMetricsCLIVersion) GetCLIVersion() string { + if c == nil { + return "" + } + return c.CLIVersion +} + +// GetSampledAt returns the SampledAt field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCLIVersion) GetSampledAt() Timestamp { + if c == nil || c.SampledAt == nil { + return Timestamp{} + } + return *c.SampledAt +} + +// GetCodeAcceptanceActivityCount returns the CodeAcceptanceActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCodeActivity) GetCodeAcceptanceActivityCount() int { + if c == nil || c.CodeAcceptanceActivityCount == nil { + return 0 + } + return *c.CodeAcceptanceActivityCount +} + +// GetCodeGenerationActivityCount returns the CodeGenerationActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCodeActivity) GetCodeGenerationActivityCount() int { + if c == nil || c.CodeGenerationActivityCount == nil { + return 0 + } + return *c.CodeGenerationActivityCount +} + +// GetLOCAddedSum returns the LOCAddedSum field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCodeActivity) GetLOCAddedSum() int { + if c == nil || c.LOCAddedSum == nil { + return 0 + } + return *c.LOCAddedSum +} + +// GetLOCDeletedSum returns the LOCDeletedSum field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCodeActivity) GetLOCDeletedSum() int { + if c == nil || c.LOCDeletedSum == nil { + return 0 + } + return *c.LOCDeletedSum +} + +// GetLOCSuggestedToAddSum returns the LOCSuggestedToAddSum field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCodeActivity) GetLOCSuggestedToAddSum() int { + if c == nil || c.LOCSuggestedToAddSum == nil { + return 0 + } + return *c.LOCSuggestedToAddSum +} + +// GetLOCSuggestedToDeleteSum returns the LOCSuggestedToDeleteSum field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsCodeActivity) GetLOCSuggestedToDeleteSum() int { + if c == nil || c.LOCSuggestedToDeleteSum == nil { + return 0 + } + return *c.LOCSuggestedToDeleteSum +} + +// GetFeature returns the Feature field. +func (c *CopilotMetricsFeature) GetFeature() string { + if c == nil { + return "" + } + return c.Feature +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsFeature) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetIDE returns the IDE field. +func (c *CopilotMetricsIDE) GetIDE() string { + if c == nil { + return "" + } + return c.IDE +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsIDE) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetFeature returns the Feature field. +func (c *CopilotMetricsLanguageFeature) GetFeature() string { + if c == nil { + return "" + } + return c.Feature +} + +// GetLanguage returns the Language field. +func (c *CopilotMetricsLanguageFeature) GetLanguage() string { + if c == nil { + return "" + } + return c.Language +} + +// GetLanguage returns the Language field. +func (c *CopilotMetricsLanguageModel) GetLanguage() string { + if c == nil { + return "" + } + return c.Language +} + +// GetModel returns the Model field. +func (c *CopilotMetricsLanguageModel) GetModel() string { + if c == nil { + return "" + } + return c.Model +} + +// GetSince returns the Since field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsListOptions) GetSince() time.Time { + if c == nil || c.Since == nil { + return time.Time{} + } + return *c.Since +} + +// GetUntil returns the Until field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsListOptions) GetUntil() time.Time { + if c == nil || c.Until == nil { + return time.Time{} + } + return *c.Until +} + +// GetFeature returns the Feature field. +func (c *CopilotMetricsModelFeature) GetFeature() string { + if c == nil { + return "" + } + return c.Feature +} + +// GetModel returns the Model field. +func (c *CopilotMetricsModelFeature) GetModel() string { + if c == nil { + return "" + } + return c.Model +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsModelFeature) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetMedianMinutesToMerge returns the MedianMinutesToMerge field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetMedianMinutesToMerge() float64 { + if c == nil || c.MedianMinutesToMerge == nil { + return 0 + } + return *c.MedianMinutesToMerge +} + +// GetMedianMinutesToMergeCopilotAuthored returns the MedianMinutesToMergeCopilotAuthored field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetMedianMinutesToMergeCopilotAuthored() float64 { + if c == nil || c.MedianMinutesToMergeCopilotAuthored == nil { + return 0 + } + return *c.MedianMinutesToMergeCopilotAuthored +} + +// GetMedianMinutesToMergeCopilotReviewed returns the MedianMinutesToMergeCopilotReviewed field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetMedianMinutesToMergeCopilotReviewed() float64 { + if c == nil || c.MedianMinutesToMergeCopilotReviewed == nil { + return 0 + } + return *c.MedianMinutesToMergeCopilotReviewed +} + +// GetTotalAppliedSuggestions returns the TotalAppliedSuggestions field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalAppliedSuggestions() int { + if c == nil || c.TotalAppliedSuggestions == nil { + return 0 + } + return *c.TotalAppliedSuggestions +} + +// GetTotalCopilotAppliedSuggestions returns the TotalCopilotAppliedSuggestions field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalCopilotAppliedSuggestions() int { + if c == nil || c.TotalCopilotAppliedSuggestions == nil { + return 0 + } + return *c.TotalCopilotAppliedSuggestions +} + +// GetTotalCopilotSuggestions returns the TotalCopilotSuggestions field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalCopilotSuggestions() int { + if c == nil || c.TotalCopilotSuggestions == nil { + return 0 + } + return *c.TotalCopilotSuggestions +} + +// GetTotalCreated returns the TotalCreated field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalCreated() int { + if c == nil || c.TotalCreated == nil { + return 0 + } + return *c.TotalCreated +} + +// GetTotalCreatedByCopilot returns the TotalCreatedByCopilot field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalCreatedByCopilot() int { + if c == nil || c.TotalCreatedByCopilot == nil { + return 0 + } + return *c.TotalCreatedByCopilot +} + +// GetTotalMerged returns the TotalMerged field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalMerged() int { + if c == nil || c.TotalMerged == nil { + return 0 + } + return *c.TotalMerged +} + +// GetTotalMergedCreatedByCopilot returns the TotalMergedCreatedByCopilot field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalMergedCreatedByCopilot() int { + if c == nil || c.TotalMergedCreatedByCopilot == nil { + return 0 + } + return *c.TotalMergedCreatedByCopilot +} + +// GetTotalMergedReviewedByCopilot returns the TotalMergedReviewedByCopilot field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalMergedReviewedByCopilot() int { + if c == nil || c.TotalMergedReviewedByCopilot == nil { + return 0 + } + return *c.TotalMergedReviewedByCopilot +} + +// GetTotalReviewed returns the TotalReviewed field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalReviewed() int { + if c == nil || c.TotalReviewed == nil { + return 0 + } + return *c.TotalReviewed +} + +// GetTotalReviewedByCopilot returns the TotalReviewedByCopilot field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalReviewedByCopilot() int { + if c == nil || c.TotalReviewedByCopilot == nil { + return 0 + } + return *c.TotalReviewedByCopilot +} + +// GetTotalSuggestions returns the TotalSuggestions field if it's non-nil, zero value otherwise. +func (c *CopilotMetricsPullRequests) GetTotalSuggestions() int { + if c == nil || c.TotalSuggestions == nil { + return 0 + } + return *c.TotalSuggestions +} + +// GetDownloadLinks returns the DownloadLinks slice if it's non-nil, nil otherwise. +func (c *CopilotMetricsReport) GetDownloadLinks() []string { + if c == nil || c.DownloadLinks == nil { + return nil + } + return c.DownloadLinks +} + +// GetReportEndDay returns the ReportEndDay field. +func (c *CopilotMetricsReport) GetReportEndDay() string { + if c == nil { + return "" + } + return c.ReportEndDay +} + +// GetReportStartDay returns the ReportStartDay field. +func (c *CopilotMetricsReport) GetReportStartDay() string { + if c == nil { + return "" + } + return c.ReportStartDay +} + +// GetDay returns the Day field. +func (c *CopilotMetricsReportOptions) GetDay() string { + if c == nil { + return "" + } + return c.Day +} + +// GetCopilotChat returns the CopilotChat field. +func (c *CopilotOrganizationDetails) GetCopilotChat() string { + if c == nil { + return "" + } + return c.CopilotChat +} + +// GetPublicCodeSuggestions returns the PublicCodeSuggestions field. +func (c *CopilotOrganizationDetails) GetPublicCodeSuggestions() string { + if c == nil { + return "" + } + return c.PublicCodeSuggestions +} + +// GetSeatBreakdown returns the SeatBreakdown field. +func (c *CopilotOrganizationDetails) GetSeatBreakdown() *CopilotSeatBreakdown { + if c == nil { + return nil + } + return c.SeatBreakdown +} + +// GetSeatManagementSetting returns the SeatManagementSetting field. +func (c *CopilotOrganizationDetails) GetSeatManagementSetting() string { + if c == nil { + return "" + } + return c.SeatManagementSetting +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CopilotPeriodicMetrics) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} + } + return *c.CreatedAt +} + +// GetDayTotals returns the DayTotals slice if it's non-nil, nil otherwise. +func (c *CopilotPeriodicMetrics) GetDayTotals() []*CopilotDailyMetrics { + if c == nil || c.DayTotals == nil { + return nil + } + return c.DayTotals +} + +// GetEnterpriseID returns the EnterpriseID field if it's non-nil, zero value otherwise. +func (c *CopilotPeriodicMetrics) GetEnterpriseID() string { + if c == nil || c.EnterpriseID == nil { + return "" + } + return *c.EnterpriseID +} + +// GetOrganizationID returns the OrganizationID field if it's non-nil, zero value otherwise. +func (c *CopilotPeriodicMetrics) GetOrganizationID() string { + if c == nil || c.OrganizationID == nil { + return "" + } + return *c.OrganizationID +} + +// GetReportEndDay returns the ReportEndDay field. +func (c *CopilotPeriodicMetrics) GetReportEndDay() string { + if c == nil { + return "" + } + return c.ReportEndDay +} + +// GetReportStartDay returns the ReportStartDay field. +func (c *CopilotPeriodicMetrics) GetReportStartDay() string { + if c == nil { + return "" + } + return c.ReportStartDay +} + +// GetActiveThisCycle returns the ActiveThisCycle field. +func (c *CopilotSeatBreakdown) GetActiveThisCycle() int { + if c == nil { + return 0 + } + return c.ActiveThisCycle +} + +// GetAddedThisCycle returns the AddedThisCycle field. +func (c *CopilotSeatBreakdown) GetAddedThisCycle() int { + if c == nil { + return 0 + } + return c.AddedThisCycle +} + +// GetInactiveThisCycle returns the InactiveThisCycle field. +func (c *CopilotSeatBreakdown) GetInactiveThisCycle() int { + if c == nil { + return 0 + } + return c.InactiveThisCycle +} + +// GetPendingCancellation returns the PendingCancellation field. +func (c *CopilotSeatBreakdown) GetPendingCancellation() int { + if c == nil { + return 0 + } + return c.PendingCancellation +} + +// GetPendingInvitation returns the PendingInvitation field. +func (c *CopilotSeatBreakdown) GetPendingInvitation() int { + if c == nil { + return 0 + } + return c.PendingInvitation +} + +// GetTotal returns the Total field. +func (c *CopilotSeatBreakdown) GetTotal() int { + if c == nil { + return 0 + } + return c.Total +} + +// GetAssignee returns the Assignee field. +func (c *CopilotSeatDetails) GetAssignee() any { + if c == nil { + return nil + } + return c.Assignee +} + +// GetAssigningTeam returns the AssigningTeam field. +func (c *CopilotSeatDetails) GetAssigningTeam() *Team { + if c == nil { + return nil + } + return c.AssigningTeam +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CopilotSeatDetails) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} + } + return *c.CreatedAt +} + +// GetLastActivityAt returns the LastActivityAt field if it's non-nil, zero value otherwise. +func (c *CopilotSeatDetails) GetLastActivityAt() Timestamp { + if c == nil || c.LastActivityAt == nil { + return Timestamp{} + } + return *c.LastActivityAt +} + +// GetLastActivityEditor returns the LastActivityEditor field if it's non-nil, zero value otherwise. +func (c *CopilotSeatDetails) GetLastActivityEditor() string { + if c == nil || c.LastActivityEditor == nil { + return "" + } + return *c.LastActivityEditor +} + +// GetPendingCancellationDate returns the PendingCancellationDate field if it's non-nil, zero value otherwise. +func (c *CopilotSeatDetails) GetPendingCancellationDate() string { + if c == nil || c.PendingCancellationDate == nil { + return "" + } + return *c.PendingCancellationDate +} + +// GetPlanType returns the PlanType field if it's non-nil, zero value otherwise. +func (c *CopilotSeatDetails) GetPlanType() string { + if c == nil || c.PlanType == nil { + return "" + } + return *c.PlanType +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CopilotSeatDetails) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} + } + return *c.UpdatedAt +} + +// GetCodeAcceptanceActivityCount returns the CodeAcceptanceActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetCodeAcceptanceActivityCount() int { + if c == nil || c.CodeAcceptanceActivityCount == nil { + return 0 + } + return *c.CodeAcceptanceActivityCount +} + +// GetCodeGenerationActivityCount returns the CodeGenerationActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetCodeGenerationActivityCount() int { + if c == nil || c.CodeGenerationActivityCount == nil { + return 0 + } + return *c.CodeGenerationActivityCount +} + +// GetDay returns the Day field. +func (c *CopilotUserDailyMetrics) GetDay() string { + if c == nil { + return "" + } + return c.Day +} + +// GetEnterpriseID returns the EnterpriseID field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetEnterpriseID() string { + if c == nil || c.EnterpriseID == nil { + return "" + } + return *c.EnterpriseID +} + +// GetLOCAddedSum returns the LOCAddedSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetLOCAddedSum() int { + if c == nil || c.LOCAddedSum == nil { + return 0 + } + return *c.LOCAddedSum +} + +// GetLOCDeletedSum returns the LOCDeletedSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetLOCDeletedSum() int { + if c == nil || c.LOCDeletedSum == nil { + return 0 + } + return *c.LOCDeletedSum +} + +// GetLOCSuggestedToAddSum returns the LOCSuggestedToAddSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetLOCSuggestedToAddSum() int { + if c == nil || c.LOCSuggestedToAddSum == nil { + return 0 + } + return *c.LOCSuggestedToAddSum +} + +// GetLOCSuggestedToDeleteSum returns the LOCSuggestedToDeleteSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetLOCSuggestedToDeleteSum() int { + if c == nil || c.LOCSuggestedToDeleteSum == nil { + return 0 + } + return *c.LOCSuggestedToDeleteSum +} + +// GetOrganizationID returns the OrganizationID field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetOrganizationID() string { + if c == nil || c.OrganizationID == nil { + return "" + } + return *c.OrganizationID +} + +// GetTotalsByCLI returns the TotalsByCLI field. +func (c *CopilotUserDailyMetrics) GetTotalsByCLI() *CopilotMetricsCLI { + if c == nil { + return nil + } + return c.TotalsByCLI +} + +// GetTotalsByFeature returns the TotalsByFeature slice if it's non-nil, nil otherwise. +func (c *CopilotUserDailyMetrics) GetTotalsByFeature() []*CopilotMetricsFeature { + if c == nil || c.TotalsByFeature == nil { + return nil + } + return c.TotalsByFeature +} + +// GetTotalsByIDE returns the TotalsByIDE slice if it's non-nil, nil otherwise. +func (c *CopilotUserDailyMetrics) GetTotalsByIDE() []*CopilotUserMetricsIDE { + if c == nil || c.TotalsByIDE == nil { + return nil + } + return c.TotalsByIDE +} + +// GetTotalsByLanguageFeature returns the TotalsByLanguageFeature slice if it's non-nil, nil otherwise. +func (c *CopilotUserDailyMetrics) GetTotalsByLanguageFeature() []*CopilotMetricsLanguageFeature { + if c == nil || c.TotalsByLanguageFeature == nil { + return nil + } + return c.TotalsByLanguageFeature +} + +// GetTotalsByLanguageModel returns the TotalsByLanguageModel slice if it's non-nil, nil otherwise. +func (c *CopilotUserDailyMetrics) GetTotalsByLanguageModel() []*CopilotMetricsLanguageModel { + if c == nil || c.TotalsByLanguageModel == nil { + return nil + } + return c.TotalsByLanguageModel +} + +// GetTotalsByModelFeature returns the TotalsByModelFeature slice if it's non-nil, nil otherwise. +func (c *CopilotUserDailyMetrics) GetTotalsByModelFeature() []*CopilotMetricsModelFeature { + if c == nil || c.TotalsByModelFeature == nil { + return nil + } + return c.TotalsByModelFeature +} + +// GetUsedAgent returns the UsedAgent field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUsedAgent() bool { + if c == nil || c.UsedAgent == nil { + return false + } + return *c.UsedAgent +} + +// GetUsedChat returns the UsedChat field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUsedChat() bool { + if c == nil || c.UsedChat == nil { + return false + } + return *c.UsedChat +} + +// GetUsedCLI returns the UsedCLI field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUsedCLI() bool { + if c == nil || c.UsedCLI == nil { + return false + } + return *c.UsedCLI +} + +// GetUsedCopilotCodeReviewActive returns the UsedCopilotCodeReviewActive field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUsedCopilotCodeReviewActive() bool { + if c == nil || c.UsedCopilotCodeReviewActive == nil { + return false + } + return *c.UsedCopilotCodeReviewActive +} + +// GetUsedCopilotCodeReviewPassive returns the UsedCopilotCodeReviewPassive field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUsedCopilotCodeReviewPassive() bool { + if c == nil || c.UsedCopilotCodeReviewPassive == nil { + return false + } + return *c.UsedCopilotCodeReviewPassive +} + +// GetUsedCopilotCodingAgent returns the UsedCopilotCodingAgent field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUsedCopilotCodingAgent() bool { + if c == nil || c.UsedCopilotCodingAgent == nil { + return false + } + return *c.UsedCopilotCodingAgent +} + +// GetUserID returns the UserID field. +func (c *CopilotUserDailyMetrics) GetUserID() int { + if c == nil { + return 0 + } + return c.UserID +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserDailyMetrics) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetUserLogin returns the UserLogin field. +func (c *CopilotUserDailyMetrics) GetUserLogin() string { + if c == nil { + return "" + } + return c.UserLogin +} + +// GetIDE returns the IDE field. +func (c *CopilotUserMetricsIDE) GetIDE() string { + if c == nil { + return "" + } + return c.IDE +} + +// GetLastKnownIDEVersion returns the LastKnownIDEVersion field. +func (c *CopilotUserMetricsIDE) GetLastKnownIDEVersion() *CopilotUserMetricsIDEVersion { + if c == nil { + return nil + } + return c.LastKnownIDEVersion +} + +// GetLastKnownPluginVersion returns the LastKnownPluginVersion field. +func (c *CopilotUserMetricsIDE) GetLastKnownPluginVersion() *CopilotUserMetricsPluginVersion { + if c == nil { + return nil + } + return c.LastKnownPluginVersion +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserMetricsIDE) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetIDEVersion returns the IDEVersion field. +func (c *CopilotUserMetricsIDEVersion) GetIDEVersion() string { + if c == nil { + return "" + } + return c.IDEVersion +} + +// GetSampledAt returns the SampledAt field if it's non-nil, zero value otherwise. +func (c *CopilotUserMetricsIDEVersion) GetSampledAt() Timestamp { + if c == nil || c.SampledAt == nil { + return Timestamp{} + } + return *c.SampledAt +} + +// GetPlugin returns the Plugin field. +func (c *CopilotUserMetricsPluginVersion) GetPlugin() string { + if c == nil { + return "" + } + return c.Plugin +} + +// GetPluginVersion returns the PluginVersion field. +func (c *CopilotUserMetricsPluginVersion) GetPluginVersion() string { + if c == nil { + return "" + } + return c.PluginVersion +} + +// GetSampledAt returns the SampledAt field if it's non-nil, zero value otherwise. +func (c *CopilotUserMetricsPluginVersion) GetSampledAt() Timestamp { + if c == nil || c.SampledAt == nil { + return Timestamp{} + } + return *c.SampledAt +} + +// GetCodeAcceptanceActivityCount returns the CodeAcceptanceActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetCodeAcceptanceActivityCount() int { + if c == nil || c.CodeAcceptanceActivityCount == nil { + return 0 + } + return *c.CodeAcceptanceActivityCount +} + +// GetCodeGenerationActivityCount returns the CodeGenerationActivityCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetCodeGenerationActivityCount() int { + if c == nil || c.CodeGenerationActivityCount == nil { + return 0 + } + return *c.CodeGenerationActivityCount +} + +// GetDay returns the Day field. +func (c *CopilotUserPeriodicMetrics) GetDay() string { + if c == nil { + return "" + } + return c.Day +} + +// GetEnterpriseID returns the EnterpriseID field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetEnterpriseID() string { + if c == nil || c.EnterpriseID == nil { + return "" + } + return *c.EnterpriseID +} + +// GetLOCAddedSum returns the LOCAddedSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetLOCAddedSum() int { + if c == nil || c.LOCAddedSum == nil { + return 0 + } + return *c.LOCAddedSum +} + +// GetLOCDeletedSum returns the LOCDeletedSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetLOCDeletedSum() int { + if c == nil || c.LOCDeletedSum == nil { + return 0 + } + return *c.LOCDeletedSum +} + +// GetLOCSuggestedToAddSum returns the LOCSuggestedToAddSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetLOCSuggestedToAddSum() int { + if c == nil || c.LOCSuggestedToAddSum == nil { + return 0 + } + return *c.LOCSuggestedToAddSum +} + +// GetLOCSuggestedToDeleteSum returns the LOCSuggestedToDeleteSum field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetLOCSuggestedToDeleteSum() int { + if c == nil || c.LOCSuggestedToDeleteSum == nil { + return 0 + } + return *c.LOCSuggestedToDeleteSum +} + +// GetOrganizationID returns the OrganizationID field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetOrganizationID() string { + if c == nil || c.OrganizationID == nil { + return "" + } + return *c.OrganizationID +} + +// GetReportEndDay returns the ReportEndDay field. +func (c *CopilotUserPeriodicMetrics) GetReportEndDay() string { + if c == nil { + return "" + } + return c.ReportEndDay +} + +// GetReportStartDay returns the ReportStartDay field. +func (c *CopilotUserPeriodicMetrics) GetReportStartDay() string { + if c == nil { + return "" + } + return c.ReportStartDay +} + +// GetTotalsByCLI returns the TotalsByCLI field. +func (c *CopilotUserPeriodicMetrics) GetTotalsByCLI() *CopilotMetricsCLI { + if c == nil { + return nil + } + return c.TotalsByCLI +} + +// GetTotalsByFeature returns the TotalsByFeature slice if it's non-nil, nil otherwise. +func (c *CopilotUserPeriodicMetrics) GetTotalsByFeature() []*CopilotMetricsFeature { + if c == nil || c.TotalsByFeature == nil { + return nil + } + return c.TotalsByFeature +} + +// GetTotalsByIDE returns the TotalsByIDE slice if it's non-nil, nil otherwise. +func (c *CopilotUserPeriodicMetrics) GetTotalsByIDE() []*CopilotUserMetricsIDE { + if c == nil || c.TotalsByIDE == nil { + return nil + } + return c.TotalsByIDE +} + +// GetTotalsByLanguageFeature returns the TotalsByLanguageFeature slice if it's non-nil, nil otherwise. +func (c *CopilotUserPeriodicMetrics) GetTotalsByLanguageFeature() []*CopilotMetricsLanguageFeature { + if c == nil || c.TotalsByLanguageFeature == nil { + return nil + } + return c.TotalsByLanguageFeature +} + +// GetTotalsByLanguageModel returns the TotalsByLanguageModel slice if it's non-nil, nil otherwise. +func (c *CopilotUserPeriodicMetrics) GetTotalsByLanguageModel() []*CopilotMetricsLanguageModel { + if c == nil || c.TotalsByLanguageModel == nil { + return nil + } + return c.TotalsByLanguageModel +} + +// GetTotalsByModelFeature returns the TotalsByModelFeature slice if it's non-nil, nil otherwise. +func (c *CopilotUserPeriodicMetrics) GetTotalsByModelFeature() []*CopilotMetricsModelFeature { + if c == nil || c.TotalsByModelFeature == nil { + return nil + } + return c.TotalsByModelFeature +} + +// GetUsedAgent returns the UsedAgent field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUsedAgent() bool { + if c == nil || c.UsedAgent == nil { + return false + } + return *c.UsedAgent +} + +// GetUsedChat returns the UsedChat field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUsedChat() bool { + if c == nil || c.UsedChat == nil { + return false + } + return *c.UsedChat +} + +// GetUsedCLI returns the UsedCLI field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUsedCLI() bool { + if c == nil || c.UsedCLI == nil { + return false + } + return *c.UsedCLI +} + +// GetUsedCopilotCodeReviewActive returns the UsedCopilotCodeReviewActive field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUsedCopilotCodeReviewActive() bool { + if c == nil || c.UsedCopilotCodeReviewActive == nil { + return false + } + return *c.UsedCopilotCodeReviewActive +} + +// GetUsedCopilotCodeReviewPassive returns the UsedCopilotCodeReviewPassive field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUsedCopilotCodeReviewPassive() bool { + if c == nil || c.UsedCopilotCodeReviewPassive == nil { + return false + } + return *c.UsedCopilotCodeReviewPassive +} + +// GetUsedCopilotCodingAgent returns the UsedCopilotCodingAgent field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUsedCopilotCodingAgent() bool { + if c == nil || c.UsedCopilotCodingAgent == nil { + return false + } + return *c.UsedCopilotCodingAgent +} + +// GetUserID returns the UserID field. +func (c *CopilotUserPeriodicMetrics) GetUserID() int { + if c == nil { + return 0 + } + return c.UserID +} + +// GetUserInitiatedInteractionCount returns the UserInitiatedInteractionCount field if it's non-nil, zero value otherwise. +func (c *CopilotUserPeriodicMetrics) GetUserInitiatedInteractionCount() int { + if c == nil || c.UserInitiatedInteractionCount == nil { + return 0 + } + return *c.UserInitiatedInteractionCount +} + +// GetUserLogin returns the UserLogin field. +func (c *CopilotUserPeriodicMetrics) GetUserLogin() string { + if c == nil { + return "" + } + return c.UserLogin +} + +// GetAzureSubscription returns the AzureSubscription field if it's non-nil, zero value otherwise. +func (c *CostCenter) GetAzureSubscription() string { + if c == nil || c.AzureSubscription == nil { + return "" + } + return *c.AzureSubscription +} + +// GetID returns the ID field. +func (c *CostCenter) GetID() string { + if c == nil { + return "" + } + return c.ID +} + +// GetName returns the Name field. +func (c *CostCenter) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetResources returns the Resources slice if it's non-nil, nil otherwise. +func (c *CostCenter) GetResources() []*CostCenterResource { + if c == nil || c.Resources == nil { + return nil + } + return c.Resources +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CostCenter) GetState() string { + if c == nil || c.State == nil { + return "" + } + return *c.State +} + +// GetName returns the Name field. +func (c *CostCenterRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetName returns the Name field. +func (c *CostCenterResource) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetType returns the Type field. +func (c *CostCenterResource) GetType() string { + if c == nil { + return "" + } + return c.Type +} + +// GetOrganizations returns the Organizations slice if it's non-nil, nil otherwise. +func (c *CostCenterResourceRequest) GetOrganizations() []string { + if c == nil || c.Organizations == nil { + return nil + } + return c.Organizations +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (c *CostCenterResourceRequest) GetRepositories() []string { + if c == nil || c.Repositories == nil { + return nil + } + return c.Repositories +} + +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (c *CostCenterResourceRequest) GetUsers() []string { + if c == nil || c.Users == nil { + return nil + } + return c.Users +} + +// GetCostCenters returns the CostCenters slice if it's non-nil, nil otherwise. +func (c *CostCenters) GetCostCenters() []*CostCenter { + if c == nil || c.CostCenters == nil { + return nil + } + return c.CostCenters +} + +// GetCluster returns the Cluster field if it's non-nil, zero value otherwise. +func (c *CreateArtifactDeploymentRequest) GetCluster() string { + if c == nil || c.Cluster == nil { + return "" + } + return *c.Cluster +} + +// GetDeploymentName returns the DeploymentName field. +func (c *CreateArtifactDeploymentRequest) GetDeploymentName() string { + if c == nil { + return "" + } + return c.DeploymentName +} + +// GetDigest returns the Digest field. +func (c *CreateArtifactDeploymentRequest) GetDigest() string { + if c == nil { + return "" + } + return c.Digest +} + +// GetGithubRepository returns the GithubRepository field if it's non-nil, zero value otherwise. +func (c *CreateArtifactDeploymentRequest) GetGithubRepository() string { + if c == nil || c.GithubRepository == nil { + return "" + } + return *c.GithubRepository +} + +// GetLogicalEnvironment returns the LogicalEnvironment field. +func (c *CreateArtifactDeploymentRequest) GetLogicalEnvironment() string { + if c == nil { + return "" + } + return c.LogicalEnvironment +} + +// GetName returns the Name field. +func (c *CreateArtifactDeploymentRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetPhysicalEnvironment returns the PhysicalEnvironment field if it's non-nil, zero value otherwise. +func (c *CreateArtifactDeploymentRequest) GetPhysicalEnvironment() string { + if c == nil || c.PhysicalEnvironment == nil { + return "" + } + return *c.PhysicalEnvironment +} + +// GetRuntimeRisks returns the RuntimeRisks slice if it's non-nil, nil otherwise. +func (c *CreateArtifactDeploymentRequest) GetRuntimeRisks() []DeploymentRuntimeRisk { + if c == nil || c.RuntimeRisks == nil { + return nil + } + return c.RuntimeRisks +} + +// GetStatus returns the Status field. +func (c *CreateArtifactDeploymentRequest) GetStatus() string { + if c == nil { + return "" + } + return c.Status +} + +// GetTags returns the Tags map if it's non-nil, an empty map otherwise. +func (c *CreateArtifactDeploymentRequest) GetTags() map[string]string { + if c == nil || c.Tags == nil { + return map[string]string{} + } + return c.Tags +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (c *CreateArtifactDeploymentRequest) GetVersion() string { + if c == nil || c.Version == nil { + return "" + } + return *c.Version +} + +// GetArtifactURL returns the ArtifactURL field if it's non-nil, zero value otherwise. +func (c *CreateArtifactStorageRequest) GetArtifactURL() string { + if c == nil || c.ArtifactURL == nil { + return "" + } + return *c.ArtifactURL +} + +// GetDigest returns the Digest field. +func (c *CreateArtifactStorageRequest) GetDigest() string { + if c == nil { + return "" + } + return c.Digest +} + +// GetGithubRepository returns the GithubRepository field if it's non-nil, zero value otherwise. +func (c *CreateArtifactStorageRequest) GetGithubRepository() string { + if c == nil || c.GithubRepository == nil { + return "" + } + return *c.GithubRepository +} + +// GetName returns the Name field. +func (c *CreateArtifactStorageRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (c *CreateArtifactStorageRequest) GetPath() string { + if c == nil || c.Path == nil { + return "" + } + return *c.Path +} + +// GetRegistryURL returns the RegistryURL field. +func (c *CreateArtifactStorageRequest) GetRegistryURL() string { + if c == nil { + return "" + } + return c.RegistryURL +} + +// GetRepository returns the Repository field if it's non-nil, zero value otherwise. +func (c *CreateArtifactStorageRequest) GetRepository() string { + if c == nil || c.Repository == nil { + return "" + } + return *c.Repository +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CreateArtifactStorageRequest) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (c *CreateArtifactStorageRequest) GetVersion() string { + if c == nil || c.Version == nil { + return "" + } + return *c.Version +} + +// GetIsAlphanumeric returns the IsAlphanumeric field if it's non-nil, zero value otherwise. +func (c *CreateAutolinkRequest) GetIsAlphanumeric() bool { + if c == nil || c.IsAlphanumeric == nil { + return false + } + return *c.IsAlphanumeric +} + +// GetKeyPrefix returns the KeyPrefix field. +func (c *CreateAutolinkRequest) GetKeyPrefix() string { + if c == nil { + return "" + } + return c.KeyPrefix +} + +// GetURLTemplate returns the URLTemplate field. +func (c *CreateAutolinkRequest) GetURLTemplate() string { + if c == nil { + return "" + } + return c.URLTemplate +} + +// GetActions returns the Actions slice if it's non-nil, nil otherwise. +func (c *CreateCheckRunOptions) GetActions() []*CheckRunAction { + if c == nil || c.Actions == nil { + return nil + } + return c.Actions +} + +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (c *CreateCheckRunOptions) GetCompletedAt() Timestamp { + if c == nil || c.CompletedAt == nil { + return Timestamp{} + } + return *c.CompletedAt +} + +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (c *CreateCheckRunOptions) GetConclusion() string { + if c == nil || c.Conclusion == nil { + return "" + } + return *c.Conclusion +} + +// GetDetailsURL returns the DetailsURL field if it's non-nil, zero value otherwise. +func (c *CreateCheckRunOptions) GetDetailsURL() string { + if c == nil || c.DetailsURL == nil { + return "" + } + return *c.DetailsURL +} + +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (c *CreateCheckRunOptions) GetExternalID() string { + if c == nil || c.ExternalID == nil { + return "" + } + return *c.ExternalID +} + +// GetHeadSHA returns the HeadSHA field. +func (c *CreateCheckRunOptions) GetHeadSHA() string { + if c == nil { + return "" + } + return c.HeadSHA +} + +// GetName returns the Name field. +func (c *CreateCheckRunOptions) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetOutput returns the Output field. +func (c *CreateCheckRunOptions) GetOutput() *CheckRunOutput { + if c == nil { + return nil + } + return c.Output +} + +// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. +func (c *CreateCheckRunOptions) GetStartedAt() Timestamp { + if c == nil || c.StartedAt == nil { + return Timestamp{} + } + return *c.StartedAt +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *CreateCheckRunOptions) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. +func (c *CreateCheckSuiteOptions) GetHeadBranch() string { + if c == nil || c.HeadBranch == nil { + return "" + } + return *c.HeadBranch +} + +// GetHeadSHA returns the HeadSHA field. +func (c *CreateCheckSuiteOptions) GetHeadSHA() string { + if c == nil { + return "" + } + return c.HeadSHA +} + +// GetClientIP returns the ClientIP field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetClientIP() string { + if c == nil || c.ClientIP == nil { + return "" + } + return *c.ClientIP +} + +// GetDevcontainerPath returns the DevcontainerPath field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetDevcontainerPath() string { + if c == nil || c.DevcontainerPath == nil { + return "" + } + return *c.DevcontainerPath +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetDisplayName() string { + if c == nil || c.DisplayName == nil { + return "" + } + return *c.DisplayName +} + +// GetGeo returns the Geo field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetGeo() string { + if c == nil || c.Geo == nil { + return "" + } + return *c.Geo +} + +// GetIdleTimeoutMinutes returns the IdleTimeoutMinutes field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetIdleTimeoutMinutes() int { + if c == nil || c.IdleTimeoutMinutes == nil { + return 0 + } + return *c.IdleTimeoutMinutes +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetLocation() string { + if c == nil || c.Location == nil { + return "" + } + return *c.Location +} + +// GetMachine returns the Machine field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetMachine() string { + if c == nil || c.Machine == nil { + return "" + } + return *c.Machine +} + +// GetMultiRepoPermissionsOptOut returns the MultiRepoPermissionsOptOut field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetMultiRepoPermissionsOptOut() bool { + if c == nil || c.MultiRepoPermissionsOptOut == nil { + return false + } + return *c.MultiRepoPermissionsOptOut +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetRef() string { + if c == nil || c.Ref == nil { + return "" + } + return *c.Ref +} + +// GetRetentionPeriodMinutes returns the RetentionPeriodMinutes field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetRetentionPeriodMinutes() int { + if c == nil || c.RetentionPeriodMinutes == nil { + return 0 + } + return *c.RetentionPeriodMinutes +} + +// GetWorkingDirectory returns the WorkingDirectory field if it's non-nil, zero value otherwise. +func (c *CreateCodespaceOptions) GetWorkingDirectory() string { + if c == nil || c.WorkingDirectory == nil { + return "" + } + return *c.WorkingDirectory +} + +// GetSigner returns the Signer field. +func (c *CreateCommitOptions) GetSigner() MessageSigner { + if c == nil { + return nil + } + return c.Signer +} + +// GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. +func (c *CreateCustomOrgRoleRequest) GetBaseRole() string { + if c == nil || c.BaseRole == nil { + return "" + } + return *c.BaseRole +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateCustomOrgRoleRequest) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetName returns the Name field. +func (c *CreateCustomOrgRoleRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetPermissions returns the Permissions slice if it's non-nil, nil otherwise. +func (c *CreateCustomOrgRoleRequest) GetPermissions() []string { + if c == nil || c.Permissions == nil { + return nil + } + return c.Permissions +} + +// GetBaseRole returns the BaseRole field. +func (c *CreateCustomRepoRoleRequest) GetBaseRole() string { + if c == nil { + return "" + } + return c.BaseRole +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateCustomRepoRoleRequest) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetName returns the Name field. +func (c *CreateCustomRepoRoleRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetPermissions returns the Permissions slice if it's non-nil, nil otherwise. +func (c *CreateCustomRepoRoleRequest) GetPermissions() []string { + if c == nil || c.Permissions == nil { + return nil + } + return c.Permissions +} + +// GetName returns the Name field. +func (c *CreateDeploymentBranchPolicyRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *CreateDeploymentBranchPolicyRequest) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetAllowsPublicRepositories returns the AllowsPublicRepositories field if it's non-nil, zero value otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetAllowsPublicRepositories() bool { + if c == nil || c.AllowsPublicRepositories == nil { + return false + } + return *c.AllowsPublicRepositories +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetNetworkConfigurationID() string { + if c == nil || c.NetworkConfigurationID == nil { + return "" + } + return *c.NetworkConfigurationID +} + +// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetRestrictedToWorkflows() bool { + if c == nil || c.RestrictedToWorkflows == nil { + return false + } + return *c.RestrictedToWorkflows +} + +// GetRunners returns the Runners slice if it's non-nil, nil otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetRunners() []int64 { + if c == nil || c.Runners == nil { + return nil + } + return c.Runners +} + +// GetSelectedOrganizationIDs returns the SelectedOrganizationIDs slice if it's non-nil, nil otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetSelectedOrganizationIDs() []int64 { + if c == nil || c.SelectedOrganizationIDs == nil { + return nil + } + return c.SelectedOrganizationIDs +} + +// GetSelectedWorkflows returns the SelectedWorkflows slice if it's non-nil, nil otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetSelectedWorkflows() []string { + if c == nil || c.SelectedWorkflows == nil { + return nil + } + return c.SelectedWorkflows +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (c *CreateEnterpriseRunnerGroupRequest) GetVisibility() string { + if c == nil || c.Visibility == nil { + return "" + } + return *c.Visibility +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetInstallation returns the Installation field. +func (c *CreateEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetMasterBranch() string { + if c == nil || c.MasterBranch == nil { + return "" + } + return *c.MasterBranch +} + +// GetOrg returns the Org field. +func (c *CreateEvent) GetOrg() *Organization { + if c == nil { + return nil + } + return c.Org +} + +// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetPusherType() string { + if c == nil || c.PusherType == nil { + return "" + } + return *c.PusherType +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetRef() string { + if c == nil || c.Ref == nil { + return "" + } + return *c.Ref +} + +// GetRefType returns the RefType field if it's non-nil, zero value otherwise. +func (c *CreateEvent) GetRefType() string { + if c == nil || c.RefType == nil { + return "" + } + return *c.RefType +} + +// GetRepo returns the Repo field. +func (c *CreateEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *CreateEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetBody returns the Body field. +func (c *CreateGistCommentRequest) GetBody() string { + if c == nil { + return "" + } + return c.Body +} + +// GetContent returns the Content field. +func (c *CreateGistFile) GetContent() string { + if c == nil { + return "" + } + return c.Content +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateGistRequest) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (c *CreateGistRequest) GetPublic() bool { + if c == nil || c.Public == nil { + return false + } + return *c.Public +} + +// GetEnableStaticIP returns the EnableStaticIP field if it's non-nil, zero value otherwise. +func (c *CreateHostedRunnerRequest) GetEnableStaticIP() bool { + if c == nil || c.EnableStaticIP == nil { + return false + } + return *c.EnableStaticIP +} + +// GetImage returns the Image field. +func (c *CreateHostedRunnerRequest) GetImage() HostedRunnerImage { + if c == nil { + return HostedRunnerImage{} + } + return c.Image +} + +// GetImageGen returns the ImageGen field if it's non-nil, zero value otherwise. +func (c *CreateHostedRunnerRequest) GetImageGen() bool { + if c == nil || c.ImageGen == nil { + return false + } + return *c.ImageGen +} + +// GetMaximumRunners returns the MaximumRunners field if it's non-nil, zero value otherwise. +func (c *CreateHostedRunnerRequest) GetMaximumRunners() int64 { + if c == nil || c.MaximumRunners == nil { + return 0 + } + return *c.MaximumRunners +} + +// GetName returns the Name field. +func (c *CreateHostedRunnerRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetRunnerGroupID returns the RunnerGroupID field. +func (c *CreateHostedRunnerRequest) GetRunnerGroupID() int64 { + if c == nil { + return 0 + } + return c.RunnerGroupID +} + +// GetSize returns the Size field. +func (c *CreateHostedRunnerRequest) GetSize() string { + if c == nil { + return "" + } + return c.Size +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (c *CreateIssueLabelRequest) GetColor() string { + if c == nil || c.Color == nil { + return "" + } + return *c.Color +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateIssueLabelRequest) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetName returns the Name field. +func (c *CreateIssueLabelRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetAssignee returns the Assignee field if it's non-nil, zero value otherwise. +func (c *CreateIssueRequest) GetAssignee() string { + if c == nil || c.Assignee == nil { + return "" + } + return *c.Assignee +} + +// GetAssignees returns the Assignees slice if it's non-nil, nil otherwise. +func (c *CreateIssueRequest) GetAssignees() []string { + if c == nil || c.Assignees == nil { + return nil + } + return c.Assignees +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CreateIssueRequest) GetBody() string { + if c == nil || c.Body == nil { + return "" + } + return *c.Body +} + +// GetIssueFieldValues returns the IssueFieldValues slice if it's non-nil, nil otherwise. +func (c *CreateIssueRequest) GetIssueFieldValues() []*IssueRequestFieldValue { + if c == nil || c.IssueFieldValues == nil { + return nil + } + return c.IssueFieldValues +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (c *CreateIssueRequest) GetLabels() []string { + if c == nil || c.Labels == nil { + return nil + } + return c.Labels +} + +// GetMilestone returns the Milestone field if it's non-nil, zero value otherwise. +func (c *CreateIssueRequest) GetMilestone() int { + if c == nil || c.Milestone == nil { + return 0 + } + return *c.Milestone +} + +// GetTitle returns the Title field. +func (c *CreateIssueRequest) GetTitle() string { + if c == nil { + return "" + } + return c.Title +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *CreateIssueRequest) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (c *CreateJITConfigRequest) GetLabels() []string { + if c == nil || c.Labels == nil { + return nil + } + return c.Labels +} + +// GetName returns the Name field. +func (c *CreateJITConfigRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetRunnerGroupID returns the RunnerGroupID field. +func (c *CreateJITConfigRequest) GetRunnerGroupID() int64 { + if c == nil { + return 0 + } + return c.RunnerGroupID +} + +// GetWorkFolder returns the WorkFolder field if it's non-nil, zero value otherwise. +func (c *CreateJITConfigRequest) GetWorkFolder() string { + if c == nil || c.WorkFolder == nil { + return "" + } + return *c.WorkFolder +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateMilestoneRequest) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. +func (c *CreateMilestoneRequest) GetDueOn() Timestamp { + if c == nil || c.DueOn == nil { + return Timestamp{} + } + return *c.DueOn +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (c *CreateMilestoneRequest) GetState() string { + if c == nil || c.State == nil { + return "" + } + return *c.State +} + +// GetTitle returns the Title field. +func (c *CreateMilestoneRequest) GetTitle() string { + if c == nil { + return "" + } + return c.Title +} + +// GetAccountID returns the AccountID field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetAccountID() string { + if c == nil || c.AccountID == nil { + return "" + } + return *c.AccountID +} + +// GetAudience returns the Audience field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetAudience() string { + if c == nil || c.Audience == nil { + return "" + } + return *c.Audience +} + +// GetAuthType returns the AuthType field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetAuthType() string { + if c == nil || c.AuthType == nil { + return "" + } + return *c.AuthType +} + +// GetAWSRegion returns the AWSRegion field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetAWSRegion() string { + if c == nil || c.AWSRegion == nil { + return "" + } + return *c.AWSRegion +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetClientID() string { + if c == nil || c.ClientID == nil { + return "" + } + return *c.ClientID +} + +// GetDomain returns the Domain field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetDomain() string { + if c == nil || c.Domain == nil { + return "" + } + return *c.Domain +} + +// GetDomainOwner returns the DomainOwner field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetDomainOwner() string { + if c == nil || c.DomainOwner == nil { + return "" + } + return *c.DomainOwner +} + +// GetEncryptedValue returns the EncryptedValue field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetEncryptedValue() string { + if c == nil || c.EncryptedValue == nil { + return "" + } + return *c.EncryptedValue +} + +// GetIdentityMappingName returns the IdentityMappingName field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetIdentityMappingName() string { + if c == nil || c.IdentityMappingName == nil { + return "" + } + return *c.IdentityMappingName +} + +// GetJFrogOIDCProviderName returns the JFrogOIDCProviderName field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetJFrogOIDCProviderName() string { + if c == nil || c.JFrogOIDCProviderName == nil { + return "" + } + return *c.JFrogOIDCProviderName +} + +// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetKeyID() string { + if c == nil || c.KeyID == nil { + return "" + } + return *c.KeyID +} + +// GetRegistryType returns the RegistryType field. +func (c *CreateOrganizationPrivateRegistry) GetRegistryType() PrivateRegistryType { + if c == nil { + return "" + } + return c.RegistryType +} + +// GetReplacesBase returns the ReplacesBase field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetReplacesBase() bool { + if c == nil || c.ReplacesBase == nil { + return false + } + return *c.ReplacesBase +} + +// GetRoleName returns the RoleName field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetRoleName() string { + if c == nil || c.RoleName == nil { + return "" + } + return *c.RoleName +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (c *CreateOrganizationPrivateRegistry) GetSelectedRepositoryIDs() []int64 { + if c == nil || c.SelectedRepositoryIDs == nil { + return nil + } + return c.SelectedRepositoryIDs +} + +// GetTenantID returns the TenantID field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetTenantID() string { + if c == nil || c.TenantID == nil { + return "" + } + return *c.TenantID +} + +// GetURL returns the URL field. +func (c *CreateOrganizationPrivateRegistry) GetURL() string { + if c == nil { + return "" + } + return c.URL +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (c *CreateOrganizationPrivateRegistry) GetUsername() string { + if c == nil || c.Username == nil { + return "" + } + return *c.Username +} + +// GetVisibility returns the Visibility field. +func (c *CreateOrganizationPrivateRegistry) GetVisibility() PrivateRegistryVisibility { + if c == nil { + return "" + } + return c.Visibility +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *CreateOrgInvitationOptions) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetInviteeID returns the InviteeID field if it's non-nil, zero value otherwise. +func (c *CreateOrgInvitationOptions) GetInviteeID() int64 { + if c == nil || c.InviteeID == nil { + return 0 + } + return *c.InviteeID +} + +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (c *CreateOrgInvitationOptions) GetRole() string { + if c == nil || c.Role == nil { + return "" + } + return *c.Role +} + +// GetTeamID returns the TeamID slice if it's non-nil, nil otherwise. +func (c *CreateOrgInvitationOptions) GetTeamID() []int64 { + if c == nil || c.TeamID == nil { + return nil + } + return c.TeamID +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (c *CreateOrUpdateIssueTypesOptions) GetColor() string { + if c == nil || c.Color == nil { + return "" + } + return *c.Color +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateOrUpdateIssueTypesOptions) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetIsEnabled returns the IsEnabled field. +func (c *CreateOrUpdateIssueTypesOptions) GetIsEnabled() bool { + if c == nil { + return false + } + return c.IsEnabled +} + +// GetIsPrivate returns the IsPrivate field if it's non-nil, zero value otherwise. +func (c *CreateOrUpdateIssueTypesOptions) GetIsPrivate() bool { + if c == nil || c.IsPrivate == nil { + return false + } + return *c.IsPrivate +} + +// GetName returns the Name field. +func (c *CreateOrUpdateIssueTypesOptions) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CreateProjectV2DraftItemRequest) GetBody() string { + if c == nil || c.Body == nil { + return "" + } + return *c.Body +} + +// GetTitle returns the Title field. +func (c *CreateProjectV2DraftItemRequest) GetTitle() string { + if c == nil { + return "" + } + return c.Title +} + +// GetFilter returns the Filter field if it's non-nil, zero value otherwise. +func (c *CreateProjectV2ViewRequest) GetFilter() string { + if c == nil || c.Filter == nil { + return "" + } + return *c.Filter +} + +// GetLayout returns the Layout field. +func (c *CreateProjectV2ViewRequest) GetLayout() string { + if c == nil { + return "" + } + return c.Layout +} + +// GetName returns the Name field. +func (c *CreateProjectV2ViewRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetVisibleFields returns the VisibleFields slice if it's non-nil, nil otherwise. +func (c *CreateProjectV2ViewRequest) GetVisibleFields() []int64 { + if c == nil || c.VisibleFields == nil { + return nil + } + return c.VisibleFields +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (c *CreateProtectedChanges) GetFrom() bool { + if c == nil || c.From == nil { + return false + } + return *c.From +} + +// GetBase returns the Base field. +func (c *CreatePullRequest) GetBase() string { + if c == nil { + return "" + } + return c.Base +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CreatePullRequest) GetBody() string { + if c == nil || c.Body == nil { + return "" + } + return *c.Body +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (c *CreatePullRequest) GetDraft() bool { + if c == nil || c.Draft == nil { + return false + } + return *c.Draft +} + +// GetHead returns the Head field. +func (c *CreatePullRequest) GetHead() string { + if c == nil { + return "" + } + return c.Head +} + +// GetHeadRepo returns the HeadRepo field if it's non-nil, zero value otherwise. +func (c *CreatePullRequest) GetHeadRepo() string { + if c == nil || c.HeadRepo == nil { + return "" + } + return *c.HeadRepo +} + +// GetIssue returns the Issue field if it's non-nil, zero value otherwise. +func (c *CreatePullRequest) GetIssue() int { + if c == nil || c.Issue == nil { + return 0 + } + return *c.Issue +} + +// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. +func (c *CreatePullRequest) GetMaintainerCanModify() bool { + if c == nil || c.MaintainerCanModify == nil { + return false + } + return *c.MaintainerCanModify +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (c *CreatePullRequest) GetTitle() string { + if c == nil || c.Title == nil { + return "" + } + return *c.Title +} + +// GetRef returns the Ref field. +func (c *CreateRef) GetRef() string { + if c == nil { + return "" + } + return c.Ref +} + +// GetSHA returns the SHA field. +func (c *CreateRef) GetSHA() string { + if c == nil { + return "" + } + return c.SHA +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetBody() string { + if c == nil || c.Body == nil { + return "" + } + return *c.Body +} + +// GetDiscussionCategoryName returns the DiscussionCategoryName field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetDiscussionCategoryName() string { + if c == nil || c.DiscussionCategoryName == nil { + return "" + } + return *c.DiscussionCategoryName +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetDraft() bool { + if c == nil || c.Draft == nil { + return false + } + return *c.Draft +} + +// GetGenerateReleaseNotes returns the GenerateReleaseNotes field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetGenerateReleaseNotes() bool { + if c == nil || c.GenerateReleaseNotes == nil { + return false + } + return *c.GenerateReleaseNotes +} + +// GetMakeLatest returns the MakeLatest field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetMakeLatest() string { + if c == nil || c.MakeLatest == nil { + return "" + } + return *c.MakeLatest +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetPrerelease() bool { + if c == nil || c.Prerelease == nil { + return false + } + return *c.Prerelease +} + +// GetTagName returns the TagName field. +func (c *CreateReleaseRequest) GetTagName() string { + if c == nil { + return "" + } + return c.TagName +} + +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (c *CreateReleaseRequest) GetTargetCommitish() string { + if c == nil || c.TargetCommitish == nil { + return "" + } + return *c.TargetCommitish +} + +// GetAllowsPublicRepositories returns the AllowsPublicRepositories field if it's non-nil, zero value otherwise. +func (c *CreateRunnerGroupRequest) GetAllowsPublicRepositories() bool { + if c == nil || c.AllowsPublicRepositories == nil { + return false + } + return *c.AllowsPublicRepositories +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CreateRunnerGroupRequest) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (c *CreateRunnerGroupRequest) GetNetworkConfigurationID() string { + if c == nil || c.NetworkConfigurationID == nil { + return "" + } + return *c.NetworkConfigurationID +} + +// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise. +func (c *CreateRunnerGroupRequest) GetRestrictedToWorkflows() bool { + if c == nil || c.RestrictedToWorkflows == nil { + return false + } + return *c.RestrictedToWorkflows +} + +// GetRunners returns the Runners slice if it's non-nil, nil otherwise. +func (c *CreateRunnerGroupRequest) GetRunners() []int64 { + if c == nil || c.Runners == nil { + return nil + } + return c.Runners +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (c *CreateRunnerGroupRequest) GetSelectedRepositoryIDs() []int64 { + if c == nil || c.SelectedRepositoryIDs == nil { + return nil + } + return c.SelectedRepositoryIDs +} + +// GetSelectedWorkflows returns the SelectedWorkflows slice if it's non-nil, nil otherwise. +func (c *CreateRunnerGroupRequest) GetSelectedWorkflows() []string { + if c == nil || c.SelectedWorkflows == nil { + return nil + } + return c.SelectedWorkflows +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (c *CreateRunnerGroupRequest) GetVisibility() string { + if c == nil || c.Visibility == nil { + return "" + } + return *c.Visibility +} + +// GetMessage returns the Message field. +func (c *CreateTag) GetMessage() string { + if c == nil { + return "" + } + return c.Message +} + +// GetObject returns the Object field. +func (c *CreateTag) GetObject() string { + if c == nil { + return "" + } + return c.Object +} + +// GetTag returns the Tag field. +func (c *CreateTag) GetTag() string { + if c == nil { + return "" + } + return c.Tag +} + +// GetTagger returns the Tagger field. +func (c *CreateTag) GetTagger() *CommitAuthor { + if c == nil { + return nil + } + return c.Tagger +} + +// GetType returns the Type field. +func (c *CreateTag) GetType() string { + if c == nil { + return "" + } + return c.Type +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CreateTeamRequest) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetMaintainers returns the Maintainers slice if it's non-nil, nil otherwise. +func (c *CreateTeamRequest) GetMaintainers() []string { + if c == nil || c.Maintainers == nil { + return nil + } + return c.Maintainers +} + +// GetName returns the Name field. +func (c *CreateTeamRequest) GetName() string { + if c == nil { + return "" + } + return c.Name +} + +// GetNotificationSetting returns the NotificationSetting field if it's non-nil, zero value otherwise. +func (c *CreateTeamRequest) GetNotificationSetting() string { + if c == nil || c.NotificationSetting == nil { + return "" + } + return *c.NotificationSetting +} + +// GetParentTeamID returns the ParentTeamID field if it's non-nil, zero value otherwise. +func (c *CreateTeamRequest) GetParentTeamID() int64 { + if c == nil || c.ParentTeamID == nil { + return 0 + } + return *c.ParentTeamID +} + +// GetParentTeamSlug returns the ParentTeamSlug field if it's non-nil, zero value otherwise. +func (c *CreateTeamRequest) GetParentTeamSlug() string { + if c == nil || c.ParentTeamSlug == nil { + return "" + } + return *c.ParentTeamSlug +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (c *CreateTeamRequest) GetPermission() string { + if c == nil || c.Permission == nil { + return "" + } + return *c.Permission +} + +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (c *CreateTeamRequest) GetPrivacy() string { + if c == nil || c.Privacy == nil { + return "" + } + return *c.Privacy +} + +// GetRepoNames returns the RepoNames slice if it's non-nil, nil otherwise. +func (c *CreateTeamRequest) GetRepoNames() []string { + if c == nil || c.RepoNames == nil { + return nil + } + return c.RepoNames +} + +// GetCanAdminsBypass returns the CanAdminsBypass field if it's non-nil, zero value otherwise. +func (c *CreateUpdateEnvironment) GetCanAdminsBypass() bool { + if c == nil || c.CanAdminsBypass == nil { + return false + } + return *c.CanAdminsBypass +} + +// GetDeploymentBranchPolicy returns the DeploymentBranchPolicy field. +func (c *CreateUpdateEnvironment) GetDeploymentBranchPolicy() *BranchPolicy { + if c == nil { + return nil + } + return c.DeploymentBranchPolicy +} + +// GetPreventSelfReview returns the PreventSelfReview field if it's non-nil, zero value otherwise. +func (c *CreateUpdateEnvironment) GetPreventSelfReview() bool { + if c == nil || c.PreventSelfReview == nil { + return false + } + return *c.PreventSelfReview +} + +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (c *CreateUpdateEnvironment) GetReviewers() []*EnvReviewers { + if c == nil || c.Reviewers == nil { + return nil + } + return c.Reviewers +} + +// GetWaitTimer returns the WaitTimer field if it's non-nil, zero value otherwise. +func (c *CreateUpdateEnvironment) GetWaitTimer() int { + if c == nil || c.WaitTimer == nil { + return 0 + } + return *c.WaitTimer +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *CreateUserRequest) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetLogin returns the Login field. +func (c *CreateUserRequest) GetLogin() string { + if c == nil { + return "" + } + return c.Login +} + +// GetSuspended returns the Suspended field if it's non-nil, zero value otherwise. +func (c *CreateUserRequest) GetSuspended() bool { + if c == nil || c.Suspended == nil { + return false + } + return *c.Suspended +} + +// GetInputs returns the Inputs map if it's non-nil, an empty map otherwise. +func (c *CreateWorkflowDispatchEventRequest) GetInputs() map[string]any { + if c == nil || c.Inputs == nil { + return map[string]any{} + } + return c.Inputs +} + +// GetRef returns the Ref field. +func (c *CreateWorkflowDispatchEventRequest) GetRef() string { + if c == nil { + return "" + } + return c.Ref +} + +// GetReturnRunDetails returns the ReturnRunDetails field if it's non-nil, zero value otherwise. +func (c *CreateWorkflowDispatchEventRequest) GetReturnRunDetails() bool { + if c == nil || c.ReturnRunDetails == nil { + return false + } + return *c.ReturnRunDetails +} + +// GetCreated returns the Created field if it's non-nil, zero value otherwise. +func (c *CreationInfo) GetCreated() Timestamp { + if c == nil || c.Created == nil { + return Timestamp{} + } + return *c.Created +} + +// GetCreators returns the Creators slice if it's non-nil, nil otherwise. +func (c *CreationInfo) GetCreators() []string { + if c == nil || c.Creators == nil { + return nil + } + return c.Creators +} + +// GetAuthorizedCredentialExpiresAt returns the AuthorizedCredentialExpiresAt field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialExpiresAt() Timestamp { + if c == nil || c.AuthorizedCredentialExpiresAt == nil { + return Timestamp{} + } + return *c.AuthorizedCredentialExpiresAt +} + +// GetAuthorizedCredentialID returns the AuthorizedCredentialID field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialID() int64 { + if c == nil || c.AuthorizedCredentialID == nil { + return 0 + } + return *c.AuthorizedCredentialID +} + +// GetAuthorizedCredentialNote returns the AuthorizedCredentialNote field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialNote() string { + if c == nil || c.AuthorizedCredentialNote == nil { + return "" + } + return *c.AuthorizedCredentialNote +} + +// GetAuthorizedCredentialTitle returns the AuthorizedCredentialTitle field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetAuthorizedCredentialTitle() string { + if c == nil || c.AuthorizedCredentialTitle == nil { + return "" + } + return *c.AuthorizedCredentialTitle +} + +// GetCredentialAccessedAt returns the CredentialAccessedAt field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialAccessedAt() Timestamp { + if c == nil || c.CredentialAccessedAt == nil { + return Timestamp{} + } + return *c.CredentialAccessedAt +} + +// GetCredentialAuthorizedAt returns the CredentialAuthorizedAt field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialAuthorizedAt() Timestamp { + if c == nil || c.CredentialAuthorizedAt == nil { + return Timestamp{} + } + return *c.CredentialAuthorizedAt +} + +// GetCredentialID returns the CredentialID field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialID() int64 { + if c == nil || c.CredentialID == nil { + return 0 + } + return *c.CredentialID +} + +// GetCredentialType returns the CredentialType field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetCredentialType() string { + if c == nil || c.CredentialType == nil { + return "" + } + return *c.CredentialType +} + +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetFingerprint() string { + if c == nil || c.Fingerprint == nil { + return "" + } + return *c.Fingerprint +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (c *CredentialAuthorization) GetScopes() []string { + if c == nil || c.Scopes == nil { + return nil + } + return c.Scopes +} + +// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. +func (c *CredentialAuthorization) GetTokenLastEight() string { + if c == nil || c.TokenLastEight == nil { + return "" + } + return *c.TokenLastEight +} + +// GetLogin returns the Login field. +func (c *CredentialAuthorizationsListOptions) GetLogin() string { + if c == nil { + return "" + } + return c.Login +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *Credit) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetUser returns the User field. +func (c *Credit) GetUser() *User { + if c == nil { + return nil + } + return c.User +} + +// GetApp returns the App field. +func (c *CustomDeploymentProtectionRule) GetApp() *CustomDeploymentProtectionRuleApp { + if c == nil { + return nil + } + return c.App +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRule) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRule) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRule) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRuleApp) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetIntegrationURL returns the IntegrationURL field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRuleApp) GetIntegrationURL() string { + if c == nil || c.IntegrationURL == nil { + return "" + } + return *c.IntegrationURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRuleApp) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRuleApp) GetSlug() string { + if c == nil || c.Slug == nil { + return "" + } + return *c.Slug +} + +// GetIntegrationID returns the IntegrationID field if it's non-nil, zero value otherwise. +func (c *CustomDeploymentProtectionRuleRequest) GetIntegrationID() int64 { + if c == nil || c.IntegrationID == nil { + return 0 + } + return *c.IntegrationID +} + +// GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetBaseRole() string { + if c == nil || c.BaseRole == nil { + return "" + } + return *c.BaseRole +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} + } + return *c.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetOrg returns the Org field. +func (c *CustomOrgRole) GetOrg() *Organization { + if c == nil { + return nil + } + return c.Org +} + +// GetPermissions returns the Permissions slice if it's non-nil, nil otherwise. +func (c *CustomOrgRole) GetPermissions() []string { + if c == nil || c.Permissions == nil { + return nil + } + return c.Permissions +} + +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetSource() string { + if c == nil || c.Source == nil { + return "" + } + return *c.Source +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CustomOrgRole) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} + } + return *c.UpdatedAt +} + +// GetPatternScope returns the PatternScope field if it's non-nil, zero value otherwise. +func (c *CustomPatternBackfillScan) GetPatternScope() string { + if c == nil || c.PatternScope == nil { + return "" + } + return *c.PatternScope +} + +// GetPatternSlug returns the PatternSlug field if it's non-nil, zero value otherwise. +func (c *CustomPatternBackfillScan) GetPatternSlug() string { + if c == nil || c.PatternSlug == nil { + return "" + } + return *c.PatternSlug +} + +// GetAllowedValues returns the AllowedValues slice if it's non-nil, nil otherwise. +func (c *CustomProperty) GetAllowedValues() []string { + if c == nil || c.AllowedValues == nil { + return nil + } + return c.AllowedValues +} + +// GetDefaultValue returns the DefaultValue field. +func (c *CustomProperty) GetDefaultValue() any { + if c == nil { + return nil + } + return c.DefaultValue +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CustomProperty) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetPropertyName returns the PropertyName field if it's non-nil, zero value otherwise. +func (c *CustomProperty) GetPropertyName() string { + if c == nil || c.PropertyName == nil { + return "" + } + return *c.PropertyName +} + +// GetRequired returns the Required field if it's non-nil, zero value otherwise. +func (c *CustomProperty) GetRequired() bool { + if c == nil || c.Required == nil { + return false + } + return *c.Required +} + +// GetSourceType returns the SourceType field if it's non-nil, zero value otherwise. +func (c *CustomProperty) GetSourceType() string { + if c == nil || c.SourceType == nil { + return "" + } + return *c.SourceType +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *CustomProperty) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetValuesEditableBy returns the ValuesEditableBy field if it's non-nil, zero value otherwise. +func (c *CustomProperty) GetValuesEditableBy() string { + if c == nil || c.ValuesEditableBy == nil { + return "" + } + return *c.ValuesEditableBy +} + +// GetValueType returns the ValueType field. +func (c *CustomProperty) GetValueType() PropertyValueType { + if c == nil { + return "" + } + return c.ValueType +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CustomPropertyEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" + } + return *c.Action +} + +// GetDefinition returns the Definition field. +func (c *CustomPropertyEvent) GetDefinition() *CustomProperty { + if c == nil { + return nil + } + return c.Definition +} + +// GetEnterprise returns the Enterprise field. +func (c *CustomPropertyEvent) GetEnterprise() *Enterprise { + if c == nil { + return nil + } + return c.Enterprise +} + +// GetInstallation returns the Installation field. +func (c *CustomPropertyEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetOrg returns the Org field. +func (c *CustomPropertyEvent) GetOrg() *Organization { + if c == nil { + return nil + } + return c.Org +} + +// GetSender returns the Sender field. +func (c *CustomPropertyEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetPropertyName returns the PropertyName field. +func (c *CustomPropertyValue) GetPropertyName() string { + if c == nil { + return "" + } + return c.PropertyName +} + +// GetValue returns the Value field. +func (c *CustomPropertyValue) GetValue() any { + if c == nil { + return nil + } + return c.Value +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *CustomPropertyValuesEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" + } + return *c.Action +} + +// GetEnterprise returns the Enterprise field. +func (c *CustomPropertyValuesEvent) GetEnterprise() *Enterprise { + if c == nil { + return nil + } + return c.Enterprise +} + +// GetInstallation returns the Installation field. +func (c *CustomPropertyValuesEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetNewPropertyValues returns the NewPropertyValues slice if it's non-nil, nil otherwise. +func (c *CustomPropertyValuesEvent) GetNewPropertyValues() []*CustomPropertyValue { + if c == nil || c.NewPropertyValues == nil { + return nil + } + return c.NewPropertyValues +} + +// GetOldPropertyValues returns the OldPropertyValues slice if it's non-nil, nil otherwise. +func (c *CustomPropertyValuesEvent) GetOldPropertyValues() []*CustomPropertyValue { + if c == nil || c.OldPropertyValues == nil { + return nil + } + return c.OldPropertyValues +} + +// GetOrg returns the Org field. +func (c *CustomPropertyValuesEvent) GetOrg() *Organization { + if c == nil { + return nil + } + return c.Org +} + +// GetRepo returns the Repo field. +func (c *CustomPropertyValuesEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *CustomPropertyValuesEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. +func (c *CustomRepoRoles) GetBaseRole() string { + if c == nil || c.BaseRole == nil { + return "" + } + return *c.BaseRole +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (c *CustomRepoRoles) GetCreatedAt() Timestamp { + if c == nil || c.CreatedAt == nil { + return Timestamp{} + } + return *c.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (c *CustomRepoRoles) GetDescription() string { + if c == nil || c.Description == nil { + return "" + } + return *c.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *CustomRepoRoles) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *CustomRepoRoles) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetOrg returns the Org field. +func (c *CustomRepoRoles) GetOrg() *Organization { + if c == nil { + return nil + } + return c.Org +} + +// GetPermissions returns the Permissions slice if it's non-nil, nil otherwise. +func (c *CustomRepoRoles) GetPermissions() []string { + if c == nil || c.Permissions == nil { + return nil + } + return c.Permissions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (c *CustomRepoRoles) GetUpdatedAt() Timestamp { + if c == nil || c.UpdatedAt == nil { + return Timestamp{} + } + return *c.UpdatedAt +} + +// GetEncryptedToken returns the EncryptedToken field. +func (d *DatadogConfig) GetEncryptedToken() string { + if d == nil { + return "" + } + return d.EncryptedToken +} + +// GetKeyID returns the KeyID field. +func (d *DatadogConfig) GetKeyID() string { + if d == nil { + return "" + } + return d.KeyID +} + +// GetSite returns the Site field. +func (d *DatadogConfig) GetSite() string { + if d == nil { + return "" + } + return d.Site +} + +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (d *DefaultSetupConfiguration) GetLanguages() []string { + if d == nil || d.Languages == nil { + return nil + } + return d.Languages +} + +// GetQuerySuite returns the QuerySuite field if it's non-nil, zero value otherwise. +func (d *DefaultSetupConfiguration) GetQuerySuite() string { + if d == nil || d.QuerySuite == nil { + return "" + } + return *d.QuerySuite +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *DefaultSetupConfiguration) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DefaultSetupConfiguration) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetCanApprovePullRequestReviews returns the CanApprovePullRequestReviews field if it's non-nil, zero value otherwise. +func (d *DefaultWorkflowPermissionEnterprise) GetCanApprovePullRequestReviews() bool { + if d == nil || d.CanApprovePullRequestReviews == nil { + return false + } + return *d.CanApprovePullRequestReviews +} + +// GetDefaultWorkflowPermissions returns the DefaultWorkflowPermissions field if it's non-nil, zero value otherwise. +func (d *DefaultWorkflowPermissionEnterprise) GetDefaultWorkflowPermissions() string { + if d == nil || d.DefaultWorkflowPermissions == nil { + return "" + } + return *d.DefaultWorkflowPermissions +} + +// GetCanApprovePullRequestReviews returns the CanApprovePullRequestReviews field if it's non-nil, zero value otherwise. +func (d *DefaultWorkflowPermissionOrganization) GetCanApprovePullRequestReviews() bool { + if d == nil || d.CanApprovePullRequestReviews == nil { + return false + } + return *d.CanApprovePullRequestReviews +} + +// GetDefaultWorkflowPermissions returns the DefaultWorkflowPermissions field if it's non-nil, zero value otherwise. +func (d *DefaultWorkflowPermissionOrganization) GetDefaultWorkflowPermissions() string { + if d == nil || d.DefaultWorkflowPermissions == nil { + return "" + } + return *d.DefaultWorkflowPermissions +} + +// GetCanApprovePullRequestReviews returns the CanApprovePullRequestReviews field if it's non-nil, zero value otherwise. +func (d *DefaultWorkflowPermissionRepository) GetCanApprovePullRequestReviews() bool { + if d == nil || d.CanApprovePullRequestReviews == nil { + return false + } + return *d.CanApprovePullRequestReviews +} + +// GetDefaultWorkflowPermissions returns the DefaultWorkflowPermissions field if it's non-nil, zero value otherwise. +func (d *DefaultWorkflowPermissionRepository) GetDefaultWorkflowPermissions() string { + if d == nil || d.DefaultWorkflowPermissions == nil { + return "" + } + return *d.DefaultWorkflowPermissions +} + +// GetConfirmDeleteURL returns the ConfirmDeleteURL field if it's non-nil, zero value otherwise. +func (d *DeleteAnalysis) GetConfirmDeleteURL() string { + if d == nil || d.ConfirmDeleteURL == nil { + return "" + } + return *d.ConfirmDeleteURL +} + +// GetNextAnalysisURL returns the NextAnalysisURL field if it's non-nil, zero value otherwise. +func (d *DeleteAnalysis) GetNextAnalysisURL() string { + if d == nil || d.NextAnalysisURL == nil { + return "" + } + return *d.NextAnalysisURL +} + +// GetCostCenterState returns the CostCenterState field. +func (d *DeleteCostCenterResponse) GetCostCenterState() string { + if d == nil { + return "" + } + return d.CostCenterState +} + +// GetID returns the ID field. +func (d *DeleteCostCenterResponse) GetID() string { + if d == nil { + return "" + } + return d.ID +} + +// GetMessage returns the Message field. +func (d *DeleteCostCenterResponse) GetMessage() string { + if d == nil { + return "" + } + return d.Message +} + +// GetName returns the Name field. +func (d *DeleteCostCenterResponse) GetName() string { + if d == nil { + return "" + } + return d.Name +} + +// GetInstallation returns the Installation field. +func (d *DeleteEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrg returns the Org field. +func (d *DeleteEvent) GetOrg() *Organization { + if d == nil { + return nil + } + return d.Org +} + +// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. +func (d *DeleteEvent) GetPusherType() string { + if d == nil || d.PusherType == nil { + return "" + } + return *d.PusherType +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (d *DeleteEvent) GetRef() string { + if d == nil || d.Ref == nil { + return "" + } + return *d.Ref +} + +// GetRefType returns the RefType field if it's non-nil, zero value otherwise. +func (d *DeleteEvent) GetRefType() string { + if d == nil || d.RefType == nil { + return "" + } + return *d.RefType +} + +// GetRepo returns the Repo field. +func (d *DeleteEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeleteEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetAutoDismissedAt returns the AutoDismissedAt field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetAutoDismissedAt() Timestamp { + if d == nil || d.AutoDismissedAt == nil { + return Timestamp{} + } + return *d.AutoDismissedAt +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetDependency returns the Dependency field. +func (d *DependabotAlert) GetDependency() *Dependency { + if d == nil { + return nil + } + return d.Dependency +} + +// GetDismissedAt returns the DismissedAt field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetDismissedAt() Timestamp { + if d == nil || d.DismissedAt == nil { + return Timestamp{} + } + return *d.DismissedAt +} + +// GetDismissedBy returns the DismissedBy field. +func (d *DependabotAlert) GetDismissedBy() *User { + if d == nil { + return nil + } + return d.DismissedBy +} + +// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetDismissedComment() string { + if d == nil || d.DismissedComment == nil { + return "" + } + return *d.DismissedComment +} + +// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetDismissedReason() string { + if d == nil || d.DismissedReason == nil { + return "" + } + return *d.DismissedReason +} + +// GetFixedAt returns the FixedAt field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetFixedAt() Timestamp { + if d == nil || d.FixedAt == nil { + return Timestamp{} + } + return *d.FixedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetHTMLURL() string { + if d == nil || d.HTMLURL == nil { + return "" + } + return *d.HTMLURL +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetNumber() int { + if d == nil || d.Number == nil { + return 0 + } + return *d.Number +} + +// GetRepository returns the Repository field. +func (d *DependabotAlert) GetRepository() *Repository { + if d == nil { + return nil + } + return d.Repository +} + +// GetSecurityAdvisory returns the SecurityAdvisory field. +func (d *DependabotAlert) GetSecurityAdvisory() *DependabotSecurityAdvisory { + if d == nil { + return nil + } + return d.SecurityAdvisory +} + +// GetSecurityVulnerability returns the SecurityVulnerability field. +func (d *DependabotAlert) GetSecurityVulnerability() *AdvisoryVulnerability { + if d == nil { + return nil + } + return d.SecurityVulnerability +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (d *DependabotAlert) GetURL() string { + if d == nil || d.URL == nil { + return "" + } + return *d.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DependabotAlertEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetAlert returns the Alert field. +func (d *DependabotAlertEvent) GetAlert() *DependabotAlert { + if d == nil { + return nil + } + return d.Alert +} + +// GetEnterprise returns the Enterprise field. +func (d *DependabotAlertEvent) GetEnterprise() *Enterprise { + if d == nil { + return nil + } + return d.Enterprise +} + +// GetInstallation returns the Installation field. +func (d *DependabotAlertEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrganization returns the Organization field. +func (d *DependabotAlertEvent) GetOrganization() *Organization { + if d == nil { + return nil + } + return d.Organization +} + +// GetRepo returns the Repo field. +func (d *DependabotAlertEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DependabotAlertEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetDismissedComment returns the DismissedComment field if it's non-nil, zero value otherwise. +func (d *DependabotAlertState) GetDismissedComment() string { + if d == nil || d.DismissedComment == nil { + return "" + } + return *d.DismissedComment +} + +// GetDismissedReason returns the DismissedReason field if it's non-nil, zero value otherwise. +func (d *DependabotAlertState) GetDismissedReason() string { + if d == nil || d.DismissedReason == nil { + return "" + } + return *d.DismissedReason +} + +// GetState returns the State field. +func (d *DependabotAlertState) GetState() string { + if d == nil { + return "" + } + return d.State +} + +// GetClassification returns the Classification field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetClassification() string { + if d == nil || d.Classification == nil { + return "" + } + return *d.Classification +} + +// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetCVEID() string { + if d == nil || d.CVEID == nil { + return "" + } + return *d.CVEID +} + +// GetCVSS returns the CVSS field. +func (d *DependabotSecurityAdvisory) GetCVSS() *AdvisoryCVSS { + if d == nil { + return nil + } + return d.CVSS +} + +// GetCVSSSeverities returns the CVSSSeverities field. +func (d *DependabotSecurityAdvisory) GetCVSSSeverities() *AdvisoryCVSSSeverities { + if d == nil { + return nil + } + return d.CVSSSeverities +} + +// GetCWEs returns the CWEs slice if it's non-nil, nil otherwise. +func (d *DependabotSecurityAdvisory) GetCWEs() []*AdvisoryCWEs { + if d == nil || d.CWEs == nil { + return nil + } + return d.CWEs +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEPSS returns the EPSS field. +func (d *DependabotSecurityAdvisory) GetEPSS() *AdvisoryEPSS { + if d == nil { + return nil + } + return d.EPSS +} + +// GetGHSAID returns the GHSAID field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetGHSAID() string { + if d == nil || d.GHSAID == nil { + return "" + } + return *d.GHSAID +} + +// GetIdentifiers returns the Identifiers slice if it's non-nil, nil otherwise. +func (d *DependabotSecurityAdvisory) GetIdentifiers() []*AdvisoryIdentifier { + if d == nil || d.Identifiers == nil { + return nil + } + return d.Identifiers +} + +// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetPublishedAt() Timestamp { + if d == nil || d.PublishedAt == nil { + return Timestamp{} + } + return *d.PublishedAt +} + +// GetReferences returns the References slice if it's non-nil, nil otherwise. +func (d *DependabotSecurityAdvisory) GetReferences() []*AdvisoryReference { + if d == nil || d.References == nil { + return nil + } + return d.References +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetSeverity() string { + if d == nil || d.Severity == nil { + return "" + } + return *d.Severity +} + +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetSummary() string { + if d == nil || d.Summary == nil { + return "" + } + return *d.Summary +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetVulnerabilities returns the Vulnerabilities slice if it's non-nil, nil otherwise. +func (d *DependabotSecurityAdvisory) GetVulnerabilities() []*AdvisoryVulnerability { + if d == nil || d.Vulnerabilities == nil { + return nil + } + return d.Vulnerabilities +} + +// GetWithdrawnAt returns the WithdrawnAt field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityAdvisory) GetWithdrawnAt() Timestamp { + if d == nil || d.WithdrawnAt == nil { + return Timestamp{} + } + return *d.WithdrawnAt +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (d *DependabotSecurityUpdates) GetStatus() string { + if d == nil || d.Status == nil { + return "" + } + return *d.Status +} + +// GetManifestPath returns the ManifestPath field if it's non-nil, zero value otherwise. +func (d *Dependency) GetManifestPath() string { + if d == nil || d.ManifestPath == nil { + return "" + } + return *d.ManifestPath +} + +// GetPackage returns the Package field. +func (d *Dependency) GetPackage() *VulnerabilityPackage { + if d == nil { + return nil + } + return d.Package +} + +// GetScope returns the Scope field if it's non-nil, zero value otherwise. +func (d *Dependency) GetScope() string { + if d == nil || d.Scope == nil { + return "" + } + return *d.Scope +} + +// GetLabeledRunners returns the LabeledRunners field if it's non-nil, zero value otherwise. +func (d *DependencyGraphAutosubmitActionOptions) GetLabeledRunners() bool { + if d == nil || d.LabeledRunners == nil { + return false + } + return *d.LabeledRunners +} + +// GetDetector returns the Detector field. +func (d *DependencyGraphSnapshot) GetDetector() *DependencyGraphSnapshotDetector { + if d == nil { + return nil + } + return d.Detector +} + +// GetJob returns the Job field. +func (d *DependencyGraphSnapshot) GetJob() *DependencyGraphSnapshotJob { + if d == nil { + return nil + } + return d.Job +} + +// GetMetadata returns the Metadata map if it's non-nil, an empty map otherwise. +func (d *DependencyGraphSnapshot) GetMetadata() map[string]any { + if d == nil || d.Metadata == nil { + return map[string]any{} + } + return d.Metadata +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshot) GetRef() string { + if d == nil || d.Ref == nil { + return "" + } + return *d.Ref +} + +// GetScanned returns the Scanned field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshot) GetScanned() Timestamp { + if d == nil || d.Scanned == nil { + return Timestamp{} + } + return *d.Scanned +} + +// GetSha returns the Sha field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshot) GetSha() string { + if d == nil || d.Sha == nil { + return "" + } + return *d.Sha +} + +// GetVersion returns the Version field. +func (d *DependencyGraphSnapshot) GetVersion() int { + if d == nil { + return 0 + } + return d.Version +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotCreationData) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetID returns the ID field. +func (d *DependencyGraphSnapshotCreationData) GetID() int64 { + if d == nil { + return 0 + } + return d.ID +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotCreationData) GetMessage() string { + if d == nil || d.Message == nil { + return "" + } + return *d.Message +} + +// GetResult returns the Result field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotCreationData) GetResult() string { + if d == nil || d.Result == nil { + return "" + } + return *d.Result +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotDetector) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotDetector) GetURL() string { + if d == nil || d.URL == nil { + return "" + } + return *d.URL +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotDetector) GetVersion() string { + if d == nil || d.Version == nil { + return "" + } + return *d.Version +} + +// GetCorrelator returns the Correlator field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotJob) GetCorrelator() string { + if d == nil || d.Correlator == nil { + return "" + } + return *d.Correlator +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotJob) GetHTMLURL() string { + if d == nil || d.HTMLURL == nil { + return "" + } + return *d.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotJob) GetID() string { + if d == nil || d.ID == nil { + return "" + } + return *d.ID +} + +// GetFile returns the File field. +func (d *DependencyGraphSnapshotManifest) GetFile() *DependencyGraphSnapshotManifestFile { + if d == nil { + return nil + } + return d.File +} + +// GetMetadata returns the Metadata map if it's non-nil, an empty map otherwise. +func (d *DependencyGraphSnapshotManifest) GetMetadata() map[string]any { + if d == nil || d.Metadata == nil { + return map[string]any{} + } + return d.Metadata +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotManifest) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetSourceLocation returns the SourceLocation field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotManifestFile) GetSourceLocation() string { + if d == nil || d.SourceLocation == nil { + return "" + } + return *d.SourceLocation +} + +// GetDependencies returns the Dependencies slice if it's non-nil, nil otherwise. +func (d *DependencyGraphSnapshotResolvedDependency) GetDependencies() []string { + if d == nil || d.Dependencies == nil { + return nil + } + return d.Dependencies +} + +// GetMetadata returns the Metadata map if it's non-nil, an empty map otherwise. +func (d *DependencyGraphSnapshotResolvedDependency) GetMetadata() map[string]any { + if d == nil || d.Metadata == nil { + return map[string]any{} + } + return d.Metadata +} + +// GetPackageURL returns the PackageURL field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotResolvedDependency) GetPackageURL() string { + if d == nil || d.PackageURL == nil { + return "" + } + return *d.PackageURL +} + +// GetRelationship returns the Relationship field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotResolvedDependency) GetRelationship() string { + if d == nil || d.Relationship == nil { + return "" + } + return *d.Relationship +} + +// GetScope returns the Scope field if it's non-nil, zero value otherwise. +func (d *DependencyGraphSnapshotResolvedDependency) GetScope() string { + if d == nil || d.Scope == nil { + return "" + } + return *d.Scope +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DeployKeyEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetInstallation returns the Installation field. +func (d *DeployKeyEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetKey returns the Key field. +func (d *DeployKeyEvent) GetKey() *Key { + if d == nil { + return nil + } + return d.Key +} + +// GetOrganization returns the Organization field. +func (d *DeployKeyEvent) GetOrganization() *Organization { + if d == nil { + return nil + } + return d.Organization +} + +// GetRepo returns the Repo field. +func (d *DeployKeyEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeployKeyEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *Deployment) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetCreator returns the Creator field. +func (d *Deployment) GetCreator() *User { + if d == nil { + return nil + } + return d.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *Deployment) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *Deployment) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *Deployment) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *Deployment) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetPayload returns the Payload field. +func (d *Deployment) GetPayload() json.RawMessage { + if d == nil { + return json.RawMessage{} + } + return d.Payload +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (d *Deployment) GetRef() string { + if d == nil || d.Ref == nil { + return "" + } + return *d.Ref +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (d *Deployment) GetRepositoryURL() string { + if d == nil || d.RepositoryURL == nil { + return "" + } + return *d.RepositoryURL +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (d *Deployment) GetSHA() string { + if d == nil || d.SHA == nil { + return "" + } + return *d.SHA +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (d *Deployment) GetStatusesURL() string { + if d == nil || d.StatusesURL == nil { + return "" + } + return *d.StatusesURL +} + +// GetTask returns the Task field if it's non-nil, zero value otherwise. +func (d *Deployment) GetTask() string { + if d == nil || d.Task == nil { + return "" + } + return *d.Task +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *Deployment) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (d *Deployment) GetURL() string { + if d == nil || d.URL == nil { + return "" + } + return *d.URL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicy) GetType() string { + if d == nil || d.Type == nil { + return "" + } + return *d.Type +} + +// GetBranchPolicies returns the BranchPolicies slice if it's non-nil, nil otherwise. +func (d *DeploymentBranchPolicyResponse) GetBranchPolicies() []*DeploymentBranchPolicy { + if d == nil || d.BranchPolicies == nil { + return nil + } + return d.BranchPolicies +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (d *DeploymentBranchPolicyResponse) GetTotalCount() int { + if d == nil || d.TotalCount == nil { + return 0 + } + return *d.TotalCount +} + +// GetDeployment returns the Deployment field. +func (d *DeploymentEvent) GetDeployment() *Deployment { + if d == nil { + return nil + } + return d.Deployment +} + +// GetInstallation returns the Installation field. +func (d *DeploymentEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrg returns the Org field. +func (d *DeploymentEvent) GetOrg() *Organization { + if d == nil { + return nil + } + return d.Org +} + +// GetRepo returns the Repo field. +func (d *DeploymentEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeploymentEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetWorkflow returns the Workflow field. +func (d *DeploymentEvent) GetWorkflow() *Workflow { + if d == nil { + return nil + } + return d.Workflow +} + +// GetWorkflowRun returns the WorkflowRun field. +func (d *DeploymentEvent) GetWorkflowRun() *WorkflowRun { + if d == nil { + return nil + } + return d.WorkflowRun +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DeploymentProtectionRuleEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetDeployment returns the Deployment field. +func (d *DeploymentProtectionRuleEvent) GetDeployment() *Deployment { + if d == nil { + return nil + } + return d.Deployment +} + +// GetDeploymentCallbackURL returns the DeploymentCallbackURL field if it's non-nil, zero value otherwise. +func (d *DeploymentProtectionRuleEvent) GetDeploymentCallbackURL() string { + if d == nil || d.DeploymentCallbackURL == nil { + return "" + } + return *d.DeploymentCallbackURL +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *DeploymentProtectionRuleEvent) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (d *DeploymentProtectionRuleEvent) GetEvent() string { + if d == nil || d.Event == nil { + return "" + } + return *d.Event +} + +// GetInstallation returns the Installation field. +func (d *DeploymentProtectionRuleEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrganization returns the Organization field. +func (d *DeploymentProtectionRuleEvent) GetOrganization() *Organization { + if d == nil { + return nil + } + return d.Organization +} + +// GetPullRequests returns the PullRequests slice if it's non-nil, nil otherwise. +func (d *DeploymentProtectionRuleEvent) GetPullRequests() []*PullRequest { + if d == nil || d.PullRequests == nil { + return nil + } + return d.PullRequests +} + +// GetRepo returns the Repo field. +func (d *DeploymentProtectionRuleEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeploymentProtectionRuleEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetAutoMerge returns the AutoMerge field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetAutoMerge() bool { + if d == nil || d.AutoMerge == nil { + return false + } + return *d.AutoMerge +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetPayload returns the Payload field. +func (d *DeploymentRequest) GetPayload() any { + if d == nil { + return nil + } + return d.Payload +} + +// GetProductionEnvironment returns the ProductionEnvironment field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetProductionEnvironment() bool { + if d == nil || d.ProductionEnvironment == nil { + return false + } + return *d.ProductionEnvironment +} + +// GetRef returns the Ref field. +func (d *DeploymentRequest) GetRef() string { + if d == nil { + return "" + } + return d.Ref +} + +// GetRequiredContexts returns the RequiredContexts slice if it's non-nil, nil otherwise. +func (d *DeploymentRequest) GetRequiredContexts() []string { + if d == nil || d.RequiredContexts == nil { + return nil + } + return d.RequiredContexts +} + +// GetTask returns the Task field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetTask() string { + if d == nil || d.Task == nil { + return "" + } + return *d.Task +} + +// GetTransientEnvironment returns the TransientEnvironment field if it's non-nil, zero value otherwise. +func (d *DeploymentRequest) GetTransientEnvironment() bool { + if d == nil || d.TransientEnvironment == nil { + return false + } + return *d.TransientEnvironment +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DeploymentReviewEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetApprover returns the Approver field. +func (d *DeploymentReviewEvent) GetApprover() *User { + if d == nil { + return nil + } + return d.Approver +} + +// GetComment returns the Comment field if it's non-nil, zero value otherwise. +func (d *DeploymentReviewEvent) GetComment() string { + if d == nil || d.Comment == nil { + return "" + } + return *d.Comment +} + +// GetEnterprise returns the Enterprise field. +func (d *DeploymentReviewEvent) GetEnterprise() *Enterprise { + if d == nil { + return nil + } + return d.Enterprise +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *DeploymentReviewEvent) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetInstallation returns the Installation field. +func (d *DeploymentReviewEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrganization returns the Organization field. +func (d *DeploymentReviewEvent) GetOrganization() *Organization { + if d == nil { + return nil + } + return d.Organization +} + +// GetRepo returns the Repo field. +func (d *DeploymentReviewEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetRequester returns the Requester field. +func (d *DeploymentReviewEvent) GetRequester() *User { + if d == nil { + return nil + } + return d.Requester +} + +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (d *DeploymentReviewEvent) GetReviewers() []*RequiredReviewer { + if d == nil || d.Reviewers == nil { + return nil + } + return d.Reviewers +} + +// GetSender returns the Sender field. +func (d *DeploymentReviewEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetSince returns the Since field if it's non-nil, zero value otherwise. +func (d *DeploymentReviewEvent) GetSince() string { + if d == nil || d.Since == nil { + return "" + } + return *d.Since +} + +// GetWorkflowJobRun returns the WorkflowJobRun field. +func (d *DeploymentReviewEvent) GetWorkflowJobRun() *WorkflowJobRun { + if d == nil { + return nil + } + return d.WorkflowJobRun +} + +// GetWorkflowJobRuns returns the WorkflowJobRuns slice if it's non-nil, nil otherwise. +func (d *DeploymentReviewEvent) GetWorkflowJobRuns() []*WorkflowJobRun { + if d == nil || d.WorkflowJobRuns == nil { + return nil + } + return d.WorkflowJobRuns +} + +// GetWorkflowRun returns the WorkflowRun field. +func (d *DeploymentReviewEvent) GetWorkflowRun() *WorkflowRun { + if d == nil { + return nil + } + return d.WorkflowRun +} + +// GetEnvironment returns the Environment field. +func (d *DeploymentsListOptions) GetEnvironment() string { + if d == nil { + return "" + } + return d.Environment +} + +// GetRef returns the Ref field. +func (d *DeploymentsListOptions) GetRef() string { + if d == nil { + return "" + } + return d.Ref +} + +// GetSHA returns the SHA field. +func (d *DeploymentsListOptions) GetSHA() string { + if d == nil { + return "" + } + return d.SHA +} + +// GetTask returns the Task field. +func (d *DeploymentsListOptions) GetTask() string { + if d == nil { + return "" + } + return d.Task +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetCreator returns the Creator field. +func (d *DeploymentStatus) GetCreator() *User { + if d == nil { + return nil + } + return d.Creator +} + +// GetDeploymentURL returns the DeploymentURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetDeploymentURL() string { + if d == nil || d.DeploymentURL == nil { + return "" + } + return *d.DeploymentURL +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetEnvironmentURL returns the EnvironmentURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetEnvironmentURL() string { + if d == nil || d.EnvironmentURL == nil { + return "" + } + return *d.EnvironmentURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetLogURL returns the LogURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetLogURL() string { + if d == nil || d.LogURL == nil { + return "" + } + return *d.LogURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetRepositoryURL() string { + if d == nil || d.RepositoryURL == nil { + return "" + } + return *d.RepositoryURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetTargetURL() string { + if d == nil || d.TargetURL == nil { + return "" + } + return *d.TargetURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatus) GetURL() string { + if d == nil || d.URL == nil { + return "" + } + return *d.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetDeployment returns the Deployment field. +func (d *DeploymentStatusEvent) GetDeployment() *Deployment { + if d == nil { + return nil + } + return d.Deployment +} + +// GetDeploymentStatus returns the DeploymentStatus field. +func (d *DeploymentStatusEvent) GetDeploymentStatus() *DeploymentStatus { + if d == nil { + return nil + } + return d.DeploymentStatus +} + +// GetInstallation returns the Installation field. +func (d *DeploymentStatusEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrg returns the Org field. +func (d *DeploymentStatusEvent) GetOrg() *Organization { + if d == nil { + return nil + } + return d.Org +} + +// GetRepo returns the Repo field. +func (d *DeploymentStatusEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DeploymentStatusEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetAutoInactive returns the AutoInactive field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetAutoInactive() bool { + if d == nil || d.AutoInactive == nil { + return false + } + return *d.AutoInactive +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetEnvironment() string { + if d == nil || d.Environment == nil { + return "" + } + return *d.Environment +} + +// GetEnvironmentURL returns the EnvironmentURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetEnvironmentURL() string { + if d == nil || d.EnvironmentURL == nil { + return "" + } + return *d.EnvironmentURL +} + +// GetLogURL returns the LogURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetLogURL() string { + if d == nil || d.LogURL == nil { + return "" + } + return *d.LogURL +} + +// GetState returns the State field. +func (d *DeploymentStatusRequest) GetState() string { + if d == nil { + return "" + } + return d.State +} + +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (d *DeploymentStatusRequest) GetTargetURL() string { + if d == nil || d.TargetURL == nil { + return "" + } + return *d.TargetURL +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (d *DevContainer) GetDisplayName() string { + if d == nil || d.DisplayName == nil { + return "" + } + return *d.DisplayName +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DevContainer) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetPath returns the Path field. +func (d *DevContainer) GetPath() string { + if d == nil { + return "" + } + return d.Path +} + +// GetDevcontainers returns the Devcontainers slice if it's non-nil, nil otherwise. +func (d *DevContainerConfigurations) GetDevcontainers() []*DevContainer { + if d == nil || d.Devcontainers == nil { + return nil + } + return d.Devcontainers +} + +// GetTotalCount returns the TotalCount field. +func (d *DevContainerConfigurations) GetTotalCount() int64 { + if d == nil { + return 0 + } + return d.TotalCount +} + +// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. +func (d *Discussion) GetActiveLockReason() string { + if d == nil || d.ActiveLockReason == nil { + return "" + } + return *d.ActiveLockReason +} + +// GetAnswerChosenAt returns the AnswerChosenAt field if it's non-nil, zero value otherwise. +func (d *Discussion) GetAnswerChosenAt() Timestamp { + if d == nil || d.AnswerChosenAt == nil { + return Timestamp{} + } + return *d.AnswerChosenAt +} + +// GetAnswerChosenBy returns the AnswerChosenBy field if it's non-nil, zero value otherwise. +func (d *Discussion) GetAnswerChosenBy() string { + if d == nil || d.AnswerChosenBy == nil { + return "" + } + return *d.AnswerChosenBy +} + +// GetAnswerHTMLURL returns the AnswerHTMLURL field if it's non-nil, zero value otherwise. +func (d *Discussion) GetAnswerHTMLURL() string { + if d == nil || d.AnswerHTMLURL == nil { + return "" + } + return *d.AnswerHTMLURL +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (d *Discussion) GetAuthorAssociation() string { + if d == nil || d.AuthorAssociation == nil { + return "" + } + return *d.AuthorAssociation +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (d *Discussion) GetBody() string { + if d == nil || d.Body == nil { + return "" + } + return *d.Body +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (d *Discussion) GetComments() int { + if d == nil || d.Comments == nil { + return 0 + } + return *d.Comments +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *Discussion) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetDiscussionCategory returns the DiscussionCategory field. +func (d *Discussion) GetDiscussionCategory() *DiscussionCategory { + if d == nil { + return nil + } + return d.DiscussionCategory +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (d *Discussion) GetHTMLURL() string { + if d == nil || d.HTMLURL == nil { + return "" + } + return *d.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *Discussion) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetLocked returns the Locked field if it's non-nil, zero value otherwise. +func (d *Discussion) GetLocked() bool { + if d == nil || d.Locked == nil { + return false + } + return *d.Locked +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *Discussion) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (d *Discussion) GetNumber() int { + if d == nil || d.Number == nil { + return 0 + } + return *d.Number +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (d *Discussion) GetRepositoryURL() string { + if d == nil || d.RepositoryURL == nil { + return "" + } + return *d.RepositoryURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *Discussion) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (d *Discussion) GetTitle() string { + if d == nil || d.Title == nil { + return "" + } + return *d.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *Discussion) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetUser returns the User field. +func (d *Discussion) GetUser() *User { + if d == nil { + return nil + } + return d.User +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetDescription() string { + if d == nil || d.Description == nil { + return "" + } + return *d.Description +} + +// GetEmoji returns the Emoji field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetEmoji() string { + if d == nil || d.Emoji == nil { + return "" + } + return *d.Emoji +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetID() int64 { + if d == nil || d.ID == nil { + return 0 + } + return *d.ID +} + +// GetIsAnswerable returns the IsAnswerable field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetIsAnswerable() bool { + if d == nil || d.IsAnswerable == nil { + return false + } + return *d.IsAnswerable +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetName() string { + if d == nil || d.Name == nil { + return "" + } + return *d.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetRepositoryID returns the RepositoryID field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetRepositoryID() int64 { + if d == nil || d.RepositoryID == nil { + return 0 + } + return *d.RepositoryID +} + +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetSlug() string { + if d == nil || d.Slug == nil { + return "" + } + return *d.Slug +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DiscussionCategory) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetAuthor returns the Author field. +func (d *DiscussionComment) GetAuthor() *User { + if d == nil { + return nil + } + return d.Author +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetBody() string { + if d == nil || d.Body == nil { + return "" + } + return *d.Body +} + +// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetBodyHTML() string { + if d == nil || d.BodyHTML == nil { + return "" + } + return *d.BodyHTML +} + +// GetBodyVersion returns the BodyVersion field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetBodyVersion() string { + if d == nil || d.BodyVersion == nil { + return "" + } + return *d.BodyVersion +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetCreatedAt() Timestamp { + if d == nil || d.CreatedAt == nil { + return Timestamp{} + } + return *d.CreatedAt +} + +// GetDiscussionURL returns the DiscussionURL field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetDiscussionURL() string { + if d == nil || d.DiscussionURL == nil { + return "" + } + return *d.DiscussionURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetHTMLURL() string { + if d == nil || d.HTMLURL == nil { + return "" + } + return *d.HTMLURL +} + +// GetLastEditedAt returns the LastEditedAt field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetLastEditedAt() Timestamp { + if d == nil || d.LastEditedAt == nil { + return Timestamp{} + } + return *d.LastEditedAt +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetNodeID() string { + if d == nil || d.NodeID == nil { + return "" + } + return *d.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetNumber() int { + if d == nil || d.Number == nil { + return 0 + } + return *d.Number +} + +// GetReactions returns the Reactions field. +func (d *DiscussionComment) GetReactions() *Reactions { + if d == nil { + return nil + } + return d.Reactions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetUpdatedAt() Timestamp { + if d == nil || d.UpdatedAt == nil { + return Timestamp{} + } + return *d.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (d *DiscussionComment) GetURL() string { + if d == nil || d.URL == nil { + return "" + } + return *d.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DiscussionCommentEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetComment returns the Comment field. +func (d *DiscussionCommentEvent) GetComment() *CommentDiscussion { + if d == nil { + return nil + } + return d.Comment +} + +// GetDiscussion returns the Discussion field. +func (d *DiscussionCommentEvent) GetDiscussion() *Discussion { + if d == nil { + return nil + } + return d.Discussion +} + +// GetInstallation returns the Installation field. +func (d *DiscussionCommentEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrg returns the Org field. +func (d *DiscussionCommentEvent) GetOrg() *Organization { + if d == nil { + return nil + } + return d.Org +} + +// GetRepo returns the Repo field. +func (d *DiscussionCommentEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DiscussionCommentEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetDirection returns the Direction field. +func (d *DiscussionCommentListOptions) GetDirection() string { + if d == nil { + return "" + } + return d.Direction +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (d *DiscussionEvent) GetAction() string { + if d == nil || d.Action == nil { + return "" + } + return *d.Action +} + +// GetDiscussion returns the Discussion field. +func (d *DiscussionEvent) GetDiscussion() *Discussion { + if d == nil { + return nil + } + return d.Discussion +} + +// GetInstallation returns the Installation field. +func (d *DiscussionEvent) GetInstallation() *Installation { + if d == nil { + return nil + } + return d.Installation +} + +// GetOrg returns the Org field. +func (d *DiscussionEvent) GetOrg() *Organization { + if d == nil { + return nil + } + return d.Org +} + +// GetRepo returns the Repo field. +func (d *DiscussionEvent) GetRepo() *Repository { + if d == nil { + return nil + } + return d.Repo +} + +// GetSender returns the Sender field. +func (d *DiscussionEvent) GetSender() *User { + if d == nil { + return nil + } + return d.Sender +} + +// GetDirection returns the Direction field. +func (d *DiscussionListOptions) GetDirection() string { + if d == nil { + return "" + } + return d.Direction +} + +// GetApps returns the Apps slice if it's non-nil, nil otherwise. +func (d *DismissalRestrictions) GetApps() []*App { + if d == nil || d.Apps == nil { + return nil + } + return d.Apps +} + +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (d *DismissalRestrictions) GetTeams() []*Team { + if d == nil || d.Teams == nil { + return nil + } + return d.Teams +} + +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (d *DismissalRestrictions) GetUsers() []*User { + if d == nil || d.Users == nil { + return nil + } + return d.Users +} + +// GetApps returns the Apps field if it's non-nil, zero value otherwise. +func (d *DismissalRestrictionsRequest) GetApps() []string { + if d == nil || d.Apps == nil { + return nil + } + return *d.Apps +} + +// GetTeams returns the Teams field if it's non-nil, zero value otherwise. +func (d *DismissalRestrictionsRequest) GetTeams() []string { + if d == nil || d.Teams == nil { + return nil + } + return *d.Teams +} + +// GetUsers returns the Users field if it's non-nil, zero value otherwise. +func (d *DismissalRestrictionsRequest) GetUsers() []string { + if d == nil || d.Users == nil { + return nil + } + return *d.Users +} + +// GetDismissalCommitID returns the DismissalCommitID field if it's non-nil, zero value otherwise. +func (d *DismissedReview) GetDismissalCommitID() string { + if d == nil || d.DismissalCommitID == nil { + return "" + } + return *d.DismissalCommitID +} + +// GetDismissalMessage returns the DismissalMessage field if it's non-nil, zero value otherwise. +func (d *DismissedReview) GetDismissalMessage() string { + if d == nil || d.DismissalMessage == nil { + return "" + } + return *d.DismissalMessage +} + +// GetReviewID returns the ReviewID field if it's non-nil, zero value otherwise. +func (d *DismissedReview) GetReviewID() int64 { + if d == nil || d.ReviewID == nil { + return 0 + } + return *d.ReviewID +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (d *DismissedReview) GetState() string { + if d == nil || d.State == nil { + return "" + } + return *d.State +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (d *DismissStaleReviewsOnPushChanges) GetFrom() bool { + if d == nil || d.From == nil { + return false + } + return *d.From +} + +// GetClientPayload returns the ClientPayload field if it's non-nil, zero value otherwise. +func (d *DispatchRequestOptions) GetClientPayload() json.RawMessage { + if d == nil || d.ClientPayload == nil { + return json.RawMessage{} + } + return *d.ClientPayload +} + +// GetEventType returns the EventType field. +func (d *DispatchRequestOptions) GetEventType() string { + if d == nil { + return "" + } + return d.EventType +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetBody() string { + if d == nil || d.Body == nil { + return "" + } + return *d.Body +} + +// GetLine returns the Line field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetLine() int { + if d == nil || d.Line == nil { + return 0 + } + return *d.Line +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetPath() string { + if d == nil || d.Path == nil { + return "" + } + return *d.Path +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetPosition() int { + if d == nil || d.Position == nil { + return 0 + } + return *d.Position +} + +// GetSide returns the Side field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetSide() string { + if d == nil || d.Side == nil { + return "" + } + return *d.Side +} + +// GetStartLine returns the StartLine field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetStartLine() int { + if d == nil || d.StartLine == nil { + return 0 + } + return *d.StartLine +} + +// GetStartSide returns the StartSide field if it's non-nil, zero value otherwise. +func (d *DraftReviewComment) GetStartSide() string { + if d == nil || d.StartSide == nil { + return "" + } + return *d.StartSide +} + +// GetRef returns the Ref field. +func (e *EditBase) GetRef() *EditRef { + if e == nil { + return nil + } + return e.Ref +} + +// GetSHA returns the SHA field. +func (e *EditBase) GetSHA() *EditSHA { + if e == nil { + return nil + } + return e.SHA +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (e *EditBody) GetFrom() string { + if e == nil || e.From == nil { + return "" + } + return *e.From +} + +// GetBase returns the Base field. +func (e *EditChange) GetBase() *EditBase { + if e == nil { + return nil + } + return e.Base +} + +// GetBody returns the Body field. +func (e *EditChange) GetBody() *EditBody { + if e == nil { + return nil + } + return e.Body +} + +// GetDefaultBranch returns the DefaultBranch field. +func (e *EditChange) GetDefaultBranch() *EditDefaultBranch { + if e == nil { + return nil + } + return e.DefaultBranch +} + +// GetOwner returns the Owner field. +func (e *EditChange) GetOwner() *EditOwner { + if e == nil { + return nil + } + return e.Owner +} + +// GetRepo returns the Repo field. +func (e *EditChange) GetRepo() *EditRepo { + if e == nil { + return nil + } + return e.Repo +} + +// GetTitle returns the Title field. +func (e *EditChange) GetTitle() *EditTitle { + if e == nil { + return nil + } + return e.Title +} + +// GetTopics returns the Topics field. +func (e *EditChange) GetTopics() *EditTopics { + if e == nil { + return nil + } + return e.Topics +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (e *EditDefaultBranch) GetFrom() string { + if e == nil || e.From == nil { + return "" + } + return *e.From +} + +// GetOwnerInfo returns the OwnerInfo field. +func (e *EditOwner) GetOwnerInfo() *OwnerInfo { + if e == nil { + return nil + } + return e.OwnerInfo +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (e *EditRef) GetFrom() string { + if e == nil || e.From == nil { + return "" + } + return *e.From +} + +// GetName returns the Name field. +func (e *EditRepo) GetName() *RepoName { + if e == nil { + return nil + } + return e.Name +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (e *EditSHA) GetFrom() string { + if e == nil || e.From == nil { + return "" + } + return *e.From +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (e *EditTitle) GetFrom() string { + if e == nil || e.From == nil { + return "" + } + return *e.From +} + +// GetFrom returns the From slice if it's non-nil, nil otherwise. +func (e *EditTopics) GetFrom() []string { + if e == nil || e.From == nil { + return nil + } + return e.From +} + +// GetEncryptedValue returns the EncryptedValue field. +func (e *EncryptedSecret) GetEncryptedValue() string { + if e == nil { + return "" + } + return e.EncryptedValue +} + +// GetKeyID returns the KeyID field. +func (e *EncryptedSecret) GetKeyID() string { + if e == nil { + return "" + } + return e.KeyID +} + +// GetName returns the Name field. +func (e *EncryptedSecret) GetName() string { + if e == nil { + return "" + } + return e.Name +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs field. +func (e *EncryptedSecret) GetSelectedRepositoryIDs() SelectedRepoIDs { + if e == nil { + return nil + } + return e.SelectedRepositoryIDs +} + +// GetVisibility returns the Visibility field. +func (e *EncryptedSecret) GetVisibility() string { + if e == nil { + return "" + } + return e.Visibility +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetAvatarURL() string { + if e == nil || e.AvatarURL == nil { + return "" + } + return *e.AvatarURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetCreatedAt() Timestamp { + if e == nil || e.CreatedAt == nil { + return Timestamp{} + } + return *e.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetDescription() string { + if e == nil || e.Description == nil { + return "" + } + return *e.Description +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetHTMLURL() string { + if e == nil || e.HTMLURL == nil { + return "" + } + return *e.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetID() int { + if e == nil || e.ID == nil { + return 0 + } + return *e.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetName() string { + if e == nil || e.Name == nil { + return "" + } + return *e.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetNodeID() string { + if e == nil || e.NodeID == nil { + return "" + } + return *e.NodeID +} + +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetSlug() string { + if e == nil || e.Slug == nil { + return "" + } + return *e.Slug +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetUpdatedAt() Timestamp { + if e == nil || e.UpdatedAt == nil { + return Timestamp{} + } + return *e.UpdatedAt +} + +// GetWebsiteURL returns the WebsiteURL field if it's non-nil, zero value otherwise. +func (e *Enterprise) GetWebsiteURL() string { + if e == nil || e.WebsiteURL == nil { + return "" + } + return *e.WebsiteURL +} + +// GetDiscountAmount returns the DiscountAmount field. +func (e *EnterpriseAggregatedUsageItem) GetDiscountAmount() float64 { + if e == nil { + return 0 + } + return e.DiscountAmount +} + +// GetDiscountQuantity returns the DiscountQuantity field. +func (e *EnterpriseAggregatedUsageItem) GetDiscountQuantity() float64 { + if e == nil { + return 0 + } + return e.DiscountQuantity +} + +// GetGrossAmount returns the GrossAmount field. +func (e *EnterpriseAggregatedUsageItem) GetGrossAmount() float64 { + if e == nil { + return 0 + } + return e.GrossAmount +} + +// GetGrossQuantity returns the GrossQuantity field. +func (e *EnterpriseAggregatedUsageItem) GetGrossQuantity() float64 { + if e == nil { + return 0 + } + return e.GrossQuantity +} + +// GetModel returns the Model field. +func (e *EnterpriseAggregatedUsageItem) GetModel() string { + if e == nil { + return "" + } + return e.Model +} + +// GetNetAmount returns the NetAmount field. +func (e *EnterpriseAggregatedUsageItem) GetNetAmount() float64 { + if e == nil { + return 0 + } + return e.NetAmount +} + +// GetNetQuantity returns the NetQuantity field. +func (e *EnterpriseAggregatedUsageItem) GetNetQuantity() float64 { + if e == nil { + return 0 + } + return e.NetQuantity +} + +// GetPricePerUnit returns the PricePerUnit field. +func (e *EnterpriseAggregatedUsageItem) GetPricePerUnit() float64 { + if e == nil { + return 0 + } + return e.PricePerUnit +} + +// GetProduct returns the Product field. +func (e *EnterpriseAggregatedUsageItem) GetProduct() string { + if e == nil { + return "" + } + return e.Product +} + +// GetSKU returns the SKU field. +func (e *EnterpriseAggregatedUsageItem) GetSKU() string { + if e == nil { + return "" + } + return e.SKU +} + +// GetUnitType returns the UnitType field. +func (e *EnterpriseAggregatedUsageItem) GetUnitType() string { + if e == nil { + return "" + } + return e.UnitType +} + +// GetCostCenter returns the CostCenter field. +func (e *EnterpriseAggregatedUsageReport) GetCostCenter() *BillingCostCenter { + if e == nil { + return nil + } + return e.CostCenter +} + +// GetEnterprise returns the Enterprise field. +func (e *EnterpriseAggregatedUsageReport) GetEnterprise() string { + if e == nil { + return "" + } + return e.Enterprise +} + +// GetModel returns the Model field if it's non-nil, zero value otherwise. +func (e *EnterpriseAggregatedUsageReport) GetModel() string { + if e == nil || e.Model == nil { + return "" + } + return *e.Model +} + +// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. +func (e *EnterpriseAggregatedUsageReport) GetOrganization() string { + if e == nil || e.Organization == nil { + return "" + } + return *e.Organization +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (e *EnterpriseAggregatedUsageReport) GetProduct() string { + if e == nil || e.Product == nil { + return "" + } + return *e.Product +} + +// GetTimePeriod returns the TimePeriod field. +func (e *EnterpriseAggregatedUsageReport) GetTimePeriod() EnterpriseUsageTimePeriod { + if e == nil { + return EnterpriseUsageTimePeriod{} + } + return e.TimePeriod +} + +// GetUsageItems returns the UsageItems slice if it's non-nil, nil otherwise. +func (e *EnterpriseAggregatedUsageReport) GetUsageItems() []*EnterpriseAggregatedUsageItem { + if e == nil || e.UsageItems == nil { + return nil + } + return e.UsageItems +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (e *EnterpriseAggregatedUsageReport) GetUser() string { + if e == nil || e.User == nil { + return "" + } + return *e.User +} + +// GetBudgetAlerting returns the BudgetAlerting field. +func (e *EnterpriseBudget) GetBudgetAlerting() *EnterpriseBudgetAlerting { + if e == nil { + return nil + } + return e.BudgetAlerting +} + +// GetBudgetAmount returns the BudgetAmount field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetBudgetAmount() int { + if e == nil || e.BudgetAmount == nil { + return 0 + } + return *e.BudgetAmount +} + +// GetBudgetEntityName returns the BudgetEntityName field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetBudgetEntityName() string { + if e == nil || e.BudgetEntityName == nil { + return "" + } + return *e.BudgetEntityName +} + +// GetBudgetProductSKU returns the BudgetProductSKU field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetBudgetProductSKU() string { + if e == nil || e.BudgetProductSKU == nil { + return "" + } + return *e.BudgetProductSKU +} + +// GetBudgetScope returns the BudgetScope field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetBudgetScope() string { + if e == nil || e.BudgetScope == nil { + return "" + } + return *e.BudgetScope +} + +// GetBudgetType returns the BudgetType field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetBudgetType() string { + if e == nil || e.BudgetType == nil { + return "" + } + return *e.BudgetType +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetID() string { + if e == nil || e.ID == nil { + return "" + } + return *e.ID +} + +// GetPreventFurtherUsage returns the PreventFurtherUsage field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudget) GetPreventFurtherUsage() bool { + if e == nil || e.PreventFurtherUsage == nil { + return false + } + return *e.PreventFurtherUsage +} + +// GetAlertRecipients returns the AlertRecipients slice if it's non-nil, nil otherwise. +func (e *EnterpriseBudgetAlerting) GetAlertRecipients() []string { + if e == nil || e.AlertRecipients == nil { + return nil + } + return e.AlertRecipients +} + +// GetWillAlert returns the WillAlert field if it's non-nil, zero value otherwise. +func (e *EnterpriseBudgetAlerting) GetWillAlert() bool { + if e == nil || e.WillAlert == nil { + return false + } + return *e.WillAlert +} + +// GetTotalSeatsConsumed returns the TotalSeatsConsumed field. +func (e *EnterpriseConsumedLicenses) GetTotalSeatsConsumed() int { + if e == nil { + return 0 + } + return e.TotalSeatsConsumed +} + +// GetTotalSeatsPurchased returns the TotalSeatsPurchased field. +func (e *EnterpriseConsumedLicenses) GetTotalSeatsPurchased() int { + if e == nil { + return 0 + } + return e.TotalSeatsPurchased +} + +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (e *EnterpriseConsumedLicenses) GetUsers() []*EnterpriseLicensedUsers { + if e == nil || e.Users == nil { + return nil + } + return e.Users +} + +// GetBudgetAlerting returns the BudgetAlerting field. +func (e *EnterpriseCreateBudget) GetBudgetAlerting() *EnterpriseBudgetAlerting { + if e == nil { + return nil + } + return e.BudgetAlerting +} + +// GetBudgetAmount returns the BudgetAmount field. +func (e *EnterpriseCreateBudget) GetBudgetAmount() int { + if e == nil { + return 0 + } + return e.BudgetAmount +} + +// GetBudgetEntityName returns the BudgetEntityName field if it's non-nil, zero value otherwise. +func (e *EnterpriseCreateBudget) GetBudgetEntityName() string { + if e == nil || e.BudgetEntityName == nil { + return "" + } + return *e.BudgetEntityName +} + +// GetBudgetProductSKU returns the BudgetProductSKU field if it's non-nil, zero value otherwise. +func (e *EnterpriseCreateBudget) GetBudgetProductSKU() string { + if e == nil || e.BudgetProductSKU == nil { + return "" + } + return *e.BudgetProductSKU +} + +// GetBudgetScope returns the BudgetScope field. +func (e *EnterpriseCreateBudget) GetBudgetScope() string { + if e == nil { + return "" + } + return e.BudgetScope +} + +// GetBudgetType returns the BudgetType field. +func (e *EnterpriseCreateBudget) GetBudgetType() string { + if e == nil { + return "" + } + return e.BudgetType +} + +// GetPreventFurtherUsage returns the PreventFurtherUsage field. +func (e *EnterpriseCreateBudget) GetPreventFurtherUsage() bool { + if e == nil { + return false + } + return e.PreventFurtherUsage +} + +// GetBudget returns the Budget field. +func (e *EnterpriseCreateOrUpdateBudgetResponse) GetBudget() *EnterpriseBudget { + if e == nil { + return nil + } + return e.Budget +} + +// GetMessage returns the Message field. +func (e *EnterpriseCreateOrUpdateBudgetResponse) GetMessage() string { + if e == nil { + return "" + } + return e.Message +} + +// GetOrganizationID returns the OrganizationID field if it's non-nil, zero value otherwise. +func (e *EnterpriseCustomPropertiesValues) GetOrganizationID() int64 { + if e == nil || e.OrganizationID == nil { + return 0 + } + return *e.OrganizationID +} + +// GetOrganizationLogin returns the OrganizationLogin field if it's non-nil, zero value otherwise. +func (e *EnterpriseCustomPropertiesValues) GetOrganizationLogin() string { + if e == nil || e.OrganizationLogin == nil { + return "" + } + return *e.OrganizationLogin +} + +// GetProperties returns the Properties slice if it's non-nil, nil otherwise. +func (e *EnterpriseCustomPropertiesValues) GetProperties() []*CustomPropertyValue { + if e == nil || e.Properties == nil { + return nil + } + return e.Properties +} + +// GetProperties returns the Properties slice if it's non-nil, nil otherwise. +func (e *EnterpriseCustomPropertySchema) GetProperties() []*CustomProperty { + if e == nil || e.Properties == nil { + return nil + } + return e.Properties +} + +// GetOrganizationLogin returns the OrganizationLogin slice if it's non-nil, nil otherwise. +func (e *EnterpriseCustomPropertyValuesRequest) GetOrganizationLogin() []string { + if e == nil || e.OrganizationLogin == nil { + return nil + } + return e.OrganizationLogin +} + +// GetProperties returns the Properties slice if it's non-nil, nil otherwise. +func (e *EnterpriseCustomPropertyValuesRequest) GetProperties() []*CustomPropertyValue { + if e == nil || e.Properties == nil { + return nil + } + return e.Properties +} + +// GetID returns the ID field. +func (e *EnterpriseDeleteBudgetResponse) GetID() string { + if e == nil { + return "" + } + return e.ID +} + +// GetMessage returns the Message field. +func (e *EnterpriseDeleteBudgetResponse) GetMessage() string { + if e == nil { + return "" + } + return e.Message +} + +// GetEnterpriseServerEmails returns the EnterpriseServerEmails slice if it's non-nil, nil otherwise. +func (e *EnterpriseLicensedUsers) GetEnterpriseServerEmails() []string { + if e == nil || e.EnterpriseServerEmails == nil { + return nil + } + return e.EnterpriseServerEmails +} + +// GetEnterpriseServerUser returns the EnterpriseServerUser field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetEnterpriseServerUser() bool { + if e == nil || e.EnterpriseServerUser == nil { + return false + } + return *e.EnterpriseServerUser +} + +// GetEnterpriseServerUserIDs returns the EnterpriseServerUserIDs slice if it's non-nil, nil otherwise. +func (e *EnterpriseLicensedUsers) GetEnterpriseServerUserIDs() []string { + if e == nil || e.EnterpriseServerUserIDs == nil { + return nil + } + return e.EnterpriseServerUserIDs +} + +// GetGithubComEnterpriseRoles returns the GithubComEnterpriseRoles slice if it's non-nil, nil otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComEnterpriseRoles() []string { + if e == nil || e.GithubComEnterpriseRoles == nil { + return nil + } + return e.GithubComEnterpriseRoles +} + +// GetGithubComLogin returns the GithubComLogin field. +func (e *EnterpriseLicensedUsers) GetGithubComLogin() string { + if e == nil { + return "" + } + return e.GithubComLogin +} + +// GetGithubComMemberRoles returns the GithubComMemberRoles slice if it's non-nil, nil otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComMemberRoles() []string { + if e == nil || e.GithubComMemberRoles == nil { + return nil + } + return e.GithubComMemberRoles +} + +// GetGithubComName returns the GithubComName field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComName() string { + if e == nil || e.GithubComName == nil { + return "" + } + return *e.GithubComName +} + +// GetGithubComOrgsWithPendingInvites returns the GithubComOrgsWithPendingInvites slice if it's non-nil, nil otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComOrgsWithPendingInvites() []string { + if e == nil || e.GithubComOrgsWithPendingInvites == nil { + return nil + } + return e.GithubComOrgsWithPendingInvites +} + +// GetGithubComProfile returns the GithubComProfile field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComProfile() string { + if e == nil || e.GithubComProfile == nil { + return "" + } + return *e.GithubComProfile +} + +// GetGithubComSamlNameID returns the GithubComSamlNameID field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComSamlNameID() string { + if e == nil || e.GithubComSamlNameID == nil { + return "" + } + return *e.GithubComSamlNameID +} + +// GetGithubComTwoFactorAuth returns the GithubComTwoFactorAuth field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComTwoFactorAuth() bool { + if e == nil || e.GithubComTwoFactorAuth == nil { + return false + } + return *e.GithubComTwoFactorAuth +} + +// GetGithubComUser returns the GithubComUser field. +func (e *EnterpriseLicensedUsers) GetGithubComUser() bool { + if e == nil { + return false + } + return e.GithubComUser +} + +// GetGithubComVerifiedDomainEmails returns the GithubComVerifiedDomainEmails slice if it's non-nil, nil otherwise. +func (e *EnterpriseLicensedUsers) GetGithubComVerifiedDomainEmails() []string { + if e == nil || e.GithubComVerifiedDomainEmails == nil { + return nil + } + return e.GithubComVerifiedDomainEmails +} + +// GetLicenseType returns the LicenseType field. +func (e *EnterpriseLicensedUsers) GetLicenseType() string { + if e == nil { + return "" + } + return e.LicenseType +} + +// GetTotalUserAccounts returns the TotalUserAccounts field. +func (e *EnterpriseLicensedUsers) GetTotalUserAccounts() int { + if e == nil { + return 0 + } + return e.TotalUserAccounts +} + +// GetVisualStudioLicenseStatus returns the VisualStudioLicenseStatus field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetVisualStudioLicenseStatus() string { + if e == nil || e.VisualStudioLicenseStatus == nil { + return "" + } + return *e.VisualStudioLicenseStatus +} + +// GetVisualStudioSubscriptionEmail returns the VisualStudioSubscriptionEmail field if it's non-nil, zero value otherwise. +func (e *EnterpriseLicensedUsers) GetVisualStudioSubscriptionEmail() string { + if e == nil || e.VisualStudioSubscriptionEmail == nil { + return "" + } + return *e.VisualStudioSubscriptionEmail +} + +// GetVisualStudioSubscriptionUser returns the VisualStudioSubscriptionUser field. +func (e *EnterpriseLicensedUsers) GetVisualStudioSubscriptionUser() bool { + if e == nil { + return false + } + return e.VisualStudioSubscriptionUser +} + +// GetDescription returns the Description field. +func (e *EnterpriseLicenseSyncStatus) GetDescription() string { + if e == nil { + return "" + } + return e.Description +} + +// GetProperties returns the Properties field. +func (e *EnterpriseLicenseSyncStatus) GetProperties() *ServerInstanceProperties { + if e == nil { + return nil + } + return e.Properties +} + +// GetTitle returns the Title field. +func (e *EnterpriseLicenseSyncStatus) GetTitle() string { + if e == nil { + return "" + } + return e.Title +} + +// GetBudgets returns the Budgets slice if it's non-nil, nil otherwise. +func (e *EnterpriseListBudgets) GetBudgets() []*EnterpriseBudget { + if e == nil || e.Budgets == nil { + return nil + } + return e.Budgets +} + +// GetHasNextPage returns the HasNextPage field if it's non-nil, zero value otherwise. +func (e *EnterpriseListBudgets) GetHasNextPage() bool { + if e == nil || e.HasNextPage == nil { + return false + } + return *e.HasNextPage +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (e *EnterpriseListBudgets) GetTotalCount() int { + if e == nil || e.TotalCount == nil { + return 0 + } + return *e.TotalCount +} + +// GetModel returns the Model field. +func (e *EnterprisePremiumRequestUsageReportOptions) GetModel() string { + if e == nil { + return "" + } + return e.Model +} + +// GetOrganization returns the Organization field. +func (e *EnterprisePremiumRequestUsageReportOptions) GetOrganization() string { + if e == nil { + return "" + } + return e.Organization +} + +// GetProduct returns the Product field. +func (e *EnterprisePremiumRequestUsageReportOptions) GetProduct() string { + if e == nil { + return "" + } + return e.Product +} + +// GetUser returns the User field. +func (e *EnterprisePremiumRequestUsageReportOptions) GetUser() string { + if e == nil { + return "" + } + return e.User +} + +// GetAllowsPublicRepositories returns the AllowsPublicRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetAllowsPublicRepositories() bool { + if e == nil || e.AllowsPublicRepositories == nil { + return false + } + return *e.AllowsPublicRepositories +} + +// GetDefault returns the Default field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetDefault() bool { + if e == nil || e.Default == nil { + return false + } + return *e.Default +} + +// GetHostedRunnersURL returns the HostedRunnersURL field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetHostedRunnersURL() string { + if e == nil || e.HostedRunnersURL == nil { + return "" + } + return *e.HostedRunnersURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetID() int64 { + if e == nil || e.ID == nil { + return 0 + } + return *e.ID +} + +// GetInherited returns the Inherited field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetInherited() bool { + if e == nil || e.Inherited == nil { + return false + } + return *e.Inherited +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetName() string { + if e == nil || e.Name == nil { + return "" + } + return *e.Name +} + +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetNetworkConfigurationID() string { + if e == nil || e.NetworkConfigurationID == nil { + return "" + } + return *e.NetworkConfigurationID +} + +// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetRestrictedToWorkflows() bool { + if e == nil || e.RestrictedToWorkflows == nil { + return false + } + return *e.RestrictedToWorkflows +} + +// GetRunnersURL returns the RunnersURL field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetRunnersURL() string { + if e == nil || e.RunnersURL == nil { + return "" + } + return *e.RunnersURL +} + +// GetSelectedOrganizationsURL returns the SelectedOrganizationsURL field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetSelectedOrganizationsURL() string { + if e == nil || e.SelectedOrganizationsURL == nil { + return "" + } + return *e.SelectedOrganizationsURL +} + +// GetSelectedWorkflows returns the SelectedWorkflows slice if it's non-nil, nil otherwise. +func (e *EnterpriseRunnerGroup) GetSelectedWorkflows() []string { + if e == nil || e.SelectedWorkflows == nil { + return nil + } + return e.SelectedWorkflows +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetVisibility() string { + if e == nil || e.Visibility == nil { + return "" + } + return *e.Visibility +} + +// GetWorkflowRestrictionsReadOnly returns the WorkflowRestrictionsReadOnly field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroup) GetWorkflowRestrictionsReadOnly() bool { + if e == nil || e.WorkflowRestrictionsReadOnly == nil { + return false + } + return *e.WorkflowRestrictionsReadOnly +} + +// GetRunnerGroups returns the RunnerGroups slice if it's non-nil, nil otherwise. +func (e *EnterpriseRunnerGroups) GetRunnerGroups() []*EnterpriseRunnerGroup { + if e == nil || e.RunnerGroups == nil { + return nil + } + return e.RunnerGroups +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (e *EnterpriseRunnerGroups) GetTotalCount() int { + if e == nil || e.TotalCount == nil { + return 0 + } + return *e.TotalCount +} + +// GetAdvancedSecurityEnabledForNewRepositories returns the AdvancedSecurityEnabledForNewRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetAdvancedSecurityEnabledForNewRepositories() bool { + if e == nil || e.AdvancedSecurityEnabledForNewRepositories == nil { + return false + } + return *e.AdvancedSecurityEnabledForNewRepositories +} + +// GetSecretScanningEnabledForNewRepositories returns the SecretScanningEnabledForNewRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningEnabledForNewRepositories() bool { + if e == nil || e.SecretScanningEnabledForNewRepositories == nil { + return false + } + return *e.SecretScanningEnabledForNewRepositories +} + +// GetSecretScanningPushProtectionCustomLink returns the SecretScanningPushProtectionCustomLink field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningPushProtectionCustomLink() string { + if e == nil || e.SecretScanningPushProtectionCustomLink == nil { + return "" + } + return *e.SecretScanningPushProtectionCustomLink +} + +// GetSecretScanningPushProtectionEnabledForNewRepositories returns the SecretScanningPushProtectionEnabledForNewRepositories field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningPushProtectionEnabledForNewRepositories() bool { + if e == nil || e.SecretScanningPushProtectionEnabledForNewRepositories == nil { + return false + } + return *e.SecretScanningPushProtectionEnabledForNewRepositories +} + +// GetSecretScanningValidityChecksEnabled returns the SecretScanningValidityChecksEnabled field if it's non-nil, zero value otherwise. +func (e *EnterpriseSecurityAnalysisSettings) GetSecretScanningValidityChecksEnabled() bool { + if e == nil || e.SecretScanningValidityChecksEnabled == nil { + return false + } + return *e.SecretScanningValidityChecksEnabled +} + +// GetCreatedAt returns the CreatedAt field. +func (e *EnterpriseTeam) GetCreatedAt() Timestamp { + if e == nil { + return Timestamp{} + } + return e.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (e *EnterpriseTeam) GetDescription() string { + if e == nil || e.Description == nil { + return "" + } + return *e.Description +} + +// GetGroupID returns the GroupID field. +func (e *EnterpriseTeam) GetGroupID() string { + if e == nil { + return "" + } + return e.GroupID +} + +// GetHTMLURL returns the HTMLURL field. +func (e *EnterpriseTeam) GetHTMLURL() string { + if e == nil { + return "" + } + return e.HTMLURL +} + +// GetID returns the ID field. +func (e *EnterpriseTeam) GetID() int64 { + if e == nil { + return 0 + } + return e.ID +} + +// GetMemberURL returns the MemberURL field. +func (e *EnterpriseTeam) GetMemberURL() string { + if e == nil { + return "" + } + return e.MemberURL +} + +// GetName returns the Name field. +func (e *EnterpriseTeam) GetName() string { + if e == nil { + return "" + } + return e.Name +} + +// GetOrganizationSelectionType returns the OrganizationSelectionType field if it's non-nil, zero value otherwise. +func (e *EnterpriseTeam) GetOrganizationSelectionType() string { + if e == nil || e.OrganizationSelectionType == nil { + return "" + } + return *e.OrganizationSelectionType +} + +// GetSlug returns the Slug field. +func (e *EnterpriseTeam) GetSlug() string { + if e == nil { + return "" + } + return e.Slug +} + +// GetUpdatedAt returns the UpdatedAt field. +func (e *EnterpriseTeam) GetUpdatedAt() Timestamp { + if e == nil { + return Timestamp{} + } + return e.UpdatedAt +} + +// GetURL returns the URL field. +func (e *EnterpriseTeam) GetURL() string { + if e == nil { + return "" + } + return e.URL +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (e *EnterpriseTeamCreateOrUpdateRequest) GetDescription() string { + if e == nil || e.Description == nil { + return "" + } + return *e.Description +} + +// GetGroupID returns the GroupID field if it's non-nil, zero value otherwise. +func (e *EnterpriseTeamCreateOrUpdateRequest) GetGroupID() string { + if e == nil || e.GroupID == nil { + return "" + } + return *e.GroupID +} + +// GetName returns the Name field. +func (e *EnterpriseTeamCreateOrUpdateRequest) GetName() string { + if e == nil { + return "" + } + return e.Name +} + +// GetOrganizationSelectionType returns the OrganizationSelectionType field if it's non-nil, zero value otherwise. +func (e *EnterpriseTeamCreateOrUpdateRequest) GetOrganizationSelectionType() string { + if e == nil || e.OrganizationSelectionType == nil { + return "" + } + return *e.OrganizationSelectionType +} + +// GetBudgetAlerting returns the BudgetAlerting field. +func (e *EnterpriseUpdateBudget) GetBudgetAlerting() *EnterpriseBudgetAlerting { + if e == nil { + return nil + } + return e.BudgetAlerting +} + +// GetBudgetAmount returns the BudgetAmount field if it's non-nil, zero value otherwise. +func (e *EnterpriseUpdateBudget) GetBudgetAmount() int { + if e == nil || e.BudgetAmount == nil { + return 0 + } + return *e.BudgetAmount +} + +// GetBudgetEntityName returns the BudgetEntityName field if it's non-nil, zero value otherwise. +func (e *EnterpriseUpdateBudget) GetBudgetEntityName() string { + if e == nil || e.BudgetEntityName == nil { + return "" + } + return *e.BudgetEntityName +} + +// GetBudgetProductSKU returns the BudgetProductSKU field if it's non-nil, zero value otherwise. +func (e *EnterpriseUpdateBudget) GetBudgetProductSKU() string { + if e == nil || e.BudgetProductSKU == nil { + return "" + } + return *e.BudgetProductSKU +} + +// GetBudgetScope returns the BudgetScope field if it's non-nil, zero value otherwise. +func (e *EnterpriseUpdateBudget) GetBudgetScope() string { + if e == nil || e.BudgetScope == nil { + return "" + } + return *e.BudgetScope +} + +// GetBudgetType returns the BudgetType field if it's non-nil, zero value otherwise. +func (e *EnterpriseUpdateBudget) GetBudgetType() string { + if e == nil || e.BudgetType == nil { + return "" + } + return *e.BudgetType +} + +// GetPreventFurtherUsage returns the PreventFurtherUsage field if it's non-nil, zero value otherwise. +func (e *EnterpriseUpdateBudget) GetPreventFurtherUsage() bool { + if e == nil || e.PreventFurtherUsage == nil { + return false + } + return *e.PreventFurtherUsage +} + +// GetDate returns the Date field. +func (e *EnterpriseUsageItem) GetDate() string { + if e == nil { + return "" + } + return e.Date +} + +// GetDiscountAmount returns the DiscountAmount field. +func (e *EnterpriseUsageItem) GetDiscountAmount() float64 { + if e == nil { + return 0 + } + return e.DiscountAmount +} + +// GetGrossAmount returns the GrossAmount field. +func (e *EnterpriseUsageItem) GetGrossAmount() float64 { + if e == nil { + return 0 + } + return e.GrossAmount +} + +// GetNetAmount returns the NetAmount field. +func (e *EnterpriseUsageItem) GetNetAmount() float64 { + if e == nil { + return 0 + } + return e.NetAmount +} + +// GetOrganizationName returns the OrganizationName field. +func (e *EnterpriseUsageItem) GetOrganizationName() string { + if e == nil { + return "" + } + return e.OrganizationName +} + +// GetPricePerUnit returns the PricePerUnit field. +func (e *EnterpriseUsageItem) GetPricePerUnit() float64 { + if e == nil { + return 0 + } + return e.PricePerUnit +} + +// GetProduct returns the Product field. +func (e *EnterpriseUsageItem) GetProduct() string { + if e == nil { + return "" + } + return e.Product +} + +// GetQuantity returns the Quantity field. +func (e *EnterpriseUsageItem) GetQuantity() float64 { + if e == nil { + return 0 + } + return e.Quantity +} + +// GetRepositoryName returns the RepositoryName field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageItem) GetRepositoryName() string { + if e == nil || e.RepositoryName == nil { + return "" + } + return *e.RepositoryName +} + +// GetSKU returns the SKU field. +func (e *EnterpriseUsageItem) GetSKU() string { + if e == nil { + return "" + } + return e.SKU +} + +// GetUnitType returns the UnitType field. +func (e *EnterpriseUsageItem) GetUnitType() string { + if e == nil { + return "" + } + return e.UnitType +} + +// GetUsageItems returns the UsageItems slice if it's non-nil, nil otherwise. +func (e *EnterpriseUsageReport) GetUsageItems() []*EnterpriseUsageItem { + if e == nil || e.UsageItems == nil { + return nil + } + return e.UsageItems +} + +// GetCostCenterID returns the CostCenterID field. +func (e *EnterpriseUsageReportOptions) GetCostCenterID() string { + if e == nil { + return "" + } + return e.CostCenterID +} + +// GetDay returns the Day field. +func (e *EnterpriseUsageReportOptions) GetDay() int { + if e == nil { + return 0 + } + return e.Day +} + +// GetMonth returns the Month field. +func (e *EnterpriseUsageReportOptions) GetMonth() int { + if e == nil { + return 0 + } + return e.Month +} + +// GetYear returns the Year field. +func (e *EnterpriseUsageReportOptions) GetYear() int { + if e == nil { + return 0 + } + return e.Year +} + +// GetDiscountAmount returns the DiscountAmount field. +func (e *EnterpriseUsageSummaryItem) GetDiscountAmount() float64 { + if e == nil { + return 0 + } + return e.DiscountAmount +} + +// GetDiscountQuantity returns the DiscountQuantity field. +func (e *EnterpriseUsageSummaryItem) GetDiscountQuantity() float64 { + if e == nil { + return 0 + } + return e.DiscountQuantity +} + +// GetGrossAmount returns the GrossAmount field. +func (e *EnterpriseUsageSummaryItem) GetGrossAmount() float64 { + if e == nil { + return 0 + } + return e.GrossAmount +} + +// GetGrossQuantity returns the GrossQuantity field. +func (e *EnterpriseUsageSummaryItem) GetGrossQuantity() float64 { + if e == nil { + return 0 + } + return e.GrossQuantity +} + +// GetNetAmount returns the NetAmount field. +func (e *EnterpriseUsageSummaryItem) GetNetAmount() float64 { + if e == nil { + return 0 + } + return e.NetAmount +} + +// GetNetQuantity returns the NetQuantity field. +func (e *EnterpriseUsageSummaryItem) GetNetQuantity() float64 { + if e == nil { + return 0 + } + return e.NetQuantity +} + +// GetPricePerUnit returns the PricePerUnit field. +func (e *EnterpriseUsageSummaryItem) GetPricePerUnit() float64 { + if e == nil { + return 0 + } + return e.PricePerUnit +} + +// GetProduct returns the Product field. +func (e *EnterpriseUsageSummaryItem) GetProduct() string { + if e == nil { + return "" + } + return e.Product +} + +// GetSKU returns the SKU field. +func (e *EnterpriseUsageSummaryItem) GetSKU() string { + if e == nil { + return "" + } + return e.SKU +} + +// GetUnitType returns the UnitType field. +func (e *EnterpriseUsageSummaryItem) GetUnitType() string { + if e == nil { + return "" + } + return e.UnitType +} + +// GetOrganization returns the Organization field. +func (e *EnterpriseUsageSummaryOptions) GetOrganization() string { + if e == nil { + return "" + } + return e.Organization +} + +// GetProduct returns the Product field. +func (e *EnterpriseUsageSummaryOptions) GetProduct() string { + if e == nil { + return "" + } + return e.Product +} + +// GetRepository returns the Repository field. +func (e *EnterpriseUsageSummaryOptions) GetRepository() string { + if e == nil { + return "" + } + return e.Repository +} + +// GetSKU returns the SKU field. +func (e *EnterpriseUsageSummaryOptions) GetSKU() string { + if e == nil { + return "" + } + return e.SKU +} + +// GetCostCenter returns the CostCenter field. +func (e *EnterpriseUsageSummaryReport) GetCostCenter() *BillingCostCenter { + if e == nil { + return nil + } + return e.CostCenter +} + +// GetEnterprise returns the Enterprise field. +func (e *EnterpriseUsageSummaryReport) GetEnterprise() string { + if e == nil { + return "" + } + return e.Enterprise +} + +// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageSummaryReport) GetOrganization() string { + if e == nil || e.Organization == nil { + return "" + } + return *e.Organization +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageSummaryReport) GetProduct() string { + if e == nil || e.Product == nil { + return "" + } + return *e.Product +} + +// GetRepository returns the Repository field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageSummaryReport) GetRepository() string { + if e == nil || e.Repository == nil { + return "" + } + return *e.Repository +} + +// GetSKU returns the SKU field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageSummaryReport) GetSKU() string { + if e == nil || e.SKU == nil { + return "" + } + return *e.SKU +} + +// GetTimePeriod returns the TimePeriod field. +func (e *EnterpriseUsageSummaryReport) GetTimePeriod() EnterpriseUsageTimePeriod { + if e == nil { + return EnterpriseUsageTimePeriod{} + } + return e.TimePeriod +} + +// GetUsageItems returns the UsageItems slice if it's non-nil, nil otherwise. +func (e *EnterpriseUsageSummaryReport) GetUsageItems() []*EnterpriseUsageSummaryItem { + if e == nil || e.UsageItems == nil { + return nil + } + return e.UsageItems +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageTimePeriod) GetDay() int { + if e == nil || e.Day == nil { + return 0 + } + return *e.Day +} + +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (e *EnterpriseUsageTimePeriod) GetMonth() int { + if e == nil || e.Month == nil { + return 0 + } + return *e.Month +} + +// GetYear returns the Year field. +func (e *EnterpriseUsageTimePeriod) GetYear() int { + if e == nil { + return 0 + } + return e.Year +} + +// GetCanAdminsBypass returns the CanAdminsBypass field if it's non-nil, zero value otherwise. +func (e *Environment) GetCanAdminsBypass() bool { + if e == nil || e.CanAdminsBypass == nil { + return false + } + return *e.CanAdminsBypass +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (e *Environment) GetCreatedAt() Timestamp { + if e == nil || e.CreatedAt == nil { + return Timestamp{} + } + return *e.CreatedAt +} + +// GetDeploymentBranchPolicy returns the DeploymentBranchPolicy field. +func (e *Environment) GetDeploymentBranchPolicy() *BranchPolicy { + if e == nil { + return nil + } + return e.DeploymentBranchPolicy +} + +// GetEnvironmentName returns the EnvironmentName field if it's non-nil, zero value otherwise. +func (e *Environment) GetEnvironmentName() string { + if e == nil || e.EnvironmentName == nil { + return "" + } + return *e.EnvironmentName +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (e *Environment) GetHTMLURL() string { + if e == nil || e.HTMLURL == nil { + return "" + } + return *e.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *Environment) GetID() int64 { + if e == nil || e.ID == nil { + return 0 + } + return *e.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (e *Environment) GetName() string { + if e == nil || e.Name == nil { + return "" + } + return *e.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (e *Environment) GetNodeID() string { + if e == nil || e.NodeID == nil { + return "" + } + return *e.NodeID +} + +// GetOwner returns the Owner field if it's non-nil, zero value otherwise. +func (e *Environment) GetOwner() string { + if e == nil || e.Owner == nil { + return "" + } + return *e.Owner +} + +// GetProtectionRules returns the ProtectionRules slice if it's non-nil, nil otherwise. +func (e *Environment) GetProtectionRules() []*ProtectionRule { + if e == nil || e.ProtectionRules == nil { + return nil + } + return e.ProtectionRules +} + +// GetRepo returns the Repo field if it's non-nil, zero value otherwise. +func (e *Environment) GetRepo() string { + if e == nil || e.Repo == nil { + return "" + } + return *e.Repo +} + +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (e *Environment) GetReviewers() []*EnvReviewers { + if e == nil || e.Reviewers == nil { + return nil + } + return e.Reviewers +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (e *Environment) GetUpdatedAt() Timestamp { + if e == nil || e.UpdatedAt == nil { + return Timestamp{} + } + return *e.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (e *Environment) GetURL() string { + if e == nil || e.URL == nil { + return "" + } + return *e.URL +} + +// GetWaitTimer returns the WaitTimer field if it's non-nil, zero value otherwise. +func (e *Environment) GetWaitTimer() int { + if e == nil || e.WaitTimer == nil { + return 0 + } + return *e.WaitTimer +} + +// GetEnvironments returns the Environments slice if it's non-nil, nil otherwise. +func (e *EnvResponse) GetEnvironments() []*Environment { + if e == nil || e.Environments == nil { + return nil + } + return e.Environments +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (e *EnvResponse) GetTotalCount() int { + if e == nil || e.TotalCount == nil { + return 0 + } + return *e.TotalCount +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *EnvReviewers) GetID() int64 { + if e == nil || e.ID == nil { + return 0 + } + return *e.ID +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (e *EnvReviewers) GetType() string { + if e == nil || e.Type == nil { + return "" + } + return *e.Type +} + +// GetCode returns the Code field. +func (e *Error) GetCode() string { + if e == nil { + return "" + } + return e.Code +} + +// GetField returns the Field field. +func (e *Error) GetField() string { + if e == nil { + return "" + } + return e.Field +} + +// GetMessage returns the Message field. +func (e *Error) GetMessage() string { + if e == nil { + return "" + } + return e.Message +} + +// GetResource returns the Resource field. +func (e *Error) GetResource() string { + if e == nil { + return "" + } + return e.Resource +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (e *ErrorBlock) GetCreatedAt() Timestamp { + if e == nil || e.CreatedAt == nil { + return Timestamp{} + } + return *e.CreatedAt +} + +// GetReason returns the Reason field. +func (e *ErrorBlock) GetReason() string { + if e == nil { + return "" + } + return e.Reason +} + +// GetBlock returns the Block field. +func (e *ErrorResponse) GetBlock() *ErrorBlock { + if e == nil { + return nil + } + return e.Block +} + +// GetDocumentationURL returns the DocumentationURL field. +func (e *ErrorResponse) GetDocumentationURL() string { + if e == nil { + return "" + } + return e.DocumentationURL +} + +// GetErrors returns the Errors slice if it's non-nil, nil otherwise. +func (e *ErrorResponse) GetErrors() []Error { + if e == nil || e.Errors == nil { + return nil + } + return e.Errors +} + +// GetMessage returns the Message field. +func (e *ErrorResponse) GetMessage() string { + if e == nil { + return "" + } + return e.Message +} + +// GetActor returns the Actor field. +func (e *Event) GetActor() *User { + if e == nil { + return nil + } + return e.Actor +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (e *Event) GetCreatedAt() Timestamp { + if e == nil || e.CreatedAt == nil { + return Timestamp{} + } + return *e.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (e *Event) GetID() string { + if e == nil || e.ID == nil { + return "" + } + return *e.ID +} + +// GetOrg returns the Org field. +func (e *Event) GetOrg() *Organization { + if e == nil { + return nil + } + return e.Org +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (e *Event) GetPublic() bool { + if e == nil || e.Public == nil { + return false + } + return *e.Public +} + +// GetRawPayload returns the RawPayload field if it's non-nil, zero value otherwise. +func (e *Event) GetRawPayload() json.RawMessage { + if e == nil || e.RawPayload == nil { + return json.RawMessage{} + } + return *e.RawPayload +} + +// GetRepo returns the Repo field. +func (e *Event) GetRepo() *Repository { + if e == nil { + return nil + } + return e.Repo +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (e *Event) GetType() string { + if e == nil || e.Type == nil { + return "" + } + return *e.Type +} + +// GetGroupID returns the GroupID field if it's non-nil, zero value otherwise. +func (e *ExternalGroup) GetGroupID() int64 { + if e == nil || e.GroupID == nil { + return 0 + } + return *e.GroupID +} + +// GetGroupName returns the GroupName field if it's non-nil, zero value otherwise. +func (e *ExternalGroup) GetGroupName() string { + if e == nil || e.GroupName == nil { + return "" + } + return *e.GroupName +} + +// GetMembers returns the Members slice if it's non-nil, nil otherwise. +func (e *ExternalGroup) GetMembers() []*ExternalGroupMember { + if e == nil || e.Members == nil { + return nil + } + return e.Members +} + +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (e *ExternalGroup) GetTeams() []*ExternalGroupTeam { + if e == nil || e.Teams == nil { + return nil + } + return e.Teams +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (e *ExternalGroup) GetUpdatedAt() Timestamp { + if e == nil || e.UpdatedAt == nil { + return Timestamp{} + } + return *e.UpdatedAt +} + +// GetGroups returns the Groups slice if it's non-nil, nil otherwise. +func (e *ExternalGroupList) GetGroups() []*ExternalGroup { + if e == nil || e.Groups == nil { + return nil + } + return e.Groups +} + +// GetMemberEmail returns the MemberEmail field if it's non-nil, zero value otherwise. +func (e *ExternalGroupMember) GetMemberEmail() string { + if e == nil || e.MemberEmail == nil { + return "" + } + return *e.MemberEmail +} + +// GetMemberID returns the MemberID field if it's non-nil, zero value otherwise. +func (e *ExternalGroupMember) GetMemberID() int64 { + if e == nil || e.MemberID == nil { + return 0 + } + return *e.MemberID +} + +// GetMemberLogin returns the MemberLogin field if it's non-nil, zero value otherwise. +func (e *ExternalGroupMember) GetMemberLogin() string { + if e == nil || e.MemberLogin == nil { + return "" + } + return *e.MemberLogin +} + +// GetMemberName returns the MemberName field if it's non-nil, zero value otherwise. +func (e *ExternalGroupMember) GetMemberName() string { + if e == nil || e.MemberName == nil { + return "" + } + return *e.MemberName +} + +// GetTeamID returns the TeamID field if it's non-nil, zero value otherwise. +func (e *ExternalGroupTeam) GetTeamID() int64 { + if e == nil || e.TeamID == nil { + return 0 + } + return *e.TeamID +} + +// GetTeamName returns the TeamName field if it's non-nil, zero value otherwise. +func (e *ExternalGroupTeam) GetTeamName() string { + if e == nil || e.TeamName == nil { + return "" + } + return *e.TeamName +} + +// GetHRef returns the HRef field if it's non-nil, zero value otherwise. +func (f *FeedLink) GetHRef() string { + if f == nil || f.HRef == nil { + return "" + } + return *f.HRef +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (f *FeedLink) GetType() string { + if f == nil || f.Type == nil { + return "" + } + return *f.Type +} + +// GetCurrentUser returns the CurrentUser field. +func (f *FeedLinks) GetCurrentUser() *FeedLink { + if f == nil { + return nil + } + return f.CurrentUser +} + +// GetCurrentUserActor returns the CurrentUserActor field. +func (f *FeedLinks) GetCurrentUserActor() *FeedLink { + if f == nil { + return nil + } + return f.CurrentUserActor +} + +// GetCurrentUserOrganization returns the CurrentUserOrganization field. +func (f *FeedLinks) GetCurrentUserOrganization() *FeedLink { + if f == nil { + return nil + } + return f.CurrentUserOrganization +} + +// GetCurrentUserOrganizations returns the CurrentUserOrganizations slice if it's non-nil, nil otherwise. +func (f *FeedLinks) GetCurrentUserOrganizations() []*FeedLink { + if f == nil || f.CurrentUserOrganizations == nil { + return nil + } + return f.CurrentUserOrganizations +} + +// GetCurrentUserPublic returns the CurrentUserPublic field. +func (f *FeedLinks) GetCurrentUserPublic() *FeedLink { + if f == nil { + return nil + } + return f.CurrentUserPublic +} + +// GetTimeline returns the Timeline field. +func (f *FeedLinks) GetTimeline() *FeedLink { + if f == nil { + return nil + } + return f.Timeline +} + +// GetUser returns the User field. +func (f *FeedLinks) GetUser() *FeedLink { + if f == nil { + return nil + } + return f.User +} + +// GetCurrentUserActorURL returns the CurrentUserActorURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserActorURL() string { + if f == nil || f.CurrentUserActorURL == nil { + return "" + } + return *f.CurrentUserActorURL +} + +// GetCurrentUserOrganizationURL returns the CurrentUserOrganizationURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserOrganizationURL() string { + if f == nil || f.CurrentUserOrganizationURL == nil { + return "" + } + return *f.CurrentUserOrganizationURL +} + +// GetCurrentUserOrganizationURLs returns the CurrentUserOrganizationURLs slice if it's non-nil, nil otherwise. +func (f *Feeds) GetCurrentUserOrganizationURLs() []string { + if f == nil || f.CurrentUserOrganizationURLs == nil { + return nil + } + return f.CurrentUserOrganizationURLs +} + +// GetCurrentUserPublicURL returns the CurrentUserPublicURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserPublicURL() string { + if f == nil || f.CurrentUserPublicURL == nil { + return "" + } + return *f.CurrentUserPublicURL +} + +// GetCurrentUserURL returns the CurrentUserURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetCurrentUserURL() string { + if f == nil || f.CurrentUserURL == nil { + return "" + } + return *f.CurrentUserURL +} + +// GetLinks returns the Links field. +func (f *Feeds) GetLinks() *FeedLinks { + if f == nil { + return nil + } + return f.Links +} + +// GetTimelineURL returns the TimelineURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetTimelineURL() string { + if f == nil || f.TimelineURL == nil { + return "" + } + return *f.TimelineURL +} + +// GetUserURL returns the UserURL field if it's non-nil, zero value otherwise. +func (f *Feeds) GetUserURL() string { + if f == nil || f.UserURL == nil { + return "" + } + return *f.UserURL +} + +// GetFieldName returns the FieldName field if it's non-nil, zero value otherwise. +func (f *FieldValue) GetFieldName() string { + if f == nil || f.FieldName == nil { + return "" + } + return *f.FieldName +} + +// GetFieldNodeID returns the FieldNodeID field if it's non-nil, zero value otherwise. +func (f *FieldValue) GetFieldNodeID() string { + if f == nil || f.FieldNodeID == nil { + return "" + } + return *f.FieldNodeID +} + +// GetFieldType returns the FieldType field if it's non-nil, zero value otherwise. +func (f *FieldValue) GetFieldType() string { + if f == nil || f.FieldType == nil { + return "" + } + return *f.FieldType +} + +// GetFrom returns the From field. +func (f *FieldValue) GetFrom() json.RawMessage { + if f == nil { + return json.RawMessage{} + } + return f.From +} + +// GetProjectNumber returns the ProjectNumber field if it's non-nil, zero value otherwise. +func (f *FieldValue) GetProjectNumber() int64 { + if f == nil || f.ProjectNumber == nil { + return 0 + } + return *f.ProjectNumber +} + +// GetTo returns the To field. +func (f *FieldValue) GetTo() json.RawMessage { + if f == nil { + return json.RawMessage{} + } + return f.To +} + +// GetParameters returns the Parameters field. +func (f *FileExtensionRestrictionBranchRule) GetParameters() FileExtensionRestrictionRuleParameters { + if f == nil { + return FileExtensionRestrictionRuleParameters{} + } + return f.Parameters +} + +// GetRestrictedFileExtensions returns the RestrictedFileExtensions slice if it's non-nil, nil otherwise. +func (f *FileExtensionRestrictionRuleParameters) GetRestrictedFileExtensions() []string { + if f == nil || f.RestrictedFileExtensions == nil { + return nil + } + return f.RestrictedFileExtensions +} + +// GetParameters returns the Parameters field. +func (f *FilePathRestrictionBranchRule) GetParameters() FilePathRestrictionRuleParameters { + if f == nil { + return FilePathRestrictionRuleParameters{} + } + return f.Parameters +} + +// GetRestrictedFilePaths returns the RestrictedFilePaths slice if it's non-nil, nil otherwise. +func (f *FilePathRestrictionRuleParameters) GetRestrictedFilePaths() []string { + if f == nil || f.RestrictedFilePaths == nil { + return nil + } + return f.RestrictedFilePaths +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (f *FineGrainedPersonalAccessTokenRequest) GetCreatedAt() Timestamp { + if f == nil || f.CreatedAt == nil { + return Timestamp{} + } + return *f.CreatedAt +} + +// GetID returns the ID field. +func (f *FineGrainedPersonalAccessTokenRequest) GetID() int64 { + if f == nil { + return 0 + } + return f.ID +} + +// GetOwner returns the Owner field. +func (f *FineGrainedPersonalAccessTokenRequest) GetOwner() User { + if f == nil { + return User{} + } + return f.Owner +} + +// GetPermissions returns the Permissions field. +func (f *FineGrainedPersonalAccessTokenRequest) GetPermissions() PersonalAccessTokenPermissions { + if f == nil { + return PersonalAccessTokenPermissions{} + } + return f.Permissions +} + +// GetReason returns the Reason field. +func (f *FineGrainedPersonalAccessTokenRequest) GetReason() string { + if f == nil { + return "" + } + return f.Reason +} + +// GetRepositoriesURL returns the RepositoriesURL field. +func (f *FineGrainedPersonalAccessTokenRequest) GetRepositoriesURL() string { + if f == nil { + return "" + } + return f.RepositoriesURL +} + +// GetRepositorySelection returns the RepositorySelection field. +func (f *FineGrainedPersonalAccessTokenRequest) GetRepositorySelection() string { + if f == nil { + return "" + } + return f.RepositorySelection +} + +// GetTokenExpired returns the TokenExpired field. +func (f *FineGrainedPersonalAccessTokenRequest) GetTokenExpired() bool { + if f == nil { + return false + } + return f.TokenExpired +} + +// GetTokenExpiresAt returns the TokenExpiresAt field if it's non-nil, zero value otherwise. +func (f *FineGrainedPersonalAccessTokenRequest) GetTokenExpiresAt() Timestamp { + if f == nil || f.TokenExpiresAt == nil { + return Timestamp{} + } + return *f.TokenExpiresAt +} + +// GetTokenID returns the TokenID field. +func (f *FineGrainedPersonalAccessTokenRequest) GetTokenID() int64 { + if f == nil { + return 0 + } + return f.TokenID +} + +// GetTokenLastUsedAt returns the TokenLastUsedAt field if it's non-nil, zero value otherwise. +func (f *FineGrainedPersonalAccessTokenRequest) GetTokenLastUsedAt() Timestamp { + if f == nil || f.TokenLastUsedAt == nil { + return Timestamp{} + } + return *f.TokenLastUsedAt +} + +// GetTokenName returns the TokenName field. +func (f *FineGrainedPersonalAccessTokenRequest) GetTokenName() string { + if f == nil { + return "" + } + return f.TokenName +} + +// GetIdentifier returns the Identifier field if it's non-nil, zero value otherwise. +func (f *FirstPatchedVersion) GetIdentifier() string { + if f == nil || f.Identifier == nil { + return "" + } + return *f.Identifier +} + +// GetForkee returns the Forkee field. +func (f *ForkEvent) GetForkee() *Repository { + if f == nil { + return nil + } + return f.Forkee +} + +// GetInstallation returns the Installation field. +func (f *ForkEvent) GetInstallation() *Installation { + if f == nil { + return nil + } + return f.Installation +} + +// GetRepo returns the Repo field. +func (f *ForkEvent) GetRepo() *Repository { + if f == nil { + return nil + } + return f.Repo +} + +// GetSender returns the Sender field. +func (f *ForkEvent) GetSender() *User { + if f == nil { + return nil + } + return f.Sender +} + +// GetConfigurationFilePath returns the ConfigurationFilePath field if it's non-nil, zero value otherwise. +func (g *GenerateNotesRequest) GetConfigurationFilePath() string { + if g == nil || g.ConfigurationFilePath == nil { + return "" + } + return *g.ConfigurationFilePath +} + +// GetPreviousTagName returns the PreviousTagName field if it's non-nil, zero value otherwise. +func (g *GenerateNotesRequest) GetPreviousTagName() string { + if g == nil || g.PreviousTagName == nil { + return "" + } + return *g.PreviousTagName +} + +// GetTagName returns the TagName field. +func (g *GenerateNotesRequest) GetTagName() string { + if g == nil { + return "" + } + return g.TagName +} + +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (g *GenerateNotesRequest) GetTargetCommitish() string { + if g == nil || g.TargetCommitish == nil { + return "" + } + return *g.TargetCommitish +} + +// GetInclude returns the Include field if it's non-nil, zero value otherwise. +func (g *GetAuditLogOptions) GetInclude() string { + if g == nil || g.Include == nil { + return "" + } + return *g.Include +} + +// GetOrder returns the Order field if it's non-nil, zero value otherwise. +func (g *GetAuditLogOptions) GetOrder() string { + if g == nil || g.Order == nil { + return "" + } + return *g.Order +} + +// GetPhrase returns the Phrase field if it's non-nil, zero value otherwise. +func (g *GetAuditLogOptions) GetPhrase() string { + if g == nil || g.Phrase == nil { + return "" + } + return *g.Phrase +} + +// GetRef returns the Ref field. +func (g *GetCodeownersErrorsOptions) GetRef() string { + if g == nil { + return "" + } + return g.Ref +} + +// GetFields returns the Fields slice if it's non-nil, nil otherwise. +func (g *GetProjectItemOptions) GetFields() []int64 { + if g == nil || g.Fields == nil { + return nil + } + return g.Fields +} + +// GetExcludedAttributes returns the ExcludedAttributes field if it's non-nil, zero value otherwise. +func (g *GetProvisionedSCIMGroupEnterpriseOptions) GetExcludedAttributes() string { + if g == nil || g.ExcludedAttributes == nil { + return "" + } + return *g.ExcludedAttributes +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (g *Gist) GetComments() int { + if g == nil || g.Comments == nil { + return 0 + } + return *g.Comments +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *Gist) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (g *Gist) GetDescription() string { + if g == nil || g.Description == nil { + return "" + } + return *g.Description +} + +// GetFiles returns the Files map if it's non-nil, an empty map otherwise. +func (g *Gist) GetFiles() map[GistFilename]GistFile { + if g == nil || g.Files == nil { + return map[GistFilename]GistFile{} + } + return g.Files +} + +// GetGitPullURL returns the GitPullURL field if it's non-nil, zero value otherwise. +func (g *Gist) GetGitPullURL() string { + if g == nil || g.GitPullURL == nil { + return "" + } + return *g.GitPullURL +} + +// GetGitPushURL returns the GitPushURL field if it's non-nil, zero value otherwise. +func (g *Gist) GetGitPushURL() string { + if g == nil || g.GitPushURL == nil { + return "" + } + return *g.GitPushURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (g *Gist) GetHTMLURL() string { + if g == nil || g.HTMLURL == nil { + return "" + } + return *g.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *Gist) GetID() string { + if g == nil || g.ID == nil { + return "" + } + return *g.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (g *Gist) GetNodeID() string { + if g == nil || g.NodeID == nil { + return "" + } + return *g.NodeID +} + +// GetOwner returns the Owner field. +func (g *Gist) GetOwner() *User { + if g == nil { + return nil + } + return g.Owner +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (g *Gist) GetPublic() bool { + if g == nil || g.Public == nil { + return false + } + return *g.Public +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *Gist) GetUpdatedAt() Timestamp { + if g == nil || g.UpdatedAt == nil { + return Timestamp{} + } + return *g.UpdatedAt +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (g *GistComment) GetBody() string { + if g == nil || g.Body == nil { + return "" + } + return *g.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *GistComment) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GistComment) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GistComment) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetUser returns the User field. +func (g *GistComment) GetUser() *User { + if g == nil { + return nil + } + return g.User +} + +// GetChangeStatus returns the ChangeStatus field. +func (g *GistCommit) GetChangeStatus() *CommitStats { + if g == nil { + return nil + } + return g.ChangeStatus +} + +// GetCommittedAt returns the CommittedAt field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetCommittedAt() Timestamp { + if g == nil || g.CommittedAt == nil { + return Timestamp{} + } + return *g.CommittedAt +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetNodeID() string { + if g == nil || g.NodeID == nil { + return "" + } + return *g.NodeID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetUser returns the User field. +func (g *GistCommit) GetUser() *User { + if g == nil { + return nil + } + return g.User +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (g *GistCommit) GetVersion() string { + if g == nil || g.Version == nil { + return "" + } + return *g.Version +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (g *GistFile) GetContent() string { + if g == nil || g.Content == nil { + return "" + } + return *g.Content +} + +// GetFilename returns the Filename field if it's non-nil, zero value otherwise. +func (g *GistFile) GetFilename() string { + if g == nil || g.Filename == nil { + return "" + } + return *g.Filename +} + +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (g *GistFile) GetLanguage() string { + if g == nil || g.Language == nil { + return "" + } + return *g.Language +} + +// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. +func (g *GistFile) GetRawURL() string { + if g == nil || g.RawURL == nil { + return "" + } + return *g.RawURL +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (g *GistFile) GetSize() int { + if g == nil || g.Size == nil { + return 0 + } + return *g.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (g *GistFile) GetType() string { + if g == nil || g.Type == nil { + return "" + } + return *g.Type +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *GistFork) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GistFork) GetID() string { + if g == nil || g.ID == nil { + return "" + } + return *g.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (g *GistFork) GetNodeID() string { + if g == nil || g.NodeID == nil { + return "" + } + return *g.NodeID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *GistFork) GetUpdatedAt() Timestamp { + if g == nil || g.UpdatedAt == nil { + return Timestamp{} + } + return *g.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GistFork) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetUser returns the User field. +func (g *GistFork) GetUser() *User { + if g == nil { + return nil + } + return g.User +} + +// GetSince returns the Since field. +func (g *GistListOptions) GetSince() time.Time { + if g == nil { + return time.Time{} + } + return g.Since +} + +// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. +func (g *GistStats) GetPrivateGists() int { + if g == nil || g.PrivateGists == nil { + return 0 + } + return *g.PrivateGists +} + +// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. +func (g *GistStats) GetPublicGists() int { + if g == nil || g.PublicGists == nil { + return 0 + } + return *g.PublicGists +} + +// GetTotalGists returns the TotalGists field if it's non-nil, zero value otherwise. +func (g *GistStats) GetTotalGists() int { + if g == nil || g.TotalGists == nil { + return 0 + } + return *g.TotalGists +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (g *GitHubAppAuthorizationEvent) GetAction() string { + if g == nil || g.Action == nil { + return "" + } + return *g.Action +} + +// GetInstallation returns the Installation field. +func (g *GitHubAppAuthorizationEvent) GetInstallation() *Installation { + if g == nil { + return nil + } + return g.Installation +} + +// GetSender returns the Sender field. +func (g *GitHubAppAuthorizationEvent) GetSender() *User { + if g == nil { + return nil + } + return g.Sender +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (g *Gitignore) GetName() string { + if g == nil || g.Name == nil { + return "" + } + return *g.Name +} + +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (g *Gitignore) GetSource() string { + if g == nil || g.Source == nil { + return "" + } + return *g.Source +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (g *GitObject) GetSHA() string { + if g == nil || g.SHA == nil { + return "" + } + return *g.SHA +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (g *GitObject) GetType() string { + if g == nil || g.Type == nil { + return "" + } + return *g.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *GitObject) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetCredits returns the Credits slice if it's non-nil, nil otherwise. +func (g *GlobalSecurityAdvisory) GetCredits() []*Credit { + if g == nil || g.Credits == nil { + return nil + } + return g.Credits +} + +// GetGithubReviewedAt returns the GithubReviewedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetGithubReviewedAt() Timestamp { + if g == nil || g.GithubReviewedAt == nil { + return Timestamp{} + } + return *g.GithubReviewedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetNVDPublishedAt returns the NVDPublishedAt field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetNVDPublishedAt() Timestamp { + if g == nil || g.NVDPublishedAt == nil { + return Timestamp{} + } + return *g.NVDPublishedAt +} + +// GetReferences returns the References slice if it's non-nil, nil otherwise. +func (g *GlobalSecurityAdvisory) GetReferences() []string { + if g == nil || g.References == nil { + return nil + } + return g.References +} + +// GetRepositoryAdvisoryURL returns the RepositoryAdvisoryURL field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetRepositoryAdvisoryURL() string { + if g == nil || g.RepositoryAdvisoryURL == nil { + return "" + } + return *g.RepositoryAdvisoryURL +} + +// GetSourceCodeLocation returns the SourceCodeLocation field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetSourceCodeLocation() string { + if g == nil || g.SourceCodeLocation == nil { + return "" + } + return *g.SourceCodeLocation +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityAdvisory) GetType() string { + if g == nil || g.Type == nil { + return "" + } + return *g.Type +} + +// GetVulnerabilities returns the Vulnerabilities slice if it's non-nil, nil otherwise. +func (g *GlobalSecurityAdvisory) GetVulnerabilities() []*GlobalSecurityVulnerability { + if g == nil || g.Vulnerabilities == nil { + return nil + } + return g.Vulnerabilities +} + +// GetFirstPatchedVersion returns the FirstPatchedVersion field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityVulnerability) GetFirstPatchedVersion() string { + if g == nil || g.FirstPatchedVersion == nil { + return "" + } + return *g.FirstPatchedVersion +} + +// GetPackage returns the Package field. +func (g *GlobalSecurityVulnerability) GetPackage() *VulnerabilityPackage { + if g == nil { + return nil + } + return g.Package +} + +// GetVulnerableFunctions returns the VulnerableFunctions slice if it's non-nil, nil otherwise. +func (g *GlobalSecurityVulnerability) GetVulnerableFunctions() []string { + if g == nil || g.VulnerableFunctions == nil { + return nil + } + return g.VulnerableFunctions +} + +// GetVulnerableVersionRange returns the VulnerableVersionRange field if it's non-nil, zero value otherwise. +func (g *GlobalSecurityVulnerability) GetVulnerableVersionRange() string { + if g == nil || g.VulnerableVersionRange == nil { + return "" + } + return *g.VulnerableVersionRange +} + +// GetInstallation returns the Installation field. +func (g *GollumEvent) GetInstallation() *Installation { + if g == nil { + return nil + } + return g.Installation +} + +// GetOrg returns the Org field. +func (g *GollumEvent) GetOrg() *Organization { + if g == nil { + return nil + } + return g.Org +} + +// GetPages returns the Pages slice if it's non-nil, nil otherwise. +func (g *GollumEvent) GetPages() []*Page { + if g == nil || g.Pages == nil { + return nil + } + return g.Pages +} + +// GetRepo returns the Repo field. +func (g *GollumEvent) GetRepo() *Repository { + if g == nil { + return nil + } + return g.Repo +} + +// GetSender returns the Sender field. +func (g *GollumEvent) GetSender() *User { + if g == nil { + return nil + } + return g.Sender +} + +// GetBucket returns the Bucket field. +func (g *GoogleCloudConfig) GetBucket() string { + if g == nil { + return "" + } + return g.Bucket +} + +// GetEncryptedJSONCredentials returns the EncryptedJSONCredentials field. +func (g *GoogleCloudConfig) GetEncryptedJSONCredentials() string { + if g == nil { + return "" + } + return g.EncryptedJSONCredentials +} + +// GetKeyID returns the KeyID field. +func (g *GoogleCloudConfig) GetKeyID() string { + if g == nil { + return "" + } + return g.KeyID +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (g *GPGEmail) GetEmail() string { + if g == nil || g.Email == nil { + return "" + } + return *g.Email +} + +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (g *GPGEmail) GetVerified() bool { + if g == nil || g.Verified == nil { + return false + } + return *g.Verified +} + +// GetCanCertify returns the CanCertify field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanCertify() bool { + if g == nil || g.CanCertify == nil { + return false + } + return *g.CanCertify +} + +// GetCanEncryptComms returns the CanEncryptComms field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanEncryptComms() bool { + if g == nil || g.CanEncryptComms == nil { + return false + } + return *g.CanEncryptComms +} + +// GetCanEncryptStorage returns the CanEncryptStorage field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanEncryptStorage() bool { + if g == nil || g.CanEncryptStorage == nil { + return false + } + return *g.CanEncryptStorage +} + +// GetCanSign returns the CanSign field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCanSign() bool { + if g == nil || g.CanSign == nil { + return false + } + return *g.CanSign +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetEmails returns the Emails slice if it's non-nil, nil otherwise. +func (g *GPGKey) GetEmails() []*GPGEmail { + if g == nil || g.Emails == nil { + return nil + } + return g.Emails +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetExpiresAt() Timestamp { + if g == nil || g.ExpiresAt == nil { + return Timestamp{} + } + return *g.ExpiresAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetKeyID() string { + if g == nil || g.KeyID == nil { + return "" + } + return *g.KeyID +} + +// GetPrimaryKeyID returns the PrimaryKeyID field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetPrimaryKeyID() int64 { + if g == nil || g.PrimaryKeyID == nil { + return 0 + } + return *g.PrimaryKeyID +} + +// GetPublicKey returns the PublicKey field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetPublicKey() string { + if g == nil || g.PublicKey == nil { + return "" + } + return *g.PublicKey +} + +// GetRawKey returns the RawKey field if it's non-nil, zero value otherwise. +func (g *GPGKey) GetRawKey() string { + if g == nil || g.RawKey == nil { + return "" + } + return *g.RawKey +} + +// GetSubkeys returns the Subkeys slice if it's non-nil, nil otherwise. +func (g *GPGKey) GetSubkeys() []*GPGKey { + if g == nil || g.Subkeys == nil { + return nil + } + return g.Subkeys +} + +// GetApp returns the App field. +func (g *Grant) GetApp() *AuthorizationApp { + if g == nil { + return nil + } + return g.App +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (g *Grant) GetCreatedAt() Timestamp { + if g == nil || g.CreatedAt == nil { + return Timestamp{} + } + return *g.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *Grant) GetID() int64 { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (g *Grant) GetScopes() []string { + if g == nil || g.Scopes == nil { + return nil + } + return g.Scopes +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (g *Grant) GetUpdatedAt() Timestamp { + if g == nil || g.UpdatedAt == nil { + return Timestamp{} + } + return *g.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (g *Grant) GetURL() string { + if g == nil || g.URL == nil { + return "" + } + return *g.URL +} + +// GetAdded returns the Added slice if it's non-nil, nil otherwise. +func (h *HeadCommit) GetAdded() []string { + if h == nil || h.Added == nil { + return nil + } + return h.Added +} + +// GetAuthor returns the Author field. +func (h *HeadCommit) GetAuthor() *CommitAuthor { + if h == nil { + return nil + } + return h.Author +} + +// GetCommitter returns the Committer field. +func (h *HeadCommit) GetCommitter() *CommitAuthor { + if h == nil { + return nil + } + return h.Committer +} + +// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetDistinct() bool { + if h == nil || h.Distinct == nil { + return false + } + return *h.Distinct +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetID() string { + if h == nil || h.ID == nil { + return "" + } + return *h.ID +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetMessage() string { + if h == nil || h.Message == nil { + return "" + } + return *h.Message +} + +// GetModified returns the Modified slice if it's non-nil, nil otherwise. +func (h *HeadCommit) GetModified() []string { + if h == nil || h.Modified == nil { + return nil + } + return h.Modified +} + +// GetRemoved returns the Removed slice if it's non-nil, nil otherwise. +func (h *HeadCommit) GetRemoved() []string { + if h == nil || h.Removed == nil { + return nil + } + return h.Removed +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetSHA() string { + if h == nil || h.SHA == nil { + return "" + } + return *h.SHA +} + +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetTimestamp() Timestamp { + if h == nil || h.Timestamp == nil { + return Timestamp{} + } + return *h.Timestamp +} + +// GetTreeID returns the TreeID field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetTreeID() string { + if h == nil || h.TreeID == nil { + return "" + } + return *h.TreeID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (h *HeadCommit) GetURL() string { + if h == nil || h.URL == nil { + return "" + } + return *h.URL +} + +// GetDomain returns the Domain field. +func (h *HecConfig) GetDomain() string { + if h == nil { + return "" + } + return h.Domain +} + +// GetEncryptedToken returns the EncryptedToken field. +func (h *HecConfig) GetEncryptedToken() string { + if h == nil { + return "" + } + return h.EncryptedToken +} + +// GetKeyID returns the KeyID field. +func (h *HecConfig) GetKeyID() string { + if h == nil { + return "" + } + return h.KeyID +} + +// GetPath returns the Path field. +func (h *HecConfig) GetPath() string { + if h == nil { + return "" + } + return h.Path +} + +// GetPort returns the Port field. +func (h *HecConfig) GetPort() uint16 { + if h == nil { + return 0 + } + return h.Port +} + +// GetSSLVerify returns the SSLVerify field. +func (h *HecConfig) GetSSLVerify() bool { + if h == nil { + return false + } + return h.SSLVerify +} + +// GetActive returns the Active field if it's non-nil, zero value otherwise. +func (h *Hook) GetActive() bool { + if h == nil || h.Active == nil { + return false + } + return *h.Active +} + +// GetConfig returns the Config field. +func (h *Hook) GetConfig() *HookConfig { + if h == nil { + return nil + } + return h.Config +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (h *Hook) GetCreatedAt() Timestamp { + if h == nil || h.CreatedAt == nil { + return Timestamp{} + } + return *h.CreatedAt +} + +// GetEvents returns the Events slice if it's non-nil, nil otherwise. +func (h *Hook) GetEvents() []string { + if h == nil || h.Events == nil { + return nil + } + return h.Events +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *Hook) GetID() int64 { + if h == nil || h.ID == nil { + return 0 + } + return *h.ID +} + +// GetLastResponse returns the LastResponse map if it's non-nil, an empty map otherwise. +func (h *Hook) GetLastResponse() map[string]any { + if h == nil || h.LastResponse == nil { + return map[string]any{} + } + return h.LastResponse +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (h *Hook) GetName() string { + if h == nil || h.Name == nil { + return "" + } + return *h.Name +} + +// GetPingURL returns the PingURL field if it's non-nil, zero value otherwise. +func (h *Hook) GetPingURL() string { + if h == nil || h.PingURL == nil { + return "" + } + return *h.PingURL +} + +// GetTestURL returns the TestURL field if it's non-nil, zero value otherwise. +func (h *Hook) GetTestURL() string { + if h == nil || h.TestURL == nil { + return "" + } + return *h.TestURL +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (h *Hook) GetType() string { + if h == nil || h.Type == nil { + return "" + } + return *h.Type +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (h *Hook) GetUpdatedAt() Timestamp { + if h == nil || h.UpdatedAt == nil { + return Timestamp{} + } + return *h.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (h *Hook) GetURL() string { + if h == nil || h.URL == nil { + return "" + } + return *h.URL +} + +// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. +func (h *HookConfig) GetContentType() string { + if h == nil || h.ContentType == nil { + return "" + } + return *h.ContentType +} + +// GetInsecureSSL returns the InsecureSSL field if it's non-nil, zero value otherwise. +func (h *HookConfig) GetInsecureSSL() string { + if h == nil || h.InsecureSSL == nil { + return "" + } + return *h.InsecureSSL +} + +// GetSecret returns the Secret field if it's non-nil, zero value otherwise. +func (h *HookConfig) GetSecret() string { + if h == nil || h.Secret == nil { + return "" + } + return *h.Secret +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (h *HookConfig) GetURL() string { + if h == nil || h.URL == nil { + return "" + } + return *h.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetAction() string { + if h == nil || h.Action == nil { + return "" + } + return *h.Action +} + +// GetDeliveredAt returns the DeliveredAt field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetDeliveredAt() Timestamp { + if h == nil || h.DeliveredAt == nil { + return Timestamp{} + } + return *h.DeliveredAt +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetDuration() float64 { + if h == nil || h.Duration == nil { + return 0 + } + return *h.Duration +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetEvent() string { + if h == nil || h.Event == nil { + return "" + } + return *h.Event +} + +// GetGUID returns the GUID field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetGUID() string { + if h == nil || h.GUID == nil { + return "" + } + return *h.GUID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetID() int64 { + if h == nil || h.ID == nil { + return 0 + } + return *h.ID +} + +// GetInstallationID returns the InstallationID field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetInstallationID() int64 { + if h == nil || h.InstallationID == nil { + return 0 + } + return *h.InstallationID +} + +// GetRedelivery returns the Redelivery field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetRedelivery() bool { + if h == nil || h.Redelivery == nil { + return false + } + return *h.Redelivery +} + +// GetRepositoryID returns the RepositoryID field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetRepositoryID() int64 { + if h == nil || h.RepositoryID == nil { + return 0 + } + return *h.RepositoryID +} + +// GetRequest returns the Request field. +func (h *HookDelivery) GetRequest() *HookRequest { + if h == nil { + return nil + } + return h.Request +} + +// GetResponse returns the Response field. +func (h *HookDelivery) GetResponse() *HookResponse { + if h == nil { + return nil + } + return h.Response +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetStatus() string { + if h == nil || h.Status == nil { + return "" + } + return *h.Status +} + +// GetStatusCode returns the StatusCode field if it's non-nil, zero value otherwise. +func (h *HookDelivery) GetStatusCode() int { + if h == nil || h.StatusCode == nil { + return 0 + } + return *h.StatusCode +} + +// GetHeaders returns the Headers map if it's non-nil, an empty map otherwise. +func (h *HookRequest) GetHeaders() map[string]string { + if h == nil || h.Headers == nil { + return map[string]string{} + } + return h.Headers +} + +// GetRawPayload returns the RawPayload field if it's non-nil, zero value otherwise. +func (h *HookRequest) GetRawPayload() json.RawMessage { + if h == nil || h.RawPayload == nil { + return json.RawMessage{} + } + return *h.RawPayload +} + +// GetHeaders returns the Headers map if it's non-nil, an empty map otherwise. +func (h *HookResponse) GetHeaders() map[string]string { + if h == nil || h.Headers == nil { + return map[string]string{} + } + return h.Headers +} + +// GetRawPayload returns the RawPayload field if it's non-nil, zero value otherwise. +func (h *HookResponse) GetRawPayload() json.RawMessage { + if h == nil || h.RawPayload == nil { + return json.RawMessage{} + } + return *h.RawPayload +} + +// GetActiveHooks returns the ActiveHooks field if it's non-nil, zero value otherwise. +func (h *HookStats) GetActiveHooks() int { + if h == nil || h.ActiveHooks == nil { + return 0 + } + return *h.ActiveHooks +} + +// GetInactiveHooks returns the InactiveHooks field if it's non-nil, zero value otherwise. +func (h *HookStats) GetInactiveHooks() int { + if h == nil || h.InactiveHooks == nil { + return 0 + } + return *h.InactiveHooks +} + +// GetTotalHooks returns the TotalHooks field if it's non-nil, zero value otherwise. +func (h *HookStats) GetTotalHooks() int { + if h == nil || h.TotalHooks == nil { + return 0 + } + return *h.TotalHooks +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetID() int64 { + if h == nil || h.ID == nil { + return 0 + } + return *h.ID +} + +// GetImageDetails returns the ImageDetails field. +func (h *HostedRunner) GetImageDetails() *HostedRunnerImageDetail { + if h == nil { + return nil + } + return h.ImageDetails +} + +// GetLastActiveOn returns the LastActiveOn field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetLastActiveOn() Timestamp { + if h == nil || h.LastActiveOn == nil { + return Timestamp{} + } + return *h.LastActiveOn +} + +// GetMachineSizeDetails returns the MachineSizeDetails field. +func (h *HostedRunner) GetMachineSizeDetails() *HostedRunnerMachineSpec { + if h == nil { + return nil + } + return h.MachineSizeDetails +} + +// GetMaximumRunners returns the MaximumRunners field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetMaximumRunners() int64 { + if h == nil || h.MaximumRunners == nil { + return 0 + } + return *h.MaximumRunners +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetName() string { + if h == nil || h.Name == nil { + return "" + } + return *h.Name +} + +// GetPlatform returns the Platform field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetPlatform() string { + if h == nil || h.Platform == nil { + return "" + } + return *h.Platform +} + +// GetPublicIPEnabled returns the PublicIPEnabled field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetPublicIPEnabled() bool { + if h == nil || h.PublicIPEnabled == nil { + return false + } + return *h.PublicIPEnabled +} + +// GetPublicIPs returns the PublicIPs slice if it's non-nil, nil otherwise. +func (h *HostedRunner) GetPublicIPs() []*HostedRunnerPublicIP { + if h == nil || h.PublicIPs == nil { + return nil + } + return h.PublicIPs +} + +// GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetRunnerGroupID() int64 { + if h == nil || h.RunnerGroupID == nil { + return 0 + } + return *h.RunnerGroupID +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetStatus() string { + if h == nil || h.Status == nil { + return "" + } + return *h.Status +} + +// GetID returns the ID field. +func (h *HostedRunnerCustomImage) GetID() int64 { + if h == nil { + return 0 + } + return h.ID +} + +// GetLatestVersion returns the LatestVersion field. +func (h *HostedRunnerCustomImage) GetLatestVersion() string { + if h == nil { + return "" + } + return h.LatestVersion +} + +// GetName returns the Name field. +func (h *HostedRunnerCustomImage) GetName() string { + if h == nil { + return "" + } + return h.Name +} + +// GetPlatform returns the Platform field. +func (h *HostedRunnerCustomImage) GetPlatform() string { + if h == nil { + return "" + } + return h.Platform +} + +// GetSource returns the Source field. +func (h *HostedRunnerCustomImage) GetSource() string { + if h == nil { + return "" + } + return h.Source +} + +// GetState returns the State field. +func (h *HostedRunnerCustomImage) GetState() string { + if h == nil { + return "" + } + return h.State +} + +// GetTotalVersionsSize returns the TotalVersionsSize field. +func (h *HostedRunnerCustomImage) GetTotalVersionsSize() int { + if h == nil { + return 0 + } + return h.TotalVersionsSize +} + +// GetVersionsCount returns the VersionsCount field. +func (h *HostedRunnerCustomImage) GetVersionsCount() int { + if h == nil { + return 0 + } + return h.VersionsCount +} + +// GetImages returns the Images slice if it's non-nil, nil otherwise. +func (h *HostedRunnerCustomImages) GetImages() []*HostedRunnerCustomImage { + if h == nil || h.Images == nil { + return nil + } + return h.Images +} + +// GetTotalCount returns the TotalCount field. +func (h *HostedRunnerCustomImages) GetTotalCount() int { + if h == nil { + return 0 + } + return h.TotalCount +} + +// GetCreatedOn returns the CreatedOn field. +func (h *HostedRunnerCustomImageVersion) GetCreatedOn() Timestamp { + if h == nil { + return Timestamp{} + } + return h.CreatedOn +} + +// GetSizeGB returns the SizeGB field. +func (h *HostedRunnerCustomImageVersion) GetSizeGB() int { + if h == nil { + return 0 + } + return h.SizeGB +} + +// GetState returns the State field. +func (h *HostedRunnerCustomImageVersion) GetState() string { + if h == nil { + return "" + } + return h.State +} + +// GetStateDetails returns the StateDetails field. +func (h *HostedRunnerCustomImageVersion) GetStateDetails() string { + if h == nil { + return "" + } + return h.StateDetails +} + +// GetVersion returns the Version field. +func (h *HostedRunnerCustomImageVersion) GetVersion() string { + if h == nil { + return "" + } + return h.Version +} + +// GetImageVersions returns the ImageVersions slice if it's non-nil, nil otherwise. +func (h *HostedRunnerCustomImageVersions) GetImageVersions() []*HostedRunnerCustomImageVersion { + if h == nil || h.ImageVersions == nil { + return nil + } + return h.ImageVersions +} + +// GetTotalCount returns the TotalCount field. +func (h *HostedRunnerCustomImageVersions) GetTotalCount() int { + if h == nil { + return 0 + } + return h.TotalCount +} + +// GetID returns the ID field. +func (h *HostedRunnerImage) GetID() string { + if h == nil { + return "" + } + return h.ID +} + +// GetSource returns the Source field. +func (h *HostedRunnerImage) GetSource() string { + if h == nil { + return "" + } + return h.Source +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImage) GetVersion() string { + if h == nil || h.Version == nil { + return "" + } + return *h.Version +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetDisplayName() string { + if h == nil || h.DisplayName == nil { + return "" + } + return *h.DisplayName +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetID() string { + if h == nil || h.ID == nil { + return "" + } + return *h.ID +} + +// GetSizeGB returns the SizeGB field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetSizeGB() int64 { + if h == nil || h.SizeGB == nil { + return 0 + } + return *h.SizeGB +} + +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetSource() string { + if h == nil || h.Source == nil { + return "" + } + return *h.Source +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetVersion() string { + if h == nil || h.Version == nil { + return "" + } + return *h.Version +} + +// GetImages returns the Images slice if it's non-nil, nil otherwise. +func (h *HostedRunnerImages) GetImages() []*HostedRunnerImageSpecs { + if h == nil || h.Images == nil { + return nil + } + return h.Images +} + +// GetTotalCount returns the TotalCount field. +func (h *HostedRunnerImages) GetTotalCount() int { + if h == nil { + return 0 + } + return h.TotalCount +} + +// GetDisplayName returns the DisplayName field. +func (h *HostedRunnerImageSpecs) GetDisplayName() string { + if h == nil { + return "" + } + return h.DisplayName +} + +// GetID returns the ID field. +func (h *HostedRunnerImageSpecs) GetID() string { + if h == nil { + return "" + } + return h.ID +} + +// GetPlatform returns the Platform field. +func (h *HostedRunnerImageSpecs) GetPlatform() string { + if h == nil { + return "" + } + return h.Platform +} + +// GetSizeGB returns the SizeGB field. +func (h *HostedRunnerImageSpecs) GetSizeGB() int { + if h == nil { + return 0 + } + return h.SizeGB +} + +// GetSource returns the Source field. +func (h *HostedRunnerImageSpecs) GetSource() string { + if h == nil { + return "" + } + return h.Source +} + +// GetCPUCores returns the CPUCores field. +func (h *HostedRunnerMachineSpec) GetCPUCores() int { + if h == nil { + return 0 + } + return h.CPUCores +} + +// GetID returns the ID field. +func (h *HostedRunnerMachineSpec) GetID() string { + if h == nil { + return "" + } + return h.ID +} + +// GetMemoryGB returns the MemoryGB field. +func (h *HostedRunnerMachineSpec) GetMemoryGB() int { + if h == nil { + return 0 + } + return h.MemoryGB +} + +// GetStorageGB returns the StorageGB field. +func (h *HostedRunnerMachineSpec) GetStorageGB() int { + if h == nil { + return 0 + } + return h.StorageGB +} + +// GetMachineSpecs returns the MachineSpecs slice if it's non-nil, nil otherwise. +func (h *HostedRunnerMachineSpecs) GetMachineSpecs() []*HostedRunnerMachineSpec { + if h == nil || h.MachineSpecs == nil { + return nil + } + return h.MachineSpecs +} + +// GetTotalCount returns the TotalCount field. +func (h *HostedRunnerMachineSpecs) GetTotalCount() int { + if h == nil { + return 0 + } + return h.TotalCount +} + +// GetPlatforms returns the Platforms slice if it's non-nil, nil otherwise. +func (h *HostedRunnerPlatforms) GetPlatforms() []string { + if h == nil || h.Platforms == nil { + return nil + } + return h.Platforms +} + +// GetTotalCount returns the TotalCount field. +func (h *HostedRunnerPlatforms) GetTotalCount() int { + if h == nil { + return 0 + } + return h.TotalCount +} + +// GetEnabled returns the Enabled field. +func (h *HostedRunnerPublicIP) GetEnabled() bool { + if h == nil { + return false + } + return h.Enabled +} + +// GetLength returns the Length field. +func (h *HostedRunnerPublicIP) GetLength() int { + if h == nil { + return 0 + } + return h.Length +} + +// GetPrefix returns the Prefix field. +func (h *HostedRunnerPublicIP) GetPrefix() string { + if h == nil { + return "" + } + return h.Prefix +} + +// GetPublicIPs returns the PublicIPs field. +func (h *HostedRunnerPublicIPLimits) GetPublicIPs() *PublicIPUsage { + if h == nil { + return nil + } + return h.PublicIPs +} + +// GetRunners returns the Runners slice if it's non-nil, nil otherwise. +func (h *HostedRunners) GetRunners() []*HostedRunner { + if h == nil || h.Runners == nil { + return nil + } + return h.Runners +} + +// GetTotalCount returns the TotalCount field. +func (h *HostedRunners) GetTotalCount() int { + if h == nil { + return 0 + } + return h.TotalCount +} + +// GetContexts returns the Contexts slice if it's non-nil, nil otherwise. +func (h *Hovercard) GetContexts() []*UserContext { + if h == nil || h.Contexts == nil { + return nil + } + return h.Contexts +} + +// GetSubjectID returns the SubjectID field. +func (h *HovercardOptions) GetSubjectID() string { + if h == nil { + return "" + } + return h.SubjectID +} + +// GetSubjectType returns the SubjectType field. +func (h *HovercardOptions) GetSubjectType() string { + if h == nil { + return "" + } + return h.SubjectType +} + +// GetGroupDescription returns the GroupDescription field if it's non-nil, zero value otherwise. +func (i *IDPGroup) GetGroupDescription() string { + if i == nil || i.GroupDescription == nil { + return "" + } + return *i.GroupDescription +} + +// GetGroupID returns the GroupID field if it's non-nil, zero value otherwise. +func (i *IDPGroup) GetGroupID() string { + if i == nil || i.GroupID == nil { + return "" + } + return *i.GroupID +} + +// GetGroupName returns the GroupName field if it's non-nil, zero value otherwise. +func (i *IDPGroup) GetGroupName() string { + if i == nil || i.GroupName == nil { + return "" + } + return *i.GroupName +} + +// GetGroups returns the Groups slice if it's non-nil, nil otherwise. +func (i *IDPGroupList) GetGroups() []*IDPGroup { + if i == nil || i.Groups == nil { + return nil + } + return i.Groups +} + +// GetEnforcedRepositories returns the EnforcedRepositories field if it's non-nil, zero value otherwise. +func (i *ImmutableReleasePolicy) GetEnforcedRepositories() string { + if i == nil || i.EnforcedRepositories == nil { + return "" + } + return *i.EnforcedRepositories +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (i *ImmutableReleasePolicy) GetSelectedRepositoryIDs() []int64 { + if i == nil || i.SelectedRepositoryIDs == nil { + return nil + } + return i.SelectedRepositoryIDs +} + +// GetEnforcedRepositories returns the EnforcedRepositories field if it's non-nil, zero value otherwise. +func (i *ImmutableReleaseSettings) GetEnforcedRepositories() string { + if i == nil || i.EnforcedRepositories == nil { + return "" + } + return *i.EnforcedRepositories +} + +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field if it's non-nil, zero value otherwise. +func (i *ImmutableReleaseSettings) GetSelectedRepositoriesURL() string { + if i == nil || i.SelectedRepositoriesURL == nil { + return "" + } + return *i.SelectedRepositoriesURL +} + +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (i *ImpersonateUserOptions) GetScopes() []string { + if i == nil || i.Scopes == nil { + return nil + } + return i.Scopes +} + +// GetAuthorsCount returns the AuthorsCount field if it's non-nil, zero value otherwise. +func (i *Import) GetAuthorsCount() int { + if i == nil || i.AuthorsCount == nil { + return 0 + } + return *i.AuthorsCount +} + +// GetAuthorsURL returns the AuthorsURL field if it's non-nil, zero value otherwise. +func (i *Import) GetAuthorsURL() string { + if i == nil || i.AuthorsURL == nil { + return "" + } + return *i.AuthorsURL +} + +// GetCommitCount returns the CommitCount field if it's non-nil, zero value otherwise. +func (i *Import) GetCommitCount() int { + if i == nil || i.CommitCount == nil { + return 0 + } + return *i.CommitCount +} + +// GetFailedStep returns the FailedStep field if it's non-nil, zero value otherwise. +func (i *Import) GetFailedStep() string { + if i == nil || i.FailedStep == nil { + return "" + } + return *i.FailedStep +} + +// GetHasLargeFiles returns the HasLargeFiles field if it's non-nil, zero value otherwise. +func (i *Import) GetHasLargeFiles() bool { + if i == nil || i.HasLargeFiles == nil { + return false + } + return *i.HasLargeFiles +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *Import) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetHumanName returns the HumanName field if it's non-nil, zero value otherwise. +func (i *Import) GetHumanName() string { + if i == nil || i.HumanName == nil { + return "" + } + return *i.HumanName +} + +// GetLargeFilesCount returns the LargeFilesCount field if it's non-nil, zero value otherwise. +func (i *Import) GetLargeFilesCount() int { + if i == nil || i.LargeFilesCount == nil { + return 0 + } + return *i.LargeFilesCount +} + +// GetLargeFilesSize returns the LargeFilesSize field if it's non-nil, zero value otherwise. +func (i *Import) GetLargeFilesSize() int { + if i == nil || i.LargeFilesSize == nil { + return 0 + } + return *i.LargeFilesSize +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (i *Import) GetMessage() string { + if i == nil || i.Message == nil { + return "" + } + return *i.Message +} + +// GetPercent returns the Percent field if it's non-nil, zero value otherwise. +func (i *Import) GetPercent() int { + if i == nil || i.Percent == nil { + return 0 + } + return *i.Percent +} + +// GetProjectChoices returns the ProjectChoices slice if it's non-nil, nil otherwise. +func (i *Import) GetProjectChoices() []*Import { + if i == nil || i.ProjectChoices == nil { + return nil + } + return i.ProjectChoices +} + +// GetPushPercent returns the PushPercent field if it's non-nil, zero value otherwise. +func (i *Import) GetPushPercent() int { + if i == nil || i.PushPercent == nil { + return 0 + } + return *i.PushPercent +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (i *Import) GetRepositoryURL() string { + if i == nil || i.RepositoryURL == nil { + return "" + } + return *i.RepositoryURL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (i *Import) GetStatus() string { + if i == nil || i.Status == nil { + return "" + } + return *i.Status +} + +// GetStatusText returns the StatusText field if it's non-nil, zero value otherwise. +func (i *Import) GetStatusText() string { + if i == nil || i.StatusText == nil { + return "" + } + return *i.StatusText +} + +// GetTFVCProject returns the TFVCProject field if it's non-nil, zero value otherwise. +func (i *Import) GetTFVCProject() string { + if i == nil || i.TFVCProject == nil { + return "" + } + return *i.TFVCProject +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *Import) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetUseLFS returns the UseLFS field if it's non-nil, zero value otherwise. +func (i *Import) GetUseLFS() string { + if i == nil || i.UseLFS == nil { + return "" + } + return *i.UseLFS +} + +// GetVCS returns the VCS field if it's non-nil, zero value otherwise. +func (i *Import) GetVCS() string { + if i == nil || i.VCS == nil { + return "" + } + return *i.VCS +} + +// GetVCSPassword returns the VCSPassword field if it's non-nil, zero value otherwise. +func (i *Import) GetVCSPassword() string { + if i == nil || i.VCSPassword == nil { + return "" + } + return *i.VCSPassword +} + +// GetVCSURL returns the VCSURL field if it's non-nil, zero value otherwise. +func (i *Import) GetVCSURL() string { + if i == nil || i.VCSURL == nil { + return "" + } + return *i.VCSURL +} + +// GetVCSUsername returns the VCSUsername field if it's non-nil, zero value otherwise. +func (i *Import) GetVCSUsername() string { + if i == nil || i.VCSUsername == nil { + return "" + } + return *i.VCSUsername +} + +// GetLicense returns the License field. +func (i *InitialConfigOptions) GetLicense() string { + if i == nil { + return "" + } + return i.License +} + +// GetPassword returns the Password field. +func (i *InitialConfigOptions) GetPassword() string { + if i == nil { + return "" + } + return i.Password +} + +// GetAccessibleRepositoriesURL returns the AccessibleRepositoriesURL field if it's non-nil, zero value otherwise. +func (i *InstallableOrganization) GetAccessibleRepositoriesURL() string { + if i == nil || i.AccessibleRepositoriesURL == nil { + return "" + } + return *i.AccessibleRepositoriesURL +} + +// GetID returns the ID field. +func (i *InstallableOrganization) GetID() int64 { + if i == nil { + return 0 + } + return i.ID +} + +// GetLogin returns the Login field. +func (i *InstallableOrganization) GetLogin() string { + if i == nil { + return "" + } + return i.Login +} + +// GetClientID returns the ClientID field. +func (i *InstallAppRequest) GetClientID() string { + if i == nil { + return "" + } + return i.ClientID +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (i *InstallAppRequest) GetRepositories() []string { + if i == nil || i.Repositories == nil { + return nil + } + return i.Repositories +} + +// GetRepositorySelection returns the RepositorySelection field. +func (i *InstallAppRequest) GetRepositorySelection() string { + if i == nil { + return "" + } + return i.RepositorySelection +} + +// GetAccessTokensURL returns the AccessTokensURL field if it's non-nil, zero value otherwise. +func (i *Installation) GetAccessTokensURL() string { + if i == nil || i.AccessTokensURL == nil { + return "" + } + return *i.AccessTokensURL +} + +// GetAccount returns the Account field. +func (i *Installation) GetAccount() *User { + if i == nil { + return nil + } + return i.Account +} + +// GetAppID returns the AppID field if it's non-nil, zero value otherwise. +func (i *Installation) GetAppID() int64 { + if i == nil || i.AppID == nil { + return 0 + } + return *i.AppID +} + +// GetAppSlug returns the AppSlug field if it's non-nil, zero value otherwise. +func (i *Installation) GetAppSlug() string { + if i == nil || i.AppSlug == nil { + return "" + } + return *i.AppSlug +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (i *Installation) GetClientID() string { + if i == nil || i.ClientID == nil { + return "" + } + return *i.ClientID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *Installation) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetEvents returns the Events slice if it's non-nil, nil otherwise. +func (i *Installation) GetEvents() []string { + if i == nil || i.Events == nil { + return nil + } + return i.Events +} + +// GetHasMultipleSingleFiles returns the HasMultipleSingleFiles field if it's non-nil, zero value otherwise. +func (i *Installation) GetHasMultipleSingleFiles() bool { + if i == nil || i.HasMultipleSingleFiles == nil { + return false + } + return *i.HasMultipleSingleFiles +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *Installation) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *Installation) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *Installation) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetPermissions returns the Permissions field. +func (i *Installation) GetPermissions() *InstallationPermissions { + if i == nil { + return nil + } + return i.Permissions +} + +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (i *Installation) GetRepositoriesURL() string { + if i == nil || i.RepositoriesURL == nil { + return "" + } + return *i.RepositoriesURL +} + +// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. +func (i *Installation) GetRepositorySelection() string { + if i == nil || i.RepositorySelection == nil { + return "" + } + return *i.RepositorySelection +} + +// GetSingleFileName returns the SingleFileName field if it's non-nil, zero value otherwise. +func (i *Installation) GetSingleFileName() string { + if i == nil || i.SingleFileName == nil { + return "" + } + return *i.SingleFileName +} + +// GetSingleFilePaths returns the SingleFilePaths slice if it's non-nil, nil otherwise. +func (i *Installation) GetSingleFilePaths() []string { + if i == nil || i.SingleFilePaths == nil { + return nil + } + return i.SingleFilePaths +} + +// GetSuspendedAt returns the SuspendedAt field if it's non-nil, zero value otherwise. +func (i *Installation) GetSuspendedAt() Timestamp { + if i == nil || i.SuspendedAt == nil { + return Timestamp{} + } + return *i.SuspendedAt +} + +// GetSuspendedBy returns the SuspendedBy field. +func (i *Installation) GetSuspendedBy() *User { + if i == nil { + return nil + } + return i.SuspendedBy +} + +// GetTargetID returns the TargetID field if it's non-nil, zero value otherwise. +func (i *Installation) GetTargetID() int64 { + if i == nil || i.TargetID == nil { + return 0 + } + return *i.TargetID +} + +// GetTargetType returns the TargetType field if it's non-nil, zero value otherwise. +func (i *Installation) GetTargetType() string { + if i == nil || i.TargetType == nil { + return "" + } + return *i.TargetType +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *Installation) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetLogin returns the Login field. +func (i *InstallationChanges) GetLogin() *InstallationLoginChange { + if i == nil { + return nil + } + return i.Login +} + +// GetSlug returns the Slug field. +func (i *InstallationChanges) GetSlug() *InstallationSlugChange { + if i == nil { + return nil + } + return i.Slug +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *InstallationEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetInstallation returns the Installation field. +func (i *InstallationEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetOrg returns the Org field. +func (i *InstallationEvent) GetOrg() *Organization { + if i == nil { + return nil + } + return i.Org +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (i *InstallationEvent) GetRepositories() []*Repository { + if i == nil || i.Repositories == nil { + return nil + } + return i.Repositories +} + +// GetRequester returns the Requester field. +func (i *InstallationEvent) GetRequester() *User { + if i == nil { + return nil + } + return i.Requester +} + +// GetSender returns the Sender field. +func (i *InstallationEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (i *InstallationLoginChange) GetFrom() string { + if i == nil || i.From == nil { + return "" + } + return *i.From +} + +// GetActions returns the Actions field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetActions() string { + if i == nil || i.Actions == nil { + return "" + } + return *i.Actions +} + +// GetActionsVariables returns the ActionsVariables field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetActionsVariables() string { + if i == nil || i.ActionsVariables == nil { + return "" + } + return *i.ActionsVariables +} + +// GetAdministration returns the Administration field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetAdministration() string { + if i == nil || i.Administration == nil { + return "" + } + return *i.Administration +} + +// GetAttestations returns the Attestations field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetAttestations() string { + if i == nil || i.Attestations == nil { + return "" + } + return *i.Attestations +} + +// GetBlocking returns the Blocking field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetBlocking() string { + if i == nil || i.Blocking == nil { + return "" + } + return *i.Blocking +} + +// GetChecks returns the Checks field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetChecks() string { + if i == nil || i.Checks == nil { + return "" + } + return *i.Checks +} + +// GetCodespaces returns the Codespaces field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetCodespaces() string { + if i == nil || i.Codespaces == nil { + return "" + } + return *i.Codespaces +} + +// GetCodespacesLifecycleAdmin returns the CodespacesLifecycleAdmin field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetCodespacesLifecycleAdmin() string { + if i == nil || i.CodespacesLifecycleAdmin == nil { + return "" + } + return *i.CodespacesLifecycleAdmin +} + +// GetCodespacesMetadata returns the CodespacesMetadata field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetCodespacesMetadata() string { + if i == nil || i.CodespacesMetadata == nil { + return "" + } + return *i.CodespacesMetadata +} + +// GetCodespacesSecrets returns the CodespacesSecrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetCodespacesSecrets() string { + if i == nil || i.CodespacesSecrets == nil { + return "" + } + return *i.CodespacesSecrets +} + +// GetCodespacesUserSecrets returns the CodespacesUserSecrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetCodespacesUserSecrets() string { + if i == nil || i.CodespacesUserSecrets == nil { + return "" + } + return *i.CodespacesUserSecrets +} + +// GetContentReferences returns the ContentReferences field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetContentReferences() string { + if i == nil || i.ContentReferences == nil { + return "" + } + return *i.ContentReferences +} + +// GetContents returns the Contents field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetContents() string { + if i == nil || i.Contents == nil { + return "" + } + return *i.Contents +} + +// GetCopilotMessages returns the CopilotMessages field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetCopilotMessages() string { + if i == nil || i.CopilotMessages == nil { + return "" + } + return *i.CopilotMessages +} + +// GetDependabotSecrets returns the DependabotSecrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetDependabotSecrets() string { + if i == nil || i.DependabotSecrets == nil { + return "" + } + return *i.DependabotSecrets +} + +// GetDeployments returns the Deployments field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetDeployments() string { + if i == nil || i.Deployments == nil { + return "" + } + return *i.Deployments +} + +// GetDiscussions returns the Discussions field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetDiscussions() string { + if i == nil || i.Discussions == nil { + return "" + } + return *i.Discussions +} + +// GetEmails returns the Emails field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEmails() string { + if i == nil || i.Emails == nil { + return "" + } + return *i.Emails +} + +// GetEnterpriseAIControls returns the EnterpriseAIControls field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseAIControls() string { + if i == nil || i.EnterpriseAIControls == nil { + return "" + } + return *i.EnterpriseAIControls +} + +// GetEnterpriseCopilotMetrics returns the EnterpriseCopilotMetrics field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseCopilotMetrics() string { + if i == nil || i.EnterpriseCopilotMetrics == nil { + return "" + } + return *i.EnterpriseCopilotMetrics +} + +// GetEnterpriseCredentials returns the EnterpriseCredentials field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseCredentials() string { + if i == nil || i.EnterpriseCredentials == nil { + return "" + } + return *i.EnterpriseCredentials +} + +// GetEnterpriseCustomEnterpriseRoles returns the EnterpriseCustomEnterpriseRoles field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseCustomEnterpriseRoles() string { + if i == nil || i.EnterpriseCustomEnterpriseRoles == nil { + return "" + } + return *i.EnterpriseCustomEnterpriseRoles +} + +// GetEnterpriseCustomOrgRoles returns the EnterpriseCustomOrgRoles field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseCustomOrgRoles() string { + if i == nil || i.EnterpriseCustomOrgRoles == nil { + return "" + } + return *i.EnterpriseCustomOrgRoles +} + +// GetEnterpriseCustomProperties returns the EnterpriseCustomProperties field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseCustomProperties() string { + if i == nil || i.EnterpriseCustomProperties == nil { + return "" + } + return *i.EnterpriseCustomProperties +} + +// GetEnterpriseCustomPropertiesForOrgs returns the EnterpriseCustomPropertiesForOrgs field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseCustomPropertiesForOrgs() string { + if i == nil || i.EnterpriseCustomPropertiesForOrgs == nil { + return "" + } + return *i.EnterpriseCustomPropertiesForOrgs +} + +// GetEnterpriseOrganizationInstallations returns the EnterpriseOrganizationInstallations field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseOrganizationInstallations() string { + if i == nil || i.EnterpriseOrganizationInstallations == nil { + return "" + } + return *i.EnterpriseOrganizationInstallations +} + +// GetEnterpriseOrganizations returns the EnterpriseOrganizations field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseOrganizations() string { + if i == nil || i.EnterpriseOrganizations == nil { + return "" + } + return *i.EnterpriseOrganizations +} + +// GetEnterpriseOrgInstallationRepos returns the EnterpriseOrgInstallationRepos field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseOrgInstallationRepos() string { + if i == nil || i.EnterpriseOrgInstallationRepos == nil { + return "" + } + return *i.EnterpriseOrgInstallationRepos +} + +// GetEnterprisePeople returns the EnterprisePeople field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterprisePeople() string { + if i == nil || i.EnterprisePeople == nil { + return "" + } + return *i.EnterprisePeople +} + +// GetEnterpriseSSO returns the EnterpriseSSO field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseSSO() string { + if i == nil || i.EnterpriseSSO == nil { + return "" + } + return *i.EnterpriseSSO +} + +// GetEnterpriseTeams returns the EnterpriseTeams field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnterpriseTeams() string { + if i == nil || i.EnterpriseTeams == nil { + return "" + } + return *i.EnterpriseTeams +} + +// GetEnvironments returns the Environments field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetEnvironments() string { + if i == nil || i.Environments == nil { + return "" + } + return *i.Environments +} + +// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetFollowers() string { + if i == nil || i.Followers == nil { + return "" + } + return *i.Followers +} + +// GetGists returns the Gists field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetGists() string { + if i == nil || i.Gists == nil { + return "" + } + return *i.Gists +} + +// GetGitSigningSSHPublicKeys returns the GitSigningSSHPublicKeys field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetGitSigningSSHPublicKeys() string { + if i == nil || i.GitSigningSSHPublicKeys == nil { + return "" + } + return *i.GitSigningSSHPublicKeys +} + +// GetGPGKeys returns the GPGKeys field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetGPGKeys() string { + if i == nil || i.GPGKeys == nil { + return "" + } + return *i.GPGKeys +} + +// GetInteractionLimits returns the InteractionLimits field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetInteractionLimits() string { + if i == nil || i.InteractionLimits == nil { + return "" + } + return *i.InteractionLimits +} + +// GetIssues returns the Issues field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetIssues() string { + if i == nil || i.Issues == nil { + return "" + } + return *i.Issues +} + +// GetKeys returns the Keys field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetKeys() string { + if i == nil || i.Keys == nil { + return "" + } + return *i.Keys +} + +// GetMembers returns the Members field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetMembers() string { + if i == nil || i.Members == nil { + return "" + } + return *i.Members +} + +// GetMergeQueues returns the MergeQueues field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetMergeQueues() string { + if i == nil || i.MergeQueues == nil { + return "" + } + return *i.MergeQueues +} + +// GetMetadata returns the Metadata field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetMetadata() string { + if i == nil || i.Metadata == nil { + return "" + } + return *i.Metadata +} + +// GetOrganizationActionsVariables returns the OrganizationActionsVariables field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationActionsVariables() string { + if i == nil || i.OrganizationActionsVariables == nil { + return "" + } + return *i.OrganizationActionsVariables +} + +// GetOrganizationAdministration returns the OrganizationAdministration field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationAdministration() string { + if i == nil || i.OrganizationAdministration == nil { + return "" + } + return *i.OrganizationAdministration +} + +// GetOrganizationAnnouncementBanners returns the OrganizationAnnouncementBanners field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationAnnouncementBanners() string { + if i == nil || i.OrganizationAnnouncementBanners == nil { + return "" + } + return *i.OrganizationAnnouncementBanners +} + +// GetOrganizationAPIInsights returns the OrganizationAPIInsights field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationAPIInsights() string { + if i == nil || i.OrganizationAPIInsights == nil { + return "" + } + return *i.OrganizationAPIInsights +} + +// GetOrganizationCodespaces returns the OrganizationCodespaces field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCodespaces() string { + if i == nil || i.OrganizationCodespaces == nil { + return "" + } + return *i.OrganizationCodespaces +} + +// GetOrganizationCodespacesSecrets returns the OrganizationCodespacesSecrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCodespacesSecrets() string { + if i == nil || i.OrganizationCodespacesSecrets == nil { + return "" + } + return *i.OrganizationCodespacesSecrets +} + +// GetOrganizationCodespacesSettings returns the OrganizationCodespacesSettings field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCodespacesSettings() string { + if i == nil || i.OrganizationCodespacesSettings == nil { + return "" + } + return *i.OrganizationCodespacesSettings +} + +// GetOrganizationCopilotMetrics returns the OrganizationCopilotMetrics field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCopilotMetrics() string { + if i == nil || i.OrganizationCopilotMetrics == nil { + return "" + } + return *i.OrganizationCopilotMetrics +} + +// GetOrganizationCopilotSeatManagement returns the OrganizationCopilotSeatManagement field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCopilotSeatManagement() string { + if i == nil || i.OrganizationCopilotSeatManagement == nil { + return "" + } + return *i.OrganizationCopilotSeatManagement +} + +// GetOrganizationCustomOrgRoles returns the OrganizationCustomOrgRoles field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCustomOrgRoles() string { + if i == nil || i.OrganizationCustomOrgRoles == nil { + return "" + } + return *i.OrganizationCustomOrgRoles +} + +// GetOrganizationCustomProperties returns the OrganizationCustomProperties field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCustomProperties() string { + if i == nil || i.OrganizationCustomProperties == nil { + return "" + } + return *i.OrganizationCustomProperties +} + +// GetOrganizationCustomRoles returns the OrganizationCustomRoles field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationCustomRoles() string { + if i == nil || i.OrganizationCustomRoles == nil { + return "" + } + return *i.OrganizationCustomRoles +} + +// GetOrganizationDependabotSecrets returns the OrganizationDependabotSecrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationDependabotSecrets() string { + if i == nil || i.OrganizationDependabotSecrets == nil { + return "" + } + return *i.OrganizationDependabotSecrets +} + +// GetOrganizationEvents returns the OrganizationEvents field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationEvents() string { + if i == nil || i.OrganizationEvents == nil { + return "" + } + return *i.OrganizationEvents +} + +// GetOrganizationHooks returns the OrganizationHooks field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationHooks() string { + if i == nil || i.OrganizationHooks == nil { + return "" + } + return *i.OrganizationHooks +} + +// GetOrganizationKnowledgeBases returns the OrganizationKnowledgeBases field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationKnowledgeBases() string { + if i == nil || i.OrganizationKnowledgeBases == nil { + return "" + } + return *i.OrganizationKnowledgeBases +} + +// GetOrganizationPackages returns the OrganizationPackages field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationPackages() string { + if i == nil || i.OrganizationPackages == nil { + return "" + } + return *i.OrganizationPackages +} + +// GetOrganizationPersonalAccessTokenRequests returns the OrganizationPersonalAccessTokenRequests field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationPersonalAccessTokenRequests() string { + if i == nil || i.OrganizationPersonalAccessTokenRequests == nil { + return "" + } + return *i.OrganizationPersonalAccessTokenRequests +} + +// GetOrganizationPersonalAccessTokens returns the OrganizationPersonalAccessTokens field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationPersonalAccessTokens() string { + if i == nil || i.OrganizationPersonalAccessTokens == nil { + return "" + } + return *i.OrganizationPersonalAccessTokens +} + +// GetOrganizationPlan returns the OrganizationPlan field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationPlan() string { + if i == nil || i.OrganizationPlan == nil { + return "" + } + return *i.OrganizationPlan +} + +// GetOrganizationPreReceiveHooks returns the OrganizationPreReceiveHooks field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationPreReceiveHooks() string { + if i == nil || i.OrganizationPreReceiveHooks == nil { + return "" + } + return *i.OrganizationPreReceiveHooks +} + +// GetOrganizationProjects returns the OrganizationProjects field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationProjects() string { + if i == nil || i.OrganizationProjects == nil { + return "" + } + return *i.OrganizationProjects +} + +// GetOrganizationSecrets returns the OrganizationSecrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationSecrets() string { + if i == nil || i.OrganizationSecrets == nil { + return "" + } + return *i.OrganizationSecrets +} + +// GetOrganizationSelfHostedRunners returns the OrganizationSelfHostedRunners field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationSelfHostedRunners() string { + if i == nil || i.OrganizationSelfHostedRunners == nil { + return "" + } + return *i.OrganizationSelfHostedRunners +} + +// GetOrganizationUserBlocking returns the OrganizationUserBlocking field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetOrganizationUserBlocking() string { + if i == nil || i.OrganizationUserBlocking == nil { + return "" + } + return *i.OrganizationUserBlocking +} + +// GetPackages returns the Packages field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetPackages() string { + if i == nil || i.Packages == nil { + return "" + } + return *i.Packages +} + +// GetPages returns the Pages field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetPages() string { + if i == nil || i.Pages == nil { + return "" + } + return *i.Pages +} + +// GetPlan returns the Plan field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetPlan() string { + if i == nil || i.Plan == nil { + return "" + } + return *i.Plan +} + +// GetProfile returns the Profile field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetProfile() string { + if i == nil || i.Profile == nil { + return "" + } + return *i.Profile +} + +// GetPullRequests returns the PullRequests field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetPullRequests() string { + if i == nil || i.PullRequests == nil { + return "" + } + return *i.PullRequests +} + +// GetRepositoryAdvisories returns the RepositoryAdvisories field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetRepositoryAdvisories() string { + if i == nil || i.RepositoryAdvisories == nil { + return "" + } + return *i.RepositoryAdvisories +} + +// GetRepositoryCustomProperties returns the RepositoryCustomProperties field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetRepositoryCustomProperties() string { + if i == nil || i.RepositoryCustomProperties == nil { + return "" + } + return *i.RepositoryCustomProperties +} + +// GetRepositoryHooks returns the RepositoryHooks field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetRepositoryHooks() string { + if i == nil || i.RepositoryHooks == nil { + return "" + } + return *i.RepositoryHooks +} + +// GetRepositoryPreReceiveHooks returns the RepositoryPreReceiveHooks field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetRepositoryPreReceiveHooks() string { + if i == nil || i.RepositoryPreReceiveHooks == nil { + return "" + } + return *i.RepositoryPreReceiveHooks +} + +// GetRepositoryProjects returns the RepositoryProjects field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetRepositoryProjects() string { + if i == nil || i.RepositoryProjects == nil { + return "" + } + return *i.RepositoryProjects +} + +// GetSecrets returns the Secrets field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetSecrets() string { + if i == nil || i.Secrets == nil { + return "" + } + return *i.Secrets +} + +// GetSecretScanningAlerts returns the SecretScanningAlerts field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetSecretScanningAlerts() string { + if i == nil || i.SecretScanningAlerts == nil { + return "" + } + return *i.SecretScanningAlerts +} + +// GetSecurityEvents returns the SecurityEvents field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetSecurityEvents() string { + if i == nil || i.SecurityEvents == nil { + return "" + } + return *i.SecurityEvents +} + +// GetSingleFile returns the SingleFile field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetSingleFile() string { + if i == nil || i.SingleFile == nil { + return "" + } + return *i.SingleFile +} + +// GetStarring returns the Starring field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetStarring() string { + if i == nil || i.Starring == nil { + return "" + } + return *i.Starring +} + +// GetStatuses returns the Statuses field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetStatuses() string { + if i == nil || i.Statuses == nil { + return "" + } + return *i.Statuses +} + +// GetTeamDiscussions returns the TeamDiscussions field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetTeamDiscussions() string { + if i == nil || i.TeamDiscussions == nil { + return "" + } + return *i.TeamDiscussions +} + +// GetUserEvents returns the UserEvents field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetUserEvents() string { + if i == nil || i.UserEvents == nil { + return "" + } + return *i.UserEvents +} + +// GetVulnerabilityAlerts returns the VulnerabilityAlerts field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetVulnerabilityAlerts() string { + if i == nil || i.VulnerabilityAlerts == nil { + return "" + } + return *i.VulnerabilityAlerts +} + +// GetWatching returns the Watching field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetWatching() string { + if i == nil || i.Watching == nil { + return "" + } + return *i.Watching +} + +// GetWorkflows returns the Workflows field if it's non-nil, zero value otherwise. +func (i *InstallationPermissions) GetWorkflows() string { + if i == nil || i.Workflows == nil { + return "" + } + return *i.Workflows +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *InstallationRepositoriesEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetInstallation returns the Installation field. +func (i *InstallationRepositoriesEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetOrg returns the Org field. +func (i *InstallationRepositoriesEvent) GetOrg() *Organization { + if i == nil { + return nil + } + return i.Org +} + +// GetRepositoriesAdded returns the RepositoriesAdded slice if it's non-nil, nil otherwise. +func (i *InstallationRepositoriesEvent) GetRepositoriesAdded() []*Repository { + if i == nil || i.RepositoriesAdded == nil { + return nil + } + return i.RepositoriesAdded +} + +// GetRepositoriesRemoved returns the RepositoriesRemoved slice if it's non-nil, nil otherwise. +func (i *InstallationRepositoriesEvent) GetRepositoriesRemoved() []*Repository { + if i == nil || i.RepositoriesRemoved == nil { + return nil + } + return i.RepositoriesRemoved +} + +// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. +func (i *InstallationRepositoriesEvent) GetRepositorySelection() string { + if i == nil || i.RepositorySelection == nil { + return "" + } + return *i.RepositorySelection +} + +// GetSender returns the Sender field. +func (i *InstallationRepositoriesEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetAccount returns the Account field. +func (i *InstallationRequest) GetAccount() *User { + if i == nil { + return nil + } + return i.Account +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *InstallationRequest) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *InstallationRequest) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *InstallationRequest) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetRequester returns the Requester field. +func (i *InstallationRequest) GetRequester() *User { + if i == nil { + return nil + } + return i.Requester +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (i *InstallationSlugChange) GetFrom() string { + if i == nil || i.From == nil { + return "" + } + return *i.From +} + +// GetAccount returns the Account field. +func (i *InstallationTargetEvent) GetAccount() *User { + if i == nil { + return nil + } + return i.Account +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *InstallationTargetEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetChanges returns the Changes field. +func (i *InstallationTargetEvent) GetChanges() *InstallationChanges { + if i == nil { + return nil + } + return i.Changes +} + +// GetEnterprise returns the Enterprise field. +func (i *InstallationTargetEvent) GetEnterprise() *Enterprise { + if i == nil { + return nil + } + return i.Enterprise +} + +// GetInstallation returns the Installation field. +func (i *InstallationTargetEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetOrganization returns the Organization field. +func (i *InstallationTargetEvent) GetOrganization() *Organization { + if i == nil { + return nil + } + return i.Organization +} + +// GetRepository returns the Repository field. +func (i *InstallationTargetEvent) GetRepository() *Repository { + if i == nil { + return nil + } + return i.Repository +} + +// GetSender returns the Sender field. +func (i *InstallationTargetEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetTargetType returns the TargetType field if it's non-nil, zero value otherwise. +func (i *InstallationTargetEvent) GetTargetType() string { + if i == nil || i.TargetType == nil { + return "" + } + return *i.TargetType +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (i *InstallationToken) GetExpiresAt() Timestamp { + if i == nil || i.ExpiresAt == nil { + return Timestamp{} + } + return *i.ExpiresAt +} + +// GetPermissions returns the Permissions field. +func (i *InstallationToken) GetPermissions() *InstallationPermissions { + if i == nil { + return nil + } + return i.Permissions +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (i *InstallationToken) GetRepositories() []*Repository { + if i == nil || i.Repositories == nil { + return nil + } + return i.Repositories +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (i *InstallationToken) GetToken() string { + if i == nil || i.Token == nil { + return "" + } + return *i.Token +} + +// GetPermissions returns the Permissions field. +func (i *InstallationTokenListRepoOptions) GetPermissions() *InstallationPermissions { + if i == nil { + return nil + } + return i.Permissions +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (i *InstallationTokenListRepoOptions) GetRepositories() []string { + if i == nil || i.Repositories == nil { + return nil + } + return i.Repositories +} + +// GetRepositoryIDs returns the RepositoryIDs slice if it's non-nil, nil otherwise. +func (i *InstallationTokenListRepoOptions) GetRepositoryIDs() []int64 { + if i == nil || i.RepositoryIDs == nil { + return nil + } + return i.RepositoryIDs +} + +// GetPermissions returns the Permissions field. +func (i *InstallationTokenOptions) GetPermissions() *InstallationPermissions { + if i == nil { + return nil + } + return i.Permissions +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (i *InstallationTokenOptions) GetRepositories() []string { + if i == nil || i.Repositories == nil { + return nil + } + return i.Repositories +} + +// GetRepositoryIDs returns the RepositoryIDs slice if it's non-nil, nil otherwise. +func (i *InstallationTokenOptions) GetRepositoryIDs() []int64 { + if i == nil || i.RepositoryIDs == nil { + return nil + } + return i.RepositoryIDs +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (i *InteractionRestriction) GetExpiresAt() Timestamp { + if i == nil || i.ExpiresAt == nil { + return Timestamp{} + } + return *i.ExpiresAt +} + +// GetLimit returns the Limit field if it's non-nil, zero value otherwise. +func (i *InteractionRestriction) GetLimit() string { + if i == nil || i.Limit == nil { + return "" + } + return *i.Limit +} + +// GetOrigin returns the Origin field if it's non-nil, zero value otherwise. +func (i *InteractionRestriction) GetOrigin() string { + if i == nil || i.Origin == nil { + return "" + } + return *i.Origin +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *Invitation) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (i *Invitation) GetEmail() string { + if i == nil || i.Email == nil { + return "" + } + return *i.Email +} + +// GetFailedAt returns the FailedAt field if it's non-nil, zero value otherwise. +func (i *Invitation) GetFailedAt() Timestamp { + if i == nil || i.FailedAt == nil { + return Timestamp{} + } + return *i.FailedAt +} + +// GetFailedReason returns the FailedReason field if it's non-nil, zero value otherwise. +func (i *Invitation) GetFailedReason() string { + if i == nil || i.FailedReason == nil { + return "" + } + return *i.FailedReason +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *Invitation) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetInvitationTeamURL returns the InvitationTeamURL field if it's non-nil, zero value otherwise. +func (i *Invitation) GetInvitationTeamURL() string { + if i == nil || i.InvitationTeamURL == nil { + return "" + } + return *i.InvitationTeamURL +} + +// GetInviter returns the Inviter field. +func (i *Invitation) GetInviter() *User { + if i == nil { + return nil + } + return i.Inviter +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (i *Invitation) GetLogin() string { + if i == nil || i.Login == nil { + return "" + } + return *i.Login +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *Invitation) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (i *Invitation) GetRole() string { + if i == nil || i.Role == nil { + return "" + } + return *i.Role +} + +// GetTeamCount returns the TeamCount field if it's non-nil, zero value otherwise. +func (i *Invitation) GetTeamCount() int { + if i == nil || i.TeamCount == nil { + return 0 + } + return *i.TeamCount +} + +// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. +func (i *Issue) GetActiveLockReason() string { + if i == nil || i.ActiveLockReason == nil { + return "" + } + return *i.ActiveLockReason +} + +// GetAssignee returns the Assignee field. +func (i *Issue) GetAssignee() *User { + if i == nil { + return nil + } + return i.Assignee +} + +// GetAssignees returns the Assignees slice if it's non-nil, nil otherwise. +func (i *Issue) GetAssignees() []*User { + if i == nil || i.Assignees == nil { + return nil + } + return i.Assignees +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (i *Issue) GetAuthorAssociation() string { + if i == nil || i.AuthorAssociation == nil { + return "" + } + return *i.AuthorAssociation +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (i *Issue) GetBody() string { + if i == nil || i.Body == nil { + return "" + } + return *i.Body +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (i *Issue) GetClosedAt() Timestamp { + if i == nil || i.ClosedAt == nil { + return Timestamp{} + } + return *i.ClosedAt +} + +// GetClosedBy returns the ClosedBy field. +func (i *Issue) GetClosedBy() *User { + if i == nil { + return nil + } + return i.ClosedBy +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (i *Issue) GetComments() int { + if i == nil || i.Comments == nil { + return 0 + } + return *i.Comments +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetCommentsURL() string { + if i == nil || i.CommentsURL == nil { + return "" + } + return *i.CommentsURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *Issue) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (i *Issue) GetDraft() bool { + if i == nil || i.Draft == nil { + return false + } + return *i.Draft +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetEventsURL() string { + if i == nil || i.EventsURL == nil { + return "" + } + return *i.EventsURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *Issue) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetIssueDependenciesSummary returns the IssueDependenciesSummary field. +func (i *Issue) GetIssueDependenciesSummary() *IssueDependenciesSummary { + if i == nil { + return nil + } + return i.IssueDependenciesSummary +} + +// GetIssueFieldValues returns the IssueFieldValues slice if it's non-nil, nil otherwise. +func (i *Issue) GetIssueFieldValues() []*IssueFieldValue { + if i == nil || i.IssueFieldValues == nil { + return nil + } + return i.IssueFieldValues +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (i *Issue) GetLabels() []*Label { + if i == nil || i.Labels == nil { + return nil + } + return i.Labels +} + +// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetLabelsURL() string { + if i == nil || i.LabelsURL == nil { + return "" + } + return *i.LabelsURL +} + +// GetLocked returns the Locked field if it's non-nil, zero value otherwise. +func (i *Issue) GetLocked() bool { + if i == nil || i.Locked == nil { + return false + } + return *i.Locked +} + +// GetMilestone returns the Milestone field. +func (i *Issue) GetMilestone() *Milestone { + if i == nil { + return nil + } + return i.Milestone +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *Issue) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (i *Issue) GetNumber() int { + if i == nil || i.Number == nil { + return 0 + } + return *i.Number +} + +// GetParentIssueURL returns the ParentIssueURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetParentIssueURL() string { + if i == nil || i.ParentIssueURL == nil { + return "" + } + return *i.ParentIssueURL +} + +// GetPerformedViaGithubApp returns the PerformedViaGithubApp field. +func (i *Issue) GetPerformedViaGithubApp() *App { + if i == nil { + return nil + } + return i.PerformedViaGithubApp +} + +// GetPinnedComment returns the PinnedComment field. +func (i *Issue) GetPinnedComment() *IssueComment { + if i == nil { + return nil + } + return i.PinnedComment +} + +// GetPullRequestLinks returns the PullRequestLinks field. +func (i *Issue) GetPullRequestLinks() *PullRequestLinks { + if i == nil { + return nil + } + return i.PullRequestLinks +} + +// GetReactions returns the Reactions field. +func (i *Issue) GetReactions() *Reactions { + if i == nil { + return nil + } + return i.Reactions +} + +// GetRepository returns the Repository field. +func (i *Issue) GetRepository() *Repository { + if i == nil { + return nil + } + return i.Repository +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (i *Issue) GetRepositoryURL() string { + if i == nil || i.RepositoryURL == nil { + return "" + } + return *i.RepositoryURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (i *Issue) GetState() string { + if i == nil || i.State == nil { + return "" + } + return *i.State +} + +// GetStateReason returns the StateReason field if it's non-nil, zero value otherwise. +func (i *Issue) GetStateReason() string { + if i == nil || i.StateReason == nil { + return "" + } + return *i.StateReason +} + +// GetSubIssuesSummary returns the SubIssuesSummary field. +func (i *Issue) GetSubIssuesSummary() *SubIssuesSummary { + if i == nil { + return nil + } + return i.SubIssuesSummary +} + +// GetTextMatches returns the TextMatches slice if it's non-nil, nil otherwise. +func (i *Issue) GetTextMatches() []*TextMatch { + if i == nil || i.TextMatches == nil { + return nil + } + return i.TextMatches +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (i *Issue) GetTitle() string { + if i == nil || i.Title == nil { + return "" + } + return *i.Title +} + +// GetType returns the Type field. +func (i *Issue) GetType() *IssueType { + if i == nil { + return nil + } + return i.Type +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *Issue) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *Issue) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetUser returns the User field. +func (i *Issue) GetUser() *User { + if i == nil { + return nil + } + return i.User +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetAuthorAssociation() string { + if i == nil || i.AuthorAssociation == nil { + return "" + } + return *i.AuthorAssociation +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetBody() string { + if i == nil || i.Body == nil { + return "" + } + return *i.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetHTMLURL() string { + if i == nil || i.HTMLURL == nil { + return "" + } + return *i.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetIssueURL() string { + if i == nil || i.IssueURL == nil { + return "" + } + return *i.IssueURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetReactions returns the Reactions field. +func (i *IssueComment) GetReactions() *Reactions { + if i == nil { + return nil + } + return i.Reactions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *IssueComment) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetUser returns the User field. +func (i *IssueComment) GetUser() *User { + if i == nil { + return nil + } + return i.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *IssueCommentEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetChanges returns the Changes field. +func (i *IssueCommentEvent) GetChanges() *EditChange { + if i == nil { + return nil + } + return i.Changes +} + +// GetComment returns the Comment field. +func (i *IssueCommentEvent) GetComment() *IssueComment { + if i == nil { + return nil + } + return i.Comment +} + +// GetInstallation returns the Installation field. +func (i *IssueCommentEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetIssue returns the Issue field. +func (i *IssueCommentEvent) GetIssue() *Issue { + if i == nil { + return nil + } + return i.Issue +} + +// GetOrganization returns the Organization field. +func (i *IssueCommentEvent) GetOrganization() *Organization { + if i == nil { + return nil + } + return i.Organization +} + +// GetRepo returns the Repo field. +func (i *IssueCommentEvent) GetRepo() *Repository { + if i == nil { + return nil + } + return i.Repo +} + +// GetSender returns the Sender field. +func (i *IssueCommentEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetBlockedBy returns the BlockedBy field if it's non-nil, zero value otherwise. +func (i *IssueDependenciesSummary) GetBlockedBy() int { + if i == nil || i.BlockedBy == nil { + return 0 + } + return *i.BlockedBy +} + +// GetBlocking returns the Blocking field if it's non-nil, zero value otherwise. +func (i *IssueDependenciesSummary) GetBlocking() int { + if i == nil || i.Blocking == nil { + return 0 + } + return *i.Blocking +} + +// GetTotalBlockedBy returns the TotalBlockedBy field if it's non-nil, zero value otherwise. +func (i *IssueDependenciesSummary) GetTotalBlockedBy() int { + if i == nil || i.TotalBlockedBy == nil { + return 0 + } + return *i.TotalBlockedBy +} + +// GetTotalBlocking returns the TotalBlocking field if it's non-nil, zero value otherwise. +func (i *IssueDependenciesSummary) GetTotalBlocking() int { + if i == nil || i.TotalBlocking == nil { + return 0 + } + return *i.TotalBlocking +} + +// GetIssueID returns the IssueID field. +func (i *IssueDependencyRequest) GetIssueID() int64 { + if i == nil { + return 0 + } + return i.IssueID +} + +// GetAction returns the Action field. +func (i *IssueEvent) GetAction() string { + if i == nil { + return "" + } + return i.Action +} + +// GetActor returns the Actor field. +func (i *IssueEvent) GetActor() *User { + if i == nil { + return nil + } + return i.Actor +} + +// GetAssignee returns the Assignee field. +func (i *IssueEvent) GetAssignee() *User { + if i == nil { + return nil + } + return i.Assignee +} + +// GetAssigner returns the Assigner field. +func (i *IssueEvent) GetAssigner() *User { + if i == nil { + return nil + } + return i.Assigner +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetCommitID() string { + if i == nil || i.CommitID == nil { + return "" + } + return *i.CommitID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetDismissedReview returns the DismissedReview field. +func (i *IssueEvent) GetDismissedReview() *DismissedReview { + if i == nil { + return nil + } + return i.DismissedReview +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetEvent() string { + if i == nil || i.Event == nil { + return "" + } + return *i.Event +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetIssue returns the Issue field. +func (i *IssueEvent) GetIssue() *Issue { + if i == nil { + return nil + } + return i.Issue +} + +// GetLabel returns the Label field. +func (i *IssueEvent) GetLabel() *Label { + if i == nil { + return nil + } + return i.Label +} + +// GetLockReason returns the LockReason field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetLockReason() string { + if i == nil || i.LockReason == nil { + return "" + } + return *i.LockReason +} + +// GetMilestone returns the Milestone field. +func (i *IssueEvent) GetMilestone() *Milestone { + if i == nil { + return nil + } + return i.Milestone +} + +// GetPerformedViaGithubApp returns the PerformedViaGithubApp field. +func (i *IssueEvent) GetPerformedViaGithubApp() *App { + if i == nil { + return nil + } + return i.PerformedViaGithubApp +} + +// GetRename returns the Rename field. +func (i *IssueEvent) GetRename() *Rename { + if i == nil { + return nil + } + return i.Rename +} + +// GetRepository returns the Repository field. +func (i *IssueEvent) GetRepository() *Repository { + if i == nil { + return nil + } + return i.Repository +} + +// GetRequestedReviewer returns the RequestedReviewer field. +func (i *IssueEvent) GetRequestedReviewer() *User { + if i == nil { + return nil + } + return i.RequestedReviewer +} + +// GetRequestedTeam returns the RequestedTeam field. +func (i *IssueEvent) GetRequestedTeam() *Team { + if i == nil { + return nil + } + return i.RequestedTeam +} + +// GetReviewRequester returns the ReviewRequester field. +func (i *IssueEvent) GetReviewRequester() *User { + if i == nil { + return nil + } + return i.ReviewRequester +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetDataType returns the DataType field. +func (i *IssueFieldValue) GetDataType() string { + if i == nil { + return "" + } + return i.DataType +} + +// GetIssueFieldID returns the IssueFieldID field. +func (i *IssueFieldValue) GetIssueFieldID() int64 { + if i == nil { + return 0 + } + return i.IssueFieldID +} + +// GetNodeID returns the NodeID field. +func (i *IssueFieldValue) GetNodeID() string { + if i == nil { + return "" + } + return i.NodeID +} + +// GetSingleSelectOption returns the SingleSelectOption field. +func (i *IssueFieldValue) GetSingleSelectOption() *IssueFieldValueSingleSelectOption { + if i == nil { + return nil + } + return i.SingleSelectOption +} + +// GetValue returns the Value field. +func (i *IssueFieldValue) GetValue() any { + if i == nil { + return nil + } + return i.Value +} + +// GetColor returns the Color field. +func (i *IssueFieldValueSingleSelectOption) GetColor() string { + if i == nil { + return "" + } + return i.Color +} + +// GetID returns the ID field. +func (i *IssueFieldValueSingleSelectOption) GetID() int64 { + if i == nil { + return 0 + } + return i.ID +} + +// GetName returns the Name field. +func (i *IssueFieldValueSingleSelectOption) GetName() string { + if i == nil { + return "" + } + return i.Name +} + +// GetAssignee returns the Assignee field if it's non-nil, zero value otherwise. +func (i *IssueImport) GetAssignee() string { + if i == nil || i.Assignee == nil { + return "" + } + return *i.Assignee +} + +// GetBody returns the Body field. +func (i *IssueImport) GetBody() string { + if i == nil { + return "" + } + return i.Body +} + +// GetClosed returns the Closed field if it's non-nil, zero value otherwise. +func (i *IssueImport) GetClosed() bool { + if i == nil || i.Closed == nil { + return false + } + return *i.Closed +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (i *IssueImport) GetClosedAt() Timestamp { + if i == nil || i.ClosedAt == nil { + return Timestamp{} + } + return *i.ClosedAt +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueImport) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (i *IssueImport) GetLabels() []string { + if i == nil || i.Labels == nil { + return nil + } + return i.Labels +} + +// GetMilestone returns the Milestone field if it's non-nil, zero value otherwise. +func (i *IssueImport) GetMilestone() int { + if i == nil || i.Milestone == nil { + return 0 + } + return *i.Milestone +} + +// GetTitle returns the Title field. +func (i *IssueImport) GetTitle() string { + if i == nil { + return "" + } + return i.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueImport) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetCode returns the Code field if it's non-nil, zero value otherwise. +func (i *IssueImportError) GetCode() string { + if i == nil || i.Code == nil { + return "" + } + return *i.Code +} + +// GetField returns the Field field if it's non-nil, zero value otherwise. +func (i *IssueImportError) GetField() string { + if i == nil || i.Field == nil { + return "" + } + return *i.Field +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (i *IssueImportError) GetLocation() string { + if i == nil || i.Location == nil { + return "" + } + return *i.Location +} + +// GetResource returns the Resource field if it's non-nil, zero value otherwise. +func (i *IssueImportError) GetResource() string { + if i == nil || i.Resource == nil { + return "" + } + return *i.Resource +} + +// GetValue returns the Value field if it's non-nil, zero value otherwise. +func (i *IssueImportError) GetValue() string { + if i == nil || i.Value == nil { + return "" + } + return *i.Value +} + +// GetComments returns the Comments slice if it's non-nil, nil otherwise. +func (i *IssueImportRequest) GetComments() []*Comment { + if i == nil || i.Comments == nil { + return nil + } + return i.Comments +} + +// GetIssueImport returns the IssueImport field. +func (i *IssueImportRequest) GetIssueImport() IssueImport { + if i == nil { + return IssueImport{} + } + return i.IssueImport +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetDocumentationURL returns the DocumentationURL field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetDocumentationURL() string { + if i == nil || i.DocumentationURL == nil { + return "" + } + return *i.DocumentationURL +} + +// GetErrors returns the Errors slice if it's non-nil, nil otherwise. +func (i *IssueImportResponse) GetErrors() []*IssueImportError { + if i == nil || i.Errors == nil { + return nil + } + return i.Errors +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetID() int { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetImportIssuesURL returns the ImportIssuesURL field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetImportIssuesURL() string { + if i == nil || i.ImportIssuesURL == nil { + return "" + } + return *i.ImportIssuesURL +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetMessage() string { + if i == nil || i.Message == nil { + return "" + } + return *i.Message +} + +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetRepositoryURL() string { + if i == nil || i.RepositoryURL == nil { + return "" + } + return *i.RepositoryURL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetStatus() string { + if i == nil || i.Status == nil { + return "" + } + return *i.Status +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (i *IssueImportResponse) GetURL() string { + if i == nil || i.URL == nil { + return "" + } + return *i.URL +} + +// GetDirection returns the Direction field. +func (i *IssueListByOrgOptions) GetDirection() string { + if i == nil { + return "" + } + return i.Direction +} + +// GetFilter returns the Filter field. +func (i *IssueListByOrgOptions) GetFilter() string { + if i == nil { + return "" + } + return i.Filter +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (i *IssueListByOrgOptions) GetLabels() []string { + if i == nil || i.Labels == nil { + return nil + } + return i.Labels +} + +// GetSince returns the Since field. +func (i *IssueListByOrgOptions) GetSince() time.Time { + if i == nil { + return time.Time{} + } + return i.Since +} + +// GetSort returns the Sort field. +func (i *IssueListByOrgOptions) GetSort() string { + if i == nil { + return "" + } + return i.Sort +} + +// GetState returns the State field. +func (i *IssueListByOrgOptions) GetState() string { + if i == nil { + return "" + } + return i.State +} + +// GetType returns the Type field. +func (i *IssueListByOrgOptions) GetType() string { + if i == nil { + return "" + } + return i.Type +} + +// GetAssignee returns the Assignee field. +func (i *IssueListByRepoOptions) GetAssignee() string { + if i == nil { + return "" + } + return i.Assignee +} + +// GetCreator returns the Creator field. +func (i *IssueListByRepoOptions) GetCreator() string { + if i == nil { + return "" + } + return i.Creator +} + +// GetDirection returns the Direction field. +func (i *IssueListByRepoOptions) GetDirection() string { + if i == nil { + return "" + } + return i.Direction +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (i *IssueListByRepoOptions) GetLabels() []string { + if i == nil || i.Labels == nil { + return nil + } + return i.Labels +} + +// GetMentioned returns the Mentioned field. +func (i *IssueListByRepoOptions) GetMentioned() string { + if i == nil { + return "" + } + return i.Mentioned +} + +// GetMilestone returns the Milestone field. +func (i *IssueListByRepoOptions) GetMilestone() string { + if i == nil { + return "" + } + return i.Milestone +} + +// GetSince returns the Since field. +func (i *IssueListByRepoOptions) GetSince() time.Time { + if i == nil { + return time.Time{} + } + return i.Since +} + +// GetSort returns the Sort field. +func (i *IssueListByRepoOptions) GetSort() string { + if i == nil { + return "" + } + return i.Sort +} + +// GetState returns the State field. +func (i *IssueListByRepoOptions) GetState() string { + if i == nil { + return "" + } + return i.State +} + +// GetType returns the Type field. +func (i *IssueListByRepoOptions) GetType() string { + if i == nil { + return "" + } + return i.Type +} + +// GetDirection returns the Direction field if it's non-nil, zero value otherwise. +func (i *IssueListCommentsOptions) GetDirection() string { + if i == nil || i.Direction == nil { + return "" + } + return *i.Direction +} + +// GetSince returns the Since field if it's non-nil, zero value otherwise. +func (i *IssueListCommentsOptions) GetSince() time.Time { + if i == nil || i.Since == nil { + return time.Time{} + } + return *i.Since +} + +// GetSort returns the Sort field if it's non-nil, zero value otherwise. +func (i *IssueListCommentsOptions) GetSort() string { + if i == nil || i.Sort == nil { + return "" + } + return *i.Sort +} + +// GetFieldID returns the FieldID field. +func (i *IssueRequestFieldValue) GetFieldID() int64 { + if i == nil { + return 0 + } + return i.FieldID +} + +// GetValue returns the Value field. +func (i *IssueRequestFieldValue) GetValue() any { + if i == nil { + return nil + } + return i.Value +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (i *IssuesEvent) GetAction() string { + if i == nil || i.Action == nil { + return "" + } + return *i.Action +} + +// GetAssignee returns the Assignee field. +func (i *IssuesEvent) GetAssignee() *User { + if i == nil { + return nil + } + return i.Assignee +} + +// GetChanges returns the Changes field. +func (i *IssuesEvent) GetChanges() *EditChange { + if i == nil { + return nil + } + return i.Changes +} + +// GetInstallation returns the Installation field. +func (i *IssuesEvent) GetInstallation() *Installation { + if i == nil { + return nil + } + return i.Installation +} + +// GetIssue returns the Issue field. +func (i *IssuesEvent) GetIssue() *Issue { + if i == nil { + return nil + } + return i.Issue +} + +// GetLabel returns the Label field. +func (i *IssuesEvent) GetLabel() *Label { + if i == nil { + return nil + } + return i.Label +} + +// GetMilestone returns the Milestone field. +func (i *IssuesEvent) GetMilestone() *Milestone { + if i == nil { + return nil + } + return i.Milestone +} + +// GetOrg returns the Org field. +func (i *IssuesEvent) GetOrg() *Organization { + if i == nil { + return nil + } + return i.Org +} + +// GetRepo returns the Repo field. +func (i *IssuesEvent) GetRepo() *Repository { + if i == nil { + return nil + } + return i.Repo +} + +// GetSender returns the Sender field. +func (i *IssuesEvent) GetSender() *User { + if i == nil { + return nil + } + return i.Sender +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (i *IssuesSearchResult) GetIncompleteResults() bool { + if i == nil || i.IncompleteResults == nil { + return false + } + return *i.IncompleteResults +} + +// GetIssues returns the Issues slice if it's non-nil, nil otherwise. +func (i *IssuesSearchResult) GetIssues() []*Issue { + if i == nil || i.Issues == nil { + return nil + } + return i.Issues +} + +// GetSearchType returns the SearchType field if it's non-nil, zero value otherwise. +func (i *IssuesSearchResult) GetSearchType() string { + if i == nil || i.SearchType == nil { + return "" + } + return *i.SearchType +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (i *IssuesSearchResult) GetTotal() int { + if i == nil || i.Total == nil { + return 0 + } + return *i.Total +} + +// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetClosedIssues() int { + if i == nil || i.ClosedIssues == nil { + return 0 + } + return *i.ClosedIssues +} + +// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetOpenIssues() int { + if i == nil || i.OpenIssues == nil { + return 0 + } + return *i.OpenIssues +} + +// GetTotalIssues returns the TotalIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetTotalIssues() int { + if i == nil || i.TotalIssues == nil { + return 0 + } + return *i.TotalIssues +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (i *IssueType) GetColor() string { + if i == nil || i.Color == nil { + return "" + } + return *i.Color +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueType) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (i *IssueType) GetDescription() string { + if i == nil || i.Description == nil { + return "" + } + return *i.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueType) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (i *IssueType) GetName() string { + if i == nil || i.Name == nil { + return "" + } + return *i.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *IssueType) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" + } + return *i.NodeID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueType) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} + } + return *i.UpdatedAt +} + +// GetEncodedJITConfig returns the EncodedJITConfig field if it's non-nil, zero value otherwise. +func (j *JITRunnerConfig) GetEncodedJITConfig() string { + if j == nil || j.EncodedJITConfig == nil { + return "" + } + return *j.EncodedJITConfig +} + +// GetRunner returns the Runner field. +func (j *JITRunnerConfig) GetRunner() *Runner { + if j == nil { + return nil + } + return j.Runner +} + +// GetJobs returns the Jobs slice if it's non-nil, nil otherwise. +func (j *Jobs) GetJobs() []*WorkflowJob { + if j == nil || j.Jobs == nil { + return nil + } + return j.Jobs +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (j *Jobs) GetTotalCount() int { + if j == nil || j.TotalCount == nil { + return 0 + } + return *j.TotalCount +} + +// GetAddedBy returns the AddedBy field if it's non-nil, zero value otherwise. +func (k *Key) GetAddedBy() string { + if k == nil || k.AddedBy == nil { + return "" + } + return *k.AddedBy +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (k *Key) GetCreatedAt() Timestamp { + if k == nil || k.CreatedAt == nil { + return Timestamp{} + } + return *k.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (k *Key) GetID() int64 { + if k == nil || k.ID == nil { + return 0 + } + return *k.ID +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (k *Key) GetKey() string { + if k == nil || k.Key == nil { + return "" + } + return *k.Key +} + +// GetLastUsed returns the LastUsed field if it's non-nil, zero value otherwise. +func (k *Key) GetLastUsed() Timestamp { + if k == nil || k.LastUsed == nil { + return Timestamp{} + } + return *k.LastUsed +} + +// GetReadOnly returns the ReadOnly field if it's non-nil, zero value otherwise. +func (k *Key) GetReadOnly() bool { + if k == nil || k.ReadOnly == nil { + return false + } + return *k.ReadOnly +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (k *Key) GetTitle() string { + if k == nil || k.Title == nil { + return "" + } + return *k.Title +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (k *Key) GetURL() string { + if k == nil || k.URL == nil { + return "" + } + return *k.URL +} + +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (k *Key) GetVerified() bool { + if k == nil || k.Verified == nil { + return false + } + return *k.Verified +} + +// GetColor returns the Color field. +func (l *Label) GetColor() string { + if l == nil { + return "" + } + return l.Color +} + +// GetDefault returns the Default field. +func (l *Label) GetDefault() bool { + if l == nil { + return false + } + return l.Default +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (l *Label) GetDescription() string { + if l == nil || l.Description == nil { + return "" + } + return *l.Description +} + +// GetID returns the ID field. +func (l *Label) GetID() int64 { + if l == nil { + return 0 + } + return l.ID +} + +// GetName returns the Name field. +func (l *Label) GetName() string { + if l == nil { + return "" + } + return l.Name +} + +// GetNodeID returns the NodeID field. +func (l *Label) GetNodeID() string { + if l == nil { + return "" + } + return l.NodeID +} + +// GetURL returns the URL field. +func (l *Label) GetURL() string { + if l == nil { + return "" + } + return l.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (l *LabelEvent) GetAction() string { + if l == nil || l.Action == nil { + return "" + } + return *l.Action +} + +// GetChanges returns the Changes field. +func (l *LabelEvent) GetChanges() *EditChange { + if l == nil { + return nil + } + return l.Changes +} + +// GetInstallation returns the Installation field. +func (l *LabelEvent) GetInstallation() *Installation { + if l == nil { + return nil + } + return l.Installation +} + +// GetLabel returns the Label field. +func (l *LabelEvent) GetLabel() *Label { + if l == nil { + return nil + } + return l.Label +} + +// GetOrg returns the Org field. +func (l *LabelEvent) GetOrg() *Organization { + if l == nil { + return nil + } + return l.Org +} + +// GetRepo returns the Repo field. +func (l *LabelEvent) GetRepo() *Repository { + if l == nil { + return nil + } + return l.Repo +} + +// GetSender returns the Sender field. +func (l *LabelEvent) GetSender() *User { + if l == nil { + return nil + } + return l.Sender +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetColor() string { + if l == nil || l.Color == nil { + return "" + } + return *l.Color +} + +// GetDefault returns the Default field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetDefault() bool { + if l == nil || l.Default == nil { + return false + } + return *l.Default +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetDescription() string { + if l == nil || l.Description == nil { + return "" + } + return *l.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetID() int64 { + if l == nil || l.ID == nil { + return 0 + } + return *l.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetName() string { + if l == nil || l.Name == nil { + return "" + } + return *l.Name +} + +// GetScore returns the Score field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetScore() float64 { + if l == nil || l.Score == nil { + return 0 + } + return *l.Score +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (l *LabelResult) GetURL() string { + if l == nil || l.URL == nil { + return "" + } + return *l.URL +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (l *LabelsSearchResult) GetIncompleteResults() bool { + if l == nil || l.IncompleteResults == nil { + return false + } + return *l.IncompleteResults +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (l *LabelsSearchResult) GetLabels() []*LabelResult { + if l == nil || l.Labels == nil { + return nil + } + return l.Labels +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (l *LabelsSearchResult) GetTotal() int { + if l == nil || l.Total == nil { + return 0 + } + return *l.Total +} + +// GetOID returns the OID field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetOID() string { + if l == nil || l.OID == nil { + return "" + } + return *l.OID +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetPath() string { + if l == nil || l.Path == nil { + return "" + } + return *l.Path +} + +// GetRefName returns the RefName field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetRefName() string { + if l == nil || l.RefName == nil { + return "" + } + return *l.RefName +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (l *LargeFile) GetSize() int { + if l == nil || l.Size == nil { + return 0 + } + return *l.Size +} + +// GetProperties returns the Properties field. +func (l *LastLicenseSync) GetProperties() *LastLicenseSyncProperties { + if l == nil { + return nil + } + return l.Properties +} + +// GetType returns the Type field. +func (l *LastLicenseSync) GetType() string { + if l == nil { + return "" + } + return l.Type +} + +// GetDate returns the Date field if it's non-nil, zero value otherwise. +func (l *LastLicenseSyncProperties) GetDate() Timestamp { + if l == nil || l.Date == nil { + return Timestamp{} + } + return *l.Date +} + +// GetError returns the Error field. +func (l *LastLicenseSyncProperties) GetError() string { + if l == nil { + return "" + } + return l.Error +} + +// GetStatus returns the Status field. +func (l *LastLicenseSyncProperties) GetStatus() string { + if l == nil { + return "" + } + return l.Status +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (l *License) GetBody() string { + if l == nil || l.Body == nil { + return "" + } + return *l.Body +} + +// GetConditions returns the Conditions field if it's non-nil, zero value otherwise. +func (l *License) GetConditions() []string { + if l == nil || l.Conditions == nil { + return nil + } + return *l.Conditions +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (l *License) GetDescription() string { + if l == nil || l.Description == nil { + return "" + } + return *l.Description +} + +// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. +func (l *License) GetFeatured() bool { + if l == nil || l.Featured == nil { + return false + } + return *l.Featured +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (l *License) GetHTMLURL() string { + if l == nil || l.HTMLURL == nil { + return "" + } + return *l.HTMLURL +} + +// GetImplementation returns the Implementation field if it's non-nil, zero value otherwise. +func (l *License) GetImplementation() string { + if l == nil || l.Implementation == nil { + return "" + } + return *l.Implementation +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (l *License) GetKey() string { + if l == nil || l.Key == nil { + return "" + } + return *l.Key +} + +// GetLimitations returns the Limitations field if it's non-nil, zero value otherwise. +func (l *License) GetLimitations() []string { + if l == nil || l.Limitations == nil { + return nil + } + return *l.Limitations +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (l *License) GetName() string { + if l == nil || l.Name == nil { + return "" + } + return *l.Name +} + +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (l *License) GetPermissions() []string { + if l == nil || l.Permissions == nil { + return nil + } + return *l.Permissions +} + +// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. +func (l *License) GetSPDXID() string { + if l == nil || l.SPDXID == nil { + return "" + } + return *l.SPDXID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (l *License) GetURL() string { + if l == nil || l.URL == nil { + return "" + } + return *l.URL +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (l *LicenseCheck) GetStatus() string { + if l == nil || l.Status == nil { + return "" + } + return *l.Status +} + +// GetAdvancedSecurityEnabled returns the AdvancedSecurityEnabled field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetAdvancedSecurityEnabled() bool { + if l == nil || l.AdvancedSecurityEnabled == nil { + return false + } + return *l.AdvancedSecurityEnabled +} + +// GetAdvancedSecuritySeats returns the AdvancedSecuritySeats field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetAdvancedSecuritySeats() int { + if l == nil || l.AdvancedSecuritySeats == nil { + return 0 + } + return *l.AdvancedSecuritySeats +} + +// GetClusterSupport returns the ClusterSupport field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetClusterSupport() bool { + if l == nil || l.ClusterSupport == nil { + return false + } + return *l.ClusterSupport +} + +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetCompany() string { + if l == nil || l.Company == nil { + return "" + } + return *l.Company +} + +// GetCroquetSupport returns the CroquetSupport field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetCroquetSupport() bool { + if l == nil || l.CroquetSupport == nil { + return false + } + return *l.CroquetSupport +} + +// GetCustomTerms returns the CustomTerms field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetCustomTerms() bool { + if l == nil || l.CustomTerms == nil { + return false + } + return *l.CustomTerms +} + +// GetEvaluation returns the Evaluation field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetEvaluation() bool { + if l == nil || l.Evaluation == nil { + return false + } + return *l.Evaluation +} + +// GetExpireAt returns the ExpireAt field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetExpireAt() Timestamp { + if l == nil || l.ExpireAt == nil { + return Timestamp{} + } + return *l.ExpireAt +} + +// GetInsightsEnabled returns the InsightsEnabled field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetInsightsEnabled() bool { + if l == nil || l.InsightsEnabled == nil { + return false + } + return *l.InsightsEnabled +} + +// GetInsightsExpireAt returns the InsightsExpireAt field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetInsightsExpireAt() Timestamp { + if l == nil || l.InsightsExpireAt == nil { + return Timestamp{} + } + return *l.InsightsExpireAt +} + +// GetLearningLabEvaluationExpires returns the LearningLabEvaluationExpires field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetLearningLabEvaluationExpires() Timestamp { + if l == nil || l.LearningLabEvaluationExpires == nil { + return Timestamp{} + } + return *l.LearningLabEvaluationExpires +} + +// GetLearningLabSeats returns the LearningLabSeats field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetLearningLabSeats() int { + if l == nil || l.LearningLabSeats == nil { + return 0 + } + return *l.LearningLabSeats +} + +// GetPerpetual returns the Perpetual field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetPerpetual() bool { + if l == nil || l.Perpetual == nil { + return false + } + return *l.Perpetual +} + +// GetReferenceNumber returns the ReferenceNumber field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetReferenceNumber() string { + if l == nil || l.ReferenceNumber == nil { + return "" + } + return *l.ReferenceNumber +} + +// GetSeats returns the Seats field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetSeats() int { + if l == nil || l.Seats == nil { + return 0 + } + return *l.Seats +} + +// GetSSHAllowed returns the SSHAllowed field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetSSHAllowed() bool { + if l == nil || l.SSHAllowed == nil { + return false + } + return *l.SSHAllowed +} + +// GetSupportKey returns the SupportKey field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetSupportKey() bool { + if l == nil || l.SupportKey == nil { + return false + } + return *l.SupportKey +} + +// GetUnlimitedSeating returns the UnlimitedSeating field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetUnlimitedSeating() bool { + if l == nil || l.UnlimitedSeating == nil { + return false + } + return *l.UnlimitedSeating +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (l *LinearHistoryRequirementEnforcementLevelChanges) GetFrom() string { + if l == nil || l.From == nil { + return "" + } + return *l.From +} + +// GetDirection returns the Direction field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetDirection() string { + if l == nil || l.Direction == nil { + return "" + } + return *l.Direction +} + +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetEcosystem() string { + if l == nil || l.Ecosystem == nil { + return "" + } + return *l.Ecosystem +} + +// GetPackage returns the Package field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetPackage() string { + if l == nil || l.Package == nil { + return "" + } + return *l.Package +} + +// GetScope returns the Scope field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetScope() string { + if l == nil || l.Scope == nil { + return "" + } + return *l.Scope +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetSeverity() string { + if l == nil || l.Severity == nil { + return "" + } + return *l.Severity +} + +// GetSort returns the Sort field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetSort() string { + if l == nil || l.Sort == nil { + return "" + } + return *l.Sort +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (l *ListAlertsOptions) GetState() string { + if l == nil || l.State == nil { + return "" + } + return *l.State +} + +// GetCollab returns the Collab field. +func (l *ListAllIssuesOptions) GetCollab() bool { + if l == nil { + return false + } + return l.Collab +} + +// GetDirection returns the Direction field. +func (l *ListAllIssuesOptions) GetDirection() string { + if l == nil { + return "" + } + return l.Direction +} + +// GetFilter returns the Filter field. +func (l *ListAllIssuesOptions) GetFilter() string { + if l == nil { + return "" + } + return l.Filter +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (l *ListAllIssuesOptions) GetLabels() []string { + if l == nil || l.Labels == nil { + return nil + } + return l.Labels +} + +// GetOrgs returns the Orgs field. +func (l *ListAllIssuesOptions) GetOrgs() bool { + if l == nil { + return false + } + return l.Orgs +} + +// GetOwned returns the Owned field. +func (l *ListAllIssuesOptions) GetOwned() bool { + if l == nil { + return false + } + return l.Owned +} + +// GetPulls returns the Pulls field. +func (l *ListAllIssuesOptions) GetPulls() bool { + if l == nil { + return false + } + return l.Pulls +} + +// GetSince returns the Since field. +func (l *ListAllIssuesOptions) GetSince() time.Time { + if l == nil { + return time.Time{} + } + return l.Since +} + +// GetSort returns the Sort field. +func (l *ListAllIssuesOptions) GetSort() string { + if l == nil { + return "" + } + return l.Sort +} + +// GetState returns the State field. +func (l *ListAllIssuesOptions) GetState() string { + if l == nil { + return "" + } + return l.State +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (l *ListArtifactsOptions) GetName() string { + if l == nil || l.Name == nil { + return "" + } + return *l.Name +} + +// GetAppID returns the AppID field if it's non-nil, zero value otherwise. +func (l *ListCheckRunsOptions) GetAppID() int64 { + if l == nil || l.AppID == nil { + return 0 + } + return *l.AppID +} + +// GetCheckName returns the CheckName field if it's non-nil, zero value otherwise. +func (l *ListCheckRunsOptions) GetCheckName() string { + if l == nil || l.CheckName == nil { + return "" + } + return *l.CheckName +} + +// GetFilter returns the Filter field if it's non-nil, zero value otherwise. +func (l *ListCheckRunsOptions) GetFilter() string { + if l == nil || l.Filter == nil { + return "" + } + return *l.Filter +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (l *ListCheckRunsOptions) GetStatus() string { + if l == nil || l.Status == nil { + return "" + } + return *l.Status +} + +// GetCheckRuns returns the CheckRuns slice if it's non-nil, nil otherwise. +func (l *ListCheckRunsResults) GetCheckRuns() []*CheckRun { + if l == nil || l.CheckRuns == nil { + return nil + } + return l.CheckRuns +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (l *ListCheckRunsResults) GetTotal() int { + if l == nil || l.Total == nil { + return 0 + } + return *l.Total +} + +// GetAppID returns the AppID field if it's non-nil, zero value otherwise. +func (l *ListCheckSuiteOptions) GetAppID() int64 { + if l == nil || l.AppID == nil { + return 0 + } + return *l.AppID +} + +// GetCheckName returns the CheckName field if it's non-nil, zero value otherwise. +func (l *ListCheckSuiteOptions) GetCheckName() string { + if l == nil || l.CheckName == nil { + return "" + } + return *l.CheckName +} + +// GetCheckSuites returns the CheckSuites slice if it's non-nil, nil otherwise. +func (l *ListCheckSuiteResults) GetCheckSuites() []*CheckSuite { + if l == nil || l.CheckSuites == nil { + return nil + } + return l.CheckSuites +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (l *ListCheckSuiteResults) GetTotal() int { + if l == nil || l.Total == nil { + return 0 + } + return *l.Total +} + +// GetDirection returns the Direction field. +func (l *ListCodeQualityFindingsOptions) GetDirection() string { + if l == nil { + return "" + } + return l.Direction +} + +// GetState returns the State field. +func (l *ListCodeQualityFindingsOptions) GetState() string { + if l == nil { + return "" + } + return l.State +} + +// GetAfter returns the After field. +func (l *ListCodeSecurityConfigurationRepositoriesOptions) GetAfter() string { + if l == nil { + return "" + } + return l.After +} + +// GetBefore returns the Before field. +func (l *ListCodeSecurityConfigurationRepositoriesOptions) GetBefore() string { + if l == nil { + return "" + } + return l.Before +} + +// GetPerPage returns the PerPage field. +func (l *ListCodeSecurityConfigurationRepositoriesOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetStatus returns the Status field. +func (l *ListCodeSecurityConfigurationRepositoriesOptions) GetStatus() string { + if l == nil { + return "" + } + return l.Status +} + +// GetCodespaces returns the Codespaces slice if it's non-nil, nil otherwise. +func (l *ListCodespaces) GetCodespaces() []*Codespace { + if l == nil || l.Codespaces == nil { + return nil + } + return l.Codespaces +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (l *ListCodespaces) GetTotalCount() int { + if l == nil || l.TotalCount == nil { + return 0 + } + return *l.TotalCount +} + +// GetRepositoryID returns the RepositoryID field. +func (l *ListCodespacesOptions) GetRepositoryID() int64 { + if l == nil { + return 0 + } + return l.RepositoryID +} + +// GetAffiliation returns the Affiliation field. +func (l *ListCollaboratorsOptions) GetAffiliation() string { + if l == nil { + return "" + } + return l.Affiliation +} + +// GetPermission returns the Permission field. +func (l *ListCollaboratorsOptions) GetPermission() string { + if l == nil { + return "" + } + return l.Permission +} + +// GetAnon returns the Anon field. +func (l *ListContributorsOptions) GetAnon() string { + if l == nil { + return "" + } + return l.Anon +} + +// GetSeats returns the Seats slice if it's non-nil, nil otherwise. +func (l *ListCopilotSeatsResponse) GetSeats() []*CopilotSeatDetails { + if l == nil || l.Seats == nil { + return nil + } + return l.Seats +} + +// GetTotalSeats returns the TotalSeats field. +func (l *ListCopilotSeatsResponse) GetTotalSeats() int64 { + if l == nil { + return 0 + } + return l.TotalSeats +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (l *ListCostCenterOptions) GetState() string { + if l == nil || l.State == nil { + return "" + } + return *l.State +} + +// GetAfter returns the After field. +func (l *ListCursorOptions) GetAfter() string { + if l == nil { + return "" + } + return l.After +} + +// GetBefore returns the Before field. +func (l *ListCursorOptions) GetBefore() string { + if l == nil { + return "" + } + return l.Before +} + +// GetCursor returns the Cursor field. +func (l *ListCursorOptions) GetCursor() string { + if l == nil { + return "" + } + return l.Cursor +} + +// GetFirst returns the First field. +func (l *ListCursorOptions) GetFirst() int { + if l == nil { + return 0 + } + return l.First +} + +// GetLast returns the Last field. +func (l *ListCursorOptions) GetLast() int { + if l == nil { + return 0 + } + return l.Last +} + +// GetPage returns the Page field. +func (l *ListCursorOptions) GetPage() string { + if l == nil { + return "" + } + return l.Page +} + +// GetPerPage returns the PerPage field. +func (l *ListCursorOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetAvailableIntegrations returns the AvailableIntegrations slice if it's non-nil, nil otherwise. +func (l *ListCustomDeploymentRuleIntegrationsResponse) GetAvailableIntegrations() []*CustomDeploymentProtectionRuleApp { + if l == nil || l.AvailableIntegrations == nil { + return nil + } + return l.AvailableIntegrations +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (l *ListCustomDeploymentRuleIntegrationsResponse) GetTotalCount() int { + if l == nil || l.TotalCount == nil { + return 0 + } + return *l.TotalCount +} + +// GetRepositoryQuery returns the RepositoryQuery field. +func (l *ListCustomPropertyValuesOptions) GetRepositoryQuery() string { + if l == nil { + return "" + } + return l.RepositoryQuery +} + +// GetProtectionRules returns the ProtectionRules slice if it's non-nil, nil otherwise. +func (l *ListDeploymentProtectionRuleResponse) GetProtectionRules() []*CustomDeploymentProtectionRule { + if l == nil || l.ProtectionRules == nil { + return nil + } + return l.ProtectionRules +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (l *ListDeploymentProtectionRuleResponse) GetTotalCount() int { + if l == nil || l.TotalCount == nil { + return 0 + } + return *l.TotalCount +} + +// GetAfter returns the After field. +func (l *ListEnterpriseCodeSecurityConfigurationOptions) GetAfter() string { + if l == nil { + return "" + } + return l.After +} + +// GetBefore returns the Before field. +func (l *ListEnterpriseCodeSecurityConfigurationOptions) GetBefore() string { + if l == nil { + return "" + } + return l.Before +} + +// GetPerPage returns the PerPage field. +func (l *ListEnterpriseCodeSecurityConfigurationOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetVisibleToOrganization returns the VisibleToOrganization field. +func (l *ListEnterpriseRunnerGroupOptions) GetVisibleToOrganization() string { + if l == nil { + return "" + } + return l.VisibleToOrganization +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (l *ListExternalGroupsOptions) GetDisplayName() string { + if l == nil || l.DisplayName == nil { + return "" + } + return *l.DisplayName +} + +// GetDirection returns the Direction field. +func (l *ListFineGrainedPATOptions) GetDirection() string { + if l == nil { + return "" + } + return l.Direction +} + +// GetLastUsedAfter returns the LastUsedAfter field. +func (l *ListFineGrainedPATOptions) GetLastUsedAfter() string { + if l == nil { + return "" + } + return l.LastUsedAfter +} + +// GetLastUsedBefore returns the LastUsedBefore field. +func (l *ListFineGrainedPATOptions) GetLastUsedBefore() string { + if l == nil { + return "" + } + return l.LastUsedBefore +} + +// GetOwner returns the Owner slice if it's non-nil, nil otherwise. +func (l *ListFineGrainedPATOptions) GetOwner() []string { + if l == nil || l.Owner == nil { + return nil + } + return l.Owner +} + +// GetPermission returns the Permission field. +func (l *ListFineGrainedPATOptions) GetPermission() string { + if l == nil { + return "" + } + return l.Permission +} + +// GetRepository returns the Repository field. +func (l *ListFineGrainedPATOptions) GetRepository() string { + if l == nil { + return "" + } + return l.Repository +} + +// GetSort returns the Sort field. +func (l *ListFineGrainedPATOptions) GetSort() string { + if l == nil { + return "" + } + return l.Sort +} + +// GetTokenID returns the TokenID slice if it's non-nil, nil otherwise. +func (l *ListFineGrainedPATOptions) GetTokenID() []int64 { + if l == nil || l.TokenID == nil { + return nil + } + return l.TokenID +} + +// GetAffects returns the Affects field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetAffects() string { + if l == nil || l.Affects == nil { + return "" + } + return *l.Affects +} + +// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetCVEID() string { + if l == nil || l.CVEID == nil { + return "" + } + return *l.CVEID +} + +// GetCWEs returns the CWEs slice if it's non-nil, nil otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetCWEs() []string { + if l == nil || l.CWEs == nil { + return nil + } + return l.CWEs +} + +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetEcosystem() string { + if l == nil || l.Ecosystem == nil { + return "" + } + return *l.Ecosystem +} + +// GetGHSAID returns the GHSAID field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetGHSAID() string { + if l == nil || l.GHSAID == nil { + return "" + } + return *l.GHSAID +} + +// GetIsWithdrawn returns the IsWithdrawn field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetIsWithdrawn() bool { + if l == nil || l.IsWithdrawn == nil { + return false + } + return *l.IsWithdrawn +} + +// GetModified returns the Modified field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetModified() string { + if l == nil || l.Modified == nil { + return "" + } + return *l.Modified +} + +// GetPublished returns the Published field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetPublished() string { + if l == nil || l.Published == nil { + return "" + } + return *l.Published +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetSeverity() string { + if l == nil || l.Severity == nil { + return "" + } + return *l.Severity +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetType() string { + if l == nil || l.Type == nil { + return "" + } + return *l.Type +} + +// GetUpdated returns the Updated field if it's non-nil, zero value otherwise. +func (l *ListGlobalSecurityAdvisoriesOptions) GetUpdated() string { + if l == nil || l.Updated == nil { + return "" + } + return *l.Updated +} + +// GetQuery returns the Query field. +func (l *ListIDPGroupsOptions) GetQuery() string { + if l == nil { + return "" + } + return l.Query +} + +// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. +func (l *ListLicensesOptions) GetFeatured() bool { + if l == nil || l.Featured == nil { + return false + } + return *l.Featured +} + +// GetFilter returns the Filter field. +func (l *ListMembersOptions) GetFilter() string { + if l == nil { + return "" + } + return l.Filter +} + +// GetPublicOnly returns the PublicOnly field. +func (l *ListMembersOptions) GetPublicOnly() bool { + if l == nil { + return false + } + return l.PublicOnly +} + +// GetRole returns the Role field. +func (l *ListMembersOptions) GetRole() string { + if l == nil { + return "" + } + return l.Role +} + +// GetPage returns the Page field. +func (l *ListOptions) GetPage() int { + if l == nil { + return 0 + } + return l.Page +} + +// GetPerPage returns the PerPage field. +func (l *ListOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (l *ListOrganizationCopilotCodingAgentRepositoriesResponse) GetRepositories() []*Repository { + if l == nil || l.Repositories == nil { + return nil + } + return l.Repositories +} + +// GetTotalCount returns the TotalCount field. +func (l *ListOrganizationCopilotCodingAgentRepositoriesResponse) GetTotalCount() int { + if l == nil { + return 0 + } + return l.TotalCount +} + +// GetOrganizations returns the Organizations slice if it's non-nil, nil otherwise. +func (l *ListOrganizations) GetOrganizations() []*Organization { + if l == nil || l.Organizations == nil { + return nil + } + return l.Organizations +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (l *ListOrganizations) GetTotalCount() int { + if l == nil || l.TotalCount == nil { + return 0 + } + return *l.TotalCount +} + +// GetAfter returns the After field. +func (l *ListOrgCodeSecurityConfigurationOptions) GetAfter() string { + if l == nil { + return "" + } + return l.After +} + +// GetBefore returns the Before field. +func (l *ListOrgCodeSecurityConfigurationOptions) GetBefore() string { + if l == nil { + return "" + } + return l.Before +} + +// GetPerPage returns the PerPage field. +func (l *ListOrgCodeSecurityConfigurationOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetTargetType returns the TargetType field. +func (l *ListOrgCodeSecurityConfigurationOptions) GetTargetType() string { + if l == nil { + return "" + } + return l.TargetType +} + +// GetState returns the State field. +func (l *ListOrgMembershipsOptions) GetState() string { + if l == nil { + return "" + } + return l.State +} + +// GetVisibleToRepository returns the VisibleToRepository field. +func (l *ListOrgRunnerGroupOptions) GetVisibleToRepository() string { + if l == nil { + return "" + } + return l.VisibleToRepository +} + +// GetFilter returns the Filter field. +func (l *ListOutsideCollaboratorsOptions) GetFilter() string { + if l == nil { + return "" + } + return l.Filter +} + +// GetState returns the State field. +func (l *ListPackageVersionsOptions) GetState() string { + if l == nil { + return "" + } + return l.State +} + +// GetFields returns the Fields slice if it's non-nil, nil otherwise. +func (l *ListProjectItemsOptions) GetFields() []int64 { + if l == nil || l.Fields == nil { + return nil + } + return l.Fields +} + +// GetQuery returns the Query field. +func (l *ListProjectsOptions) GetQuery() string { + if l == nil { + return "" + } + return l.Query +} + +// GetAfter returns the After field. +func (l *ListProjectsPaginationOptions) GetAfter() string { + if l == nil { + return "" + } + return l.After +} + +// GetBefore returns the Before field. +func (l *ListProjectsPaginationOptions) GetBefore() string { + if l == nil { + return "" + } + return l.Before +} + +// GetPerPage returns the PerPage field. +func (l *ListProjectsPaginationOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMGroupsEnterpriseOptions) GetCount() int { + if l == nil || l.Count == nil { + return 0 + } + return *l.Count +} + +// GetExcludedAttributes returns the ExcludedAttributes field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMGroupsEnterpriseOptions) GetExcludedAttributes() string { + if l == nil || l.ExcludedAttributes == nil { + return "" + } + return *l.ExcludedAttributes +} + +// GetFilter returns the Filter field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMGroupsEnterpriseOptions) GetFilter() string { + if l == nil || l.Filter == nil { + return "" + } + return *l.Filter +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMGroupsEnterpriseOptions) GetStartIndex() int { + if l == nil || l.StartIndex == nil { + return 0 + } + return *l.StartIndex +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMUsersEnterpriseOptions) GetCount() int { + if l == nil || l.Count == nil { + return 0 + } + return *l.Count +} + +// GetFilter returns the Filter field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMUsersEnterpriseOptions) GetFilter() string { + if l == nil || l.Filter == nil { + return "" + } + return *l.Filter +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (l *ListProvisionedSCIMUsersEnterpriseOptions) GetStartIndex() int { + if l == nil || l.StartIndex == nil { + return 0 + } + return *l.StartIndex +} + +// GetContent returns the Content field. +func (l *ListReactionOptions) GetContent() string { + if l == nil { + return "" + } + return l.Content +} + +// GetClientIP returns the ClientIP field if it's non-nil, zero value otherwise. +func (l *ListRepoMachineTypesOptions) GetClientIP() string { + if l == nil || l.ClientIP == nil { + return "" + } + return *l.ClientIP +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (l *ListRepoMachineTypesOptions) GetLocation() string { + if l == nil || l.Location == nil { + return "" + } + return *l.Location +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (l *ListRepoMachineTypesOptions) GetRef() string { + if l == nil || l.Ref == nil { + return "" + } + return *l.Ref +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (l *ListRepositories) GetRepositories() []*Repository { + if l == nil || l.Repositories == nil { + return nil + } + return l.Repositories +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (l *ListRepositories) GetTotalCount() int { + if l == nil || l.TotalCount == nil { + return 0 + } + return *l.TotalCount +} + +// GetActivityType returns the ActivityType field. +func (l *ListRepositoryActivityOptions) GetActivityType() string { + if l == nil { + return "" + } + return l.ActivityType +} + +// GetActor returns the Actor field. +func (l *ListRepositoryActivityOptions) GetActor() string { + if l == nil { + return "" + } + return l.Actor +} + +// GetAfter returns the After field. +func (l *ListRepositoryActivityOptions) GetAfter() string { + if l == nil { + return "" + } + return l.After +} + +// GetBefore returns the Before field. +func (l *ListRepositoryActivityOptions) GetBefore() string { + if l == nil { + return "" + } + return l.Before +} + +// GetDirection returns the Direction field. +func (l *ListRepositoryActivityOptions) GetDirection() string { + if l == nil { + return "" + } + return l.Direction +} + +// GetPerPage returns the PerPage field. +func (l *ListRepositoryActivityOptions) GetPerPage() int { + if l == nil { + return 0 + } + return l.PerPage +} + +// GetRef returns the Ref field. +func (l *ListRepositoryActivityOptions) GetRef() string { + if l == nil { + return "" + } + return l.Ref +} + +// GetTimePeriod returns the TimePeriod field. +func (l *ListRepositoryActivityOptions) GetTimePeriod() string { + if l == nil { + return "" + } + return l.TimePeriod +} + +// GetDirection returns the Direction field. +func (l *ListRepositorySecurityAdvisoriesOptions) GetDirection() string { + if l == nil { + return "" + } + return l.Direction +} + +// GetSort returns the Sort field. +func (l *ListRepositorySecurityAdvisoriesOptions) GetSort() string { + if l == nil { + return "" + } + return l.Sort +} + +// GetState returns the State field. +func (l *ListRepositorySecurityAdvisoriesOptions) GetState() string { + if l == nil { + return "" + } + return l.State +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (l *ListRunnersOptions) GetName() string { + if l == nil || l.Name == nil { + return "" + } + return *l.Name +} + +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (l *ListSCIMProvisionedIdentitiesOptions) GetCount() int { + if l == nil || l.Count == nil { + return 0 + } + return *l.Count +} + +// GetFilter returns the Filter field if it's non-nil, zero value otherwise. +func (l *ListSCIMProvisionedIdentitiesOptions) GetFilter() string { + if l == nil || l.Filter == nil { + return "" + } + return *l.Filter +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (l *ListSCIMProvisionedIdentitiesOptions) GetStartIndex() int { + if l == nil || l.StartIndex == nil { + return 0 + } + return *l.StartIndex +} + +// GetDirection returns the Direction field. +func (l *ListUserIssuesOptions) GetDirection() string { + if l == nil { + return "" + } + return l.Direction +} + +// GetFilter returns the Filter field. +func (l *ListUserIssuesOptions) GetFilter() string { + if l == nil { + return "" + } + return l.Filter +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (l *ListUserIssuesOptions) GetLabels() []string { + if l == nil || l.Labels == nil { + return nil + } + return l.Labels +} + +// GetSince returns the Since field. +func (l *ListUserIssuesOptions) GetSince() time.Time { + if l == nil { + return time.Time{} + } + return l.Since +} + +// GetSort returns the Sort field. +func (l *ListUserIssuesOptions) GetSort() string { + if l == nil { + return "" + } + return l.Sort +} + +// GetState returns the State field. +func (l *ListUserIssuesOptions) GetState() string { + if l == nil { + return "" + } + return l.State +} + +// GetFilter returns the Filter field. +func (l *ListWorkflowJobsOptions) GetFilter() string { + if l == nil { + return "" + } + return l.Filter +} + +// GetActor returns the Actor field. +func (l *ListWorkflowRunsOptions) GetActor() string { + if l == nil { + return "" + } + return l.Actor +} + +// GetBranch returns the Branch field. +func (l *ListWorkflowRunsOptions) GetBranch() string { + if l == nil { + return "" + } + return l.Branch +} + +// GetCheckSuiteID returns the CheckSuiteID field. +func (l *ListWorkflowRunsOptions) GetCheckSuiteID() int64 { + if l == nil { + return 0 + } + return l.CheckSuiteID +} + +// GetCreated returns the Created field. +func (l *ListWorkflowRunsOptions) GetCreated() string { + if l == nil { + return "" + } + return l.Created +} + +// GetEvent returns the Event field. +func (l *ListWorkflowRunsOptions) GetEvent() string { + if l == nil { + return "" + } + return l.Event +} + +// GetExcludePullRequests returns the ExcludePullRequests field. +func (l *ListWorkflowRunsOptions) GetExcludePullRequests() bool { + if l == nil { + return false + } + return l.ExcludePullRequests +} + +// GetHeadSHA returns the HeadSHA field. +func (l *ListWorkflowRunsOptions) GetHeadSHA() string { + if l == nil { + return "" + } + return l.HeadSHA +} + +// GetStatus returns the Status field. +func (l *ListWorkflowRunsOptions) GetStatus() string { + if l == nil { + return "" + } + return l.Status +} + +// GetEndColumn returns the EndColumn field if it's non-nil, zero value otherwise. +func (l *Location) GetEndColumn() int { + if l == nil || l.EndColumn == nil { + return 0 + } + return *l.EndColumn +} + +// GetEndLine returns the EndLine field if it's non-nil, zero value otherwise. +func (l *Location) GetEndLine() int { + if l == nil || l.EndLine == nil { + return 0 + } + return *l.EndLine +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (l *Location) GetPath() string { + if l == nil || l.Path == nil { + return "" + } + return *l.Path +} + +// GetStartColumn returns the StartColumn field if it's non-nil, zero value otherwise. +func (l *Location) GetStartColumn() int { + if l == nil || l.StartColumn == nil { + return 0 + } + return *l.StartColumn +} + +// GetStartLine returns the StartLine field if it's non-nil, zero value otherwise. +func (l *Location) GetStartLine() int { + if l == nil || l.StartLine == nil { + return 0 + } + return *l.StartLine +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (l *LockBranch) GetEnabled() bool { + if l == nil || l.Enabled == nil { + return false + } + return *l.Enabled +} + +// GetLockReason returns the LockReason field. +func (l *LockIssueOptions) GetLockReason() string { + if l == nil { + return "" + } + return l.LockReason +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (m *MaintenanceOperationStatus) GetHostname() string { + if m == nil || m.Hostname == nil { + return "" + } + return *m.Hostname +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (m *MaintenanceOperationStatus) GetMessage() string { + if m == nil || m.Message == nil { + return "" + } + return *m.Message +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (m *MaintenanceOperationStatus) GetUUID() string { + if m == nil || m.UUID == nil { + return "" + } + return *m.UUID +} + +// GetEnabled returns the Enabled field. +func (m *MaintenanceOptions) GetEnabled() bool { + if m == nil { + return false + } + return m.Enabled +} + +// GetIPExceptionList returns the IPExceptionList slice if it's non-nil, nil otherwise. +func (m *MaintenanceOptions) GetIPExceptionList() []string { + if m == nil || m.IPExceptionList == nil { + return nil + } + return m.IPExceptionList +} + +// GetMaintenanceModeMessage returns the MaintenanceModeMessage field if it's non-nil, zero value otherwise. +func (m *MaintenanceOptions) GetMaintenanceModeMessage() string { + if m == nil || m.MaintenanceModeMessage == nil { + return "" + } + return *m.MaintenanceModeMessage +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (m *MaintenanceOptions) GetUUID() string { + if m == nil || m.UUID == nil { + return "" + } + return *m.UUID +} + +// GetWhen returns the When field if it's non-nil, zero value otherwise. +func (m *MaintenanceOptions) GetWhen() string { + if m == nil || m.When == nil { + return "" + } + return *m.When +} + +// GetCanUnsetMaintenance returns the CanUnsetMaintenance field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetCanUnsetMaintenance() bool { + if m == nil || m.CanUnsetMaintenance == nil { + return false + } + return *m.CanUnsetMaintenance +} + +// GetConnectionServices returns the ConnectionServices slice if it's non-nil, nil otherwise. +func (m *MaintenanceStatus) GetConnectionServices() []*ConnectionServiceItem { + if m == nil || m.ConnectionServices == nil { + return nil + } + return m.ConnectionServices +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetHostname() string { + if m == nil || m.Hostname == nil { + return "" + } + return *m.Hostname +} + +// GetIPExceptionList returns the IPExceptionList slice if it's non-nil, nil otherwise. +func (m *MaintenanceStatus) GetIPExceptionList() []string { + if m == nil || m.IPExceptionList == nil { + return nil + } + return m.IPExceptionList +} + +// GetMaintenanceModeMessage returns the MaintenanceModeMessage field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetMaintenanceModeMessage() string { + if m == nil || m.MaintenanceModeMessage == nil { + return "" + } + return *m.MaintenanceModeMessage +} + +// GetScheduledTime returns the ScheduledTime field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetScheduledTime() Timestamp { + if m == nil || m.ScheduledTime == nil { + return Timestamp{} + } + return *m.ScheduledTime +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetStatus() string { + if m == nil || m.Status == nil { + return "" + } + return *m.Status +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetUUID() string { + if m == nil || m.UUID == nil { + return "" + } + return *m.UUID +} + +// GetContext returns the Context field. +func (m *MarkdownOptions) GetContext() string { + if m == nil { + return "" + } + return m.Context +} + +// GetMode returns the Mode field. +func (m *MarkdownOptions) GetMode() string { + if m == nil { + return "" + } + return m.Mode +} + +// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. +func (m *MarketplacePendingChange) GetEffectiveDate() Timestamp { + if m == nil || m.EffectiveDate == nil { + return Timestamp{} + } + return *m.EffectiveDate +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePendingChange) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetPlan returns the Plan field. +func (m *MarketplacePendingChange) GetPlan() *MarketplacePlan { + if m == nil { + return nil + } + return m.Plan +} + +// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. +func (m *MarketplacePendingChange) GetUnitCount() int { + if m == nil || m.UnitCount == nil { + return 0 + } + return *m.UnitCount +} + +// GetAccountsURL returns the AccountsURL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetAccountsURL() string { + if m == nil || m.AccountsURL == nil { + return "" + } + return *m.AccountsURL +} + +// GetBullets returns the Bullets field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetBullets() []string { + if m == nil || m.Bullets == nil { + return nil + } + return *m.Bullets +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetDescription() string { + if m == nil || m.Description == nil { + return "" + } + return *m.Description +} + +// GetHasFreeTrial returns the HasFreeTrial field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetHasFreeTrial() bool { + if m == nil || m.HasFreeTrial == nil { + return false + } + return *m.HasFreeTrial +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetMonthlyPriceInCents returns the MonthlyPriceInCents field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetMonthlyPriceInCents() int { + if m == nil || m.MonthlyPriceInCents == nil { + return 0 + } + return *m.MonthlyPriceInCents +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetName() string { + if m == nil || m.Name == nil { + return "" + } + return *m.Name +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetNumber() int { + if m == nil || m.Number == nil { + return 0 + } + return *m.Number +} + +// GetPriceModel returns the PriceModel field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetPriceModel() string { + if m == nil || m.PriceModel == nil { + return "" + } + return *m.PriceModel +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetUnitName returns the UnitName field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetUnitName() string { + if m == nil || m.UnitName == nil { + return "" + } + return *m.UnitName +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetYearlyPriceInCents returns the YearlyPriceInCents field if it's non-nil, zero value otherwise. +func (m *MarketplacePlan) GetYearlyPriceInCents() int { + if m == nil || m.YearlyPriceInCents == nil { + return 0 + } + return *m.YearlyPriceInCents +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetLogin() string { + if m == nil || m.Login == nil { + return "" + } + return *m.Login +} + +// GetMarketplacePendingChange returns the MarketplacePendingChange field. +func (m *MarketplacePlanAccount) GetMarketplacePendingChange() *MarketplacePendingChange { + if m == nil { + return nil + } + return m.MarketplacePendingChange +} + +// GetMarketplacePurchase returns the MarketplacePurchase field. +func (m *MarketplacePlanAccount) GetMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.MarketplacePurchase +} + +// GetOrganizationBillingEmail returns the OrganizationBillingEmail field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetOrganizationBillingEmail() string { + if m == nil || m.OrganizationBillingEmail == nil { + return "" + } + return *m.OrganizationBillingEmail +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetType() string { + if m == nil || m.Type == nil { + return "" + } + return *m.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePlanAccount) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetAccount returns the Account field. +func (m *MarketplacePurchase) GetAccount() *MarketplacePurchaseAccount { + if m == nil { + return nil + } + return m.Account +} + +// GetBillingCycle returns the BillingCycle field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetBillingCycle() string { + if m == nil || m.BillingCycle == nil { + return "" + } + return *m.BillingCycle +} + +// GetFreeTrialEndsOn returns the FreeTrialEndsOn field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetFreeTrialEndsOn() Timestamp { + if m == nil || m.FreeTrialEndsOn == nil { + return Timestamp{} + } + return *m.FreeTrialEndsOn +} + +// GetNextBillingDate returns the NextBillingDate field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetNextBillingDate() Timestamp { + if m == nil || m.NextBillingDate == nil { + return Timestamp{} + } + return *m.NextBillingDate +} + +// GetOnFreeTrial returns the OnFreeTrial field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetOnFreeTrial() bool { + if m == nil || m.OnFreeTrial == nil { + return false + } + return *m.OnFreeTrial +} + +// GetPlan returns the Plan field. +func (m *MarketplacePurchase) GetPlan() *MarketplacePlan { + if m == nil { + return nil + } + return m.Plan +} + +// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetUnitCount() int { + if m == nil || m.UnitCount == nil { + return 0 + } + return *m.UnitCount +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchase) GetUpdatedAt() Timestamp { + if m == nil || m.UpdatedAt == nil { + return Timestamp{} + } + return *m.UpdatedAt +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetEmail() string { + if m == nil || m.Email == nil { + return "" + } + return *m.Email +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetLogin() string { + if m == nil || m.Login == nil { + return "" + } + return *m.Login +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetNodeID() string { + if m == nil || m.NodeID == nil { + return "" + } + return *m.NodeID +} + +// GetOrganizationBillingEmail returns the OrganizationBillingEmail field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetOrganizationBillingEmail() string { + if m == nil || m.OrganizationBillingEmail == nil { + return "" + } + return *m.OrganizationBillingEmail +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetType() string { + if m == nil || m.Type == nil { + return "" + } + return *m.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseAccount) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. +func (m *MarketplacePurchaseEvent) GetEffectiveDate() Timestamp { + if m == nil || m.EffectiveDate == nil { + return Timestamp{} + } + return *m.EffectiveDate +} + +// GetInstallation returns the Installation field. +func (m *MarketplacePurchaseEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMarketplacePurchase returns the MarketplacePurchase field. +func (m *MarketplacePurchaseEvent) GetMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.MarketplacePurchase +} + +// GetOrg returns the Org field. +func (m *MarketplacePurchaseEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetPreviousMarketplacePurchase returns the PreviousMarketplacePurchase field. +func (m *MarketplacePurchaseEvent) GetPreviousMarketplacePurchase() *MarketplacePurchase { + if m == nil { + return nil + } + return m.PreviousMarketplacePurchase +} + +// GetSender returns the Sender field. +func (m *MarketplacePurchaseEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetIndices returns the Indices slice if it's non-nil, nil otherwise. +func (m *Match) GetIndices() []int { + if m == nil || m.Indices == nil { + return nil + } + return m.Indices +} + +// GetText returns the Text field if it's non-nil, zero value otherwise. +func (m *Match) GetText() string { + if m == nil || m.Text == nil { + return "" + } + return *m.Text +} + +// GetParameters returns the Parameters field. +func (m *MaxFilePathLengthBranchRule) GetParameters() MaxFilePathLengthRuleParameters { + if m == nil { + return MaxFilePathLengthRuleParameters{} + } + return m.Parameters +} + +// GetMaxFilePathLength returns the MaxFilePathLength field. +func (m *MaxFilePathLengthRuleParameters) GetMaxFilePathLength() int { + if m == nil { + return 0 + } + return m.MaxFilePathLength +} + +// GetParameters returns the Parameters field. +func (m *MaxFileSizeBranchRule) GetParameters() MaxFileSizeRuleParameters { + if m == nil { + return MaxFileSizeRuleParameters{} + } + return m.Parameters +} + +// GetMaxFileSize returns the MaxFileSize field. +func (m *MaxFileSizeRuleParameters) GetMaxFileSize() int64 { + if m == nil { + return 0 + } + return m.MaxFileSize +} + +// GetPermission returns the Permission field. +func (m *MemberChanges) GetPermission() *MemberChangesPermission { + if m == nil { + return nil + } + return m.Permission +} + +// GetRoleName returns the RoleName field. +func (m *MemberChanges) GetRoleName() *MemberChangesRoleName { + if m == nil { + return nil + } + return m.RoleName +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (m *MemberChangesPermission) GetFrom() string { + if m == nil || m.From == nil { + return "" + } + return *m.From +} + +// GetTo returns the To field if it's non-nil, zero value otherwise. +func (m *MemberChangesPermission) GetTo() string { + if m == nil || m.To == nil { + return "" + } + return *m.To +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (m *MemberChangesRoleName) GetFrom() string { + if m == nil || m.From == nil { + return "" + } + return *m.From +} + +// GetTo returns the To field if it's non-nil, zero value otherwise. +func (m *MemberChangesRoleName) GetTo() string { + if m == nil || m.To == nil { + return "" + } + return *m.To +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MemberEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetChanges returns the Changes field. +func (m *MemberEvent) GetChanges() *MemberChanges { + if m == nil { + return nil + } + return m.Changes +} + +// GetInstallation returns the Installation field. +func (m *MemberEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMember returns the Member field. +func (m *MemberEvent) GetMember() *User { + if m == nil { + return nil + } + return m.Member +} + +// GetOrg returns the Org field. +func (m *MemberEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetRepo returns the Repo field. +func (m *MemberEvent) GetRepo() *Repository { + if m == nil { + return nil + } + return m.Repo +} + +// GetSender returns the Sender field. +func (m *MemberEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetOrganization returns the Organization field. +func (m *Membership) GetOrganization() *Organization { + if m == nil { + return nil + } + return m.Organization +} + +// GetOrganizationURL returns the OrganizationURL field if it's non-nil, zero value otherwise. +func (m *Membership) GetOrganizationURL() string { + if m == nil || m.OrganizationURL == nil { + return "" + } + return *m.OrganizationURL +} + +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (m *Membership) GetRole() string { + if m == nil || m.Role == nil { + return "" + } + return *m.Role +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *Membership) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Membership) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetUser returns the User field. +func (m *Membership) GetUser() *User { + if m == nil { + return nil + } + return m.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MembershipEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetInstallation returns the Installation field. +func (m *MembershipEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMember returns the Member field. +func (m *MembershipEvent) GetMember() *User { + if m == nil { + return nil + } + return m.Member +} + +// GetOrg returns the Org field. +func (m *MembershipEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetScope returns the Scope field if it's non-nil, zero value otherwise. +func (m *MembershipEvent) GetScope() string { + if m == nil || m.Scope == nil { + return "" + } + return *m.Scope +} + +// GetSender returns the Sender field. +func (m *MembershipEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetTeam returns the Team field. +func (m *MembershipEvent) GetTeam() *Team { + if m == nil { + return nil + } + return m.Team +} + +// GetBaseRef returns the BaseRef field if it's non-nil, zero value otherwise. +func (m *MergeGroup) GetBaseRef() string { + if m == nil || m.BaseRef == nil { + return "" + } + return *m.BaseRef +} + +// GetBaseSHA returns the BaseSHA field if it's non-nil, zero value otherwise. +func (m *MergeGroup) GetBaseSHA() string { + if m == nil || m.BaseSHA == nil { + return "" + } + return *m.BaseSHA +} + +// GetHeadCommit returns the HeadCommit field. +func (m *MergeGroup) GetHeadCommit() *Commit { + if m == nil { + return nil + } + return m.HeadCommit +} + +// GetHeadRef returns the HeadRef field if it's non-nil, zero value otherwise. +func (m *MergeGroup) GetHeadRef() string { + if m == nil || m.HeadRef == nil { + return "" + } + return *m.HeadRef +} + +// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. +func (m *MergeGroup) GetHeadSHA() string { + if m == nil || m.HeadSHA == nil { + return "" + } + return *m.HeadSHA +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MergeGroupEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetInstallation returns the Installation field. +func (m *MergeGroupEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMergeGroup returns the MergeGroup field. +func (m *MergeGroupEvent) GetMergeGroup() *MergeGroup { + if m == nil { + return nil + } + return m.MergeGroup +} + +// GetOrg returns the Org field. +func (m *MergeGroupEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (m *MergeGroupEvent) GetReason() string { + if m == nil || m.Reason == nil { + return "" + } + return *m.Reason +} + +// GetRepo returns the Repo field. +func (m *MergeGroupEvent) GetRepo() *Repository { + if m == nil { + return nil + } + return m.Repo +} + +// GetSender returns the Sender field. +func (m *MergeGroupEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetParameters returns the Parameters field. +func (m *MergeQueueBranchRule) GetParameters() MergeQueueRuleParameters { + if m == nil { + return MergeQueueRuleParameters{} + } + return m.Parameters +} + +// GetCheckResponseTimeoutMinutes returns the CheckResponseTimeoutMinutes field. +func (m *MergeQueueRuleParameters) GetCheckResponseTimeoutMinutes() int { + if m == nil { + return 0 + } + return m.CheckResponseTimeoutMinutes +} + +// GetGroupingStrategy returns the GroupingStrategy field. +func (m *MergeQueueRuleParameters) GetGroupingStrategy() MergeGroupingStrategy { + if m == nil { + return "" + } + return m.GroupingStrategy +} + +// GetMaxEntriesToBuild returns the MaxEntriesToBuild field. +func (m *MergeQueueRuleParameters) GetMaxEntriesToBuild() int { + if m == nil { + return 0 + } + return m.MaxEntriesToBuild +} + +// GetMaxEntriesToMerge returns the MaxEntriesToMerge field. +func (m *MergeQueueRuleParameters) GetMaxEntriesToMerge() int { + if m == nil { + return 0 + } + return m.MaxEntriesToMerge +} + +// GetMergeMethod returns the MergeMethod field. +func (m *MergeQueueRuleParameters) GetMergeMethod() MergeQueueMergeMethod { + if m == nil { + return "" + } + return m.MergeMethod +} + +// GetMinEntriesToMerge returns the MinEntriesToMerge field. +func (m *MergeQueueRuleParameters) GetMinEntriesToMerge() int { + if m == nil { + return 0 + } + return m.MinEntriesToMerge +} + +// GetMinEntriesToMergeWaitMinutes returns the MinEntriesToMergeWaitMinutes field. +func (m *MergeQueueRuleParameters) GetMinEntriesToMergeWaitMinutes() int { + if m == nil { + return 0 + } + return m.MinEntriesToMergeWaitMinutes +} + +// GetText returns the Text field if it's non-nil, zero value otherwise. +func (m *Message) GetText() string { + if m == nil || m.Text == nil { + return "" + } + return *m.Text +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MetaEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetHook returns the Hook field. +func (m *MetaEvent) GetHook() *Hook { + if m == nil { + return nil + } + return m.Hook +} + +// GetHookID returns the HookID field if it's non-nil, zero value otherwise. +func (m *MetaEvent) GetHookID() int64 { + if m == nil || m.HookID == nil { + return 0 + } + return *m.HookID +} + +// GetInstallation returns the Installation field. +func (m *MetaEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetOrg returns the Org field. +func (m *MetaEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetRepo returns the Repo field. +func (m *MetaEvent) GetRepo() *Repository { + if m == nil { + return nil + } + return m.Repo +} + +// GetSender returns the Sender field. +func (m *MetaEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (m *Metric) GetHTMLURL() string { + if m == nil || m.HTMLURL == nil { + return "" + } + return *m.HTMLURL +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (m *Metric) GetKey() string { + if m == nil || m.Key == nil { + return "" + } + return *m.Key +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (m *Metric) GetName() string { + if m == nil || m.Name == nil { + return "" + } + return *m.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (m *Metric) GetNodeID() string { + if m == nil || m.NodeID == nil { + return "" + } + return *m.NodeID +} + +// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. +func (m *Metric) GetSPDXID() string { + if m == nil || m.SPDXID == nil { + return "" + } + return *m.SPDXID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Metric) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (m *Migration) GetCreatedAt() string { + if m == nil || m.CreatedAt == nil { + return "" + } + return *m.CreatedAt +} + +// GetExcludeAttachments returns the ExcludeAttachments field if it's non-nil, zero value otherwise. +func (m *Migration) GetExcludeAttachments() bool { + if m == nil || m.ExcludeAttachments == nil { + return false + } + return *m.ExcludeAttachments +} + +// GetGUID returns the GUID field if it's non-nil, zero value otherwise. +func (m *Migration) GetGUID() string { + if m == nil || m.GUID == nil { + return "" + } + return *m.GUID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *Migration) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLockRepositories returns the LockRepositories field if it's non-nil, zero value otherwise. +func (m *Migration) GetLockRepositories() bool { + if m == nil || m.LockRepositories == nil { + return false + } + return *m.LockRepositories +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (m *Migration) GetRepositories() []*Repository { + if m == nil || m.Repositories == nil { + return nil + } + return m.Repositories +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *Migration) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (m *Migration) GetUpdatedAt() string { + if m == nil || m.UpdatedAt == nil { + return "" + } + return *m.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Migration) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetExclude returns the Exclude slice if it's non-nil, nil otherwise. +func (m *MigrationOptions) GetExclude() []string { + if m == nil || m.Exclude == nil { + return nil + } + return m.Exclude +} + +// GetExcludeAttachments returns the ExcludeAttachments field. +func (m *MigrationOptions) GetExcludeAttachments() bool { + if m == nil { + return false + } + return m.ExcludeAttachments +} + +// GetExcludeReleases returns the ExcludeReleases field. +func (m *MigrationOptions) GetExcludeReleases() bool { + if m == nil { + return false + } + return m.ExcludeReleases +} + +// GetLockRepositories returns the LockRepositories field. +func (m *MigrationOptions) GetLockRepositories() bool { + if m == nil { + return false + } + return m.LockRepositories +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (m *Milestone) GetClosedAt() Timestamp { + if m == nil || m.ClosedAt == nil { + return Timestamp{} + } + return *m.ClosedAt +} + +// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. +func (m *Milestone) GetClosedIssues() int { + if m == nil || m.ClosedIssues == nil { + return 0 + } + return *m.ClosedIssues +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (m *Milestone) GetCreatedAt() Timestamp { + if m == nil || m.CreatedAt == nil { + return Timestamp{} + } + return *m.CreatedAt +} + +// GetCreator returns the Creator field. +func (m *Milestone) GetCreator() *User { + if m == nil { + return nil + } + return m.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (m *Milestone) GetDescription() string { + if m == nil || m.Description == nil { + return "" + } + return *m.Description +} + +// GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. +func (m *Milestone) GetDueOn() Timestamp { + if m == nil || m.DueOn == nil { + return Timestamp{} + } + return *m.DueOn +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (m *Milestone) GetHTMLURL() string { + if m == nil || m.HTMLURL == nil { + return "" + } + return *m.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (m *Milestone) GetID() int64 { + if m == nil || m.ID == nil { + return 0 + } + return *m.ID +} + +// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. +func (m *Milestone) GetLabelsURL() string { + if m == nil || m.LabelsURL == nil { + return "" + } + return *m.LabelsURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (m *Milestone) GetNodeID() string { + if m == nil || m.NodeID == nil { + return "" + } + return *m.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (m *Milestone) GetNumber() int { + if m == nil || m.Number == nil { + return 0 + } + return *m.Number +} + +// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. +func (m *Milestone) GetOpenIssues() int { + if m == nil || m.OpenIssues == nil { + return 0 + } + return *m.OpenIssues +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *Milestone) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (m *Milestone) GetTitle() string { + if m == nil || m.Title == nil { + return "" + } + return *m.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (m *Milestone) GetUpdatedAt() Timestamp { + if m == nil || m.UpdatedAt == nil { + return Timestamp{} + } + return *m.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (m *Milestone) GetURL() string { + if m == nil || m.URL == nil { + return "" + } + return *m.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (m *MilestoneEvent) GetAction() string { + if m == nil || m.Action == nil { + return "" + } + return *m.Action +} + +// GetChanges returns the Changes field. +func (m *MilestoneEvent) GetChanges() *EditChange { + if m == nil { + return nil + } + return m.Changes +} + +// GetInstallation returns the Installation field. +func (m *MilestoneEvent) GetInstallation() *Installation { + if m == nil { + return nil + } + return m.Installation +} + +// GetMilestone returns the Milestone field. +func (m *MilestoneEvent) GetMilestone() *Milestone { + if m == nil { + return nil + } + return m.Milestone +} + +// GetOrg returns the Org field. +func (m *MilestoneEvent) GetOrg() *Organization { + if m == nil { + return nil + } + return m.Org +} + +// GetRepo returns the Repo field. +func (m *MilestoneEvent) GetRepo() *Repository { + if m == nil { + return nil + } + return m.Repo +} + +// GetSender returns the Sender field. +func (m *MilestoneEvent) GetSender() *User { + if m == nil { + return nil + } + return m.Sender +} + +// GetDirection returns the Direction field. +func (m *MilestoneListOptions) GetDirection() string { + if m == nil { + return "" + } + return m.Direction +} + +// GetSort returns the Sort field. +func (m *MilestoneListOptions) GetSort() string { + if m == nil { + return "" + } + return m.Sort +} + +// GetState returns the State field. +func (m *MilestoneListOptions) GetState() string { + if m == nil { + return "" + } + return m.State +} + +// GetClosedMilestones returns the ClosedMilestones field if it's non-nil, zero value otherwise. +func (m *MilestoneStats) GetClosedMilestones() int { + if m == nil || m.ClosedMilestones == nil { + return 0 + } + return *m.ClosedMilestones +} + +// GetOpenMilestones returns the OpenMilestones field if it's non-nil, zero value otherwise. +func (m *MilestoneStats) GetOpenMilestones() int { + if m == nil || m.OpenMilestones == nil { + return 0 + } + return *m.OpenMilestones +} + +// GetTotalMilestones returns the TotalMilestones field if it's non-nil, zero value otherwise. +func (m *MilestoneStats) GetTotalMilestones() int { + if m == nil || m.TotalMilestones == nil { + return 0 + } + return *m.TotalMilestones +} + +// GetAnalysisKey returns the AnalysisKey field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetAnalysisKey() string { + if m == nil || m.AnalysisKey == nil { + return "" + } + return *m.AnalysisKey +} + +// GetCategory returns the Category field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetCategory() string { + if m == nil || m.Category == nil { + return "" + } + return *m.Category +} + +// GetClassifications returns the Classifications slice if it's non-nil, nil otherwise. +func (m *MostRecentInstance) GetClassifications() []string { + if m == nil || m.Classifications == nil { + return nil + } + return m.Classifications +} + +// GetCommitSHA returns the CommitSHA field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetCommitSHA() string { + if m == nil || m.CommitSHA == nil { + return "" + } + return *m.CommitSHA +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetEnvironment() string { + if m == nil || m.Environment == nil { + return "" + } + return *m.Environment +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetHTMLURL() string { + if m == nil || m.HTMLURL == nil { + return "" + } + return *m.HTMLURL +} + +// GetLocation returns the Location field. +func (m *MostRecentInstance) GetLocation() *Location { + if m == nil { + return nil + } + return m.Location +} + +// GetMessage returns the Message field. +func (m *MostRecentInstance) GetMessage() *Message { + if m == nil { + return nil + } + return m.Message +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetRef() string { + if m == nil || m.Ref == nil { + return "" + } + return *m.Ref +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (m *MostRecentInstance) GetState() string { + if m == nil || m.State == nil { + return "" + } + return *m.State +} + +// GetComputeService returns the ComputeService field. +func (n *NetworkConfiguration) GetComputeService() *ComputeService { + if n == nil { + return nil + } + return n.ComputeService +} + +// GetCreatedOn returns the CreatedOn field if it's non-nil, zero value otherwise. +func (n *NetworkConfiguration) GetCreatedOn() Timestamp { + if n == nil || n.CreatedOn == nil { + return Timestamp{} + } + return *n.CreatedOn +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (n *NetworkConfiguration) GetID() string { + if n == nil || n.ID == nil { + return "" + } + return *n.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (n *NetworkConfiguration) GetName() string { + if n == nil || n.Name == nil { + return "" + } + return *n.Name +} + +// GetNetworkSettingsIDs returns the NetworkSettingsIDs slice if it's non-nil, nil otherwise. +func (n *NetworkConfiguration) GetNetworkSettingsIDs() []string { + if n == nil || n.NetworkSettingsIDs == nil { + return nil + } + return n.NetworkSettingsIDs +} + +// GetComputeService returns the ComputeService field. +func (n *NetworkConfigurationRequest) GetComputeService() *ComputeService { + if n == nil { + return nil + } + return n.ComputeService +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (n *NetworkConfigurationRequest) GetName() string { + if n == nil || n.Name == nil { + return "" + } + return *n.Name +} + +// GetNetworkSettingsIDs returns the NetworkSettingsIDs slice if it's non-nil, nil otherwise. +func (n *NetworkConfigurationRequest) GetNetworkSettingsIDs() []string { + if n == nil || n.NetworkSettingsIDs == nil { + return nil + } + return n.NetworkSettingsIDs +} + +// GetNetworkConfigurations returns the NetworkConfigurations slice if it's non-nil, nil otherwise. +func (n *NetworkConfigurations) GetNetworkConfigurations() []*NetworkConfiguration { + if n == nil || n.NetworkConfigurations == nil { + return nil + } + return n.NetworkConfigurations +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (n *NetworkConfigurations) GetTotalCount() int64 { + if n == nil || n.TotalCount == nil { + return 0 + } + return *n.TotalCount +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetID() string { + if n == nil || n.ID == nil { + return "" + } + return *n.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetName() string { + if n == nil || n.Name == nil { + return "" + } + return *n.Name +} + +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetNetworkConfigurationID() string { + if n == nil || n.NetworkConfigurationID == nil { + return "" + } + return *n.NetworkConfigurationID +} + +// GetRegion returns the Region field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetRegion() string { + if n == nil || n.Region == nil { + return "" + } + return *n.Region +} + +// GetSubnetID returns the SubnetID field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetSubnetID() string { + if n == nil || n.SubnetID == nil { + return "" + } + return *n.SubnetID +} + +// GetClusterRoles returns the ClusterRoles slice if it's non-nil, nil otherwise. +func (n *NodeDetails) GetClusterRoles() []string { + if n == nil || n.ClusterRoles == nil { + return nil + } + return n.ClusterRoles +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (n *NodeDetails) GetHostname() string { + if n == nil || n.Hostname == nil { + return "" + } + return *n.Hostname +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (n *NodeDetails) GetUUID() string { + if n == nil || n.UUID == nil { + return "" + } + return *n.UUID +} + +// GetNodes returns the Nodes slice if it's non-nil, nil otherwise. +func (n *NodeMetadataStatus) GetNodes() []*NodeDetails { + if n == nil || n.Nodes == nil { + return nil + } + return n.Nodes +} + +// GetTopology returns the Topology field if it's non-nil, zero value otherwise. +func (n *NodeMetadataStatus) GetTopology() string { + if n == nil || n.Topology == nil { + return "" + } + return *n.Topology +} + +// GetClusterRoles returns the ClusterRoles field if it's non-nil, zero value otherwise. +func (n *NodeQueryOptions) GetClusterRoles() string { + if n == nil || n.ClusterRoles == nil { + return "" + } + return *n.ClusterRoles +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (n *NodeQueryOptions) GetUUID() string { + if n == nil || n.UUID == nil { + return "" + } + return *n.UUID +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (n *NodeReleaseVersion) GetHostname() string { + if n == nil || n.Hostname == nil { + return "" + } + return *n.Hostname +} + +// GetVersion returns the Version field. +func (n *NodeReleaseVersion) GetVersion() *ReleaseVersion { + if n == nil { + return nil + } + return n.Version +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (n *Notification) GetID() string { + if n == nil || n.ID == nil { + return "" + } + return *n.ID +} + +// GetLastReadAt returns the LastReadAt field if it's non-nil, zero value otherwise. +func (n *Notification) GetLastReadAt() Timestamp { + if n == nil || n.LastReadAt == nil { + return Timestamp{} + } + return *n.LastReadAt +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (n *Notification) GetReason() string { + if n == nil || n.Reason == nil { + return "" + } + return *n.Reason +} + +// GetRepository returns the Repository field. +func (n *Notification) GetRepository() *Repository { + if n == nil { + return nil + } + return n.Repository +} + +// GetSubject returns the Subject field. +func (n *Notification) GetSubject() *NotificationSubject { + if n == nil { + return nil + } + return n.Subject +} + +// GetUnread returns the Unread field if it's non-nil, zero value otherwise. +func (n *Notification) GetUnread() bool { + if n == nil || n.Unread == nil { + return false + } + return *n.Unread +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (n *Notification) GetUpdatedAt() Timestamp { + if n == nil || n.UpdatedAt == nil { + return Timestamp{} + } + return *n.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (n *Notification) GetURL() string { + if n == nil || n.URL == nil { + return "" + } + return *n.URL +} + +// GetAll returns the All field. +func (n *NotificationListOptions) GetAll() bool { + if n == nil { + return false + } + return n.All +} + +// GetBefore returns the Before field. +func (n *NotificationListOptions) GetBefore() time.Time { + if n == nil { + return time.Time{} + } + return n.Before +} + +// GetParticipating returns the Participating field. +func (n *NotificationListOptions) GetParticipating() bool { + if n == nil { + return false + } + return n.Participating +} + +// GetSince returns the Since field. +func (n *NotificationListOptions) GetSince() time.Time { + if n == nil { + return time.Time{} + } + return n.Since +} + +// GetLatestCommentURL returns the LatestCommentURL field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetLatestCommentURL() string { + if n == nil || n.LatestCommentURL == nil { + return "" + } + return *n.LatestCommentURL +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetTitle() string { + if n == nil || n.Title == nil { + return "" + } + return *n.Title +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetType() string { + if n == nil || n.Type == nil { + return "" + } + return *n.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (n *NotificationSubject) GetURL() string { + if n == nil || n.URL == nil { + return "" + } + return *n.URL +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (o *OAuthAPP) GetClientID() string { + if o == nil || o.ClientID == nil { + return "" + } + return *o.ClientID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (o *OAuthAPP) GetName() string { + if o == nil || o.Name == nil { + return "" + } + return *o.Name +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (o *OAuthAPP) GetURL() string { + if o == nil || o.URL == nil { + return "" + } + return *o.URL +} + +// GetCustomPropertyName returns the CustomPropertyName field. +func (o *OIDCCustomPropertyClaim) GetCustomPropertyName() string { + if o == nil { + return "" + } + return o.CustomPropertyName +} + +// GetInclusionSource returns the InclusionSource field. +func (o *OIDCCustomPropertyClaimResponse) GetInclusionSource() InclusionSource { + if o == nil { + return "" + } + return o.InclusionSource +} + +// GetIncludeClaimKeys returns the IncludeClaimKeys slice if it's non-nil, nil otherwise. +func (o *OIDCSubjectClaimCustomTemplate) GetIncludeClaimKeys() []string { + if o == nil || o.IncludeClaimKeys == nil { + return nil + } + return o.IncludeClaimKeys +} + +// GetSubClaimPrefix returns the SubClaimPrefix field if it's non-nil, zero value otherwise. +func (o *OIDCSubjectClaimCustomTemplate) GetSubClaimPrefix() string { + if o == nil || o.SubClaimPrefix == nil { + return "" + } + return *o.SubClaimPrefix +} + +// GetUseDefault returns the UseDefault field if it's non-nil, zero value otherwise. +func (o *OIDCSubjectClaimCustomTemplate) GetUseDefault() bool { + if o == nil || o.UseDefault == nil { + return false + } + return *o.UseDefault +} + +// GetUseImmutableSubject returns the UseImmutableSubject field if it's non-nil, zero value otherwise. +func (o *OIDCSubjectClaimCustomTemplate) GetUseImmutableSubject() bool { + if o == nil || o.UseImmutableSubject == nil { + return false + } + return *o.UseImmutableSubject +} + +// GetAdvancedSecurityEnabledForNewRepos returns the AdvancedSecurityEnabledForNewRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetAdvancedSecurityEnabledForNewRepos() bool { + if o == nil || o.AdvancedSecurityEnabledForNewRepos == nil { + return false + } + return *o.AdvancedSecurityEnabledForNewRepos +} + +// GetArchivedAt returns the ArchivedAt field if it's non-nil, zero value otherwise. +func (o *Organization) GetArchivedAt() Timestamp { + if o == nil || o.ArchivedAt == nil { + return Timestamp{} + } + return *o.ArchivedAt +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetAvatarURL() string { + if o == nil || o.AvatarURL == nil { + return "" + } + return *o.AvatarURL +} + +// GetBillingEmail returns the BillingEmail field if it's non-nil, zero value otherwise. +func (o *Organization) GetBillingEmail() string { + if o == nil || o.BillingEmail == nil { + return "" + } + return *o.BillingEmail +} + +// GetBlog returns the Blog field if it's non-nil, zero value otherwise. +func (o *Organization) GetBlog() string { + if o == nil || o.Blog == nil { + return "" + } + return *o.Blog +} + +// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. +func (o *Organization) GetCollaborators() int { + if o == nil || o.Collaborators == nil { + return 0 + } + return *o.Collaborators +} + +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (o *Organization) GetCompany() string { + if o == nil || o.Company == nil { + return "" + } + return *o.Company +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (o *Organization) GetCreatedAt() Timestamp { + if o == nil || o.CreatedAt == nil { + return Timestamp{} + } + return *o.CreatedAt +} + +// GetDefaultRepoPermission returns the DefaultRepoPermission field if it's non-nil, zero value otherwise. +func (o *Organization) GetDefaultRepoPermission() string { + if o == nil || o.DefaultRepoPermission == nil { + return "" + } + return *o.DefaultRepoPermission +} + +// GetDefaultRepoSettings returns the DefaultRepoSettings field if it's non-nil, zero value otherwise. +func (o *Organization) GetDefaultRepoSettings() string { + if o == nil || o.DefaultRepoSettings == nil { + return "" + } + return *o.DefaultRepoSettings +} + +// GetDefaultRepositoryBranch returns the DefaultRepositoryBranch field if it's non-nil, zero value otherwise. +func (o *Organization) GetDefaultRepositoryBranch() string { + if o == nil || o.DefaultRepositoryBranch == nil { + return "" + } + return *o.DefaultRepositoryBranch +} + +// GetDependabotAlertsEnabledForNewRepos returns the DependabotAlertsEnabledForNewRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetDependabotAlertsEnabledForNewRepos() bool { + if o == nil || o.DependabotAlertsEnabledForNewRepos == nil { + return false + } + return *o.DependabotAlertsEnabledForNewRepos +} + +// GetDependabotSecurityUpdatesEnabledForNewRepos returns the DependabotSecurityUpdatesEnabledForNewRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetDependabotSecurityUpdatesEnabledForNewRepos() bool { + if o == nil || o.DependabotSecurityUpdatesEnabledForNewRepos == nil { + return false + } + return *o.DependabotSecurityUpdatesEnabledForNewRepos +} + +// GetDependencyGraphEnabledForNewRepos returns the DependencyGraphEnabledForNewRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetDependencyGraphEnabledForNewRepos() bool { + if o == nil || o.DependencyGraphEnabledForNewRepos == nil { + return false + } + return *o.DependencyGraphEnabledForNewRepos +} + +// GetDeployKeysEnabledForRepositories returns the DeployKeysEnabledForRepositories field if it's non-nil, zero value otherwise. +func (o *Organization) GetDeployKeysEnabledForRepositories() bool { + if o == nil || o.DeployKeysEnabledForRepositories == nil { + return false + } + return *o.DeployKeysEnabledForRepositories +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (o *Organization) GetDescription() string { + if o == nil || o.Description == nil { + return "" + } + return *o.Description +} + +// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. +func (o *Organization) GetDiskUsage() int { + if o == nil || o.DiskUsage == nil { + return 0 + } + return *o.DiskUsage +} + +// GetDisplayCommenterFullNameSettingEnabled returns the DisplayCommenterFullNameSettingEnabled field if it's non-nil, zero value otherwise. +func (o *Organization) GetDisplayCommenterFullNameSettingEnabled() bool { + if o == nil || o.DisplayCommenterFullNameSettingEnabled == nil { + return false + } + return *o.DisplayCommenterFullNameSettingEnabled +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (o *Organization) GetEmail() string { + if o == nil || o.Email == nil { + return "" + } + return *o.Email +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetEventsURL() string { + if o == nil || o.EventsURL == nil { + return "" + } + return *o.EventsURL +} + +// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. +func (o *Organization) GetFollowers() int { + if o == nil || o.Followers == nil { + return 0 + } + return *o.Followers +} + +// GetFollowing returns the Following field if it's non-nil, zero value otherwise. +func (o *Organization) GetFollowing() int { + if o == nil || o.Following == nil { + return 0 + } + return *o.Following +} + +// GetHasOrganizationProjects returns the HasOrganizationProjects field if it's non-nil, zero value otherwise. +func (o *Organization) GetHasOrganizationProjects() bool { + if o == nil || o.HasOrganizationProjects == nil { + return false + } + return *o.HasOrganizationProjects +} + +// GetHasRepositoryProjects returns the HasRepositoryProjects field if it's non-nil, zero value otherwise. +func (o *Organization) GetHasRepositoryProjects() bool { + if o == nil || o.HasRepositoryProjects == nil { + return false + } + return *o.HasRepositoryProjects +} + +// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetHooksURL() string { + if o == nil || o.HooksURL == nil { + return "" + } + return *o.HooksURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetHTMLURL() string { + if o == nil || o.HTMLURL == nil { + return "" + } + return *o.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (o *Organization) GetID() int64 { + if o == nil || o.ID == nil { + return 0 + } + return *o.ID +} + +// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetIssuesURL() string { + if o == nil || o.IssuesURL == nil { + return "" + } + return *o.IssuesURL +} + +// GetIsVerified returns the IsVerified field if it's non-nil, zero value otherwise. +func (o *Organization) GetIsVerified() bool { + if o == nil || o.IsVerified == nil { + return false + } + return *o.IsVerified +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (o *Organization) GetLocation() string { + if o == nil || o.Location == nil { + return "" + } + return *o.Location +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (o *Organization) GetLogin() string { + if o == nil || o.Login == nil { + return "" + } + return *o.Login +} + +// GetMembersAllowedRepositoryCreationType returns the MembersAllowedRepositoryCreationType field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersAllowedRepositoryCreationType() string { + if o == nil || o.MembersAllowedRepositoryCreationType == nil { + return "" + } + return *o.MembersAllowedRepositoryCreationType +} + +// GetMembersCanChangeRepoVisibility returns the MembersCanChangeRepoVisibility field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanChangeRepoVisibility() bool { + if o == nil || o.MembersCanChangeRepoVisibility == nil { + return false + } + return *o.MembersCanChangeRepoVisibility +} + +// GetMembersCanCreateInternalRepos returns the MembersCanCreateInternalRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreateInternalRepos() bool { + if o == nil || o.MembersCanCreateInternalRepos == nil { + return false + } + return *o.MembersCanCreateInternalRepos +} + +// GetMembersCanCreatePages returns the MembersCanCreatePages field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreatePages() bool { + if o == nil || o.MembersCanCreatePages == nil { + return false + } + return *o.MembersCanCreatePages +} + +// GetMembersCanCreatePrivatePages returns the MembersCanCreatePrivatePages field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreatePrivatePages() bool { + if o == nil || o.MembersCanCreatePrivatePages == nil { + return false + } + return *o.MembersCanCreatePrivatePages +} + +// GetMembersCanCreatePrivateRepos returns the MembersCanCreatePrivateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreatePrivateRepos() bool { + if o == nil || o.MembersCanCreatePrivateRepos == nil { + return false + } + return *o.MembersCanCreatePrivateRepos +} + +// GetMembersCanCreatePublicPages returns the MembersCanCreatePublicPages field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreatePublicPages() bool { + if o == nil || o.MembersCanCreatePublicPages == nil { + return false + } + return *o.MembersCanCreatePublicPages +} + +// GetMembersCanCreatePublicRepos returns the MembersCanCreatePublicRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreatePublicRepos() bool { + if o == nil || o.MembersCanCreatePublicRepos == nil { + return false + } + return *o.MembersCanCreatePublicRepos +} + +// GetMembersCanCreateRepos returns the MembersCanCreateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreateRepos() bool { + if o == nil || o.MembersCanCreateRepos == nil { + return false + } + return *o.MembersCanCreateRepos +} + +// GetMembersCanCreateTeams returns the MembersCanCreateTeams field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanCreateTeams() bool { + if o == nil || o.MembersCanCreateTeams == nil { + return false + } + return *o.MembersCanCreateTeams +} + +// GetMembersCanDeleteIssues returns the MembersCanDeleteIssues field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanDeleteIssues() bool { + if o == nil || o.MembersCanDeleteIssues == nil { + return false + } + return *o.MembersCanDeleteIssues +} + +// GetMembersCanDeleteRepositories returns the MembersCanDeleteRepositories field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanDeleteRepositories() bool { + if o == nil || o.MembersCanDeleteRepositories == nil { + return false + } + return *o.MembersCanDeleteRepositories +} + +// GetMembersCanForkPrivateRepos returns the MembersCanForkPrivateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanForkPrivateRepos() bool { + if o == nil || o.MembersCanForkPrivateRepos == nil { + return false + } + return *o.MembersCanForkPrivateRepos +} + +// GetMembersCanInviteOutsideCollaborators returns the MembersCanInviteOutsideCollaborators field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanInviteOutsideCollaborators() bool { + if o == nil || o.MembersCanInviteOutsideCollaborators == nil { + return false + } + return *o.MembersCanInviteOutsideCollaborators +} + +// GetMembersCanViewDependencyInsights returns the MembersCanViewDependencyInsights field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersCanViewDependencyInsights() bool { + if o == nil || o.MembersCanViewDependencyInsights == nil { + return false + } + return *o.MembersCanViewDependencyInsights +} + +// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetMembersURL() string { + if o == nil || o.MembersURL == nil { + return "" + } + return *o.MembersURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (o *Organization) GetName() string { + if o == nil || o.Name == nil { + return "" + } + return *o.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (o *Organization) GetNodeID() string { + if o == nil || o.NodeID == nil { + return "" + } + return *o.NodeID +} + +// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetOwnedPrivateRepos() int64 { + if o == nil || o.OwnedPrivateRepos == nil { + return 0 + } + return *o.OwnedPrivateRepos +} + +// GetPlan returns the Plan field. +func (o *Organization) GetPlan() *Plan { + if o == nil { + return nil + } + return o.Plan +} + +// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. +func (o *Organization) GetPrivateGists() int { + if o == nil || o.PrivateGists == nil { + return 0 + } + return *o.PrivateGists +} + +// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. +func (o *Organization) GetPublicGists() int { + if o == nil || o.PublicGists == nil { + return 0 + } + return *o.PublicGists +} + +// GetPublicMembersURL returns the PublicMembersURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetPublicMembersURL() string { + if o == nil || o.PublicMembersURL == nil { + return "" + } + return *o.PublicMembersURL +} + +// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetPublicRepos() int { + if o == nil || o.PublicRepos == nil { + return 0 + } + return *o.PublicRepos +} + +// GetReadersCanCreateDiscussions returns the ReadersCanCreateDiscussions field if it's non-nil, zero value otherwise. +func (o *Organization) GetReadersCanCreateDiscussions() bool { + if o == nil || o.ReadersCanCreateDiscussions == nil { + return false + } + return *o.ReadersCanCreateDiscussions +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (o *Organization) GetReposURL() string { + if o == nil || o.ReposURL == nil { + return "" + } + return *o.ReposURL +} + +// GetSecretScanningEnabledForNewRepos returns the SecretScanningEnabledForNewRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetSecretScanningEnabledForNewRepos() bool { + if o == nil || o.SecretScanningEnabledForNewRepos == nil { + return false + } + return *o.SecretScanningEnabledForNewRepos +} + +// GetSecretScanningPushProtectionCustomLink returns the SecretScanningPushProtectionCustomLink field if it's non-nil, zero value otherwise. +func (o *Organization) GetSecretScanningPushProtectionCustomLink() string { + if o == nil || o.SecretScanningPushProtectionCustomLink == nil { + return "" + } + return *o.SecretScanningPushProtectionCustomLink +} + +// GetSecretScanningPushProtectionCustomLinkEnabled returns the SecretScanningPushProtectionCustomLinkEnabled field if it's non-nil, zero value otherwise. +func (o *Organization) GetSecretScanningPushProtectionCustomLinkEnabled() bool { + if o == nil || o.SecretScanningPushProtectionCustomLinkEnabled == nil { + return false + } + return *o.SecretScanningPushProtectionCustomLinkEnabled +} + +// GetSecretScanningPushProtectionEnabledForNewRepos returns the SecretScanningPushProtectionEnabledForNewRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetSecretScanningPushProtectionEnabledForNewRepos() bool { + if o == nil || o.SecretScanningPushProtectionEnabledForNewRepos == nil { + return false + } + return *o.SecretScanningPushProtectionEnabledForNewRepos +} + +// GetSecretScanningValidityChecksEnabled returns the SecretScanningValidityChecksEnabled field if it's non-nil, zero value otherwise. +func (o *Organization) GetSecretScanningValidityChecksEnabled() bool { + if o == nil || o.SecretScanningValidityChecksEnabled == nil { + return false + } + return *o.SecretScanningValidityChecksEnabled +} + +// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. +func (o *Organization) GetTotalPrivateRepos() int64 { + if o == nil || o.TotalPrivateRepos == nil { + return 0 + } + return *o.TotalPrivateRepos +} + +// GetTwitterUsername returns the TwitterUsername field if it's non-nil, zero value otherwise. +func (o *Organization) GetTwitterUsername() string { + if o == nil || o.TwitterUsername == nil { + return "" + } + return *o.TwitterUsername +} + +// GetTwoFactorRequirementEnabled returns the TwoFactorRequirementEnabled field if it's non-nil, zero value otherwise. +func (o *Organization) GetTwoFactorRequirementEnabled() bool { + if o == nil || o.TwoFactorRequirementEnabled == nil { + return false + } + return *o.TwoFactorRequirementEnabled +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (o *Organization) GetType() string { + if o == nil || o.Type == nil { + return "" + } + return *o.Type +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (o *Organization) GetUpdatedAt() Timestamp { + if o == nil || o.UpdatedAt == nil { + return Timestamp{} + } + return *o.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (o *Organization) GetURL() string { + if o == nil || o.URL == nil { + return "" + } + return *o.URL +} + +// GetWebCommitSignoffRequired returns the WebCommitSignoffRequired field if it's non-nil, zero value otherwise. +func (o *Organization) GetWebCommitSignoffRequired() bool { + if o == nil || o.WebCommitSignoffRequired == nil { + return false + } + return *o.WebCommitSignoffRequired +} + +// GetProperties returns the Properties slice if it's non-nil, nil otherwise. +func (o *OrganizationCustomPropertyValues) GetProperties() []*CustomPropertyValue { + if o == nil || o.Properties == nil { + return nil + } + return o.Properties +} + +// GetCustomRepoRoles returns the CustomRepoRoles slice if it's non-nil, nil otherwise. +func (o *OrganizationCustomRepoRoles) GetCustomRepoRoles() []*CustomRepoRoles { + if o == nil || o.CustomRepoRoles == nil { + return nil + } + return o.CustomRepoRoles +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (o *OrganizationCustomRepoRoles) GetTotalCount() int { + if o == nil || o.TotalCount == nil { + return 0 + } + return *o.TotalCount +} + +// GetCustomRepoRoles returns the CustomRepoRoles slice if it's non-nil, nil otherwise. +func (o *OrganizationCustomRoles) GetCustomRepoRoles() []*CustomOrgRole { + if o == nil || o.CustomRepoRoles == nil { + return nil + } + return o.CustomRepoRoles +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (o *OrganizationCustomRoles) GetTotalCount() int { + if o == nil || o.TotalCount == nil { + return 0 + } + return *o.TotalCount +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (o *OrganizationEvent) GetAction() string { + if o == nil || o.Action == nil { + return "" + } + return *o.Action +} + +// GetInstallation returns the Installation field. +func (o *OrganizationEvent) GetInstallation() *Installation { + if o == nil { + return nil + } + return o.Installation +} + +// GetInvitation returns the Invitation field. +func (o *OrganizationEvent) GetInvitation() *Invitation { + if o == nil { + return nil + } + return o.Invitation +} + +// GetMembership returns the Membership field. +func (o *OrganizationEvent) GetMembership() *Membership { + if o == nil { + return nil + } + return o.Membership +} + +// GetOrganization returns the Organization field. +func (o *OrganizationEvent) GetOrganization() *Organization { + if o == nil { + return nil + } + return o.Organization +} + +// GetSender returns the Sender field. +func (o *OrganizationEvent) GetSender() *User { + if o == nil { + return nil + } + return o.Sender +} + +// GetDescription returns the Description field. +func (o *OrganizationFineGrainedPermission) GetDescription() string { + if o == nil { + return "" + } + return o.Description +} + +// GetName returns the Name field. +func (o *OrganizationFineGrainedPermission) GetName() string { + if o == nil { + return "" + } + return o.Name +} + +// GetInstallations returns the Installations slice if it's non-nil, nil otherwise. +func (o *OrganizationInstallations) GetInstallations() []*Installation { + if o == nil || o.Installations == nil { + return nil + } + return o.Installations +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (o *OrganizationInstallations) GetTotalCount() int { + if o == nil || o.TotalCount == nil { + return 0 + } + return *o.TotalCount +} + +// GetPerPage returns the PerPage field. +func (o *OrganizationsListOptions) GetPerPage() int { + if o == nil { + return 0 + } + return o.PerPage +} + +// GetSince returns the Since field. +func (o *OrganizationsListOptions) GetSince() int64 { + if o == nil { + return 0 + } + return o.Since +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (o *OrgBlockEvent) GetAction() string { + if o == nil || o.Action == nil { + return "" + } + return *o.Action +} + +// GetBlockedUser returns the BlockedUser field. +func (o *OrgBlockEvent) GetBlockedUser() *User { + if o == nil { + return nil + } + return o.BlockedUser +} + +// GetInstallation returns the Installation field. +func (o *OrgBlockEvent) GetInstallation() *Installation { + if o == nil { + return nil + } + return o.Installation +} + +// GetOrganization returns the Organization field. +func (o *OrgBlockEvent) GetOrganization() *Organization { + if o == nil { + return nil + } + return o.Organization +} + +// GetSender returns the Sender field. +func (o *OrgBlockEvent) GetSender() *User { + if o == nil { + return nil + } + return o.Sender +} + +// GetDisabledOrgs returns the DisabledOrgs field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetDisabledOrgs() int { + if o == nil || o.DisabledOrgs == nil { + return 0 + } + return *o.DisabledOrgs +} + +// GetTotalOrgs returns the TotalOrgs field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetTotalOrgs() int { + if o == nil || o.TotalOrgs == nil { + return 0 + } + return *o.TotalOrgs +} + +// GetTotalTeamMembers returns the TotalTeamMembers field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetTotalTeamMembers() int { + if o == nil || o.TotalTeamMembers == nil { + return 0 + } + return *o.TotalTeamMembers +} + +// GetTotalTeams returns the TotalTeams field if it's non-nil, zero value otherwise. +func (o *OrgStats) GetTotalTeams() int { + if o == nil || o.TotalTeams == nil { + return 0 + } + return *o.TotalTeams +} + +// GetOrg returns the Org field. +func (o *OwnerInfo) GetOrg() *User { + if o == nil { + return nil + } + return o.Org +} + +// GetUser returns the User field. +func (o *OwnerInfo) GetUser() *User { + if o == nil { + return nil + } + return o.User +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *Package) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *Package) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (p *Package) GetEcosystem() string { + if p == nil || p.Ecosystem == nil { + return "" + } + return *p.Ecosystem +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *Package) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *Package) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *Package) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNamespace returns the Namespace field if it's non-nil, zero value otherwise. +func (p *Package) GetNamespace() string { + if p == nil || p.Namespace == nil { + return "" + } + return *p.Namespace +} + +// GetOwner returns the Owner field. +func (p *Package) GetOwner() *User { + if p == nil { + return nil + } + return p.Owner +} + +// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. +func (p *Package) GetPackageType() string { + if p == nil || p.PackageType == nil { + return "" + } + return *p.PackageType +} + +// GetPackageVersion returns the PackageVersion field. +func (p *Package) GetPackageVersion() *PackageVersion { + if p == nil { + return nil + } + return p.PackageVersion +} + +// GetRegistry returns the Registry field. +func (p *Package) GetRegistry() *PackageRegistry { + if p == nil { + return nil + } + return p.Registry +} + +// GetRepository returns the Repository field. +func (p *Package) GetRepository() *Repository { + if p == nil { + return nil + } + return p.Repository +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *Package) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *Package) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetVersionCount returns the VersionCount field if it's non-nil, zero value otherwise. +func (p *Package) GetVersionCount() int64 { + if p == nil || p.VersionCount == nil { + return 0 + } + return *p.VersionCount +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (p *Package) GetVisibility() string { + if p == nil || p.Visibility == nil { + return "" + } + return *p.Visibility +} + +// GetTags returns the Tags slice if it's non-nil, nil otherwise. +func (p *PackageContainerMetadata) GetTags() []string { + if p == nil || p.Tags == nil { + return nil + } + return p.Tags +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PackageEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetInstallation returns the Installation field. +func (p *PackageEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PackageEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetPackage returns the Package field. +func (p *PackageEvent) GetPackage() *Package { + if p == nil { + return nil + } + return p.Package +} + +// GetRepo returns the Repo field. +func (p *PackageEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PackageEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetLabels returns the Labels map if it's non-nil, an empty map otherwise. +func (p *PackageEventContainerMetadata) GetLabels() map[string]any { + if p == nil || p.Labels == nil { + return map[string]any{} + } + return p.Labels +} + +// GetManifest returns the Manifest map if it's non-nil, an empty map otherwise. +func (p *PackageEventContainerMetadata) GetManifest() map[string]any { + if p == nil || p.Manifest == nil { + return map[string]any{} + } + return p.Manifest +} + +// GetTag returns the Tag field. +func (p *PackageEventContainerMetadata) GetTag() *PackageEventContainerMetadataTag { + if p == nil { + return nil + } + return p.Tag +} + +// GetDigest returns the Digest field if it's non-nil, zero value otherwise. +func (p *PackageEventContainerMetadataTag) GetDigest() string { + if p == nil || p.Digest == nil { + return "" + } + return *p.Digest +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageEventContainerMetadataTag) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetReferenceCategory returns the ReferenceCategory field. +func (p *PackageExternalRef) GetReferenceCategory() string { + if p == nil { + return "" + } + return p.ReferenceCategory +} + +// GetReferenceLocator returns the ReferenceLocator field. +func (p *PackageExternalRef) GetReferenceLocator() string { + if p == nil { + return "" + } + return p.ReferenceLocator +} + +// GetReferenceType returns the ReferenceType field. +func (p *PackageExternalRef) GetReferenceType() string { + if p == nil { + return "" + } + return p.ReferenceType +} + +// GetAuthor returns the Author field. +func (p *PackageFile) GetAuthor() *User { + if p == nil { + return nil + } + return p.Author +} + +// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetContentType() string { + if p == nil || p.ContentType == nil { + return "" + } + return *p.ContentType +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetDownloadURL() string { + if p == nil || p.DownloadURL == nil { + return "" + } + return *p.DownloadURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetMD5 returns the MD5 field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetMD5() string { + if p == nil || p.MD5 == nil { + return "" + } + return *p.MD5 +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetSHA1 returns the SHA1 field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetSHA1() string { + if p == nil || p.SHA1 == nil { + return "" + } + return *p.SHA1 +} + +// GetSHA256 returns the SHA256 field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetSHA256() string { + if p == nil || p.SHA256 == nil { + return "" + } + return *p.SHA256 +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetSize() int64 { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. +func (p *PackageListOptions) GetPackageType() string { + if p == nil || p.PackageType == nil { + return "" + } + return *p.PackageType +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PackageListOptions) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (p *PackageListOptions) GetVisibility() string { + if p == nil || p.Visibility == nil { + return "" + } + return *p.Visibility +} + +// GetContainer returns the Container field. +func (p *PackageMetadata) GetContainer() *PackageContainerMetadata { + if p == nil { + return nil + } + return p.Container +} + +// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. +func (p *PackageMetadata) GetPackageType() string { + if p == nil || p.PackageType == nil { + return "" + } + return *p.PackageType +} + +// GetAuthor returns the Author map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetAuthor() map[string]string { + if p == nil || p.Author == nil { + return map[string]string{} + } + return p.Author +} + +// GetBin returns the Bin map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetBin() map[string]any { + if p == nil || p.Bin == nil { + return map[string]any{} + } + return p.Bin +} + +// GetBugs returns the Bugs map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetBugs() map[string]string { + if p == nil || p.Bugs == nil { + return map[string]string{} + } + return p.Bugs +} + +// GetCommitOID returns the CommitOID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetCommitOID() string { + if p == nil || p.CommitOID == nil { + return "" + } + return *p.CommitOID +} + +// GetContributors returns the Contributors slice if it's non-nil, nil otherwise. +func (p *PackageNPMMetadata) GetContributors() []any { + if p == nil || p.Contributors == nil { + return nil + } + return p.Contributors +} + +// GetCPU returns the CPU slice if it's non-nil, nil otherwise. +func (p *PackageNPMMetadata) GetCPU() []string { + if p == nil || p.CPU == nil { + return nil + } + return p.CPU +} + +// GetDeletedByID returns the DeletedByID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetDeletedByID() int64 { + if p == nil || p.DeletedByID == nil { + return 0 + } + return *p.DeletedByID +} + +// GetDependencies returns the Dependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDependencies() map[string]string { + if p == nil || p.Dependencies == nil { + return map[string]string{} + } + return p.Dependencies +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetDevDependencies returns the DevDependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDevDependencies() map[string]string { + if p == nil || p.DevDependencies == nil { + return map[string]string{} + } + return p.DevDependencies +} + +// GetDirectories returns the Directories map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDirectories() map[string]string { + if p == nil || p.Directories == nil { + return map[string]string{} + } + return p.Directories +} + +// GetDist returns the Dist map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDist() map[string]string { + if p == nil || p.Dist == nil { + return map[string]string{} + } + return p.Dist +} + +// GetEngines returns the Engines map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetEngines() map[string]string { + if p == nil || p.Engines == nil { + return map[string]string{} + } + return p.Engines +} + +// GetFiles returns the Files slice if it's non-nil, nil otherwise. +func (p *PackageNPMMetadata) GetFiles() []string { + if p == nil || p.Files == nil { + return nil + } + return p.Files +} + +// GetGitHead returns the GitHead field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetGitHead() string { + if p == nil || p.GitHead == nil { + return "" + } + return *p.GitHead +} + +// GetHasShrinkwrap returns the HasShrinkwrap field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetHasShrinkwrap() bool { + if p == nil || p.HasShrinkwrap == nil { + return false + } + return *p.HasShrinkwrap +} + +// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetHomepage() string { + if p == nil || p.Homepage == nil { + return "" + } + return *p.Homepage +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetID() string { + if p == nil || p.ID == nil { + return "" + } + return *p.ID +} + +// GetInstallationCommand returns the InstallationCommand field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetInstallationCommand() string { + if p == nil || p.InstallationCommand == nil { + return "" + } + return *p.InstallationCommand +} + +// GetKeywords returns the Keywords slice if it's non-nil, nil otherwise. +func (p *PackageNPMMetadata) GetKeywords() []string { + if p == nil || p.Keywords == nil { + return nil + } + return p.Keywords +} + +// GetLicense returns the License field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetLicense() string { + if p == nil || p.License == nil { + return "" + } + return *p.License +} + +// GetMain returns the Main field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetMain() string { + if p == nil || p.Main == nil { + return "" + } + return *p.Main +} + +// GetMaintainers returns the Maintainers slice if it's non-nil, nil otherwise. +func (p *PackageNPMMetadata) GetMaintainers() []any { + if p == nil || p.Maintainers == nil { + return nil + } + return p.Maintainers +} + +// GetMan returns the Man map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetMan() map[string]any { + if p == nil || p.Man == nil { + return map[string]any{} + } + return p.Man +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNodeVersion returns the NodeVersion field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetNodeVersion() string { + if p == nil || p.NodeVersion == nil { + return "" + } + return *p.NodeVersion +} + +// GetNPMUser returns the NPMUser field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetNPMUser() string { + if p == nil || p.NPMUser == nil { + return "" + } + return *p.NPMUser +} + +// GetNPMVersion returns the NPMVersion field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetNPMVersion() string { + if p == nil || p.NPMVersion == nil { + return "" + } + return *p.NPMVersion +} + +// GetOptionalDependencies returns the OptionalDependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetOptionalDependencies() map[string]string { + if p == nil || p.OptionalDependencies == nil { + return map[string]string{} + } + return p.OptionalDependencies +} + +// GetOS returns the OS slice if it's non-nil, nil otherwise. +func (p *PackageNPMMetadata) GetOS() []string { + if p == nil || p.OS == nil { + return nil + } + return p.OS +} + +// GetPeerDependencies returns the PeerDependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetPeerDependencies() map[string]string { + if p == nil || p.PeerDependencies == nil { + return map[string]string{} + } + return p.PeerDependencies +} + +// GetPublishedViaActions returns the PublishedViaActions field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetPublishedViaActions() bool { + if p == nil || p.PublishedViaActions == nil { + return false + } + return *p.PublishedViaActions +} + +// GetReadme returns the Readme field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetReadme() string { + if p == nil || p.Readme == nil { + return "" + } + return *p.Readme +} + +// GetReleaseID returns the ReleaseID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetReleaseID() int64 { + if p == nil || p.ReleaseID == nil { + return 0 + } + return *p.ReleaseID +} + +// GetRepository returns the Repository map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetRepository() map[string]string { + if p == nil || p.Repository == nil { + return map[string]string{} + } + return p.Repository +} + +// GetScripts returns the Scripts map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetScripts() map[string]any { + if p == nil || p.Scripts == nil { + return map[string]any{} + } + return p.Scripts +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetVersion() string { + if p == nil || p.Version == nil { + return "" + } + return *p.Version +} + +// GetID returns the ID field. +func (p *PackageNugetMetadata) GetID() json.RawMessage { + if p == nil { + return json.RawMessage{} + } + return p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageNugetMetadata) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetValue returns the Value field. +func (p *PackageNugetMetadata) GetValue() json.RawMessage { + if p == nil { + return json.RawMessage{} + } + return p.Value +} + +// GetAboutURL returns the AboutURL field if it's non-nil, zero value otherwise. +func (p *PackageRegistry) GetAboutURL() string { + if p == nil || p.AboutURL == nil { + return "" + } + return *p.AboutURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageRegistry) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (p *PackageRegistry) GetType() string { + if p == nil || p.Type == nil { + return "" + } + return *p.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PackageRegistry) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetVendor returns the Vendor field if it's non-nil, zero value otherwise. +func (p *PackageRegistry) GetVendor() string { + if p == nil || p.Vendor == nil { + return "" + } + return *p.Vendor +} + +// GetAuthor returns the Author field. +func (p *PackageRelease) GetAuthor() *User { + if p == nil { + return nil + } + return p.Author +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetDraft() bool { + if p == nil || p.Draft == nil { + return false + } + return *p.Draft +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetPrerelease() bool { + if p == nil || p.Prerelease == nil { + return false + } + return *p.Prerelease +} + +// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetPublishedAt() Timestamp { + if p == nil || p.PublishedAt == nil { + return Timestamp{} + } + return *p.PublishedAt +} + +// GetTagName returns the TagName field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetTagName() string { + if p == nil || p.TagName == nil { + return "" + } + return *p.TagName +} + +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetTargetCommitish() string { + if p == nil || p.TargetCommitish == nil { + return "" + } + return *p.TargetCommitish +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PackageRelease) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetIncludedGigabytesBandwidth returns the IncludedGigabytesBandwidth field. +func (p *PackagesBilling) GetIncludedGigabytesBandwidth() int { + if p == nil { + return 0 + } + return p.IncludedGigabytesBandwidth +} + +// GetTotalGigabytesBandwidthUsed returns the TotalGigabytesBandwidthUsed field. +func (p *PackagesBilling) GetTotalGigabytesBandwidthUsed() int { + if p == nil { + return 0 + } + return p.TotalGigabytesBandwidthUsed +} + +// GetTotalPaidGigabytesBandwidthUsed returns the TotalPaidGigabytesBandwidthUsed field. +func (p *PackagesBilling) GetTotalPaidGigabytesBandwidthUsed() int { + if p == nil { + return 0 + } + return p.TotalPaidGigabytesBandwidthUsed +} + +// GetAuthor returns the Author field. +func (p *PackageVersion) GetAuthor() *User { + if p == nil { + return nil + } + return p.Author +} + +// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetBodyHTML() string { + if p == nil || p.BodyHTML == nil { + return "" + } + return *p.BodyHTML +} + +// GetContainerMetadata returns the ContainerMetadata field. +func (p *PackageVersion) GetContainerMetadata() *PackageEventContainerMetadata { + if p == nil { + return nil + } + return p.ContainerMetadata +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDeletedAt returns the DeletedAt field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetDeletedAt() Timestamp { + if p == nil || p.DeletedAt == nil { + return Timestamp{} + } + return *p.DeletedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetDockerMetadata returns the DockerMetadata slice if it's non-nil, nil otherwise. +func (p *PackageVersion) GetDockerMetadata() []any { + if p == nil || p.DockerMetadata == nil { + return nil + } + return p.DockerMetadata +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetDraft() bool { + if p == nil || p.Draft == nil { + return false + } + return *p.Draft +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetInstallationCommand returns the InstallationCommand field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetInstallationCommand() string { + if p == nil || p.InstallationCommand == nil { + return "" + } + return *p.InstallationCommand +} + +// GetLicense returns the License field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetLicense() string { + if p == nil || p.License == nil { + return "" + } + return *p.License +} + +// GetManifest returns the Manifest field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetManifest() string { + if p == nil || p.Manifest == nil { + return "" + } + return *p.Manifest +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNPMMetadata returns the NPMMetadata field. +func (p *PackageVersion) GetNPMMetadata() *PackageNPMMetadata { + if p == nil { + return nil + } + return p.NPMMetadata +} + +// GetNugetMetadata returns the NugetMetadata slice if it's non-nil, nil otherwise. +func (p *PackageVersion) GetNugetMetadata() []*PackageNugetMetadata { + if p == nil || p.NugetMetadata == nil { + return nil + } + return p.NugetMetadata +} + +// GetPackageFiles returns the PackageFiles slice if it's non-nil, nil otherwise. +func (p *PackageVersion) GetPackageFiles() []*PackageFile { + if p == nil || p.PackageFiles == nil { + return nil + } + return p.PackageFiles +} + +// GetPackageHTMLURL returns the PackageHTMLURL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetPackageHTMLURL() string { + if p == nil || p.PackageHTMLURL == nil { + return "" + } + return *p.PackageHTMLURL +} + +// GetPackageURL returns the PackageURL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetPackageURL() string { + if p == nil || p.PackageURL == nil { + return "" + } + return *p.PackageURL +} + +// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetPrerelease() bool { + if p == nil || p.Prerelease == nil { + return false + } + return *p.Prerelease +} + +// GetRelease returns the Release field. +func (p *PackageVersion) GetRelease() *PackageRelease { + if p == nil { + return nil + } + return p.Release +} + +// GetRubyMetadata returns the RubyMetadata map if it's non-nil, an empty map otherwise. +func (p *PackageVersion) GetRubyMetadata() map[string]any { + if p == nil || p.RubyMetadata == nil { + return map[string]any{} + } + return p.RubyMetadata +} + +// GetSourceURL returns the SourceURL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetSourceURL() string { + if p == nil || p.SourceURL == nil { + return "" + } + return *p.SourceURL +} + +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetSummary() string { + if p == nil || p.Summary == nil { + return "" + } + return *p.Summary +} + +// GetTagName returns the TagName field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetTagName() string { + if p == nil || p.TagName == nil { + return "" + } + return *p.TagName +} + +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetTargetCommitish() string { + if p == nil || p.TargetCommitish == nil { + return "" + } + return *p.TargetCommitish +} + +// GetTargetOID returns the TargetOID field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetTargetOID() string { + if p == nil || p.TargetOID == nil { + return "" + } + return *p.TargetOID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetVersion() string { + if p == nil || p.Version == nil { + return "" + } + return *p.Version +} + +// GetInfo returns the Info field. +func (p *PackageVersionBody) GetInfo() *PackageVersionBodyInfo { + if p == nil { + return nil + } + return p.Info +} + +// GetRepo returns the Repo field. +func (p *PackageVersionBody) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetCollection returns the Collection field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetCollection() bool { + if p == nil || p.Collection == nil { + return false + } + return *p.Collection +} + +// GetMode returns the Mode field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetMode() int64 { + if p == nil || p.Mode == nil { + return 0 + } + return *p.Mode +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetOID returns the OID field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetOID() string { + if p == nil || p.OID == nil { + return "" + } + return *p.OID +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetPath() string { + if p == nil || p.Path == nil { + return "" + } + return *p.Path +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetSize() int64 { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetType() string { + if p == nil || p.Type == nil { + return "" + } + return *p.Type +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *Page) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *Page) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetPageName returns the PageName field if it's non-nil, zero value otherwise. +func (p *Page) GetPageName() string { + if p == nil || p.PageName == nil { + return "" + } + return *p.PageName +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *Page) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (p *Page) GetSummary() string { + if p == nil || p.Summary == nil { + return "" + } + return *p.Summary +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *Page) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetBuild returns the Build field. +func (p *PageBuildEvent) GetBuild() *PagesBuild { + if p == nil { + return nil + } + return p.Build +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PageBuildEvent) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetInstallation returns the Installation field. +func (p *PageBuildEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PageBuildEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetRepo returns the Repo field. +func (p *PageBuildEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PageBuildEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetBuildType returns the BuildType field if it's non-nil, zero value otherwise. +func (p *Pages) GetBuildType() string { + if p == nil || p.BuildType == nil { + return "" + } + return *p.BuildType +} + +// GetCNAME returns the CNAME field if it's non-nil, zero value otherwise. +func (p *Pages) GetCNAME() string { + if p == nil || p.CNAME == nil { + return "" + } + return *p.CNAME +} + +// GetCustom404 returns the Custom404 field if it's non-nil, zero value otherwise. +func (p *Pages) GetCustom404() bool { + if p == nil || p.Custom404 == nil { + return false + } + return *p.Custom404 +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *Pages) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetHTTPSCertificate returns the HTTPSCertificate field. +func (p *Pages) GetHTTPSCertificate() *PagesHTTPSCertificate { + if p == nil { + return nil + } + return p.HTTPSCertificate +} + +// GetHTTPSEnforced returns the HTTPSEnforced field if it's non-nil, zero value otherwise. +func (p *Pages) GetHTTPSEnforced() bool { + if p == nil || p.HTTPSEnforced == nil { + return false + } + return *p.HTTPSEnforced +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (p *Pages) GetPublic() bool { + if p == nil || p.Public == nil { + return false + } + return *p.Public +} + +// GetSource returns the Source field. +func (p *Pages) GetSource() *PagesSource { + if p == nil { + return nil + } + return p.Source +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (p *Pages) GetStatus() string { + if p == nil || p.Status == nil { + return "" + } + return *p.Status +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *Pages) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetCommit returns the Commit field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetCommit() string { + if p == nil || p.Commit == nil { + return "" + } + return *p.Commit +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetDuration() int { + if p == nil || p.Duration == nil { + return 0 + } + return *p.Duration +} + +// GetError returns the Error field. +func (p *PagesBuild) GetError() *PagesError { + if p == nil { + return nil + } + return p.Error +} + +// GetPusher returns the Pusher field. +func (p *PagesBuild) GetPusher() *User { + if p == nil { + return nil + } + return p.Pusher +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetStatus() string { + if p == nil || p.Status == nil { + return "" + } + return *p.Status +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PagesBuild) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetCAAError returns the CAAError field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetCAAError() string { + if p == nil || p.CAAError == nil { + return "" + } + return *p.CAAError +} + +// GetDNSResolves returns the DNSResolves field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetDNSResolves() bool { + if p == nil || p.DNSResolves == nil { + return false + } + return *p.DNSResolves +} + +// GetEnforcesHTTPS returns the EnforcesHTTPS field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetEnforcesHTTPS() bool { + if p == nil || p.EnforcesHTTPS == nil { + return false + } + return *p.EnforcesHTTPS +} + +// GetHasCNAMERecord returns the HasCNAMERecord field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetHasCNAMERecord() bool { + if p == nil || p.HasCNAMERecord == nil { + return false + } + return *p.HasCNAMERecord +} + +// GetHasMXRecordsPresent returns the HasMXRecordsPresent field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetHasMXRecordsPresent() bool { + if p == nil || p.HasMXRecordsPresent == nil { + return false + } + return *p.HasMXRecordsPresent +} + +// GetHost returns the Host field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetHost() string { + if p == nil || p.Host == nil { + return "" + } + return *p.Host +} + +// GetHTTPSError returns the HTTPSError field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetHTTPSError() string { + if p == nil || p.HTTPSError == nil { + return "" + } + return *p.HTTPSError +} + +// GetIsApexDomain returns the IsApexDomain field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsApexDomain() bool { + if p == nil || p.IsApexDomain == nil { + return false + } + return *p.IsApexDomain +} + +// GetIsARecord returns the IsARecord field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsARecord() bool { + if p == nil || p.IsARecord == nil { + return false + } + return *p.IsARecord +} + +// GetIsCloudflareIP returns the IsCloudflareIP field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsCloudflareIP() bool { + if p == nil || p.IsCloudflareIP == nil { + return false + } + return *p.IsCloudflareIP +} + +// GetIsCNAMEToFastly returns the IsCNAMEToFastly field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsCNAMEToFastly() bool { + if p == nil || p.IsCNAMEToFastly == nil { + return false + } + return *p.IsCNAMEToFastly +} + +// GetIsCNAMEToGithubUserDomain returns the IsCNAMEToGithubUserDomain field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsCNAMEToGithubUserDomain() bool { + if p == nil || p.IsCNAMEToGithubUserDomain == nil { + return false + } + return *p.IsCNAMEToGithubUserDomain +} + +// GetIsCNAMEToPagesDotGithubDotCom returns the IsCNAMEToPagesDotGithubDotCom field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsCNAMEToPagesDotGithubDotCom() bool { + if p == nil || p.IsCNAMEToPagesDotGithubDotCom == nil { + return false + } + return *p.IsCNAMEToPagesDotGithubDotCom +} + +// GetIsFastlyIP returns the IsFastlyIP field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsFastlyIP() bool { + if p == nil || p.IsFastlyIP == nil { + return false + } + return *p.IsFastlyIP +} + +// GetIsHTTPSEligible returns the IsHTTPSEligible field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsHTTPSEligible() bool { + if p == nil || p.IsHTTPSEligible == nil { + return false + } + return *p.IsHTTPSEligible +} + +// GetIsNonGithubPagesIPPresent returns the IsNonGithubPagesIPPresent field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsNonGithubPagesIPPresent() bool { + if p == nil || p.IsNonGithubPagesIPPresent == nil { + return false + } + return *p.IsNonGithubPagesIPPresent +} + +// GetIsOldIPAddress returns the IsOldIPAddress field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsOldIPAddress() bool { + if p == nil || p.IsOldIPAddress == nil { + return false + } + return *p.IsOldIPAddress +} + +// GetIsPagesDomain returns the IsPagesDomain field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsPagesDomain() bool { + if p == nil || p.IsPagesDomain == nil { + return false + } + return *p.IsPagesDomain +} + +// GetIsPointedToGithubPagesIP returns the IsPointedToGithubPagesIP field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsPointedToGithubPagesIP() bool { + if p == nil || p.IsPointedToGithubPagesIP == nil { + return false + } + return *p.IsPointedToGithubPagesIP +} + +// GetIsProxied returns the IsProxied field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsProxied() bool { + if p == nil || p.IsProxied == nil { + return false + } + return *p.IsProxied +} + +// GetIsServedByPages returns the IsServedByPages field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsServedByPages() bool { + if p == nil || p.IsServedByPages == nil { + return false + } + return *p.IsServedByPages +} + +// GetIsValid returns the IsValid field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsValid() bool { + if p == nil || p.IsValid == nil { + return false + } + return *p.IsValid +} + +// GetIsValidDomain returns the IsValidDomain field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetIsValidDomain() bool { + if p == nil || p.IsValidDomain == nil { + return false + } + return *p.IsValidDomain +} + +// GetNameservers returns the Nameservers field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetNameservers() string { + if p == nil || p.Nameservers == nil { + return "" + } + return *p.Nameservers +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetReason() string { + if p == nil || p.Reason == nil { + return "" + } + return *p.Reason +} + +// GetRespondsToHTTPS returns the RespondsToHTTPS field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetRespondsToHTTPS() bool { + if p == nil || p.RespondsToHTTPS == nil { + return false + } + return *p.RespondsToHTTPS +} + +// GetShouldBeARecord returns the ShouldBeARecord field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetShouldBeARecord() bool { + if p == nil || p.ShouldBeARecord == nil { + return false + } + return *p.ShouldBeARecord +} + +// GetURI returns the URI field if it's non-nil, zero value otherwise. +func (p *PagesDomain) GetURI() string { + if p == nil || p.URI == nil { + return "" + } + return *p.URI +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PagesError) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetAltDomain returns the AltDomain field. +func (p *PagesHealthCheckResponse) GetAltDomain() *PagesDomain { + if p == nil { + return nil + } + return p.AltDomain +} + +// GetDomain returns the Domain field. +func (p *PagesHealthCheckResponse) GetDomain() *PagesDomain { + if p == nil { + return nil + } + return p.Domain +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PagesHTTPSCertificate) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetDomains returns the Domains slice if it's non-nil, nil otherwise. +func (p *PagesHTTPSCertificate) GetDomains() []string { + if p == nil || p.Domains == nil { + return nil + } + return p.Domains +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (p *PagesHTTPSCertificate) GetExpiresAt() string { + if p == nil || p.ExpiresAt == nil { + return "" + } + return *p.ExpiresAt +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PagesHTTPSCertificate) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetBranch returns the Branch field if it's non-nil, zero value otherwise. +func (p *PagesSource) GetBranch() string { + if p == nil || p.Branch == nil { + return "" + } + return *p.Branch +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (p *PagesSource) GetPath() string { + if p == nil || p.Path == nil { + return "" + } + return *p.Path +} + +// GetTotalPages returns the TotalPages field if it's non-nil, zero value otherwise. +func (p *PageStats) GetTotalPages() int { + if p == nil || p.TotalPages == nil { + return 0 + } + return *p.TotalPages +} + +// GetBuildType returns the BuildType field if it's non-nil, zero value otherwise. +func (p *PagesUpdate) GetBuildType() string { + if p == nil || p.BuildType == nil { + return "" + } + return *p.BuildType +} + +// GetCNAME returns the CNAME field if it's non-nil, zero value otherwise. +func (p *PagesUpdate) GetCNAME() string { + if p == nil || p.CNAME == nil { + return "" + } + return *p.CNAME +} + +// GetHTTPSEnforced returns the HTTPSEnforced field if it's non-nil, zero value otherwise. +func (p *PagesUpdate) GetHTTPSEnforced() bool { + if p == nil || p.HTTPSEnforced == nil { + return false + } + return *p.HTTPSEnforced +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (p *PagesUpdate) GetPublic() bool { + if p == nil || p.Public == nil { + return false + } + return *p.Public +} + +// GetSource returns the Source field. +func (p *PagesUpdate) GetSource() *PagesSource { + if p == nil { + return nil + } + return p.Source +} + +// GetBuildType returns the BuildType field if it's non-nil, zero value otherwise. +func (p *PagesUpdateWithoutCNAME) GetBuildType() string { + if p == nil || p.BuildType == nil { + return "" + } + return *p.BuildType +} + +// GetHTTPSEnforced returns the HTTPSEnforced field if it's non-nil, zero value otherwise. +func (p *PagesUpdateWithoutCNAME) GetHTTPSEnforced() bool { + if p == nil || p.HTTPSEnforced == nil { + return false + } + return *p.HTTPSEnforced +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (p *PagesUpdateWithoutCNAME) GetPublic() bool { + if p == nil || p.Public == nil { + return false + } + return *p.Public +} + +// GetSource returns the Source field. +func (p *PagesUpdateWithoutCNAME) GetSource() *PagesSource { + if p == nil { + return nil + } + return p.Source +} + +// GetParameters returns the Parameters field. +func (p *PatternBranchRule) GetParameters() PatternRuleParameters { + if p == nil { + return PatternRuleParameters{} + } + return p.Parameters +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PatternRuleParameters) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNegate returns the Negate field if it's non-nil, zero value otherwise. +func (p *PatternRuleParameters) GetNegate() bool { + if p == nil || p.Negate == nil { + return false + } + return *p.Negate +} + +// GetOperator returns the Operator field. +func (p *PatternRuleParameters) GetOperator() PatternRuleOperator { + if p == nil { + return "" + } + return p.Operator +} + +// GetPattern returns the Pattern field. +func (p *PatternRuleParameters) GetPattern() string { + if p == nil { + return "" + } + return p.Pattern +} + +// GetCurrentUserCanApprove returns the CurrentUserCanApprove field if it's non-nil, zero value otherwise. +func (p *PendingDeployment) GetCurrentUserCanApprove() bool { + if p == nil || p.CurrentUserCanApprove == nil { + return false + } + return *p.CurrentUserCanApprove +} + +// GetEnvironment returns the Environment field. +func (p *PendingDeployment) GetEnvironment() *PendingDeploymentEnvironment { + if p == nil { + return nil + } + return p.Environment +} + +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (p *PendingDeployment) GetReviewers() []*RequiredReviewer { + if p == nil || p.Reviewers == nil { + return nil + } + return p.Reviewers +} + +// GetWaitTimer returns the WaitTimer field if it's non-nil, zero value otherwise. +func (p *PendingDeployment) GetWaitTimer() int64 { + if p == nil || p.WaitTimer == nil { + return 0 + } + return *p.WaitTimer +} + +// GetWaitTimerStartedAt returns the WaitTimerStartedAt field if it's non-nil, zero value otherwise. +func (p *PendingDeployment) GetWaitTimerStartedAt() Timestamp { + if p == nil || p.WaitTimerStartedAt == nil { + return Timestamp{} + } + return *p.WaitTimerStartedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PendingDeploymentEnvironment) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PendingDeploymentEnvironment) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PendingDeploymentEnvironment) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PendingDeploymentEnvironment) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PendingDeploymentEnvironment) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetComment returns the Comment field. +func (p *PendingDeploymentsRequest) GetComment() string { + if p == nil { + return "" + } + return p.Comment +} + +// GetEnvironmentIDs returns the EnvironmentIDs slice if it's non-nil, nil otherwise. +func (p *PendingDeploymentsRequest) GetEnvironmentIDs() []int64 { + if p == nil || p.EnvironmentIDs == nil { + return nil + } + return p.EnvironmentIDs +} + +// GetState returns the State field. +func (p *PendingDeploymentsRequest) GetState() string { + if p == nil { + return "" + } + return p.State +} + +// GetAccessGrantedAt returns the AccessGrantedAt field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetAccessGrantedAt() Timestamp { + if p == nil || p.AccessGrantedAt == nil { + return Timestamp{} + } + return *p.AccessGrantedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetOwner returns the Owner field. +func (p *PersonalAccessToken) GetOwner() *User { + if p == nil { + return nil + } + return p.Owner +} + +// GetPermissions returns the Permissions field. +func (p *PersonalAccessToken) GetPermissions() *PersonalAccessTokenPermissions { + if p == nil { + return nil + } + return p.Permissions +} + +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetRepositoriesURL() string { + if p == nil || p.RepositoriesURL == nil { + return "" + } + return *p.RepositoriesURL +} + +// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetRepositorySelection() string { + if p == nil || p.RepositorySelection == nil { + return "" + } + return *p.RepositorySelection +} + +// GetTokenExpired returns the TokenExpired field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenExpired() bool { + if p == nil || p.TokenExpired == nil { + return false + } + return *p.TokenExpired +} + +// GetTokenExpiresAt returns the TokenExpiresAt field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenExpiresAt() Timestamp { + if p == nil || p.TokenExpiresAt == nil { + return Timestamp{} + } + return *p.TokenExpiresAt +} + +// GetTokenID returns the TokenID field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenID() int64 { + if p == nil || p.TokenID == nil { + return 0 + } + return *p.TokenID +} + +// GetTokenLastUsedAt returns the TokenLastUsedAt field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenLastUsedAt() Timestamp { + if p == nil || p.TokenLastUsedAt == nil { + return Timestamp{} + } + return *p.TokenLastUsedAt +} + +// GetTokenName returns the TokenName field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenName() string { + if p == nil || p.TokenName == nil { + return "" + } + return *p.TokenName +} + +// GetOrg returns the Org map if it's non-nil, an empty map otherwise. +func (p *PersonalAccessTokenPermissions) GetOrg() map[string]string { + if p == nil || p.Org == nil { + return map[string]string{} + } + return p.Org +} + +// GetOther returns the Other map if it's non-nil, an empty map otherwise. +func (p *PersonalAccessTokenPermissions) GetOther() map[string]string { + if p == nil || p.Other == nil { + return map[string]string{} + } + return p.Other +} + +// GetRepo returns the Repo map if it's non-nil, an empty map otherwise. +func (p *PersonalAccessTokenPermissions) GetRepo() map[string]string { + if p == nil || p.Repo == nil { + return map[string]string{} + } + return p.Repo +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetOrg returns the Org field. +func (p *PersonalAccessTokenRequest) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetOwner returns the Owner field. +func (p *PersonalAccessTokenRequest) GetOwner() *User { + if p == nil { + return nil + } + return p.Owner +} + +// GetPermissionsAdded returns the PermissionsAdded field. +func (p *PersonalAccessTokenRequest) GetPermissionsAdded() *PersonalAccessTokenPermissions { + if p == nil { + return nil + } + return p.PermissionsAdded +} + +// GetPermissionsResult returns the PermissionsResult field. +func (p *PersonalAccessTokenRequest) GetPermissionsResult() *PersonalAccessTokenPermissions { + if p == nil { + return nil + } + return p.PermissionsResult +} + +// GetPermissionsUpgraded returns the PermissionsUpgraded field. +func (p *PersonalAccessTokenRequest) GetPermissionsUpgraded() *PersonalAccessTokenPermissions { + if p == nil { + return nil + } + return p.PermissionsUpgraded +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (p *PersonalAccessTokenRequest) GetRepositories() []*Repository { + if p == nil || p.Repositories == nil { + return nil + } + return p.Repositories +} + +// GetRepositoryCount returns the RepositoryCount field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetRepositoryCount() int64 { + if p == nil || p.RepositoryCount == nil { + return 0 + } + return *p.RepositoryCount +} + +// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetRepositorySelection() string { + if p == nil || p.RepositorySelection == nil { + return "" + } + return *p.RepositorySelection +} + +// GetTokenExpired returns the TokenExpired field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetTokenExpired() bool { + if p == nil || p.TokenExpired == nil { + return false + } + return *p.TokenExpired +} + +// GetTokenExpiresAt returns the TokenExpiresAt field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetTokenExpiresAt() Timestamp { + if p == nil || p.TokenExpiresAt == nil { + return Timestamp{} + } + return *p.TokenExpiresAt +} + +// GetTokenLastUsedAt returns the TokenLastUsedAt field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequest) GetTokenLastUsedAt() Timestamp { + if p == nil || p.TokenLastUsedAt == nil { + return Timestamp{} + } + return *p.TokenLastUsedAt +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PersonalAccessTokenRequestEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetInstallation returns the Installation field. +func (p *PersonalAccessTokenRequestEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PersonalAccessTokenRequestEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetPersonalAccessTokenRequest returns the PersonalAccessTokenRequest field. +func (p *PersonalAccessTokenRequestEvent) GetPersonalAccessTokenRequest() *PersonalAccessTokenRequest { + if p == nil { + return nil + } + return p.PersonalAccessTokenRequest +} + +// GetSender returns the Sender field. +func (p *PersonalAccessTokenRequestEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetHook returns the Hook field. +func (p *PingEvent) GetHook() *Hook { + if p == nil { + return nil + } + return p.Hook +} + +// GetHookID returns the HookID field if it's non-nil, zero value otherwise. +func (p *PingEvent) GetHookID() int64 { + if p == nil || p.HookID == nil { + return 0 + } + return *p.HookID +} + +// GetInstallation returns the Installation field. +func (p *PingEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PingEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetRepo returns the Repo field. +func (p *PingEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PingEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetZen returns the Zen field if it's non-nil, zero value otherwise. +func (p *PingEvent) GetZen() string { + if p == nil || p.Zen == nil { + return "" + } + return *p.Zen +} + +// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. +func (p *Plan) GetCollaborators() int { + if p == nil || p.Collaborators == nil { + return 0 + } + return *p.Collaborators +} + +// GetFilledSeats returns the FilledSeats field if it's non-nil, zero value otherwise. +func (p *Plan) GetFilledSeats() int { + if p == nil || p.FilledSeats == nil { + return 0 + } + return *p.FilledSeats +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *Plan) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetPrivateRepos returns the PrivateRepos field if it's non-nil, zero value otherwise. +func (p *Plan) GetPrivateRepos() int64 { + if p == nil || p.PrivateRepos == nil { + return 0 + } + return *p.PrivateRepos +} + +// GetSeats returns the Seats field if it's non-nil, zero value otherwise. +func (p *Plan) GetSeats() int { + if p == nil || p.Seats == nil { + return 0 + } + return *p.Seats +} + +// GetSpace returns the Space field if it's non-nil, zero value otherwise. +func (p *Plan) GetSpace() int { + if p == nil || p.Space == nil { + return 0 + } + return *p.Space +} + +// GetAutoTriggerChecks returns the AutoTriggerChecks slice if it's non-nil, nil otherwise. +func (p *PreferenceList) GetAutoTriggerChecks() []*AutoTriggerCheck { + if p == nil || p.AutoTriggerChecks == nil { + return nil + } + return p.AutoTriggerChecks +} + +// GetDiscountAmount returns the DiscountAmount field. +func (p *PremiumRequestUsageItem) GetDiscountAmount() float64 { + if p == nil { + return 0 + } + return p.DiscountAmount +} + +// GetDiscountQuantity returns the DiscountQuantity field. +func (p *PremiumRequestUsageItem) GetDiscountQuantity() float64 { + if p == nil { + return 0 + } + return p.DiscountQuantity +} + +// GetGrossAmount returns the GrossAmount field. +func (p *PremiumRequestUsageItem) GetGrossAmount() float64 { + if p == nil { + return 0 + } + return p.GrossAmount +} + +// GetGrossQuantity returns the GrossQuantity field. +func (p *PremiumRequestUsageItem) GetGrossQuantity() float64 { + if p == nil { + return 0 + } + return p.GrossQuantity +} + +// GetModel returns the Model field. +func (p *PremiumRequestUsageItem) GetModel() string { + if p == nil { + return "" + } + return p.Model +} + +// GetNetAmount returns the NetAmount field. +func (p *PremiumRequestUsageItem) GetNetAmount() float64 { + if p == nil { + return 0 + } + return p.NetAmount +} + +// GetNetQuantity returns the NetQuantity field. +func (p *PremiumRequestUsageItem) GetNetQuantity() float64 { + if p == nil { + return 0 + } + return p.NetQuantity +} + +// GetPricePerUnit returns the PricePerUnit field. +func (p *PremiumRequestUsageItem) GetPricePerUnit() float64 { + if p == nil { + return 0 + } + return p.PricePerUnit +} + +// GetProduct returns the Product field. +func (p *PremiumRequestUsageItem) GetProduct() string { + if p == nil { + return "" + } + return p.Product +} + +// GetSKU returns the SKU field. +func (p *PremiumRequestUsageItem) GetSKU() string { + if p == nil { + return "" + } + return p.SKU +} + +// GetUnitType returns the UnitType field. +func (p *PremiumRequestUsageItem) GetUnitType() string { + if p == nil { + return "" + } + return p.UnitType +} + +// GetModel returns the Model field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetModel() string { + if p == nil || p.Model == nil { + return "" + } + return *p.Model +} + +// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetOrganization() string { + if p == nil || p.Organization == nil { + return "" + } + return *p.Organization +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetProduct() string { + if p == nil || p.Product == nil { + return "" + } + return *p.Product +} + +// GetTimePeriod returns the TimePeriod field. +func (p *PremiumRequestUsageReport) GetTimePeriod() PremiumRequestUsageTimePeriod { + if p == nil { + return PremiumRequestUsageTimePeriod{} + } + return p.TimePeriod +} + +// GetUsageItems returns the UsageItems slice if it's non-nil, nil otherwise. +func (p *PremiumRequestUsageReport) GetUsageItems() []*PremiumRequestUsageItem { + if p == nil || p.UsageItems == nil { + return nil + } + return p.UsageItems +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReport) GetUser() string { + if p == nil || p.User == nil { + return "" + } + return *p.User +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetDay() int { + if p == nil || p.Day == nil { + return 0 + } + return *p.Day +} + +// GetModel returns the Model field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetModel() string { + if p == nil || p.Model == nil { + return "" + } + return *p.Model +} + +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetMonth() int { + if p == nil || p.Month == nil { + return 0 + } + return *p.Month +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetProduct() string { + if p == nil || p.Product == nil { + return "" + } + return *p.Product +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetUser() string { + if p == nil || p.User == nil { + return "" + } + return *p.User +} + +// GetYear returns the Year field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageReportOptions) GetYear() int { + if p == nil || p.Year == nil { + return 0 + } + return *p.Year +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageTimePeriod) GetDay() int { + if p == nil || p.Day == nil { + return 0 + } + return *p.Day +} + +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (p *PremiumRequestUsageTimePeriod) GetMonth() int { + if p == nil || p.Month == nil { + return 0 + } + return *p.Month +} + +// GetYear returns the Year field. +func (p *PremiumRequestUsageTimePeriod) GetYear() int { + if p == nil { + return 0 + } + return p.Year +} + +// GetConfigURL returns the ConfigURL field if it's non-nil, zero value otherwise. +func (p *PreReceiveHook) GetConfigURL() string { + if p == nil || p.ConfigURL == nil { + return "" + } + return *p.ConfigURL +} + +// GetEnforcement returns the Enforcement field if it's non-nil, zero value otherwise. +func (p *PreReceiveHook) GetEnforcement() string { + if p == nil || p.Enforcement == nil { + return "" + } + return *p.Enforcement +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PreReceiveHook) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PreReceiveHook) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetConfigurations returns the Configurations slice if it's non-nil, nil otherwise. +func (p *PrivateRegistries) GetConfigurations() []*PrivateRegistry { + if p == nil || p.Configurations == nil { + return nil + } + return p.Configurations +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (p *PrivateRegistries) GetTotalCount() int { + if p == nil || p.TotalCount == nil { + return 0 + } + return *p.TotalCount +} + +// GetAccountID returns the AccountID field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetAccountID() string { + if p == nil || p.AccountID == nil { + return "" + } + return *p.AccountID +} + +// GetAudience returns the Audience field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetAudience() string { + if p == nil || p.Audience == nil { + return "" + } + return *p.Audience +} + +// GetAuthType returns the AuthType field. +func (p *PrivateRegistry) GetAuthType() *PrivateRegistryAuthType { + if p == nil { + return nil + } + return p.AuthType +} + +// GetAWSRegion returns the AWSRegion field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetAWSRegion() string { + if p == nil || p.AWSRegion == nil { + return "" + } + return *p.AWSRegion +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetClientID() string { + if p == nil || p.ClientID == nil { + return "" + } + return *p.ClientID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDomain returns the Domain field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetDomain() string { + if p == nil || p.Domain == nil { + return "" + } + return *p.Domain +} + +// GetDomainOwner returns the DomainOwner field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetDomainOwner() string { + if p == nil || p.DomainOwner == nil { + return "" + } + return *p.DomainOwner +} + +// GetIdentityMappingName returns the IdentityMappingName field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetIdentityMappingName() string { + if p == nil || p.IdentityMappingName == nil { + return "" + } + return *p.IdentityMappingName +} + +// GetJFrogOIDCProviderName returns the JFrogOIDCProviderName field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetJFrogOIDCProviderName() string { + if p == nil || p.JFrogOIDCProviderName == nil { + return "" + } + return *p.JFrogOIDCProviderName +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetRegistryType returns the RegistryType field. +func (p *PrivateRegistry) GetRegistryType() *PrivateRegistryType { + if p == nil { + return nil + } + return p.RegistryType +} + +// GetReplacesBase returns the ReplacesBase field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetReplacesBase() bool { + if p == nil || p.ReplacesBase == nil { + return false + } + return *p.ReplacesBase +} + +// GetRoleName returns the RoleName field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetRoleName() string { + if p == nil || p.RoleName == nil { + return "" + } + return *p.RoleName +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (p *PrivateRegistry) GetSelectedRepositoryIDs() []int64 { + if p == nil || p.SelectedRepositoryIDs == nil { + return nil + } + return p.SelectedRepositoryIDs +} + +// GetTenantID returns the TenantID field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetTenantID() string { + if p == nil || p.TenantID == nil { + return "" + } + return *p.TenantID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (p *PrivateRegistry) GetUsername() string { + if p == nil || p.Username == nil { + return "" + } + return *p.Username +} + +// GetVisibility returns the Visibility field. +func (p *PrivateRegistry) GetVisibility() *PrivateRegistryVisibility { + if p == nil { + return nil + } + return p.Visibility +} + +// GetHRef returns the HRef field if it's non-nil, zero value otherwise. +func (p *PRLink) GetHRef() string { + if p == nil || p.HRef == nil { + return "" + } + return *p.HRef +} + +// GetComments returns the Comments field. +func (p *PRLinks) GetComments() *PRLink { + if p == nil { + return nil + } + return p.Comments +} + +// GetCommits returns the Commits field. +func (p *PRLinks) GetCommits() *PRLink { + if p == nil { + return nil + } + return p.Commits +} + +// GetHTML returns the HTML field. +func (p *PRLinks) GetHTML() *PRLink { + if p == nil { + return nil + } + return p.HTML +} + +// GetIssue returns the Issue field. +func (p *PRLinks) GetIssue() *PRLink { + if p == nil { + return nil + } + return p.Issue +} + +// GetReviewComment returns the ReviewComment field. +func (p *PRLinks) GetReviewComment() *PRLink { + if p == nil { + return nil + } + return p.ReviewComment +} + +// GetReviewComments returns the ReviewComments field. +func (p *PRLinks) GetReviewComments() *PRLink { + if p == nil { + return nil + } + return p.ReviewComments +} + +// GetSelf returns the Self field. +func (p *PRLinks) GetSelf() *PRLink { + if p == nil { + return nil + } + return p.Self +} + +// GetStatuses returns the Statuses field. +func (p *PRLinks) GetStatuses() *PRLink { + if p == nil { + return nil + } + return p.Statuses +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (p *ProjectBody) GetFrom() string { + if p == nil || p.From == nil { + return "" + } + return *p.From +} + +// GetNote returns the Note field. +func (p *ProjectCardChange) GetNote() *ProjectCardNote { + if p == nil { + return nil + } + return p.Note +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (p *ProjectCardNote) GetFrom() string { + if p == nil || p.From == nil { + return "" + } + return *p.From +} + +// GetBody returns the Body field. +func (p *ProjectChange) GetBody() *ProjectBody { + if p == nil { + return nil + } + return p.Body +} + +// GetName returns the Name field. +func (p *ProjectChange) GetName() *ProjectName { + if p == nil { + return nil + } + return p.Name +} + +// GetName returns the Name field. +func (p *ProjectColumnChange) GetName() *ProjectColumnName { + if p == nil { + return nil + } + return p.Name +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (p *ProjectColumnName) GetFrom() string { + if p == nil || p.From == nil { + return "" + } + return *p.From +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (p *ProjectName) GetFrom() string { + if p == nil || p.From == nil { + return "" + } + return *p.From +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetClosedAt() Timestamp { + if p == nil || p.ClosedAt == nil { + return Timestamp{} + } + return *p.ClosedAt +} + +// GetColumnsURL returns the ColumnsURL field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetColumnsURL() string { + if p == nil || p.ColumnsURL == nil { + return "" + } + return *p.ColumnsURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetCreator returns the Creator field. +func (p *ProjectV2) GetCreator() *User { + if p == nil { + return nil + } + return p.Creator +} + +// GetDeletedAt returns the DeletedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetDeletedAt() Timestamp { + if p == nil || p.DeletedAt == nil { + return Timestamp{} + } + return *p.DeletedAt +} + +// GetDeletedBy returns the DeletedBy field. +func (p *ProjectV2) GetDeletedBy() *User { + if p == nil { + return nil + } + return p.DeletedBy +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetIsTemplate returns the IsTemplate field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetIsTemplate() bool { + if p == nil || p.IsTemplate == nil { + return false + } + return *p.IsTemplate +} + +// GetLatestStatusUpdate returns the LatestStatusUpdate field. +func (p *ProjectV2) GetLatestStatusUpdate() *ProjectV2StatusUpdate { + if p == nil { + return nil + } + return p.LatestStatusUpdate +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetOrganizationPermission returns the OrganizationPermission field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetOrganizationPermission() string { + if p == nil || p.OrganizationPermission == nil { + return "" + } + return *p.OrganizationPermission +} + +// GetOwner returns the Owner field. +func (p *ProjectV2) GetOwner() *User { + if p == nil { + return nil + } + return p.Owner +} + +// GetOwnerURL returns the OwnerURL field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetOwnerURL() string { + if p == nil || p.OwnerURL == nil { + return "" + } + return *p.OwnerURL +} + +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetPrivate() bool { + if p == nil || p.Private == nil { + return false + } + return *p.Private +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetPublic() bool { + if p == nil || p.Public == nil { + return false + } + return *p.Public +} + +// GetShortDescription returns the ShortDescription field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetShortDescription() string { + if p == nil || p.ShortDescription == nil { + return "" + } + return *p.ShortDescription +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *ProjectV2) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *ProjectV2DraftIssue) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2DraftIssue) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2DraftIssue) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2DraftIssue) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *ProjectV2DraftIssue) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2DraftIssue) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetUser returns the User field. +func (p *ProjectV2DraftIssue) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *ProjectV2Event) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetInstallation returns the Installation field. +func (p *ProjectV2Event) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *ProjectV2Event) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetProjectsV2 returns the ProjectsV2 field. +func (p *ProjectV2Event) GetProjectsV2() *ProjectV2 { + if p == nil { + return nil + } + return p.ProjectsV2 +} + +// GetSender returns the Sender field. +func (p *ProjectV2Event) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetConfiguration returns the Configuration field. +func (p *ProjectV2Field) GetConfiguration() *ProjectV2FieldConfiguration { + if p == nil { + return nil + } + return p.Configuration +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDataType returns the DataType field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetDataType() string { + if p == nil || p.DataType == nil { + return "" + } + return *p.DataType +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetOptions returns the Options slice if it's non-nil, nil otherwise. +func (p *ProjectV2Field) GetOptions() []*ProjectV2FieldOption { + if p == nil || p.Options == nil { + return nil + } + return p.Options +} + +// GetProjectURL returns the ProjectURL field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetProjectURL() string { + if p == nil || p.ProjectURL == nil { + return "" + } + return *p.ProjectURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2Field) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldConfiguration) GetDuration() int { + if p == nil || p.Duration == nil { + return 0 + } + return *p.Duration +} + +// GetIterations returns the Iterations slice if it's non-nil, nil otherwise. +func (p *ProjectV2FieldConfiguration) GetIterations() []*ProjectV2FieldIteration { + if p == nil || p.Iterations == nil { + return nil + } + return p.Iterations +} + +// GetStartDay returns the StartDay field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldConfiguration) GetStartDay() int { + if p == nil || p.StartDay == nil { + return 0 + } + return *p.StartDay +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIteration) GetDuration() int { + if p == nil || p.Duration == nil { + return 0 + } + return *p.Duration +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIteration) GetID() string { + if p == nil || p.ID == nil { + return "" + } + return *p.ID +} + +// GetStartDate returns the StartDate field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIteration) GetStartDate() string { + if p == nil || p.StartDate == nil { + return "" + } + return *p.StartDate +} + +// GetTitle returns the Title field. +func (p *ProjectV2FieldIteration) GetTitle() *ProjectV2TextContent { + if p == nil { + return nil + } + return p.Title +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIterationConfiguration) GetDuration() int { + if p == nil || p.Duration == nil { + return 0 + } + return *p.Duration +} + +// GetIterations returns the Iterations slice if it's non-nil, nil otherwise. +func (p *ProjectV2FieldIterationConfiguration) GetIterations() []*ProjectV2FieldIterationConfigurationIteration { + if p == nil || p.Iterations == nil { + return nil + } + return p.Iterations +} + +// GetStartDate returns the StartDate field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIterationConfiguration) GetStartDate() string { + if p == nil || p.StartDate == nil { + return "" + } + return *p.StartDate +} + +// GetDuration returns the Duration field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIterationConfigurationIteration) GetDuration() int { + if p == nil || p.Duration == nil { + return 0 + } + return *p.Duration +} + +// GetStartDate returns the StartDate field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIterationConfigurationIteration) GetStartDate() string { + if p == nil || p.StartDate == nil { + return "" + } + return *p.StartDate +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldIterationConfigurationIteration) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldOption) GetColor() string { + if p == nil || p.Color == nil { + return "" + } + return *p.Color +} + +// GetDescription returns the Description field. +func (p *ProjectV2FieldOption) GetDescription() *ProjectV2TextContent { + if p == nil { + return nil + } + return p.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldOption) GetID() string { + if p == nil || p.ID == nil { + return "" + } + return *p.ID +} + +// GetName returns the Name field. +func (p *ProjectV2FieldOption) GetName() *ProjectV2TextContent { + if p == nil { + return nil + } + return p.Name +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldSingleSelectOption) GetColor() string { + if p == nil || p.Color == nil { + return "" + } + return *p.Color +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *ProjectV2FieldSingleSelectOption) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetName returns the Name field. +func (p *ProjectV2FieldSingleSelectOption) GetName() string { + if p == nil { + return "" + } + return p.Name +} + +// GetArchivedAt returns the ArchivedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetArchivedAt() Timestamp { + if p == nil || p.ArchivedAt == nil { + return Timestamp{} + } + return *p.ArchivedAt +} + +// GetContent returns the Content field. +func (p *ProjectV2Item) GetContent() *ProjectV2ItemContent { + if p == nil { + return nil + } + return p.Content +} + +// GetContentNodeID returns the ContentNodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetContentNodeID() string { + if p == nil || p.ContentNodeID == nil { + return "" + } + return *p.ContentNodeID +} + +// GetContentType returns the ContentType field. +func (p *ProjectV2Item) GetContentType() *ProjectV2ItemContentType { + if p == nil { + return nil + } + return p.ContentType +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetCreator returns the Creator field. +func (p *ProjectV2Item) GetCreator() *User { + if p == nil { + return nil + } + return p.Creator +} + +// GetFields returns the Fields slice if it's non-nil, nil otherwise. +func (p *ProjectV2Item) GetFields() []*ProjectV2ItemFieldValue { + if p == nil || p.Fields == nil { + return nil + } + return p.Fields +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetItemURL returns the ItemURL field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetItemURL() string { + if p == nil || p.ItemURL == nil { + return "" + } + return *p.ItemURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetProjectNodeID returns the ProjectNodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetProjectNodeID() string { + if p == nil || p.ProjectNodeID == nil { + return "" + } + return *p.ProjectNodeID +} + +// GetProjectURL returns the ProjectURL field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetProjectURL() string { + if p == nil || p.ProjectURL == nil { + return "" + } + return *p.ProjectURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2Item) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetArchivedAt returns the ArchivedAt field. +func (p *ProjectV2ItemChange) GetArchivedAt() *ArchivedAt { + if p == nil { + return nil + } + return p.ArchivedAt +} + +// GetFieldValue returns the FieldValue field. +func (p *ProjectV2ItemChange) GetFieldValue() *FieldValue { + if p == nil { + return nil + } + return p.FieldValue +} + +// GetDraftIssue returns the DraftIssue field. +func (p *ProjectV2ItemContent) GetDraftIssue() *ProjectV2DraftIssue { + if p == nil { + return nil + } + return p.DraftIssue +} + +// GetIssue returns the Issue field. +func (p *ProjectV2ItemContent) GetIssue() *Issue { + if p == nil { + return nil + } + return p.Issue +} + +// GetPullRequest returns the PullRequest field. +func (p *ProjectV2ItemContent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *ProjectV2ItemEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetChanges returns the Changes field. +func (p *ProjectV2ItemEvent) GetChanges() *ProjectV2ItemChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *ProjectV2ItemEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *ProjectV2ItemEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetProjectV2Item returns the ProjectV2Item field. +func (p *ProjectV2ItemEvent) GetProjectV2Item() *ProjectV2Item { + if p == nil { + return nil + } + return p.ProjectV2Item +} + +// GetSender returns the Sender field. +func (p *ProjectV2ItemEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetDataType returns the DataType field if it's non-nil, zero value otherwise. +func (p *ProjectV2ItemFieldValue) GetDataType() string { + if p == nil || p.DataType == nil { + return "" + } + return *p.DataType +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2ItemFieldValue) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *ProjectV2ItemFieldValue) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetValue returns the Value field. +func (p *ProjectV2ItemFieldValue) GetValue() any { + if p == nil { + return nil + } + return p.Value +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetCreator returns the Creator field. +func (p *ProjectV2StatusUpdate) GetCreator() *User { + if p == nil { + return nil + } + return p.Creator +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetProjectNodeID returns the ProjectNodeID field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetProjectNodeID() string { + if p == nil || p.ProjectNodeID == nil { + return "" + } + return *p.ProjectNodeID +} + +// GetStartDate returns the StartDate field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetStartDate() string { + if p == nil || p.StartDate == nil { + return "" + } + return *p.StartDate +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetStatus() string { + if p == nil || p.Status == nil { + return "" + } + return *p.Status +} + +// GetTargetDate returns the TargetDate field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetTargetDate() string { + if p == nil || p.TargetDate == nil { + return "" + } + return *p.TargetDate +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *ProjectV2StatusUpdate) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetHTML returns the HTML field if it's non-nil, zero value otherwise. +func (p *ProjectV2TextContent) GetHTML() string { + if p == nil || p.HTML == nil { + return "" + } + return *p.HTML +} + +// GetRaw returns the Raw field if it's non-nil, zero value otherwise. +func (p *ProjectV2TextContent) GetRaw() string { + if p == nil || p.Raw == nil { + return "" + } + return *p.Raw +} + +// GetCreatedAt returns the CreatedAt field. +func (p *ProjectV2View) GetCreatedAt() Timestamp { + if p == nil { + return Timestamp{} + } + return p.CreatedAt +} + +// GetCreator returns the Creator field. +func (p *ProjectV2View) GetCreator() User { + if p == nil { + return User{} + } + return p.Creator +} + +// GetFilter returns the Filter field if it's non-nil, zero value otherwise. +func (p *ProjectV2View) GetFilter() string { + if p == nil || p.Filter == nil { + return "" + } + return *p.Filter +} + +// GetGroupBy returns the GroupBy slice if it's non-nil, nil otherwise. +func (p *ProjectV2View) GetGroupBy() []int64 { + if p == nil || p.GroupBy == nil { + return nil + } + return p.GroupBy +} + +// GetHTMLURL returns the HTMLURL field. +func (p *ProjectV2View) GetHTMLURL() string { + if p == nil { + return "" + } + return p.HTMLURL +} + +// GetID returns the ID field. +func (p *ProjectV2View) GetID() int64 { + if p == nil { + return 0 + } + return p.ID +} + +// GetLayout returns the Layout field. +func (p *ProjectV2View) GetLayout() string { + if p == nil { + return "" + } + return p.Layout +} + +// GetName returns the Name field. +func (p *ProjectV2View) GetName() string { + if p == nil { + return "" + } + return p.Name +} + +// GetNodeID returns the NodeID field. +func (p *ProjectV2View) GetNodeID() string { + if p == nil { + return "" + } + return p.NodeID +} + +// GetNumber returns the Number field. +func (p *ProjectV2View) GetNumber() int { + if p == nil { + return 0 + } + return p.Number +} + +// GetProjectURL returns the ProjectURL field. +func (p *ProjectV2View) GetProjectURL() string { + if p == nil { + return "" + } + return p.ProjectURL +} + +// GetSortBy returns the SortBy slice if it's non-nil, nil otherwise. +func (p *ProjectV2View) GetSortBy() []*ProjectV2ViewSortBy { + if p == nil || p.SortBy == nil { + return nil + } + return p.SortBy +} + +// GetUpdatedAt returns the UpdatedAt field. +func (p *ProjectV2View) GetUpdatedAt() Timestamp { + if p == nil { + return Timestamp{} + } + return p.UpdatedAt +} + +// GetVerticalGroupBy returns the VerticalGroupBy slice if it's non-nil, nil otherwise. +func (p *ProjectV2View) GetVerticalGroupBy() []int64 { + if p == nil || p.VerticalGroupBy == nil { + return nil + } + return p.VerticalGroupBy +} + +// GetVisibleFields returns the VisibleFields slice if it's non-nil, nil otherwise. +func (p *ProjectV2View) GetVisibleFields() []int64 { + if p == nil || p.VisibleFields == nil { + return nil + } + return p.VisibleFields +} + +// GetDirection returns the Direction field if it's non-nil, zero value otherwise. +func (p *ProjectV2ViewSortBy) GetDirection() string { + if p == nil || p.Direction == nil { + return "" + } + return *p.Direction +} + +// GetFieldID returns the FieldID field if it's non-nil, zero value otherwise. +func (p *ProjectV2ViewSortBy) GetFieldID() int64 { + if p == nil || p.FieldID == nil { + return 0 + } + return *p.FieldID +} + +// GetAllowDeletions returns the AllowDeletions field. +func (p *Protection) GetAllowDeletions() *AllowDeletions { + if p == nil { + return nil + } + return p.AllowDeletions +} + +// GetAllowForcePushes returns the AllowForcePushes field. +func (p *Protection) GetAllowForcePushes() *AllowForcePushes { + if p == nil { + return nil + } + return p.AllowForcePushes +} + +// GetAllowForkSyncing returns the AllowForkSyncing field. +func (p *Protection) GetAllowForkSyncing() *AllowForkSyncing { + if p == nil { + return nil + } + return p.AllowForkSyncing +} + +// GetBlockCreations returns the BlockCreations field. +func (p *Protection) GetBlockCreations() *BlockCreations { + if p == nil { + return nil + } + return p.BlockCreations +} + +// GetEnforceAdmins returns the EnforceAdmins field. +func (p *Protection) GetEnforceAdmins() *AdminEnforcement { + if p == nil { + return nil + } + return p.EnforceAdmins +} + +// GetLockBranch returns the LockBranch field. +func (p *Protection) GetLockBranch() *LockBranch { + if p == nil { + return nil + } + return p.LockBranch +} + +// GetRequiredConversationResolution returns the RequiredConversationResolution field. +func (p *Protection) GetRequiredConversationResolution() *RequiredConversationResolution { + if p == nil { + return nil + } + return p.RequiredConversationResolution +} + +// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. +func (p *Protection) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcement { + if p == nil { + return nil + } + return p.RequiredPullRequestReviews +} + +// GetRequiredSignatures returns the RequiredSignatures field. +func (p *Protection) GetRequiredSignatures() *SignaturesProtectedBranch { + if p == nil { + return nil + } + return p.RequiredSignatures +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks field. +func (p *Protection) GetRequiredStatusChecks() *RequiredStatusChecks { + if p == nil { + return nil + } + return p.RequiredStatusChecks +} + +// GetRequireLinearHistory returns the RequireLinearHistory field. +func (p *Protection) GetRequireLinearHistory() *RequireLinearHistory { + if p == nil { + return nil + } + return p.RequireLinearHistory +} + +// GetRestrictions returns the Restrictions field. +func (p *Protection) GetRestrictions() *BranchRestrictions { + if p == nil { + return nil + } + return p.Restrictions +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *Protection) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetAdminEnforced returns the AdminEnforced field. +func (p *ProtectionChanges) GetAdminEnforced() *AdminEnforcedChanges { + if p == nil { + return nil + } + return p.AdminEnforced +} + +// GetAllowDeletionsEnforcementLevel returns the AllowDeletionsEnforcementLevel field. +func (p *ProtectionChanges) GetAllowDeletionsEnforcementLevel() *AllowDeletionsEnforcementLevelChanges { + if p == nil { + return nil + } + return p.AllowDeletionsEnforcementLevel +} + +// GetAuthorizedActorNames returns the AuthorizedActorNames field. +func (p *ProtectionChanges) GetAuthorizedActorNames() *AuthorizedActorNames { + if p == nil { + return nil + } + return p.AuthorizedActorNames +} + +// GetAuthorizedActorsOnly returns the AuthorizedActorsOnly field. +func (p *ProtectionChanges) GetAuthorizedActorsOnly() *AuthorizedActorsOnly { + if p == nil { + return nil + } + return p.AuthorizedActorsOnly +} + +// GetAuthorizedDismissalActorsOnly returns the AuthorizedDismissalActorsOnly field. +func (p *ProtectionChanges) GetAuthorizedDismissalActorsOnly() *AuthorizedDismissalActorsOnlyChanges { + if p == nil { + return nil + } + return p.AuthorizedDismissalActorsOnly +} + +// GetCreateProtected returns the CreateProtected field. +func (p *ProtectionChanges) GetCreateProtected() *CreateProtectedChanges { + if p == nil { + return nil + } + return p.CreateProtected +} + +// GetDismissStaleReviewsOnPush returns the DismissStaleReviewsOnPush field. +func (p *ProtectionChanges) GetDismissStaleReviewsOnPush() *DismissStaleReviewsOnPushChanges { + if p == nil { + return nil + } + return p.DismissStaleReviewsOnPush +} + +// GetLinearHistoryRequirementEnforcementLevel returns the LinearHistoryRequirementEnforcementLevel field. +func (p *ProtectionChanges) GetLinearHistoryRequirementEnforcementLevel() *LinearHistoryRequirementEnforcementLevelChanges { + if p == nil { + return nil + } + return p.LinearHistoryRequirementEnforcementLevel +} + +// GetPullRequestReviewsEnforcementLevel returns the PullRequestReviewsEnforcementLevel field. +func (p *ProtectionChanges) GetPullRequestReviewsEnforcementLevel() *PullRequestReviewsEnforcementLevelChanges { + if p == nil { + return nil + } + return p.PullRequestReviewsEnforcementLevel +} + +// GetRequireCodeOwnerReview returns the RequireCodeOwnerReview field. +func (p *ProtectionChanges) GetRequireCodeOwnerReview() *RequireCodeOwnerReviewChanges { + if p == nil { + return nil + } + return p.RequireCodeOwnerReview +} + +// GetRequiredConversationResolutionLevel returns the RequiredConversationResolutionLevel field. +func (p *ProtectionChanges) GetRequiredConversationResolutionLevel() *RequiredConversationResolutionLevelChanges { + if p == nil { + return nil + } + return p.RequiredConversationResolutionLevel +} + +// GetRequiredDeploymentsEnforcementLevel returns the RequiredDeploymentsEnforcementLevel field. +func (p *ProtectionChanges) GetRequiredDeploymentsEnforcementLevel() *RequiredDeploymentsEnforcementLevelChanges { + if p == nil { + return nil + } + return p.RequiredDeploymentsEnforcementLevel +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks field. +func (p *ProtectionChanges) GetRequiredStatusChecks() *RequiredStatusChecksChanges { + if p == nil { + return nil + } + return p.RequiredStatusChecks +} + +// GetRequiredStatusChecksEnforcementLevel returns the RequiredStatusChecksEnforcementLevel field. +func (p *ProtectionChanges) GetRequiredStatusChecksEnforcementLevel() *RequiredStatusChecksEnforcementLevelChanges { + if p == nil { + return nil + } + return p.RequiredStatusChecksEnforcementLevel +} + +// GetRequireLastPushApproval returns the RequireLastPushApproval field. +func (p *ProtectionChanges) GetRequireLastPushApproval() *RequireLastPushApprovalChanges { + if p == nil { + return nil + } + return p.RequireLastPushApproval +} + +// GetSignatureRequirementEnforcementLevel returns the SignatureRequirementEnforcementLevel field. +func (p *ProtectionChanges) GetSignatureRequirementEnforcementLevel() *SignatureRequirementEnforcementLevelChanges { + if p == nil { + return nil + } + return p.SignatureRequirementEnforcementLevel +} + +// GetAllowDeletions returns the AllowDeletions field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetAllowDeletions() bool { + if p == nil || p.AllowDeletions == nil { + return false + } + return *p.AllowDeletions +} + +// GetAllowForcePushes returns the AllowForcePushes field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetAllowForcePushes() bool { + if p == nil || p.AllowForcePushes == nil { + return false + } + return *p.AllowForcePushes +} + +// GetAllowForkSyncing returns the AllowForkSyncing field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetAllowForkSyncing() bool { + if p == nil || p.AllowForkSyncing == nil { + return false + } + return *p.AllowForkSyncing +} + +// GetBlockCreations returns the BlockCreations field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetBlockCreations() bool { + if p == nil || p.BlockCreations == nil { + return false + } + return *p.BlockCreations +} + +// GetEnforceAdmins returns the EnforceAdmins field. +func (p *ProtectionRequest) GetEnforceAdmins() bool { + if p == nil { + return false + } + return p.EnforceAdmins +} + +// GetLockBranch returns the LockBranch field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetLockBranch() bool { + if p == nil || p.LockBranch == nil { + return false + } + return *p.LockBranch +} + +// GetRequiredConversationResolution returns the RequiredConversationResolution field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetRequiredConversationResolution() bool { + if p == nil || p.RequiredConversationResolution == nil { + return false + } + return *p.RequiredConversationResolution +} + +// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. +func (p *ProtectionRequest) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcementRequest { + if p == nil { + return nil + } + return p.RequiredPullRequestReviews +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks field. +func (p *ProtectionRequest) GetRequiredStatusChecks() *RequiredStatusChecks { + if p == nil { + return nil + } + return p.RequiredStatusChecks +} + +// GetRequireLinearHistory returns the RequireLinearHistory field if it's non-nil, zero value otherwise. +func (p *ProtectionRequest) GetRequireLinearHistory() bool { + if p == nil || p.RequireLinearHistory == nil { + return false + } + return *p.RequireLinearHistory +} + +// GetRestrictions returns the Restrictions field. +func (p *ProtectionRequest) GetRestrictions() *BranchRestrictionsRequest { + if p == nil { + return nil + } + return p.Restrictions +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *ProtectionRule) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *ProtectionRule) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetPreventSelfReview returns the PreventSelfReview field if it's non-nil, zero value otherwise. +func (p *ProtectionRule) GetPreventSelfReview() bool { + if p == nil || p.PreventSelfReview == nil { + return false + } + return *p.PreventSelfReview +} + +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (p *ProtectionRule) GetReviewers() []*RequiredReviewer { + if p == nil || p.Reviewers == nil { + return nil + } + return p.Reviewers +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (p *ProtectionRule) GetType() string { + if p == nil || p.Type == nil { + return "" + } + return *p.Type +} + +// GetWaitTimer returns the WaitTimer field if it's non-nil, zero value otherwise. +func (p *ProtectionRule) GetWaitTimer() int { + if p == nil || p.WaitTimer == nil { + return 0 + } + return *p.WaitTimer +} + +// GetInstallation returns the Installation field. +func (p *PublicEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PublicEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetRepo returns the Repo field. +func (p *PublicEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PublicEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetCurrentUsage returns the CurrentUsage field. +func (p *PublicIPUsage) GetCurrentUsage() int64 { + if p == nil { + return 0 + } + return p.CurrentUsage +} + +// GetMaximum returns the Maximum field. +func (p *PublicIPUsage) GetMaximum() int64 { + if p == nil { + return 0 + } + return p.Maximum +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (p *PublicKey) GetKey() string { + if p == nil || p.Key == nil { + return "" + } + return *p.Key +} + +// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. +func (p *PublicKey) GetKeyID() string { + if p == nil || p.KeyID == nil { + return "" + } + return *p.KeyID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PublishCodespaceOptions) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (p *PublishCodespaceOptions) GetPrivate() bool { + if p == nil || p.Private == nil { + return false + } + return *p.Private +} + +// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetActiveLockReason() string { + if p == nil || p.ActiveLockReason == nil { + return "" + } + return *p.ActiveLockReason +} + +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetAdditions() int { + if p == nil || p.Additions == nil { + return 0 + } + return *p.Additions +} + +// GetAssignee returns the Assignee field. +func (p *PullRequest) GetAssignee() *User { + if p == nil { + return nil + } + return p.Assignee +} + +// GetAssignees returns the Assignees slice if it's non-nil, nil otherwise. +func (p *PullRequest) GetAssignees() []*User { + if p == nil || p.Assignees == nil { + return nil + } + return p.Assignees +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetAuthorAssociation() string { + if p == nil || p.AuthorAssociation == nil { + return "" + } + return *p.AuthorAssociation +} + +// GetAutoMerge returns the AutoMerge field. +func (p *PullRequest) GetAutoMerge() *PullRequestAutoMerge { + if p == nil { + return nil + } + return p.AutoMerge +} + +// GetBase returns the Base field. +func (p *PullRequest) GetBase() *PullRequestBranch { + if p == nil { + return nil + } + return p.Base +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetChangedFiles returns the ChangedFiles field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetChangedFiles() int { + if p == nil || p.ChangedFiles == nil { + return 0 + } + return *p.ChangedFiles +} + +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetClosedAt() Timestamp { + if p == nil || p.ClosedAt == nil { + return Timestamp{} + } + return *p.ClosedAt +} + +// GetComments returns the Comments field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetComments() int { + if p == nil || p.Comments == nil { + return 0 + } + return *p.Comments +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetCommentsURL() string { + if p == nil || p.CommentsURL == nil { + return "" + } + return *p.CommentsURL +} + +// GetCommits returns the Commits field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetCommits() int { + if p == nil || p.Commits == nil { + return 0 + } + return *p.Commits +} + +// GetCommitsURL returns the CommitsURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetCommitsURL() string { + if p == nil || p.CommitsURL == nil { + return "" + } + return *p.CommitsURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetDeletions() int { + if p == nil || p.Deletions == nil { + return 0 + } + return *p.Deletions +} + +// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetDiffURL() string { + if p == nil || p.DiffURL == nil { + return "" + } + return *p.DiffURL +} + +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetDraft() bool { + if p == nil || p.Draft == nil { + return false + } + return *p.Draft +} + +// GetHead returns the Head field. +func (p *PullRequest) GetHead() *PullRequestBranch { + if p == nil { + return nil + } + return p.Head +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetIssueURL() string { + if p == nil || p.IssueURL == nil { + return "" + } + return *p.IssueURL +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (p *PullRequest) GetLabels() []*Label { + if p == nil || p.Labels == nil { + return nil + } + return p.Labels +} + +// GetLinks returns the Links field. +func (p *PullRequest) GetLinks() *PRLinks { + if p == nil { + return nil + } + return p.Links +} + +// GetLocked returns the Locked field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetLocked() bool { + if p == nil || p.Locked == nil { + return false + } + return *p.Locked +} + +// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMaintainerCanModify() bool { + if p == nil || p.MaintainerCanModify == nil { + return false + } + return *p.MaintainerCanModify +} + +// GetMergeable returns the Mergeable field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergeable() bool { + if p == nil || p.Mergeable == nil { + return false + } + return *p.Mergeable +} + +// GetMergeableState returns the MergeableState field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergeableState() string { + if p == nil || p.MergeableState == nil { + return "" + } + return *p.MergeableState +} + +// GetMergeCommitSHA returns the MergeCommitSHA field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergeCommitSHA() string { + if p == nil || p.MergeCommitSHA == nil { + return "" + } + return *p.MergeCommitSHA +} + +// GetMerged returns the Merged field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMerged() bool { + if p == nil || p.Merged == nil { + return false + } + return *p.Merged +} + +// GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetMergedAt() Timestamp { + if p == nil || p.MergedAt == nil { + return Timestamp{} + } + return *p.MergedAt +} + +// GetMergedBy returns the MergedBy field. +func (p *PullRequest) GetMergedBy() *User { + if p == nil { + return nil + } + return p.MergedBy +} + +// GetMilestone returns the Milestone field. +func (p *PullRequest) GetMilestone() *Milestone { + if p == nil { + return nil + } + return p.Milestone +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetPatchURL() string { + if p == nil || p.PatchURL == nil { + return "" + } + return *p.PatchURL +} + +// GetRebaseable returns the Rebaseable field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetRebaseable() bool { + if p == nil || p.Rebaseable == nil { + return false + } + return *p.Rebaseable +} + +// GetRequestedReviewers returns the RequestedReviewers slice if it's non-nil, nil otherwise. +func (p *PullRequest) GetRequestedReviewers() []*User { + if p == nil || p.RequestedReviewers == nil { + return nil + } + return p.RequestedReviewers +} + +// GetRequestedTeams returns the RequestedTeams slice if it's non-nil, nil otherwise. +func (p *PullRequest) GetRequestedTeams() []*Team { + if p == nil || p.RequestedTeams == nil { + return nil + } + return p.RequestedTeams +} + +// GetReviewComments returns the ReviewComments field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetReviewComments() int { + if p == nil || p.ReviewComments == nil { + return 0 + } + return *p.ReviewComments +} + +// GetReviewCommentsURL returns the ReviewCommentsURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetReviewCommentsURL() string { + if p == nil || p.ReviewCommentsURL == nil { + return "" + } + return *p.ReviewCommentsURL +} + +// GetReviewCommentURL returns the ReviewCommentURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetReviewCommentURL() string { + if p == nil || p.ReviewCommentURL == nil { + return "" + } + return *p.ReviewCommentURL +} + +// GetStack returns the Stack field. +func (p *PullRequest) GetStack() *PullRequestStack { + if p == nil { + return nil + } + return p.Stack +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetStatusesURL() string { + if p == nil || p.StatusesURL == nil { + return "" + } + return *p.StatusesURL +} + +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetTitle() string { + if p == nil || p.Title == nil { + return "" + } + return *p.Title +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetUser returns the User field. +func (p *PullRequest) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetCommitMessage returns the CommitMessage field if it's non-nil, zero value otherwise. +func (p *PullRequestAutoMerge) GetCommitMessage() string { + if p == nil || p.CommitMessage == nil { + return "" + } + return *p.CommitMessage +} + +// GetCommitTitle returns the CommitTitle field if it's non-nil, zero value otherwise. +func (p *PullRequestAutoMerge) GetCommitTitle() string { + if p == nil || p.CommitTitle == nil { + return "" + } + return *p.CommitTitle +} + +// GetEnabledBy returns the EnabledBy field. +func (p *PullRequestAutoMerge) GetEnabledBy() *User { + if p == nil { + return nil + } + return p.EnabledBy +} + +// GetMergeMethod returns the MergeMethod field if it's non-nil, zero value otherwise. +func (p *PullRequestAutoMerge) GetMergeMethod() string { + if p == nil || p.MergeMethod == nil { + return "" + } + return *p.MergeMethod +} + +// GetLabel returns the Label field if it's non-nil, zero value otherwise. +func (p *PullRequestBranch) GetLabel() string { + if p == nil || p.Label == nil { + return "" + } + return *p.Label +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (p *PullRequestBranch) GetRef() string { + if p == nil || p.Ref == nil { + return "" + } + return *p.Ref +} + +// GetRepo returns the Repo field. +func (p *PullRequestBranch) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *PullRequestBranch) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetUser returns the User field. +func (p *PullRequestBranch) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetParameters returns the Parameters field. +func (p *PullRequestBranchRule) GetParameters() PullRequestRuleParameters { + if p == nil { + return PullRequestRuleParameters{} + } + return p.Parameters +} + +// GetExpectedHeadSHA returns the ExpectedHeadSHA field if it's non-nil, zero value otherwise. +func (p *PullRequestBranchUpdateOptions) GetExpectedHeadSHA() string { + if p == nil || p.ExpectedHeadSHA == nil { + return "" + } + return *p.ExpectedHeadSHA +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PullRequestBranchUpdateResponse) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequestBranchUpdateResponse) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetAuthorAssociation() string { + if p == nil || p.AuthorAssociation == nil { + return "" + } + return *p.AuthorAssociation +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetCommitID() string { + if p == nil || p.CommitID == nil { + return "" + } + return *p.CommitID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDiffHunk returns the DiffHunk field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetDiffHunk() string { + if p == nil || p.DiffHunk == nil { + return "" + } + return *p.DiffHunk +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetInReplyTo returns the InReplyTo field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetInReplyTo() int64 { + if p == nil || p.InReplyTo == nil { + return 0 + } + return *p.InReplyTo +} + +// GetLine returns the Line field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetLine() int { + if p == nil || p.Line == nil { + return 0 + } + return *p.Line +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetOriginalCommitID returns the OriginalCommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetOriginalCommitID() string { + if p == nil || p.OriginalCommitID == nil { + return "" + } + return *p.OriginalCommitID +} + +// GetOriginalLine returns the OriginalLine field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetOriginalLine() int { + if p == nil || p.OriginalLine == nil { + return 0 + } + return *p.OriginalLine +} + +// GetOriginalPosition returns the OriginalPosition field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetOriginalPosition() int { + if p == nil || p.OriginalPosition == nil { + return 0 + } + return *p.OriginalPosition +} + +// GetOriginalStartLine returns the OriginalStartLine field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetOriginalStartLine() int { + if p == nil || p.OriginalStartLine == nil { + return 0 + } + return *p.OriginalStartLine +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPath() string { + if p == nil || p.Path == nil { + return "" + } + return *p.Path +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPosition() int { + if p == nil || p.Position == nil { + return 0 + } + return *p.Position +} + +// GetPullRequestReviewID returns the PullRequestReviewID field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPullRequestReviewID() int64 { + if p == nil || p.PullRequestReviewID == nil { + return 0 + } + return *p.PullRequestReviewID +} + +// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetPullRequestURL() string { + if p == nil || p.PullRequestURL == nil { + return "" + } + return *p.PullRequestURL +} + +// GetReactions returns the Reactions field. +func (p *PullRequestComment) GetReactions() *Reactions { + if p == nil { + return nil + } + return p.Reactions +} + +// GetSide returns the Side field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetSide() string { + if p == nil || p.Side == nil { + return "" + } + return *p.Side +} + +// GetStartLine returns the StartLine field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetStartLine() int { + if p == nil || p.StartLine == nil { + return 0 + } + return *p.StartLine +} + +// GetStartSide returns the StartSide field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetStartSide() string { + if p == nil || p.StartSide == nil { + return "" + } + return *p.StartSide +} + +// GetSubjectType returns the SubjectType field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetSubjectType() string { + if p == nil || p.SubjectType == nil { + return "" + } + return *p.SubjectType +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequestComment) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetUser returns the User field. +func (p *PullRequestComment) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (p *PullRequestDismissReviewRequest) GetEvent() string { + if p == nil || p.Event == nil { + return "" + } + return *p.Event +} + +// GetMessage returns the Message field. +func (p *PullRequestDismissReviewRequest) GetMessage() string { + if p == nil { + return "" + } + return p.Message +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetAfter returns the After field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetAfter() string { + if p == nil || p.After == nil { + return "" + } + return *p.After +} + +// GetAssignee returns the Assignee field. +func (p *PullRequestEvent) GetAssignee() *User { + if p == nil { + return nil + } + return p.Assignee +} + +// GetBefore returns the Before field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetBefore() string { + if p == nil || p.Before == nil { + return "" + } + return *p.Before +} + +// GetChanges returns the Changes field. +func (p *PullRequestEvent) GetChanges() *EditChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *PullRequestEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetLabel returns the Label field. +func (p *PullRequestEvent) GetLabel() *Label { + if p == nil { + return nil + } + return p.Label +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetOrganization returns the Organization field. +func (p *PullRequestEvent) GetOrganization() *Organization { + if p == nil { + return nil + } + return p.Organization +} + +// GetPerformedViaGithubApp returns the PerformedViaGithubApp field. +func (p *PullRequestEvent) GetPerformedViaGithubApp() *App { + if p == nil { + return nil + } + return p.PerformedViaGithubApp +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (p *PullRequestEvent) GetReason() string { + if p == nil || p.Reason == nil { + return "" + } + return *p.Reason +} + +// GetRepo returns the Repo field. +func (p *PullRequestEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetRequestedReviewer returns the RequestedReviewer field. +func (p *PullRequestEvent) GetRequestedReviewer() *User { + if p == nil { + return nil + } + return p.RequestedReviewer +} + +// GetRequestedTeam returns the RequestedTeam field. +func (p *PullRequestEvent) GetRequestedTeam() *Team { + if p == nil { + return nil + } + return p.RequestedTeam +} + +// GetSender returns the Sender field. +func (p *PullRequestEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetDiffURL() string { + if p == nil || p.DiffURL == nil { + return "" + } + return *p.DiffURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetMergedAt() Timestamp { + if p == nil || p.MergedAt == nil { + return Timestamp{} + } + return *p.MergedAt +} + +// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetPatchURL() string { + if p == nil || p.PatchURL == nil { + return "" + } + return *p.PatchURL +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PullRequestLinks) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetDirection returns the Direction field. +func (p *PullRequestListCommentsOptions) GetDirection() string { + if p == nil { + return "" + } + return p.Direction +} + +// GetSince returns the Since field. +func (p *PullRequestListCommentsOptions) GetSince() time.Time { + if p == nil { + return time.Time{} + } + return p.Since +} + +// GetSort returns the Sort field. +func (p *PullRequestListCommentsOptions) GetSort() string { + if p == nil { + return "" + } + return p.Sort +} + +// GetBase returns the Base field. +func (p *PullRequestListOptions) GetBase() string { + if p == nil { + return "" + } + return p.Base +} + +// GetDirection returns the Direction field. +func (p *PullRequestListOptions) GetDirection() string { + if p == nil { + return "" + } + return p.Direction +} + +// GetHead returns the Head field. +func (p *PullRequestListOptions) GetHead() string { + if p == nil { + return "" + } + return p.Head +} + +// GetSort returns the Sort field. +func (p *PullRequestListOptions) GetSort() string { + if p == nil { + return "" + } + return p.Sort +} + +// GetState returns the State field. +func (p *PullRequestListOptions) GetState() string { + if p == nil { + return "" + } + return p.State +} + +// GetMerged returns the Merged field if it's non-nil, zero value otherwise. +func (p *PullRequestMergeResult) GetMerged() bool { + if p == nil || p.Merged == nil { + return false + } + return *p.Merged +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (p *PullRequestMergeResult) GetMessage() string { + if p == nil || p.Message == nil { + return "" + } + return *p.Message +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (p *PullRequestMergeResult) GetSHA() string { + if p == nil || p.SHA == nil { + return "" + } + return *p.SHA +} + +// GetCommitTitle returns the CommitTitle field. +func (p *PullRequestOptions) GetCommitTitle() string { + if p == nil { + return "" + } + return p.CommitTitle +} + +// GetDontDefaultIfBlank returns the DontDefaultIfBlank field. +func (p *PullRequestOptions) GetDontDefaultIfBlank() bool { + if p == nil { + return false + } + return p.DontDefaultIfBlank +} + +// GetMergeMethod returns the MergeMethod field. +func (p *PullRequestOptions) GetMergeMethod() string { + if p == nil { + return "" + } + return p.MergeMethod +} + +// GetSHA returns the SHA field. +func (p *PullRequestOptions) GetSHA() string { + if p == nil { + return "" + } + return p.SHA +} + +// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetAuthorAssociation() string { + if p == nil || p.AuthorAssociation == nil { + return "" + } + return *p.AuthorAssociation +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetCommitID() string { + if p == nil || p.CommitID == nil { + return "" + } + return *p.CommitID +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetPullRequestURL() string { + if p == nil || p.PullRequestURL == nil { + return "" + } + return *p.PullRequestURL +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. +func (p *PullRequestReview) GetSubmittedAt() Timestamp { + if p == nil || p.SubmittedAt == nil { + return Timestamp{} + } + return *p.SubmittedAt +} + +// GetUser returns the User field. +func (p *PullRequestReview) GetUser() *User { + if p == nil { + return nil + } + return p.User +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewCommentEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetChanges returns the Changes field. +func (p *PullRequestReviewCommentEvent) GetChanges() *EditChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetComment returns the Comment field. +func (p *PullRequestReviewCommentEvent) GetComment() *PullRequestComment { + if p == nil { + return nil + } + return p.Comment +} + +// GetInstallation returns the Installation field. +func (p *PullRequestReviewCommentEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PullRequestReviewCommentEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestReviewCommentEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestReviewCommentEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PullRequestReviewCommentEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetInstallation returns the Installation field. +func (p *PullRequestReviewEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrganization returns the Organization field. +func (p *PullRequestReviewEvent) GetOrganization() *Organization { + if p == nil { + return nil + } + return p.Organization +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestReviewEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestReviewEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetReview returns the Review field. +func (p *PullRequestReviewEvent) GetReview() *PullRequestReview { + if p == nil { + return nil + } + return p.Review +} + +// GetSender returns the Sender field. +func (p *PullRequestReviewEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetComments returns the Comments slice if it's non-nil, nil otherwise. +func (p *PullRequestReviewRequest) GetComments() []*DraftReviewComment { + if p == nil || p.Comments == nil { + return nil + } + return p.Comments +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetCommitID() string { + if p == nil || p.CommitID == nil { + return "" + } + return *p.CommitID +} + +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetEvent() string { + if p == nil || p.Event == nil { + return "" + } + return *p.Event +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewRequest) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetBypassPullRequestAllowances returns the BypassPullRequestAllowances field. +func (p *PullRequestReviewsEnforcement) GetBypassPullRequestAllowances() *BypassPullRequestAllowances { + if p == nil { + return nil + } + return p.BypassPullRequestAllowances +} + +// GetDismissalRestrictions returns the DismissalRestrictions field. +func (p *PullRequestReviewsEnforcement) GetDismissalRestrictions() *DismissalRestrictions { + if p == nil { + return nil + } + return p.DismissalRestrictions +} + +// GetDismissStaleReviews returns the DismissStaleReviews field. +func (p *PullRequestReviewsEnforcement) GetDismissStaleReviews() bool { + if p == nil { + return false + } + return p.DismissStaleReviews +} + +// GetRequireCodeOwnerReviews returns the RequireCodeOwnerReviews field. +func (p *PullRequestReviewsEnforcement) GetRequireCodeOwnerReviews() bool { + if p == nil { + return false + } + return p.RequireCodeOwnerReviews +} + +// GetRequiredApprovingReviewCount returns the RequiredApprovingReviewCount field. +func (p *PullRequestReviewsEnforcement) GetRequiredApprovingReviewCount() int { + if p == nil { + return 0 + } + return p.RequiredApprovingReviewCount +} + +// GetRequireLastPushApproval returns the RequireLastPushApproval field. +func (p *PullRequestReviewsEnforcement) GetRequireLastPushApproval() bool { + if p == nil { + return false + } + return p.RequireLastPushApproval +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementLevelChanges) GetFrom() string { + if p == nil || p.From == nil { + return "" + } + return *p.From +} + +// GetBypassPullRequestAllowancesRequest returns the BypassPullRequestAllowancesRequest field. +func (p *PullRequestReviewsEnforcementRequest) GetBypassPullRequestAllowancesRequest() *BypassPullRequestAllowancesRequest { + if p == nil { + return nil + } + return p.BypassPullRequestAllowancesRequest +} + +// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. +func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { + if p == nil { + return nil + } + return p.DismissalRestrictionsRequest +} + +// GetDismissStaleReviews returns the DismissStaleReviews field. +func (p *PullRequestReviewsEnforcementRequest) GetDismissStaleReviews() bool { + if p == nil { + return false + } + return p.DismissStaleReviews +} + +// GetRequireCodeOwnerReviews returns the RequireCodeOwnerReviews field. +func (p *PullRequestReviewsEnforcementRequest) GetRequireCodeOwnerReviews() bool { + if p == nil { + return false + } + return p.RequireCodeOwnerReviews +} + +// GetRequiredApprovingReviewCount returns the RequiredApprovingReviewCount field. +func (p *PullRequestReviewsEnforcementRequest) GetRequiredApprovingReviewCount() int { + if p == nil { + return 0 + } + return p.RequiredApprovingReviewCount +} + +// GetRequireLastPushApproval returns the RequireLastPushApproval field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementRequest) GetRequireLastPushApproval() bool { + if p == nil || p.RequireLastPushApproval == nil { + return false + } + return *p.RequireLastPushApproval +} + +// GetBypassPullRequestAllowancesRequest returns the BypassPullRequestAllowancesRequest field. +func (p *PullRequestReviewsEnforcementUpdate) GetBypassPullRequestAllowancesRequest() *BypassPullRequestAllowancesRequest { + if p == nil { + return nil + } + return p.BypassPullRequestAllowancesRequest +} + +// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. +func (p *PullRequestReviewsEnforcementUpdate) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { + if p == nil { + return nil + } + return p.DismissalRestrictionsRequest +} + +// GetDismissStaleReviews returns the DismissStaleReviews field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementUpdate) GetDismissStaleReviews() bool { + if p == nil || p.DismissStaleReviews == nil { + return false + } + return *p.DismissStaleReviews +} + +// GetRequireCodeOwnerReviews returns the RequireCodeOwnerReviews field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementUpdate) GetRequireCodeOwnerReviews() bool { + if p == nil || p.RequireCodeOwnerReviews == nil { + return false + } + return *p.RequireCodeOwnerReviews +} + +// GetRequiredApprovingReviewCount returns the RequiredApprovingReviewCount field. +func (p *PullRequestReviewsEnforcementUpdate) GetRequiredApprovingReviewCount() int { + if p == nil { + return 0 + } + return p.RequiredApprovingReviewCount +} + +// GetRequireLastPushApproval returns the RequireLastPushApproval field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewsEnforcementUpdate) GetRequireLastPushApproval() bool { + if p == nil || p.RequireLastPushApproval == nil { + return false + } + return *p.RequireLastPushApproval +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestReviewThreadEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetInstallation returns the Installation field. +func (p *PullRequestReviewThreadEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PullRequestReviewThreadEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestReviewThreadEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestReviewThreadEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PullRequestReviewThreadEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetThread returns the Thread field. +func (p *PullRequestReviewThreadEvent) GetThread() *PullRequestThread { + if p == nil { + return nil + } + return p.Thread +} + +// GetAllowedMergeMethods returns the AllowedMergeMethods slice if it's non-nil, nil otherwise. +func (p *PullRequestRuleParameters) GetAllowedMergeMethods() []PullRequestMergeMethod { + if p == nil || p.AllowedMergeMethods == nil { + return nil + } + return p.AllowedMergeMethods +} + +// GetDismissStaleReviewsOnPush returns the DismissStaleReviewsOnPush field. +func (p *PullRequestRuleParameters) GetDismissStaleReviewsOnPush() bool { + if p == nil { + return false + } + return p.DismissStaleReviewsOnPush +} + +// GetRequireCodeOwnerReview returns the RequireCodeOwnerReview field. +func (p *PullRequestRuleParameters) GetRequireCodeOwnerReview() bool { + if p == nil { + return false + } + return p.RequireCodeOwnerReview +} + +// GetRequiredApprovingReviewCount returns the RequiredApprovingReviewCount field. +func (p *PullRequestRuleParameters) GetRequiredApprovingReviewCount() int { + if p == nil { + return 0 + } + return p.RequiredApprovingReviewCount +} + +// GetRequiredReviewers returns the RequiredReviewers slice if it's non-nil, nil otherwise. +func (p *PullRequestRuleParameters) GetRequiredReviewers() []*RulesetRequiredReviewer { + if p == nil || p.RequiredReviewers == nil { + return nil + } + return p.RequiredReviewers +} + +// GetRequiredReviewThreadResolution returns the RequiredReviewThreadResolution field. +func (p *PullRequestRuleParameters) GetRequiredReviewThreadResolution() bool { + if p == nil { + return false + } + return p.RequiredReviewThreadResolution +} + +// GetRequireLastPushApproval returns the RequireLastPushApproval field. +func (p *PullRequestRuleParameters) GetRequireLastPushApproval() bool { + if p == nil { + return false + } + return p.RequireLastPushApproval +} + +// GetBase returns the Base field. +func (p *PullRequestStack) GetBase() *PullRequestStackBase { + if p == nil { + return nil + } + return p.Base +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequestStack) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *PullRequestStack) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (p *PullRequestStack) GetPosition() int { + if p == nil || p.Position == nil { + return 0 + } + return *p.Position +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PullRequestStack) GetSize() int { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetRef returns the Ref field. +func (p *PullRequestStackBase) GetRef() string { + if p == nil { + return "" + } + return p.Ref +} + +// GetSHA returns the SHA field. +func (p *PullRequestStackBase) GetSHA() string { + if p == nil { + return "" + } + return p.SHA +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (p *PullRequestSubmitReviewRequest) GetBody() string { + if p == nil || p.Body == nil { + return "" + } + return *p.Body +} + +// GetEvent returns the Event field. +func (p *PullRequestSubmitReviewRequest) GetEvent() string { + if p == nil { + return "" + } + return p.Event +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PullRequestTargetEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetAfter returns the After field if it's non-nil, zero value otherwise. +func (p *PullRequestTargetEvent) GetAfter() string { + if p == nil || p.After == nil { + return "" + } + return *p.After +} + +// GetAssignee returns the Assignee field. +func (p *PullRequestTargetEvent) GetAssignee() *User { + if p == nil { + return nil + } + return p.Assignee +} + +// GetBefore returns the Before field if it's non-nil, zero value otherwise. +func (p *PullRequestTargetEvent) GetBefore() string { + if p == nil || p.Before == nil { + return "" + } + return *p.Before +} + +// GetChanges returns the Changes field. +func (p *PullRequestTargetEvent) GetChanges() *EditChange { + if p == nil { + return nil + } + return p.Changes +} + +// GetInstallation returns the Installation field. +func (p *PullRequestTargetEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetLabel returns the Label field. +func (p *PullRequestTargetEvent) GetLabel() *Label { + if p == nil { + return nil + } + return p.Label +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (p *PullRequestTargetEvent) GetNumber() int { + if p == nil || p.Number == nil { + return 0 + } + return *p.Number +} + +// GetOrganization returns the Organization field. +func (p *PullRequestTargetEvent) GetOrganization() *Organization { + if p == nil { + return nil + } + return p.Organization +} + +// GetPerformedViaGithubApp returns the PerformedViaGithubApp field. +func (p *PullRequestTargetEvent) GetPerformedViaGithubApp() *App { + if p == nil { + return nil + } + return p.PerformedViaGithubApp +} + +// GetPullRequest returns the PullRequest field. +func (p *PullRequestTargetEvent) GetPullRequest() *PullRequest { + if p == nil { + return nil + } + return p.PullRequest +} + +// GetRepo returns the Repo field. +func (p *PullRequestTargetEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetRequestedReviewer returns the RequestedReviewer field. +func (p *PullRequestTargetEvent) GetRequestedReviewer() *User { + if p == nil { + return nil + } + return p.RequestedReviewer +} + +// GetRequestedTeam returns the RequestedTeam field. +func (p *PullRequestTargetEvent) GetRequestedTeam() *Team { + if p == nil { + return nil + } + return p.RequestedTeam +} + +// GetSender returns the Sender field. +func (p *PullRequestTargetEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetComments returns the Comments slice if it's non-nil, nil otherwise. +func (p *PullRequestThread) GetComments() []*PullRequestComment { + if p == nil || p.Comments == nil { + return nil + } + return p.Comments +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PullRequestThread) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PullRequestThread) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetMergeablePulls returns the MergeablePulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetMergeablePulls() int { + if p == nil || p.MergeablePulls == nil { + return 0 + } + return *p.MergeablePulls +} + +// GetMergedPulls returns the MergedPulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetMergedPulls() int { + if p == nil || p.MergedPulls == nil { + return 0 + } + return *p.MergedPulls +} + +// GetTotalPulls returns the TotalPulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetTotalPulls() int { + if p == nil || p.TotalPulls == nil { + return 0 + } + return *p.TotalPulls +} + +// GetUnmergeablePulls returns the UnmergeablePulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetUnmergeablePulls() int { + if p == nil || p.UnmergeablePulls == nil { + return 0 + } + return *p.UnmergeablePulls +} + +// GetCommits returns the Commits field if it's non-nil, zero value otherwise. +func (p *PunchCard) GetCommits() int { + if p == nil || p.Commits == nil { + return 0 + } + return *p.Commits +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (p *PunchCard) GetDay() int { + if p == nil || p.Day == nil { + return 0 + } + return *p.Day +} + +// GetHour returns the Hour field if it's non-nil, zero value otherwise. +func (p *PunchCard) GetHour() int { + if p == nil || p.Hour == nil { + return 0 + } + return *p.Hour +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetAction() string { + if p == nil || p.Action == nil { + return "" + } + return *p.Action +} + +// GetAfter returns the After field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetAfter() string { + if p == nil || p.After == nil { + return "" + } + return *p.After +} + +// GetBaseRef returns the BaseRef field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetBaseRef() string { + if p == nil || p.BaseRef == nil { + return "" + } + return *p.BaseRef +} + +// GetBefore returns the Before field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetBefore() string { + if p == nil || p.Before == nil { + return "" + } + return *p.Before +} + +// GetCommits returns the Commits slice if it's non-nil, nil otherwise. +func (p *PushEvent) GetCommits() []*HeadCommit { + if p == nil || p.Commits == nil { + return nil + } + return p.Commits +} + +// GetCompare returns the Compare field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetCompare() string { + if p == nil || p.Compare == nil { + return "" + } + return *p.Compare +} + +// GetCreated returns the Created field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetCreated() bool { + if p == nil || p.Created == nil { + return false + } + return *p.Created +} + +// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetDeleted() bool { + if p == nil || p.Deleted == nil { + return false + } + return *p.Deleted +} + +// GetDistinctSize returns the DistinctSize field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetDistinctSize() int { + if p == nil || p.DistinctSize == nil { + return 0 + } + return *p.DistinctSize +} + +// GetForced returns the Forced field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetForced() bool { + if p == nil || p.Forced == nil { + return false + } + return *p.Forced +} + +// GetHead returns the Head field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetHead() string { + if p == nil || p.Head == nil { + return "" + } + return *p.Head +} + +// GetHeadCommit returns the HeadCommit field. +func (p *PushEvent) GetHeadCommit() *HeadCommit { + if p == nil { + return nil + } + return p.HeadCommit +} + +// GetInstallation returns the Installation field. +func (p *PushEvent) GetInstallation() *Installation { + if p == nil { + return nil + } + return p.Installation +} + +// GetOrganization returns the Organization field. +func (p *PushEvent) GetOrganization() *Organization { + if p == nil { + return nil + } + return p.Organization +} + +// GetPusher returns the Pusher field. +func (p *PushEvent) GetPusher() *CommitAuthor { + if p == nil { + return nil + } + return p.Pusher +} + +// GetPushID returns the PushID field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetPushID() int64 { + if p == nil || p.PushID == nil { + return 0 + } + return *p.PushID +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetRef() string { + if p == nil || p.Ref == nil { + return "" + } + return *p.Ref +} + +// GetRepo returns the Repo field. +func (p *PushEvent) GetRepo() *PushEventRepository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PushEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PushEvent) GetSize() int { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (p *PushEventRepoOwner) GetEmail() string { + if p == nil || p.Email == nil { + return "" + } + return *p.Email +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PushEventRepoOwner) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetArchived returns the Archived field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetArchived() bool { + if p == nil || p.Archived == nil { + return false + } + return *p.Archived +} + +// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetArchiveURL() string { + if p == nil || p.ArchiveURL == nil { + return "" + } + return *p.ArchiveURL +} + +// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetCloneURL() string { + if p == nil || p.CloneURL == nil { + return "" + } + return *p.CloneURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetCustomProperties returns the CustomProperties map if it's non-nil, an empty map otherwise. +func (p *PushEventRepository) GetCustomProperties() map[string]any { + if p == nil || p.CustomProperties == nil { + return map[string]any{} + } + return p.CustomProperties +} + +// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetDefaultBranch() string { + if p == nil || p.DefaultBranch == nil { + return "" + } + return *p.DefaultBranch +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetDisabled returns the Disabled field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetDisabled() bool { + if p == nil || p.Disabled == nil { + return false + } + return *p.Disabled +} + +// GetFork returns the Fork field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetFork() bool { + if p == nil || p.Fork == nil { + return false + } + return *p.Fork +} + +// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetForksCount() int { + if p == nil || p.ForksCount == nil { + return 0 + } + return *p.ForksCount +} + +// GetFullName returns the FullName field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetFullName() string { + if p == nil || p.FullName == nil { + return "" + } + return *p.FullName +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetGitURL() string { + if p == nil || p.GitURL == nil { + return "" + } + return *p.GitURL +} + +// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasDownloads() bool { + if p == nil || p.HasDownloads == nil { + return false + } + return *p.HasDownloads +} + +// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasIssues() bool { + if p == nil || p.HasIssues == nil { + return false + } + return *p.HasIssues +} + +// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasPages() bool { + if p == nil || p.HasPages == nil { + return false + } + return *p.HasPages +} + +// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHasWiki() bool { + if p == nil || p.HasWiki == nil { + return false + } + return *p.HasWiki +} + +// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHomepage() string { + if p == nil || p.Homepage == nil { + return "" + } + return *p.Homepage +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetHTMLURL() string { + if p == nil || p.HTMLURL == nil { + return "" + } + return *p.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetLanguage() string { + if p == nil || p.Language == nil { + return "" + } + return *p.Language +} + +// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetMasterBranch() string { + if p == nil || p.MasterBranch == nil { + return "" + } + return *p.MasterBranch +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetNodeID() string { + if p == nil || p.NodeID == nil { + return "" + } + return *p.NodeID +} + +// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetOpenIssuesCount() int { + if p == nil || p.OpenIssuesCount == nil { + return 0 + } + return *p.OpenIssuesCount +} + +// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetOrganization() string { + if p == nil || p.Organization == nil { + return "" + } + return *p.Organization +} + +// GetOwner returns the Owner field. +func (p *PushEventRepository) GetOwner() *User { + if p == nil { + return nil + } + return p.Owner +} + +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetPrivate() bool { + if p == nil || p.Private == nil { + return false + } + return *p.Private +} + +// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetPullsURL() string { + if p == nil || p.PullsURL == nil { + return "" + } + return *p.PullsURL +} + +// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetPushedAt() Timestamp { + if p == nil || p.PushedAt == nil { + return Timestamp{} + } + return *p.PushedAt +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetSize() int { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetSSHURL() string { + if p == nil || p.SSHURL == nil { + return "" + } + return *p.SSHURL +} + +// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetStargazersCount() int { + if p == nil || p.StargazersCount == nil { + return 0 + } + return *p.StargazersCount +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetStatusesURL() string { + if p == nil || p.StatusesURL == nil { + return "" + } + return *p.StatusesURL +} + +// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetSVNURL() string { + if p == nil || p.SVNURL == nil { + return "" + } + return *p.SVNURL +} + +// GetTopics returns the Topics slice if it's non-nil, nil otherwise. +func (p *PushEventRepository) GetTopics() []string { + if p == nil || p.Topics == nil { + return nil + } + return p.Topics +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. +func (p *PushEventRepository) GetWatchersCount() int { + if p == nil || p.WatchersCount == nil { + return 0 + } + return *p.WatchersCount +} + +// GetExpireAt returns the ExpireAt field if it's non-nil, zero value otherwise. +func (p *PushProtectionBypass) GetExpireAt() Timestamp { + if p == nil || p.ExpireAt == nil { + return Timestamp{} + } + return *p.ExpireAt +} + +// GetReason returns the Reason field. +func (p *PushProtectionBypass) GetReason() string { + if p == nil { + return "" + } + return p.Reason +} + +// GetTokenType returns the TokenType field. +func (p *PushProtectionBypass) GetTokenType() string { + if p == nil { + return "" + } + return p.TokenType +} + +// GetPlaceholderID returns the PlaceholderID field. +func (p *PushProtectionBypassRequest) GetPlaceholderID() string { + if p == nil { + return "" + } + return p.PlaceholderID +} + +// GetReason returns the Reason field. +func (p *PushProtectionBypassRequest) GetReason() string { + if p == nil { + return "" + } + return p.Reason +} + +// GetLimit returns the Limit field. +func (r *Rate) GetLimit() int { + if r == nil { + return 0 + } + return r.Limit +} + +// GetRemaining returns the Remaining field. +func (r *Rate) GetRemaining() int { + if r == nil { + return 0 + } + return r.Remaining +} + +// GetReset returns the Reset field. +func (r *Rate) GetReset() Timestamp { + if r == nil { + return Timestamp{} + } + return r.Reset +} + +// GetResource returns the Resource field. +func (r *Rate) GetResource() string { + if r == nil { + return "" + } + return r.Resource +} + +// GetUsed returns the Used field. +func (r *Rate) GetUsed() int { + if r == nil { + return 0 + } + return r.Used +} + +// GetMessage returns the Message field. +func (r *RateLimitError) GetMessage() string { + if r == nil { + return "" + } + return r.Message +} + +// GetRate returns the Rate field. +func (r *RateLimitError) GetRate() Rate { + if r == nil { + return Rate{} + } + return r.Rate +} + +// GetActionsRunnerRegistration returns the ActionsRunnerRegistration field. +func (r *RateLimits) GetActionsRunnerRegistration() *Rate { + if r == nil { + return nil + } + return r.ActionsRunnerRegistration +} + +// GetAuditLog returns the AuditLog field. +func (r *RateLimits) GetAuditLog() *Rate { + if r == nil { + return nil + } + return r.AuditLog +} + +// GetCodeScanningUpload returns the CodeScanningUpload field. +func (r *RateLimits) GetCodeScanningUpload() *Rate { + if r == nil { + return nil + } + return r.CodeScanningUpload +} + +// GetCodeSearch returns the CodeSearch field. +func (r *RateLimits) GetCodeSearch() *Rate { + if r == nil { + return nil + } + return r.CodeSearch +} + +// GetCore returns the Core field. +func (r *RateLimits) GetCore() *Rate { + if r == nil { + return nil + } + return r.Core +} + +// GetDependencySBOM returns the DependencySBOM field. +func (r *RateLimits) GetDependencySBOM() *Rate { + if r == nil { + return nil + } + return r.DependencySBOM +} + +// GetDependencySnapshots returns the DependencySnapshots field. +func (r *RateLimits) GetDependencySnapshots() *Rate { + if r == nil { + return nil + } + return r.DependencySnapshots +} + +// GetGraphQL returns the GraphQL field. +func (r *RateLimits) GetGraphQL() *Rate { + if r == nil { + return nil + } + return r.GraphQL +} + +// GetIntegrationManifest returns the IntegrationManifest field. +func (r *RateLimits) GetIntegrationManifest() *Rate { + if r == nil { + return nil + } + return r.IntegrationManifest +} + +// GetSCIM returns the SCIM field. +func (r *RateLimits) GetSCIM() *Rate { + if r == nil { + return nil + } + return r.SCIM +} + +// GetSearch returns the Search field. +func (r *RateLimits) GetSearch() *Rate { + if r == nil { + return nil + } + return r.Search +} + +// GetSourceImport returns the SourceImport field. +func (r *RateLimits) GetSourceImport() *Rate { + if r == nil { + return nil + } + return r.SourceImport +} + +// GetType returns the Type field. +func (r *RawOptions) GetType() RawType { + if r == nil { + return 0 + } + return r.Type +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (r *Reaction) GetContent() string { + if r == nil || r.Content == nil { + return "" + } + return *r.Content +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *Reaction) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *Reaction) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *Reaction) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetUser returns the User field. +func (r *Reaction) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetConfused returns the Confused field if it's non-nil, zero value otherwise. +func (r *Reactions) GetConfused() int { + if r == nil || r.Confused == nil { + return 0 + } + return *r.Confused +} + +// GetEyes returns the Eyes field if it's non-nil, zero value otherwise. +func (r *Reactions) GetEyes() int { + if r == nil || r.Eyes == nil { + return 0 + } + return *r.Eyes +} + +// GetHeart returns the Heart field if it's non-nil, zero value otherwise. +func (r *Reactions) GetHeart() int { + if r == nil || r.Heart == nil { + return 0 + } + return *r.Heart +} + +// GetHooray returns the Hooray field if it's non-nil, zero value otherwise. +func (r *Reactions) GetHooray() int { + if r == nil || r.Hooray == nil { + return 0 + } + return *r.Hooray +} + +// GetLaugh returns the Laugh field if it's non-nil, zero value otherwise. +func (r *Reactions) GetLaugh() int { + if r == nil || r.Laugh == nil { + return 0 + } + return *r.Laugh +} + +// GetMinusOne returns the MinusOne field if it's non-nil, zero value otherwise. +func (r *Reactions) GetMinusOne() int { + if r == nil || r.MinusOne == nil { + return 0 + } + return *r.MinusOne +} + +// GetPlusOne returns the PlusOne field if it's non-nil, zero value otherwise. +func (r *Reactions) GetPlusOne() int { + if r == nil || r.PlusOne == nil { + return 0 + } + return *r.PlusOne +} + +// GetRocket returns the Rocket field if it's non-nil, zero value otherwise. +func (r *Reactions) GetRocket() int { + if r == nil || r.Rocket == nil { + return 0 + } + return *r.Rocket +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (r *Reactions) GetTotalCount() int { + if r == nil || r.TotalCount == nil { + return 0 + } + return *r.TotalCount +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *Reactions) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *ReassignedResource) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPreviousCostCenter returns the PreviousCostCenter field if it's non-nil, zero value otherwise. +func (r *ReassignedResource) GetPreviousCostCenter() string { + if r == nil || r.PreviousCostCenter == nil { + return "" + } + return *r.PreviousCostCenter +} + +// GetResourceType returns the ResourceType field if it's non-nil, zero value otherwise. +func (r *ReassignedResource) GetResourceType() string { + if r == nil || r.ResourceType == nil { + return "" + } + return *r.ResourceType +} + +// GetStatusCode returns the StatusCode field. +func (r *RedirectionError) GetStatusCode() int { + if r == nil { + return 0 + } + return r.StatusCode +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *Reference) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetObject returns the Object field. +func (r *Reference) GetObject() *GitObject { + if r == nil { + return nil + } + return r.Object +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (r *Reference) GetRef() string { + if r == nil || r.Ref == nil { + return "" + } + return *r.Ref +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *Reference) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *ReferencedWorkflow) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (r *ReferencedWorkflow) GetRef() string { + if r == nil || r.Ref == nil { + return "" + } + return *r.Ref +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *ReferencedWorkflow) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (r *RegistrationToken) GetExpiresAt() Timestamp { + if r == nil || r.ExpiresAt == nil { + return Timestamp{} + } + return *r.ExpiresAt +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (r *RegistrationToken) GetToken() string { + if r == nil || r.Token == nil { + return "" + } + return *r.Token +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *RegistryPackageEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetEnterprise returns the Enterprise field. +func (r *RegistryPackageEvent) GetEnterprise() *Enterprise { + if r == nil { + return nil + } + return r.Enterprise +} + +// GetInstallation returns the Installation field. +func (r *RegistryPackageEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrganization returns the Organization field. +func (r *RegistryPackageEvent) GetOrganization() *Organization { + if r == nil { + return nil + } + return r.Organization +} + +// GetRegistryPackage returns the RegistryPackage field. +func (r *RegistryPackageEvent) GetRegistryPackage() *Package { + if r == nil { + return nil + } + return r.RegistryPackage +} + +// GetRepository returns the Repository field. +func (r *RegistryPackageEvent) GetRepository() *Repository { + if r == nil { + return nil + } + return r.Repository +} + +// GetSender returns the Sender field. +func (r *RegistryPackageEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetBrowserDownloadURL returns the BrowserDownloadURL field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetBrowserDownloadURL() string { + if r == nil || r.BrowserDownloadURL == nil { + return "" + } + return *r.BrowserDownloadURL +} + +// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetContentType() string { + if r == nil || r.ContentType == nil { + return "" + } + return *r.ContentType +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetDigest returns the Digest field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetDigest() string { + if r == nil || r.Digest == nil { + return "" + } + return *r.Digest +} + +// GetDownloadCount returns the DownloadCount field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetDownloadCount() int { + if r == nil || r.DownloadCount == nil { + return 0 + } + return *r.DownloadCount +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetLabel returns the Label field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetLabel() string { + if r == nil || r.Label == nil { + return "" + } + return *r.Label +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetUploader returns the Uploader field. +func (r *ReleaseAsset) GetUploader() *User { + if r == nil { + return nil + } + return r.Uploader +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *ReleaseAsset) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *ReleaseEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetInstallation returns the Installation field. +func (r *ReleaseEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrg returns the Org field. +func (r *ReleaseEvent) GetOrg() *Organization { + if r == nil { + return nil + } + return r.Org +} + +// GetRelease returns the Release field. +func (r *ReleaseEvent) GetRelease() *RepositoryRelease { + if r == nil { + return nil + } + return r.Release +} + +// GetRepo returns the Repo field. +func (r *ReleaseEvent) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetSender returns the Sender field. +func (r *ReleaseEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetBuildDate returns the BuildDate field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetBuildDate() string { + if r == nil || r.BuildDate == nil { + return "" + } + return *r.BuildDate +} + +// GetBuildID returns the BuildID field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetBuildID() string { + if r == nil || r.BuildID == nil { + return "" + } + return *r.BuildID +} + +// GetPlatform returns the Platform field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetPlatform() string { + if r == nil || r.Platform == nil { + return "" + } + return *r.Platform +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetVersion() string { + if r == nil || r.Version == nil { + return "" + } + return *r.Version +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (r *RemoveResourcesFromCostCenterResponse) GetMessage() string { + if r == nil || r.Message == nil { + return "" + } + return *r.Message +} + +// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. +func (r *RemoveToken) GetExpiresAt() Timestamp { + if r == nil || r.ExpiresAt == nil { + return Timestamp{} + } + return *r.ExpiresAt +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (r *RemoveToken) GetToken() string { + if r == nil || r.Token == nil { + return "" + } + return *r.Token +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *Rename) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetTo returns the To field if it's non-nil, zero value otherwise. +func (r *Rename) GetTo() string { + if r == nil || r.To == nil { + return "" + } + return *r.To +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (r *RenameOrgResponse) GetMessage() string { + if r == nil || r.Message == nil { + return "" + } + return *r.Message +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RenameOrgResponse) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCredit) GetLogin() string { + if r == nil || r.Login == nil { + return "" + } + return *r.Login +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCredit) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCreditDetailed) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepoAdvisoryCreditDetailed) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetUser returns the User field. +func (r *RepoAdvisoryCreditDetailed) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetProperties returns the Properties slice if it's non-nil, nil otherwise. +func (r *RepoCustomPropertyValue) GetProperties() []*CustomPropertyValue { + if r == nil || r.Properties == nil { + return nil + } + return r.Properties +} + +// GetRepositoryFullName returns the RepositoryFullName field. +func (r *RepoCustomPropertyValue) GetRepositoryFullName() string { + if r == nil { + return "" + } + return r.RepositoryFullName +} + +// GetRepositoryID returns the RepositoryID field. +func (r *RepoCustomPropertyValue) GetRepositoryID() int64 { + if r == nil { + return 0 + } + return r.RepositoryID +} + +// GetRepositoryName returns the RepositoryName field. +func (r *RepoCustomPropertyValue) GetRepositoryName() string { + if r == nil { + return "" + } + return r.RepositoryName +} + +// GetDownloadLocation returns the DownloadLocation field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetDownloadLocation() string { + if r == nil || r.DownloadLocation == nil { + return "" + } + return *r.DownloadLocation +} + +// GetExternalRefs returns the ExternalRefs slice if it's non-nil, nil otherwise. +func (r *RepoDependencies) GetExternalRefs() []*PackageExternalRef { + if r == nil || r.ExternalRefs == nil { + return nil + } + return r.ExternalRefs +} + +// GetFilesAnalyzed returns the FilesAnalyzed field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetFilesAnalyzed() bool { + if r == nil || r.FilesAnalyzed == nil { + return false + } + return *r.FilesAnalyzed +} + +// GetLicenseConcluded returns the LicenseConcluded field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetLicenseConcluded() string { + if r == nil || r.LicenseConcluded == nil { + return "" + } + return *r.LicenseConcluded +} + +// GetLicenseDeclared returns the LicenseDeclared field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetLicenseDeclared() string { + if r == nil || r.LicenseDeclared == nil { + return "" + } + return *r.LicenseDeclared +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetSPDXID() string { + if r == nil || r.SPDXID == nil { + return "" + } + return *r.SPDXID +} + +// GetVersionInfo returns the VersionInfo field if it's non-nil, zero value otherwise. +func (r *RepoDependencies) GetVersionInfo() string { + if r == nil || r.VersionInfo == nil { + return "" + } + return *r.VersionInfo +} + +// GetDescription returns the Description field. +func (r *RepoFineGrainedPermission) GetDescription() string { + if r == nil { + return "" + } + return r.Description +} + +// GetName returns the Name field. +func (r *RepoFineGrainedPermission) GetName() string { + if r == nil { + return "" + } + return r.Name +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (r *RepoImmutableReleasesStatus) GetEnabled() bool { + if r == nil || r.Enabled == nil { + return false + } + return *r.Enabled +} + +// GetEnforcedByOwner returns the EnforcedByOwner field if it's non-nil, zero value otherwise. +func (r *RepoImmutableReleasesStatus) GetEnforcedByOwner() bool { + if r == nil || r.EnforcedByOwner == nil { + return false + } + return *r.EnforcedByOwner +} + +// GetBranch returns the Branch field. +func (r *RepoMergeUpstreamRequest) GetBranch() string { + if r == nil { + return "" + } + return r.Branch +} + +// GetBaseBranch returns the BaseBranch field if it's non-nil, zero value otherwise. +func (r *RepoMergeUpstreamResult) GetBaseBranch() string { + if r == nil || r.BaseBranch == nil { + return "" + } + return *r.BaseBranch +} + +// GetMergeType returns the MergeType field if it's non-nil, zero value otherwise. +func (r *RepoMergeUpstreamResult) GetMergeType() string { + if r == nil || r.MergeType == nil { + return "" + } + return *r.MergeType +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (r *RepoMergeUpstreamResult) GetMessage() string { + if r == nil || r.Message == nil { + return "" + } + return *r.Message +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RepoName) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (r *RepositoriesSearchResult) GetIncompleteResults() bool { + if r == nil || r.IncompleteResults == nil { + return false + } + return *r.IncompleteResults +} + +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (r *RepositoriesSearchResult) GetRepositories() []*Repository { + if r == nil || r.Repositories == nil { + return nil + } + return r.Repositories +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (r *RepositoriesSearchResult) GetTotal() int { + if r == nil || r.Total == nil { + return 0 + } + return *r.Total +} + +// GetAllowAutoMerge returns the AllowAutoMerge field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowAutoMerge() bool { + if r == nil || r.AllowAutoMerge == nil { + return false + } + return *r.AllowAutoMerge +} + +// GetAllowForking returns the AllowForking field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowForking() bool { + if r == nil || r.AllowForking == nil { + return false + } + return *r.AllowForking +} + +// GetAllowMergeCommit returns the AllowMergeCommit field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowMergeCommit() bool { + if r == nil || r.AllowMergeCommit == nil { + return false + } + return *r.AllowMergeCommit +} + +// GetAllowRebaseMerge returns the AllowRebaseMerge field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowRebaseMerge() bool { + if r == nil || r.AllowRebaseMerge == nil { + return false + } + return *r.AllowRebaseMerge +} + +// GetAllowSquashMerge returns the AllowSquashMerge field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowSquashMerge() bool { + if r == nil || r.AllowSquashMerge == nil { + return false + } + return *r.AllowSquashMerge +} + +// GetAllowUpdateBranch returns the AllowUpdateBranch field if it's non-nil, zero value otherwise. +func (r *Repository) GetAllowUpdateBranch() bool { + if r == nil || r.AllowUpdateBranch == nil { + return false + } + return *r.AllowUpdateBranch +} + +// GetArchived returns the Archived field if it's non-nil, zero value otherwise. +func (r *Repository) GetArchived() bool { + if r == nil || r.Archived == nil { + return false + } + return *r.Archived +} + +// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetArchiveURL() string { + if r == nil || r.ArchiveURL == nil { + return "" + } + return *r.ArchiveURL +} + +// GetAssigneesURL returns the AssigneesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetAssigneesURL() string { + if r == nil || r.AssigneesURL == nil { + return "" + } + return *r.AssigneesURL +} + +// GetAutoInit returns the AutoInit field if it's non-nil, zero value otherwise. +func (r *Repository) GetAutoInit() bool { + if r == nil || r.AutoInit == nil { + return false + } + return *r.AutoInit +} + +// GetBlobsURL returns the BlobsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetBlobsURL() string { + if r == nil || r.BlobsURL == nil { + return "" + } + return *r.BlobsURL +} + +// GetBranchesURL returns the BranchesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetBranchesURL() string { + if r == nil || r.BranchesURL == nil { + return "" + } + return *r.BranchesURL +} + +// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCloneURL() string { + if r == nil || r.CloneURL == nil { + return "" + } + return *r.CloneURL +} + +// GetCodeOfConduct returns the CodeOfConduct field. +func (r *Repository) GetCodeOfConduct() *CodeOfConduct { + if r == nil { + return nil + } + return r.CodeOfConduct +} + +// GetCollaboratorsURL returns the CollaboratorsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCollaboratorsURL() string { + if r == nil || r.CollaboratorsURL == nil { + return "" + } + return *r.CollaboratorsURL +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCommentsURL() string { + if r == nil || r.CommentsURL == nil { + return "" + } + return *r.CommentsURL +} + +// GetCommitsURL returns the CommitsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCommitsURL() string { + if r == nil || r.CommitsURL == nil { + return "" + } + return *r.CommitsURL +} + +// GetCompareURL returns the CompareURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetCompareURL() string { + if r == nil || r.CompareURL == nil { + return "" + } + return *r.CompareURL +} + +// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetContentsURL() string { + if r == nil || r.ContentsURL == nil { + return "" + } + return *r.ContentsURL +} + +// GetContributorsURL returns the ContributorsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetContributorsURL() string { + if r == nil || r.ContributorsURL == nil { + return "" + } + return *r.ContributorsURL +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *Repository) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetCustomProperties returns the CustomProperties map if it's non-nil, an empty map otherwise. +func (r *Repository) GetCustomProperties() map[string]any { + if r == nil || r.CustomProperties == nil { + return map[string]any{} + } + return r.CustomProperties +} + +// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. +func (r *Repository) GetDefaultBranch() string { + if r == nil || r.DefaultBranch == nil { + return "" + } + return *r.DefaultBranch +} + +// GetDeleteBranchOnMerge returns the DeleteBranchOnMerge field if it's non-nil, zero value otherwise. +func (r *Repository) GetDeleteBranchOnMerge() bool { + if r == nil || r.DeleteBranchOnMerge == nil { + return false + } + return *r.DeleteBranchOnMerge +} + +// GetDeploymentsURL returns the DeploymentsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetDeploymentsURL() string { + if r == nil || r.DeploymentsURL == nil { + return "" + } + return *r.DeploymentsURL +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (r *Repository) GetDescription() string { + if r == nil || r.Description == nil { + return "" + } + return *r.Description +} + +// GetDisabled returns the Disabled field if it's non-nil, zero value otherwise. +func (r *Repository) GetDisabled() bool { + if r == nil || r.Disabled == nil { + return false + } + return *r.Disabled +} + +// GetDownloadsURL returns the DownloadsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetDownloadsURL() string { + if r == nil || r.DownloadsURL == nil { + return "" + } + return *r.DownloadsURL +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetEventsURL() string { + if r == nil || r.EventsURL == nil { + return "" + } + return *r.EventsURL +} + +// GetFork returns the Fork field if it's non-nil, zero value otherwise. +func (r *Repository) GetFork() bool { + if r == nil || r.Fork == nil { + return false + } + return *r.Fork +} + +// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetForksCount() int { + if r == nil || r.ForksCount == nil { + return 0 + } + return *r.ForksCount +} + +// GetForksURL returns the ForksURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetForksURL() string { + if r == nil || r.ForksURL == nil { + return "" + } + return *r.ForksURL +} + +// GetFullName returns the FullName field if it's non-nil, zero value otherwise. +func (r *Repository) GetFullName() string { + if r == nil || r.FullName == nil { + return "" + } + return *r.FullName +} + +// GetGitCommitsURL returns the GitCommitsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitCommitsURL() string { + if r == nil || r.GitCommitsURL == nil { + return "" + } + return *r.GitCommitsURL +} + +// GetGitignoreTemplate returns the GitignoreTemplate field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitignoreTemplate() string { + if r == nil || r.GitignoreTemplate == nil { + return "" + } + return *r.GitignoreTemplate +} + +// GetGitRefsURL returns the GitRefsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitRefsURL() string { + if r == nil || r.GitRefsURL == nil { + return "" + } + return *r.GitRefsURL +} + +// GetGitTagsURL returns the GitTagsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitTagsURL() string { + if r == nil || r.GitTagsURL == nil { + return "" + } + return *r.GitTagsURL +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetGitURL() string { + if r == nil || r.GitURL == nil { + return "" + } + return *r.GitURL +} + +// GetHasDiscussions returns the HasDiscussions field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasDiscussions() bool { + if r == nil || r.HasDiscussions == nil { + return false + } + return *r.HasDiscussions +} + +// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasDownloads() bool { + if r == nil || r.HasDownloads == nil { + return false + } + return *r.HasDownloads +} + +// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasIssues() bool { + if r == nil || r.HasIssues == nil { + return false + } + return *r.HasIssues +} + +// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasPages() bool { + if r == nil || r.HasPages == nil { + return false + } + return *r.HasPages +} + +// GetHasProjects returns the HasProjects field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasProjects() bool { + if r == nil || r.HasProjects == nil { + return false + } + return *r.HasProjects +} + +// GetHasPullRequests returns the HasPullRequests field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasPullRequests() bool { + if r == nil || r.HasPullRequests == nil { + return false + } + return *r.HasPullRequests +} + +// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. +func (r *Repository) GetHasWiki() bool { + if r == nil || r.HasWiki == nil { + return false + } + return *r.HasWiki +} + +// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. +func (r *Repository) GetHomepage() string { + if r == nil || r.Homepage == nil { + return "" + } + return *r.Homepage +} + +// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetHooksURL() string { + if r == nil || r.HooksURL == nil { + return "" + } + return *r.HooksURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *Repository) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetIssueCommentURL returns the IssueCommentURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetIssueCommentURL() string { + if r == nil || r.IssueCommentURL == nil { + return "" + } + return *r.IssueCommentURL +} + +// GetIssueEventsURL returns the IssueEventsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetIssueEventsURL() string { + if r == nil || r.IssueEventsURL == nil { + return "" + } + return *r.IssueEventsURL +} + +// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetIssuesURL() string { + if r == nil || r.IssuesURL == nil { + return "" + } + return *r.IssuesURL +} + +// GetIsTemplate returns the IsTemplate field if it's non-nil, zero value otherwise. +func (r *Repository) GetIsTemplate() bool { + if r == nil || r.IsTemplate == nil { + return false + } + return *r.IsTemplate +} + +// GetKeysURL returns the KeysURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetKeysURL() string { + if r == nil || r.KeysURL == nil { + return "" + } + return *r.KeysURL +} + +// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetLabelsURL() string { + if r == nil || r.LabelsURL == nil { + return "" + } + return *r.LabelsURL +} + +// GetLanguage returns the Language field if it's non-nil, zero value otherwise. +func (r *Repository) GetLanguage() string { + if r == nil || r.Language == nil { + return "" + } + return *r.Language +} + +// GetLanguagesURL returns the LanguagesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetLanguagesURL() string { + if r == nil || r.LanguagesURL == nil { + return "" + } + return *r.LanguagesURL +} + +// GetLicense returns the License field. +func (r *Repository) GetLicense() *License { + if r == nil { + return nil + } + return r.License +} + +// GetLicenseTemplate returns the LicenseTemplate field if it's non-nil, zero value otherwise. +func (r *Repository) GetLicenseTemplate() string { + if r == nil || r.LicenseTemplate == nil { + return "" + } + return *r.LicenseTemplate +} + +// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. +func (r *Repository) GetMasterBranch() string { + if r == nil || r.MasterBranch == nil { + return "" + } + return *r.MasterBranch +} + +// GetMergeCommitMessage returns the MergeCommitMessage field if it's non-nil, zero value otherwise. +func (r *Repository) GetMergeCommitMessage() string { + if r == nil || r.MergeCommitMessage == nil { + return "" + } + return *r.MergeCommitMessage +} + +// GetMergeCommitTitle returns the MergeCommitTitle field if it's non-nil, zero value otherwise. +func (r *Repository) GetMergeCommitTitle() string { + if r == nil || r.MergeCommitTitle == nil { + return "" + } + return *r.MergeCommitTitle +} + +// GetMergesURL returns the MergesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetMergesURL() string { + if r == nil || r.MergesURL == nil { + return "" + } + return *r.MergesURL +} + +// GetMilestonesURL returns the MilestonesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetMilestonesURL() string { + if r == nil || r.MilestonesURL == nil { + return "" + } + return *r.MilestonesURL +} + +// GetMirrorURL returns the MirrorURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetMirrorURL() string { + if r == nil || r.MirrorURL == nil { + return "" + } + return *r.MirrorURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *Repository) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetNetworkCount returns the NetworkCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetNetworkCount() int { + if r == nil || r.NetworkCount == nil { + return 0 + } + return *r.NetworkCount +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *Repository) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetNotificationsURL returns the NotificationsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetNotificationsURL() string { + if r == nil || r.NotificationsURL == nil { + return "" + } + return *r.NotificationsURL +} + +// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. +func (r *Repository) GetOpenIssues() int { + if r == nil || r.OpenIssues == nil { + return 0 + } + return *r.OpenIssues +} + +// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetOpenIssuesCount() int { + if r == nil || r.OpenIssuesCount == nil { + return 0 + } + return *r.OpenIssuesCount +} + +// GetOrganization returns the Organization field. +func (r *Repository) GetOrganization() *Organization { + if r == nil { + return nil + } + return r.Organization +} + +// GetOwner returns the Owner field. +func (r *Repository) GetOwner() *User { + if r == nil { + return nil + } + return r.Owner +} + +// GetParent returns the Parent field. +func (r *Repository) GetParent() *Repository { + if r == nil { + return nil + } + return r.Parent +} + +// GetPermissions returns the Permissions field. +func (r *Repository) GetPermissions() *RepositoryPermissions { + if r == nil { + return nil + } + return r.Permissions +} + +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (r *Repository) GetPrivate() bool { + if r == nil || r.Private == nil { + return false + } + return *r.Private +} + +// GetPullRequestCreationPolicy returns the PullRequestCreationPolicy field if it's non-nil, zero value otherwise. +func (r *Repository) GetPullRequestCreationPolicy() string { + if r == nil || r.PullRequestCreationPolicy == nil { + return "" + } + return *r.PullRequestCreationPolicy +} + +// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetPullsURL() string { + if r == nil || r.PullsURL == nil { + return "" + } + return *r.PullsURL +} + +// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. +func (r *Repository) GetPushedAt() Timestamp { + if r == nil || r.PushedAt == nil { + return Timestamp{} + } + return *r.PushedAt +} + +// GetReleasesURL returns the ReleasesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetReleasesURL() string { + if r == nil || r.ReleasesURL == nil { + return "" + } + return *r.ReleasesURL +} + +// GetRoleName returns the RoleName field if it's non-nil, zero value otherwise. +func (r *Repository) GetRoleName() string { + if r == nil || r.RoleName == nil { + return "" + } + return *r.RoleName +} + +// GetSecurityAndAnalysis returns the SecurityAndAnalysis field. +func (r *Repository) GetSecurityAndAnalysis() *SecurityAndAnalysis { + if r == nil { + return nil + } + return r.SecurityAndAnalysis +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *Repository) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetSource returns the Source field. +func (r *Repository) GetSource() *Repository { + if r == nil { + return nil + } + return r.Source +} + +// GetSquashMergeCommitMessage returns the SquashMergeCommitMessage field if it's non-nil, zero value otherwise. +func (r *Repository) GetSquashMergeCommitMessage() string { + if r == nil || r.SquashMergeCommitMessage == nil { + return "" + } + return *r.SquashMergeCommitMessage +} + +// GetSquashMergeCommitTitle returns the SquashMergeCommitTitle field if it's non-nil, zero value otherwise. +func (r *Repository) GetSquashMergeCommitTitle() string { + if r == nil || r.SquashMergeCommitTitle == nil { + return "" + } + return *r.SquashMergeCommitTitle +} + +// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSSHURL() string { + if r == nil || r.SSHURL == nil { + return "" + } + return *r.SSHURL +} + +// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetStargazersCount() int { + if r == nil || r.StargazersCount == nil { + return 0 + } + return *r.StargazersCount +} + +// GetStargazersURL returns the StargazersURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetStargazersURL() string { + if r == nil || r.StargazersURL == nil { + return "" + } + return *r.StargazersURL +} + +// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetStatusesURL() string { + if r == nil || r.StatusesURL == nil { + return "" + } + return *r.StatusesURL +} + +// GetSubscribersCount returns the SubscribersCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetSubscribersCount() int { + if r == nil || r.SubscribersCount == nil { + return 0 + } + return *r.SubscribersCount +} + +// GetSubscribersURL returns the SubscribersURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSubscribersURL() string { + if r == nil || r.SubscribersURL == nil { + return "" + } + return *r.SubscribersURL +} + +// GetSubscriptionURL returns the SubscriptionURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSubscriptionURL() string { + if r == nil || r.SubscriptionURL == nil { + return "" + } + return *r.SubscriptionURL +} + +// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetSVNURL() string { + if r == nil || r.SVNURL == nil { + return "" + } + return *r.SVNURL +} + +// GetTagsURL returns the TagsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetTagsURL() string { + if r == nil || r.TagsURL == nil { + return "" + } + return *r.TagsURL +} + +// GetTeamID returns the TeamID field if it's non-nil, zero value otherwise. +func (r *Repository) GetTeamID() int64 { + if r == nil || r.TeamID == nil { + return 0 + } + return *r.TeamID +} + +// GetTeamsURL returns the TeamsURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetTeamsURL() string { + if r == nil || r.TeamsURL == nil { + return "" + } + return *r.TeamsURL +} + +// GetTemplateRepository returns the TemplateRepository field. +func (r *Repository) GetTemplateRepository() *Repository { + if r == nil { + return nil + } + return r.TemplateRepository +} + +// GetTextMatches returns the TextMatches slice if it's non-nil, nil otherwise. +func (r *Repository) GetTextMatches() []*TextMatch { + if r == nil || r.TextMatches == nil { + return nil + } + return r.TextMatches +} + +// GetTopics returns the Topics slice if it's non-nil, nil otherwise. +func (r *Repository) GetTopics() []string { + if r == nil || r.Topics == nil { + return nil + } + return r.Topics +} + +// GetTreesURL returns the TreesURL field if it's non-nil, zero value otherwise. +func (r *Repository) GetTreesURL() string { + if r == nil || r.TreesURL == nil { + return "" + } + return *r.TreesURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *Repository) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *Repository) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetUseSquashPRTitleAsDefault returns the UseSquashPRTitleAsDefault field if it's non-nil, zero value otherwise. +func (r *Repository) GetUseSquashPRTitleAsDefault() bool { + if r == nil || r.UseSquashPRTitleAsDefault == nil { + return false + } + return *r.UseSquashPRTitleAsDefault +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (r *Repository) GetVisibility() string { + if r == nil || r.Visibility == nil { + return "" + } + return *r.Visibility +} + +// GetWatchers returns the Watchers field if it's non-nil, zero value otherwise. +func (r *Repository) GetWatchers() int { + if r == nil || r.Watchers == nil { + return 0 + } + return *r.Watchers +} + +// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. +func (r *Repository) GetWatchersCount() int { + if r == nil || r.WatchersCount == nil { + return 0 + } + return *r.WatchersCount +} + +// GetWebCommitSignoffRequired returns the WebCommitSignoffRequired field if it's non-nil, zero value otherwise. +func (r *Repository) GetWebCommitSignoffRequired() bool { + if r == nil || r.WebCommitSignoffRequired == nil { + return false + } + return *r.WebCommitSignoffRequired +} + +// GetAccessLevel returns the AccessLevel field if it's non-nil, zero value otherwise. +func (r *RepositoryActionsAccessLevel) GetAccessLevel() string { + if r == nil || r.AccessLevel == nil { + return "" + } + return *r.AccessLevel +} + +// GetAdvancedSecurityCommitters returns the AdvancedSecurityCommitters field. +func (r *RepositoryActiveCommitters) GetAdvancedSecurityCommitters() int { + if r == nil { + return 0 + } + return r.AdvancedSecurityCommitters +} + +// GetAdvancedSecurityCommittersBreakdown returns the AdvancedSecurityCommittersBreakdown slice if it's non-nil, nil otherwise. +func (r *RepositoryActiveCommitters) GetAdvancedSecurityCommittersBreakdown() []*AdvancedSecurityCommittersBreakdown { + if r == nil || r.AdvancedSecurityCommittersBreakdown == nil { + return nil + } + return r.AdvancedSecurityCommittersBreakdown +} + +// GetName returns the Name field. +func (r *RepositoryActiveCommitters) GetName() string { + if r == nil { + return "" + } + return r.Name +} + +// GetActivityType returns the ActivityType field. +func (r *RepositoryActivity) GetActivityType() string { + if r == nil { + return "" + } + return r.ActivityType +} + +// GetActor returns the Actor field. +func (r *RepositoryActivity) GetActor() *RepositoryActor { + if r == nil { + return nil + } + return r.Actor +} + +// GetAfter returns the After field. +func (r *RepositoryActivity) GetAfter() string { + if r == nil { + return "" + } + return r.After +} + +// GetBefore returns the Before field. +func (r *RepositoryActivity) GetBefore() string { + if r == nil { + return "" + } + return r.Before +} + +// GetID returns the ID field. +func (r *RepositoryActivity) GetID() int64 { + if r == nil { + return 0 + } + return r.ID +} + +// GetNodeID returns the NodeID field. +func (r *RepositoryActivity) GetNodeID() string { + if r == nil { + return "" + } + return r.NodeID +} + +// GetRef returns the Ref field. +func (r *RepositoryActivity) GetRef() string { + if r == nil { + return "" + } + return r.Ref +} + +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (r *RepositoryActivity) GetTimestamp() Timestamp { + if r == nil || r.Timestamp == nil { + return Timestamp{} + } + return *r.Timestamp +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetAvatarURL() string { + if r == nil || r.AvatarURL == nil { + return "" + } + return *r.AvatarURL +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetEventsURL() string { + if r == nil || r.EventsURL == nil { + return "" + } + return *r.EventsURL +} + +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetFollowersURL() string { + if r == nil || r.FollowersURL == nil { + return "" + } + return *r.FollowersURL +} + +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetFollowingURL() string { + if r == nil || r.FollowingURL == nil { + return "" + } + return *r.FollowingURL +} + +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetGistsURL() string { + if r == nil || r.GistsURL == nil { + return "" + } + return *r.GistsURL +} + +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetGravatarID() string { + if r == nil || r.GravatarID == nil { + return "" + } + return *r.GravatarID +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetLogin() string { + if r == nil || r.Login == nil { + return "" + } + return *r.Login +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetOrganizationsURL() string { + if r == nil || r.OrganizationsURL == nil { + return "" + } + return *r.OrganizationsURL +} + +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetReceivedEventsURL() string { + if r == nil || r.ReceivedEventsURL == nil { + return "" + } + return *r.ReceivedEventsURL +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetReposURL() string { + if r == nil || r.ReposURL == nil { + return "" + } + return *r.ReposURL +} + +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetSiteAdmin() bool { + if r == nil || r.SiteAdmin == nil { + return false + } + return *r.SiteAdmin +} + +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetStarredURL() string { + if r == nil || r.StarredURL == nil { + return "" + } + return *r.StarredURL +} + +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetSubscriptionsURL() string { + if r == nil || r.SubscriptionsURL == nil { + return "" + } + return *r.SubscriptionsURL +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetUserViewType returns the UserViewType field if it's non-nil, zero value otherwise. +func (r *RepositoryActor) GetUserViewType() string { + if r == nil || r.UserViewType == nil { + return "" + } + return *r.UserViewType +} + +// GetPermission returns the Permission field. +func (r *RepositoryAddCollaboratorOptions) GetPermission() string { + if r == nil { + return "" + } + return r.Permission +} + +// GetRepository returns the Repository field. +func (r *RepositoryAttachment) GetRepository() *Repository { + if r == nil { + return nil + } + return r.Repository +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (r *RepositoryAttachment) GetStatus() string { + if r == nil || r.Status == nil { + return "" + } + return *r.Status +} + +// GetConfiguration returns the Configuration field. +func (r *RepositoryCodeSecurityConfiguration) GetConfiguration() *CodeSecurityConfiguration { + if r == nil { + return nil + } + return r.Configuration +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *RepositoryCodeSecurityConfiguration) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetBody() string { + if r == nil || r.Body == nil { + return "" + } + return *r.Body +} + +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetCommitID() string { + if r == nil || r.CommitID == nil { + return "" + } + return *r.CommitID +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetPosition returns the Position field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetPosition() int { + if r == nil || r.Position == nil { + return 0 + } + return *r.Position +} + +// GetReactions returns the Reactions field. +func (r *RepositoryComment) GetReactions() *Reactions { + if r == nil { + return nil + } + return r.Reactions +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryComment) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetUser returns the User field. +func (r *RepositoryComment) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetAuthor returns the Author field. +func (r *RepositoryCommit) GetAuthor() *User { + if r == nil { + return nil + } + return r.Author +} + +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetCommentsURL() string { + if r == nil || r.CommentsURL == nil { + return "" + } + return *r.CommentsURL +} + +// GetCommit returns the Commit field. +func (r *RepositoryCommit) GetCommit() *Commit { + if r == nil { + return nil + } + return r.Commit +} + +// GetCommitter returns the Committer field. +func (r *RepositoryCommit) GetCommitter() *User { + if r == nil { + return nil + } + return r.Committer +} + +// GetFiles returns the Files slice if it's non-nil, nil otherwise. +func (r *RepositoryCommit) GetFiles() []*CommitFile { + if r == nil || r.Files == nil { + return nil + } + return r.Files +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetParents returns the Parents slice if it's non-nil, nil otherwise. +func (r *RepositoryCommit) GetParents() []*Commit { + if r == nil || r.Parents == nil { + return nil + } + return r.Parents +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetStats returns the Stats field. +func (r *RepositoryCommit) GetStats() *CommitStats { + if r == nil { + return nil + } + return r.Stats +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryCommit) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetDownloadURL() string { + if r == nil || r.DownloadURL == nil { + return "" + } + return *r.DownloadURL +} + +// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetEncoding() string { + if r == nil || r.Encoding == nil { + return "" + } + return *r.Encoding +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetGitURL() string { + if r == nil || r.GitURL == nil { + return "" + } + return *r.GitURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetSubmoduleGitURL returns the SubmoduleGitURL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetSubmoduleGitURL() string { + if r == nil || r.SubmoduleGitURL == nil { + return "" + } + return *r.SubmoduleGitURL +} + +// GetTarget returns the Target field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetTarget() string { + if r == nil || r.Target == nil { + return "" + } + return *r.Target +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryContent) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetAuthor returns the Author field. +func (r *RepositoryContentFileOptions) GetAuthor() *CommitAuthor { + if r == nil { + return nil + } + return r.Author +} + +// GetBranch returns the Branch field if it's non-nil, zero value otherwise. +func (r *RepositoryContentFileOptions) GetBranch() string { + if r == nil || r.Branch == nil { + return "" + } + return *r.Branch +} + +// GetCommitter returns the Committer field. +func (r *RepositoryContentFileOptions) GetCommitter() *CommitAuthor { + if r == nil { + return nil + } + return r.Committer +} + +// GetContent returns the Content slice if it's non-nil, nil otherwise. +func (r *RepositoryContentFileOptions) GetContent() []byte { + if r == nil || r.Content == nil { + return nil + } + return r.Content +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (r *RepositoryContentFileOptions) GetMessage() string { + if r == nil || r.Message == nil { + return "" + } + return *r.Message +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryContentFileOptions) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetRef returns the Ref field. +func (r *RepositoryContentGetOptions) GetRef() string { + if r == nil { + return "" + } + return r.Ref +} + +// GetContent returns the Content field. +func (r *RepositoryContentResponse) GetContent() *RepositoryContent { + if r == nil { + return nil + } + return r.Content +} + +// GetDefaultBranchOnly returns the DefaultBranchOnly field. +func (r *RepositoryCreateForkOptions) GetDefaultBranchOnly() bool { + if r == nil { + return false + } + return r.DefaultBranchOnly +} + +// GetName returns the Name field. +func (r *RepositoryCreateForkOptions) GetName() string { + if r == nil { + return "" + } + return r.Name +} + +// GetOrganization returns the Organization field. +func (r *RepositoryCreateForkOptions) GetOrganization() string { + if r == nil { + return "" + } + return r.Organization +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *RepositoryDispatchEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetBranch returns the Branch field if it's non-nil, zero value otherwise. +func (r *RepositoryDispatchEvent) GetBranch() string { + if r == nil || r.Branch == nil { + return "" + } + return *r.Branch +} + +// GetClientPayload returns the ClientPayload field. +func (r *RepositoryDispatchEvent) GetClientPayload() json.RawMessage { + if r == nil { + return json.RawMessage{} + } + return r.ClientPayload +} + +// GetInstallation returns the Installation field. +func (r *RepositoryDispatchEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrg returns the Org field. +func (r *RepositoryDispatchEvent) GetOrg() *Organization { + if r == nil { + return nil + } + return r.Org +} + +// GetRepo returns the Repo field. +func (r *RepositoryDispatchEvent) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetSender returns the Sender field. +func (r *RepositoryDispatchEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *RepositoryEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetChanges returns the Changes field. +func (r *RepositoryEvent) GetChanges() *EditChange { + if r == nil { + return nil + } + return r.Changes +} + +// GetInstallation returns the Installation field. +func (r *RepositoryEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrg returns the Org field. +func (r *RepositoryEvent) GetOrg() *Organization { + if r == nil { + return nil + } + return r.Org +} + +// GetRepo returns the Repo field. +func (r *RepositoryEvent) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetSender returns the Sender field. +func (r *RepositoryEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetOrg returns the Org field. +func (r *RepositoryImportEvent) GetOrg() *Organization { + if r == nil { + return nil + } + return r.Org +} + +// GetRepo returns the Repo field. +func (r *RepositoryImportEvent) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetSender returns the Sender field. +func (r *RepositoryImportEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (r *RepositoryImportEvent) GetStatus() string { + if r == nil || r.Status == nil { + return "" + } + return *r.Status +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetExpired returns the Expired field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetExpired() bool { + if r == nil || r.Expired == nil { + return false + } + return *r.Expired +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetInvitee returns the Invitee field. +func (r *RepositoryInvitation) GetInvitee() *User { + if r == nil { + return nil + } + return r.Invitee +} + +// GetInviter returns the Inviter field. +func (r *RepositoryInvitation) GetInviter() *User { + if r == nil { + return nil + } + return r.Inviter +} + +// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetPermissions() string { + if r == nil || r.Permissions == nil { + return "" + } + return *r.Permissions +} + +// GetRepo returns the Repo field. +func (r *RepositoryInvitation) GetRepo() *Repository { + if r == nil { + return nil + } + return r.Repo +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryInvitation) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetContent() string { + if r == nil || r.Content == nil { + return "" + } + return *r.Content +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetDownloadURL() string { + if r == nil || r.DownloadURL == nil { + return "" + } + return *r.DownloadURL +} + +// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetEncoding() string { + if r == nil || r.Encoding == nil { + return "" + } + return *r.Encoding +} + +// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetGitURL() string { + if r == nil || r.GitURL == nil { + return "" + } + return *r.GitURL +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetHTMLURL() string { + if r == nil || r.HTMLURL == nil { + return "" + } + return *r.HTMLURL +} + +// GetLicense returns the License field. +func (r *RepositoryLicense) GetLicense() *License { + if r == nil { + return nil + } + return r.License +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetPath() string { + if r == nil || r.Path == nil { + return "" + } + return *r.Path +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetSize() int { + if r == nil || r.Size == nil { + return 0 + } + return *r.Size +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepositoryLicense) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetSince returns the Since field. +func (r *RepositoryListAllOptions) GetSince() int64 { + if r == nil { + return 0 + } + return r.Since +} + +// GetAffiliation returns the Affiliation field. +func (r *RepositoryListByAuthenticatedUserOptions) GetAffiliation() string { + if r == nil { + return "" + } + return r.Affiliation +} + +// GetDirection returns the Direction field. +func (r *RepositoryListByAuthenticatedUserOptions) GetDirection() string { + if r == nil { + return "" + } + return r.Direction +} + +// GetSort returns the Sort field. +func (r *RepositoryListByAuthenticatedUserOptions) GetSort() string { + if r == nil { + return "" + } + return r.Sort +} + +// GetType returns the Type field. +func (r *RepositoryListByAuthenticatedUserOptions) GetType() string { + if r == nil { + return "" + } + return r.Type +} + +// GetVisibility returns the Visibility field. +func (r *RepositoryListByAuthenticatedUserOptions) GetVisibility() string { + if r == nil { + return "" + } + return r.Visibility +} + +// GetDirection returns the Direction field. +func (r *RepositoryListByOrgOptions) GetDirection() string { + if r == nil { + return "" + } + return r.Direction +} + +// GetSort returns the Sort field. +func (r *RepositoryListByOrgOptions) GetSort() string { + if r == nil { + return "" + } + return r.Sort +} + +// GetType returns the Type field. +func (r *RepositoryListByOrgOptions) GetType() string { + if r == nil { + return "" + } + return r.Type +} + +// GetDirection returns the Direction field. +func (r *RepositoryListByUserOptions) GetDirection() string { + if r == nil { + return "" + } + return r.Direction +} + +// GetSort returns the Sort field. +func (r *RepositoryListByUserOptions) GetSort() string { + if r == nil { + return "" + } + return r.Sort +} + +// GetType returns the Type field. +func (r *RepositoryListByUserOptions) GetType() string { + if r == nil { + return "" + } + return r.Type +} + +// GetSort returns the Sort field. +func (r *RepositoryListForksOptions) GetSort() string { + if r == nil { + return "" + } + return r.Sort +} + +// GetAffiliation returns the Affiliation field. +func (r *RepositoryListOptions) GetAffiliation() string { + if r == nil { + return "" + } + return r.Affiliation +} + +// GetDirection returns the Direction field. +func (r *RepositoryListOptions) GetDirection() string { + if r == nil { + return "" + } + return r.Direction +} + +// GetSort returns the Sort field. +func (r *RepositoryListOptions) GetSort() string { + if r == nil { + return "" + } + return r.Sort +} + +// GetType returns the Type field. +func (r *RepositoryListOptions) GetType() string { + if r == nil { + return "" + } + return r.Type +} + +// GetVisibility returns the Visibility field. +func (r *RepositoryListOptions) GetVisibility() string { + if r == nil { + return "" + } + return r.Visibility +} + +// GetIncludesParents returns the IncludesParents field if it's non-nil, zero value otherwise. +func (r *RepositoryListRulesetsOptions) GetIncludesParents() bool { + if r == nil || r.IncludesParents == nil { + return false + } + return *r.IncludesParents +} + +// GetBase returns the Base field. +func (r *RepositoryMergeRequest) GetBase() string { + if r == nil { + return "" + } + return r.Base +} + +// GetCommitMessage returns the CommitMessage field if it's non-nil, zero value otherwise. +func (r *RepositoryMergeRequest) GetCommitMessage() string { + if r == nil || r.CommitMessage == nil { + return "" + } + return *r.CommitMessage +} + +// GetHead returns the Head field. +func (r *RepositoryMergeRequest) GetHead() string { + if r == nil { + return "" + } + return r.Head +} + +// GetAll returns the All slice if it's non-nil, nil otherwise. +func (r *RepositoryParticipation) GetAll() []int { + if r == nil || r.All == nil { + return nil + } + return r.All +} + +// GetOwner returns the Owner slice if it's non-nil, nil otherwise. +func (r *RepositoryParticipation) GetOwner() []int { + if r == nil || r.Owner == nil { + return nil + } + return r.Owner +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissionLevel) GetPermission() string { + if r == nil || r.Permission == nil { + return "" + } + return *r.Permission +} + +// GetRoleName returns the RoleName field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissionLevel) GetRoleName() string { + if r == nil || r.RoleName == nil { + return "" + } + return *r.RoleName +} + +// GetUser returns the User field. +func (r *RepositoryPermissionLevel) GetUser() *User { + if r == nil { + return nil + } + return r.User +} + +// GetAdmin returns the Admin field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissions) GetAdmin() bool { + if r == nil || r.Admin == nil { + return false + } + return *r.Admin +} + +// GetMaintain returns the Maintain field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissions) GetMaintain() bool { + if r == nil || r.Maintain == nil { + return false + } + return *r.Maintain +} + +// GetPull returns the Pull field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissions) GetPull() bool { + if r == nil || r.Pull == nil { + return false + } + return *r.Pull +} + +// GetPush returns the Push field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissions) GetPush() bool { + if r == nil || r.Push == nil { + return false + } + return *r.Push +} + +// GetTriage returns the Triage field if it's non-nil, zero value otherwise. +func (r *RepositoryPermissions) GetTriage() bool { + if r == nil || r.Triage == nil { + return false + } + return *r.Triage +} + +// GetAssets returns the Assets slice if it's non-nil, nil otherwise. +func (r *RepositoryRelease) GetAssets() []*ReleaseAsset { + if r == nil || r.Assets == nil { + return nil + } + return r.Assets +} + +// GetAssetsURL returns the AssetsURL field. +func (r *RepositoryRelease) GetAssetsURL() string { + if r == nil { + return "" + } + return r.AssetsURL +} + +// GetAuthor returns the Author field. +func (r *RepositoryRelease) GetAuthor() *User { + if r == nil { + return nil + } + return r.Author +} + +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetBody() string { + if r == nil || r.Body == nil { + return "" + } + return *r.Body +} + +// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetBodyHTML() string { + if r == nil || r.BodyHTML == nil { + return "" + } + return *r.BodyHTML +} + +// GetBodyText returns the BodyText field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetBodyText() string { + if r == nil || r.BodyText == nil { + return "" + } + return *r.BodyText +} + +// GetCreatedAt returns the CreatedAt field. +func (r *RepositoryRelease) GetCreatedAt() Timestamp { + if r == nil { + return Timestamp{} + } + return r.CreatedAt +} + +// GetDiscussionURL returns the DiscussionURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetDiscussionURL() string { + if r == nil || r.DiscussionURL == nil { + return "" + } + return *r.DiscussionURL +} + +// GetDraft returns the Draft field. +func (r *RepositoryRelease) GetDraft() bool { + if r == nil { + return false + } + return r.Draft +} + +// GetHTMLURL returns the HTMLURL field. +func (r *RepositoryRelease) GetHTMLURL() string { + if r == nil { + return "" + } + return r.HTMLURL +} + +// GetID returns the ID field. +func (r *RepositoryRelease) GetID() int64 { + if r == nil { + return 0 + } + return r.ID +} + +// GetImmutable returns the Immutable field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetImmutable() bool { + if r == nil || r.Immutable == nil { + return false + } + return *r.Immutable +} + +// GetMentionsCount returns the MentionsCount field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetMentionsCount() int { + if r == nil || r.MentionsCount == nil { + return 0 + } + return *r.MentionsCount +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetNodeID returns the NodeID field. +func (r *RepositoryRelease) GetNodeID() string { + if r == nil { + return "" + } + return r.NodeID +} + +// GetPrerelease returns the Prerelease field. +func (r *RepositoryRelease) GetPrerelease() bool { + if r == nil { + return false + } + return r.Prerelease +} + +// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetPublishedAt() Timestamp { + if r == nil || r.PublishedAt == nil { + return Timestamp{} + } + return *r.PublishedAt +} + +// GetReactions returns the Reactions field. +func (r *RepositoryRelease) GetReactions() *Reactions { + if r == nil { + return nil + } + return r.Reactions +} + +// GetTagName returns the TagName field. +func (r *RepositoryRelease) GetTagName() string { + if r == nil { + return "" + } + return r.TagName +} + +// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetTarballURL() string { + if r == nil || r.TarballURL == nil { + return "" + } + return *r.TarballURL +} + +// GetTargetCommitish returns the TargetCommitish field. +func (r *RepositoryRelease) GetTargetCommitish() string { + if r == nil { + return "" + } + return r.TargetCommitish +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetUploadURL returns the UploadURL field. +func (r *RepositoryRelease) GetUploadURL() string { + if r == nil { + return "" + } + return r.UploadURL +} + +// GetURL returns the URL field. +func (r *RepositoryRelease) GetURL() string { + if r == nil { + return "" + } + return r.URL +} + +// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryRelease) GetZipballURL() string { + if r == nil || r.ZipballURL == nil { + return "" + } + return *r.ZipballURL +} + +// GetBody returns the Body field. +func (r *RepositoryReleaseNotes) GetBody() string { + if r == nil { + return "" + } + return r.Body +} + +// GetName returns the Name field. +func (r *RepositoryReleaseNotes) GetName() string { + if r == nil { + return "" + } + return r.Name +} + +// GetParameters returns the Parameters field. +func (r *RepositoryRule) GetParameters() any { + if r == nil { + return nil + } + return r.Parameters +} + +// GetType returns the Type field. +func (r *RepositoryRule) GetType() RepositoryRuleType { + if r == nil { + return "" + } + return r.Type +} + +// GetBypassActors returns the BypassActors slice if it's non-nil, nil otherwise. +func (r *RepositoryRuleset) GetBypassActors() []*BypassActor { + if r == nil || r.BypassActors == nil { + return nil + } + return r.BypassActors +} + +// GetConditions returns the Conditions field. +func (r *RepositoryRuleset) GetConditions() *RepositoryRulesetConditions { + if r == nil { + return nil + } + return r.Conditions +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryRuleset) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetCurrentUserCanBypass returns the CurrentUserCanBypass field. +func (r *RepositoryRuleset) GetCurrentUserCanBypass() *BypassMode { + if r == nil { + return nil + } + return r.CurrentUserCanBypass +} + +// GetEnforcement returns the Enforcement field. +func (r *RepositoryRuleset) GetEnforcement() RulesetEnforcement { + if r == nil { + return "" + } + return r.Enforcement +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryRuleset) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetLinks returns the Links field. +func (r *RepositoryRuleset) GetLinks() *RepositoryRulesetLinks { + if r == nil { + return nil + } + return r.Links +} + +// GetName returns the Name field. +func (r *RepositoryRuleset) GetName() string { + if r == nil { + return "" + } + return r.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *RepositoryRuleset) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetRules returns the Rules field. +func (r *RepositoryRuleset) GetRules() *RepositoryRulesetRules { + if r == nil { + return nil + } + return r.Rules +} + +// GetSource returns the Source field. +func (r *RepositoryRuleset) GetSource() string { + if r == nil { + return "" + } + return r.Source +} + +// GetSourceType returns the SourceType field. +func (r *RepositoryRuleset) GetSourceType() *RulesetSourceType { + if r == nil { + return nil + } + return r.SourceType +} + +// GetTarget returns the Target field. +func (r *RepositoryRuleset) GetTarget() *RulesetTarget { + if r == nil { + return nil + } + return r.Target +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryRuleset) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetAdded returns the Added slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangedConditions) GetAdded() []*RepositoryRulesetConditions { + if r == nil || r.Added == nil { + return nil + } + return r.Added +} + +// GetDeleted returns the Deleted slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangedConditions) GetDeleted() []*RepositoryRulesetConditions { + if r == nil || r.Deleted == nil { + return nil + } + return r.Deleted +} + +// GetUpdated returns the Updated slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangedConditions) GetUpdated() []*RepositoryRulesetUpdatedConditions { + if r == nil || r.Updated == nil { + return nil + } + return r.Updated +} + +// GetConfiguration returns the Configuration field. +func (r *RepositoryRulesetChangedRule) GetConfiguration() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Configuration +} + +// GetPattern returns the Pattern field. +func (r *RepositoryRulesetChangedRule) GetPattern() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Pattern +} + +// GetRuleType returns the RuleType field. +func (r *RepositoryRulesetChangedRule) GetRuleType() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.RuleType +} + +// GetAdded returns the Added slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangedRules) GetAdded() []*RepositoryRule { + if r == nil || r.Added == nil { + return nil + } + return r.Added +} + +// GetDeleted returns the Deleted slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangedRules) GetDeleted() []*RepositoryRule { + if r == nil || r.Deleted == nil { + return nil + } + return r.Deleted +} + +// GetUpdated returns the Updated slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangedRules) GetUpdated() []*RepositoryRulesetUpdatedRules { + if r == nil || r.Updated == nil { + return nil + } + return r.Updated +} + +// GetConditions returns the Conditions field. +func (r *RepositoryRulesetChanges) GetConditions() *RepositoryRulesetChangedConditions { + if r == nil { + return nil + } + return r.Conditions +} + +// GetEnforcement returns the Enforcement field. +func (r *RepositoryRulesetChanges) GetEnforcement() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Enforcement +} + +// GetName returns the Name field. +func (r *RepositoryRulesetChanges) GetName() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Name +} + +// GetRules returns the Rules field. +func (r *RepositoryRulesetChanges) GetRules() *RepositoryRulesetChangedRules { + if r == nil { + return nil + } + return r.Rules +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetChangeSource) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetFrom returns the From slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetChangeSources) GetFrom() []string { + if r == nil || r.From == nil { + return nil + } + return r.From +} + +// GetOrganizationID returns the OrganizationID field. +func (r *RepositoryRulesetConditions) GetOrganizationID() *RepositoryRulesetOrganizationIDsConditionParameters { + if r == nil { + return nil + } + return r.OrganizationID +} + +// GetOrganizationName returns the OrganizationName field. +func (r *RepositoryRulesetConditions) GetOrganizationName() *RepositoryRulesetOrganizationNamesConditionParameters { + if r == nil { + return nil + } + return r.OrganizationName +} + +// GetOrganizationProperty returns the OrganizationProperty field. +func (r *RepositoryRulesetConditions) GetOrganizationProperty() *RepositoryRulesetOrganizationPropertyConditionParameters { + if r == nil { + return nil + } + return r.OrganizationProperty +} + +// GetRefName returns the RefName field. +func (r *RepositoryRulesetConditions) GetRefName() *RepositoryRulesetRefConditionParameters { + if r == nil { + return nil + } + return r.RefName +} + +// GetRepositoryID returns the RepositoryID field. +func (r *RepositoryRulesetConditions) GetRepositoryID() *RepositoryRulesetRepositoryIDsConditionParameters { + if r == nil { + return nil + } + return r.RepositoryID +} + +// GetRepositoryName returns the RepositoryName field. +func (r *RepositoryRulesetConditions) GetRepositoryName() *RepositoryRulesetRepositoryNamesConditionParameters { + if r == nil { + return nil + } + return r.RepositoryName +} + +// GetRepositoryProperty returns the RepositoryProperty field. +func (r *RepositoryRulesetConditions) GetRepositoryProperty() *RepositoryRulesetRepositoryPropertyConditionParameters { + if r == nil { + return nil + } + return r.RepositoryProperty +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetChanges returns the Changes field. +func (r *RepositoryRulesetEvent) GetChanges() *RepositoryRulesetChanges { + if r == nil { + return nil + } + return r.Changes +} + +// GetEnterprise returns the Enterprise field. +func (r *RepositoryRulesetEvent) GetEnterprise() *Enterprise { + if r == nil { + return nil + } + return r.Enterprise +} + +// GetInstallation returns the Installation field. +func (r *RepositoryRulesetEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrganization returns the Organization field. +func (r *RepositoryRulesetEvent) GetOrganization() *Organization { + if r == nil { + return nil + } + return r.Organization +} + +// GetRepository returns the Repository field. +func (r *RepositoryRulesetEvent) GetRepository() *Repository { + if r == nil { + return nil + } + return r.Repository +} + +// GetRepositoryRuleset returns the RepositoryRuleset field. +func (r *RepositoryRulesetEvent) GetRepositoryRuleset() *RepositoryRuleset { + if r == nil { + return nil + } + return r.RepositoryRuleset +} + +// GetSender returns the Sender field. +func (r *RepositoryRulesetEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetHRef returns the HRef field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetLink) GetHRef() string { + if r == nil || r.HRef == nil { + return "" + } + return *r.HRef +} + +// GetHTML returns the HTML field. +func (r *RepositoryRulesetLinks) GetHTML() *RepositoryRulesetLink { + if r == nil { + return nil + } + return r.HTML +} + +// GetSelf returns the Self field. +func (r *RepositoryRulesetLinks) GetSelf() *RepositoryRulesetLink { + if r == nil { + return nil + } + return r.Self +} + +// GetOrganizationIDs returns the OrganizationIDs slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetOrganizationIDsConditionParameters) GetOrganizationIDs() []int64 { + if r == nil || r.OrganizationIDs == nil { + return nil + } + return r.OrganizationIDs +} + +// GetExclude returns the Exclude slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetOrganizationNamesConditionParameters) GetExclude() []string { + if r == nil || r.Exclude == nil { + return nil + } + return r.Exclude +} + +// GetInclude returns the Include slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetOrganizationNamesConditionParameters) GetInclude() []string { + if r == nil || r.Include == nil { + return nil + } + return r.Include +} + +// GetExclude returns the Exclude slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetOrganizationPropertyConditionParameters) GetExclude() []*RepositoryRulesetRepositoryPropertyTargetParameters { + if r == nil || r.Exclude == nil { + return nil + } + return r.Exclude +} + +// GetInclude returns the Include slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetOrganizationPropertyConditionParameters) GetInclude() []*RepositoryRulesetRepositoryPropertyTargetParameters { + if r == nil || r.Include == nil { + return nil + } + return r.Include +} + +// GetExclude returns the Exclude slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRefConditionParameters) GetExclude() []string { + if r == nil || r.Exclude == nil { + return nil + } + return r.Exclude +} + +// GetInclude returns the Include slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRefConditionParameters) GetInclude() []string { + if r == nil || r.Include == nil { + return nil + } + return r.Include +} + +// GetRepositoryIDs returns the RepositoryIDs slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRepositoryIDsConditionParameters) GetRepositoryIDs() []int64 { + if r == nil || r.RepositoryIDs == nil { + return nil + } + return r.RepositoryIDs +} + +// GetExclude returns the Exclude slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRepositoryNamesConditionParameters) GetExclude() []string { + if r == nil || r.Exclude == nil { + return nil + } + return r.Exclude +} + +// GetInclude returns the Include slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRepositoryNamesConditionParameters) GetInclude() []string { + if r == nil || r.Include == nil { + return nil + } + return r.Include +} + +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetRepositoryNamesConditionParameters) GetProtected() bool { + if r == nil || r.Protected == nil { + return false + } + return *r.Protected +} + +// GetExclude returns the Exclude slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRepositoryPropertyConditionParameters) GetExclude() []*RepositoryRulesetRepositoryPropertyTargetParameters { + if r == nil || r.Exclude == nil { + return nil + } + return r.Exclude +} + +// GetInclude returns the Include slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRepositoryPropertyConditionParameters) GetInclude() []*RepositoryRulesetRepositoryPropertyTargetParameters { + if r == nil || r.Include == nil { + return nil + } + return r.Include +} + +// GetName returns the Name field. +func (r *RepositoryRulesetRepositoryPropertyTargetParameters) GetName() string { + if r == nil { + return "" + } + return r.Name +} + +// GetPropertyValues returns the PropertyValues slice if it's non-nil, nil otherwise. +func (r *RepositoryRulesetRepositoryPropertyTargetParameters) GetPropertyValues() []string { + if r == nil || r.PropertyValues == nil { + return nil + } + return r.PropertyValues +} + +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetRepositoryPropertyTargetParameters) GetSource() string { + if r == nil || r.Source == nil { + return "" + } + return *r.Source +} + +// GetBranchNamePattern returns the BranchNamePattern field. +func (r *RepositoryRulesetRules) GetBranchNamePattern() *PatternRuleParameters { + if r == nil { + return nil + } + return r.BranchNamePattern +} + +// GetCodeScanning returns the CodeScanning field. +func (r *RepositoryRulesetRules) GetCodeScanning() *CodeScanningRuleParameters { + if r == nil { + return nil + } + return r.CodeScanning +} + +// GetCommitAuthorEmailPattern returns the CommitAuthorEmailPattern field. +func (r *RepositoryRulesetRules) GetCommitAuthorEmailPattern() *PatternRuleParameters { + if r == nil { + return nil + } + return r.CommitAuthorEmailPattern +} + +// GetCommitMessagePattern returns the CommitMessagePattern field. +func (r *RepositoryRulesetRules) GetCommitMessagePattern() *PatternRuleParameters { + if r == nil { + return nil + } + return r.CommitMessagePattern +} + +// GetCommitterEmailPattern returns the CommitterEmailPattern field. +func (r *RepositoryRulesetRules) GetCommitterEmailPattern() *PatternRuleParameters { + if r == nil { + return nil + } + return r.CommitterEmailPattern +} + +// GetCopilotCodeReview returns the CopilotCodeReview field. +func (r *RepositoryRulesetRules) GetCopilotCodeReview() *CopilotCodeReviewRuleParameters { + if r == nil { + return nil + } + return r.CopilotCodeReview +} + +// GetCreation returns the Creation field. +func (r *RepositoryRulesetRules) GetCreation() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.Creation +} + +// GetDeletion returns the Deletion field. +func (r *RepositoryRulesetRules) GetDeletion() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.Deletion +} + +// GetFileExtensionRestriction returns the FileExtensionRestriction field. +func (r *RepositoryRulesetRules) GetFileExtensionRestriction() *FileExtensionRestrictionRuleParameters { + if r == nil { + return nil + } + return r.FileExtensionRestriction +} + +// GetFilePathRestriction returns the FilePathRestriction field. +func (r *RepositoryRulesetRules) GetFilePathRestriction() *FilePathRestrictionRuleParameters { + if r == nil { + return nil + } + return r.FilePathRestriction +} + +// GetMaxFilePathLength returns the MaxFilePathLength field. +func (r *RepositoryRulesetRules) GetMaxFilePathLength() *MaxFilePathLengthRuleParameters { + if r == nil { + return nil + } + return r.MaxFilePathLength +} + +// GetMaxFileSize returns the MaxFileSize field. +func (r *RepositoryRulesetRules) GetMaxFileSize() *MaxFileSizeRuleParameters { + if r == nil { + return nil + } + return r.MaxFileSize +} + +// GetMergeQueue returns the MergeQueue field. +func (r *RepositoryRulesetRules) GetMergeQueue() *MergeQueueRuleParameters { + if r == nil { + return nil + } + return r.MergeQueue +} + +// GetNonFastForward returns the NonFastForward field. +func (r *RepositoryRulesetRules) GetNonFastForward() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.NonFastForward +} + +// GetPullRequest returns the PullRequest field. +func (r *RepositoryRulesetRules) GetPullRequest() *PullRequestRuleParameters { + if r == nil { + return nil + } + return r.PullRequest +} + +// GetRepositoryCreate returns the RepositoryCreate field. +func (r *RepositoryRulesetRules) GetRepositoryCreate() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.RepositoryCreate +} + +// GetRepositoryDelete returns the RepositoryDelete field. +func (r *RepositoryRulesetRules) GetRepositoryDelete() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.RepositoryDelete +} + +// GetRepositoryName returns the RepositoryName field. +func (r *RepositoryRulesetRules) GetRepositoryName() *SimplePatternRuleParameters { + if r == nil { + return nil + } + return r.RepositoryName +} + +// GetRepositoryTransfer returns the RepositoryTransfer field. +func (r *RepositoryRulesetRules) GetRepositoryTransfer() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.RepositoryTransfer +} + +// GetRepositoryVisibility returns the RepositoryVisibility field. +func (r *RepositoryRulesetRules) GetRepositoryVisibility() *RepositoryVisibilityRuleParameters { + if r == nil { + return nil + } + return r.RepositoryVisibility +} + +// GetRequiredDeployments returns the RequiredDeployments field. +func (r *RepositoryRulesetRules) GetRequiredDeployments() *RequiredDeploymentsRuleParameters { + if r == nil { + return nil + } + return r.RequiredDeployments +} + +// GetRequiredLinearHistory returns the RequiredLinearHistory field. +func (r *RepositoryRulesetRules) GetRequiredLinearHistory() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.RequiredLinearHistory +} + +// GetRequiredSignatures returns the RequiredSignatures field. +func (r *RepositoryRulesetRules) GetRequiredSignatures() *EmptyRuleParameters { + if r == nil { + return nil + } + return r.RequiredSignatures +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks field. +func (r *RepositoryRulesetRules) GetRequiredStatusChecks() *RequiredStatusChecksRuleParameters { + if r == nil { + return nil + } + return r.RequiredStatusChecks +} + +// GetTagNamePattern returns the TagNamePattern field. +func (r *RepositoryRulesetRules) GetTagNamePattern() *PatternRuleParameters { + if r == nil { + return nil + } + return r.TagNamePattern +} + +// GetUpdate returns the Update field. +func (r *RepositoryRulesetRules) GetUpdate() *UpdateRuleParameters { + if r == nil { + return nil + } + return r.Update +} + +// GetWorkflows returns the Workflows field. +func (r *RepositoryRulesetRules) GetWorkflows() *WorkflowsRuleParameters { + if r == nil { + return nil + } + return r.Workflows +} + +// GetConditionType returns the ConditionType field. +func (r *RepositoryRulesetUpdatedCondition) GetConditionType() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.ConditionType +} + +// GetExclude returns the Exclude field. +func (r *RepositoryRulesetUpdatedCondition) GetExclude() *RepositoryRulesetChangeSources { + if r == nil { + return nil + } + return r.Exclude +} + +// GetInclude returns the Include field. +func (r *RepositoryRulesetUpdatedCondition) GetInclude() *RepositoryRulesetChangeSources { + if r == nil { + return nil + } + return r.Include +} + +// GetTarget returns the Target field. +func (r *RepositoryRulesetUpdatedCondition) GetTarget() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Target +} + +// GetChanges returns the Changes field. +func (r *RepositoryRulesetUpdatedConditions) GetChanges() *RepositoryRulesetUpdatedCondition { + if r == nil { + return nil + } + return r.Changes +} + +// GetCondition returns the Condition field. +func (r *RepositoryRulesetUpdatedConditions) GetCondition() *RepositoryRulesetConditions { + if r == nil { + return nil + } + return r.Condition +} + +// GetChanges returns the Changes field. +func (r *RepositoryRulesetUpdatedRules) GetChanges() *RepositoryRulesetChangedRule { + if r == nil { + return nil + } + return r.Changes +} + +// GetRule returns the Rule field. +func (r *RepositoryRulesetUpdatedRules) GetRule() *RepositoryRule { + if r == nil { + return nil + } + return r.Rule +} + +// GetCommit returns the Commit field. +func (r *RepositoryTag) GetCommit() *Commit { + if r == nil { + return nil + } + return r.Commit +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RepositoryTag) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryTag) GetTarballURL() string { + if r == nil || r.TarballURL == nil { + return "" + } + return *r.TarballURL +} + +// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. +func (r *RepositoryTag) GetZipballURL() string { + if r == nil || r.ZipballURL == nil { + return "" + } + return *r.ZipballURL +} + +// GetInternal returns the Internal field. +func (r *RepositoryVisibilityRuleParameters) GetInternal() bool { + if r == nil { + return false + } + return r.Internal +} + +// GetPrivate returns the Private field. +func (r *RepositoryVisibilityRuleParameters) GetPrivate() bool { + if r == nil { + return false + } + return r.Private +} + +// GetAffectedPackageName returns the AffectedPackageName field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetAffectedPackageName() string { + if r == nil || r.AffectedPackageName == nil { + return "" + } + return *r.AffectedPackageName +} + +// GetAffectedRange returns the AffectedRange field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetAffectedRange() string { + if r == nil || r.AffectedRange == nil { + return "" + } + return *r.AffectedRange +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetDismissedAt returns the DismissedAt field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetDismissedAt() Timestamp { + if r == nil || r.DismissedAt == nil { + return Timestamp{} + } + return *r.DismissedAt +} + +// GetDismisser returns the Dismisser field. +func (r *RepositoryVulnerabilityAlert) GetDismisser() *User { + if r == nil { + return nil + } + return r.Dismisser +} + +// GetDismissReason returns the DismissReason field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetDismissReason() string { + if r == nil || r.DismissReason == nil { + return "" + } + return *r.DismissReason +} + +// GetExternalIdentifier returns the ExternalIdentifier field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetExternalIdentifier() string { + if r == nil || r.ExternalIdentifier == nil { + return "" + } + return *r.ExternalIdentifier +} + +// GetExternalReference returns the ExternalReference field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetExternalReference() string { + if r == nil || r.ExternalReference == nil { + return "" + } + return *r.ExternalReference +} + +// GetFixedIn returns the FixedIn field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetFixedIn() string { + if r == nil || r.FixedIn == nil { + return "" + } + return *r.FixedIn +} + +// GetGitHubSecurityAdvisoryID returns the GitHubSecurityAdvisoryID field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetGitHubSecurityAdvisoryID() string { + if r == nil || r.GitHubSecurityAdvisoryID == nil { + return "" + } + return *r.GitHubSecurityAdvisoryID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlert) GetSeverity() string { + if r == nil || r.Severity == nil { + return "" + } + return *r.Severity +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (r *RepositoryVulnerabilityAlertEvent) GetAction() string { + if r == nil || r.Action == nil { + return "" + } + return *r.Action +} + +// GetAlert returns the Alert field. +func (r *RepositoryVulnerabilityAlertEvent) GetAlert() *RepositoryVulnerabilityAlert { + if r == nil { + return nil + } + return r.Alert +} + +// GetInstallation returns the Installation field. +func (r *RepositoryVulnerabilityAlertEvent) GetInstallation() *Installation { + if r == nil { + return nil + } + return r.Installation +} + +// GetOrg returns the Org field. +func (r *RepositoryVulnerabilityAlertEvent) GetOrg() *Organization { + if r == nil { + return nil + } + return r.Org +} + +// GetRepository returns the Repository field. +func (r *RepositoryVulnerabilityAlertEvent) GetRepository() *Repository { + if r == nil { + return nil + } + return r.Repository +} + +// GetSender returns the Sender field. +func (r *RepositoryVulnerabilityAlertEvent) GetSender() *User { + if r == nil { + return nil + } + return r.Sender +} + +// GetForkRepos returns the ForkRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetForkRepos() int { + if r == nil || r.ForkRepos == nil { + return 0 + } + return *r.ForkRepos +} + +// GetOrgRepos returns the OrgRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetOrgRepos() int { + if r == nil || r.OrgRepos == nil { + return 0 + } + return *r.OrgRepos +} + +// GetRootRepos returns the RootRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetRootRepos() int { + if r == nil || r.RootRepos == nil { + return 0 + } + return *r.RootRepos +} + +// GetTotalPushes returns the TotalPushes field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetTotalPushes() int { + if r == nil || r.TotalPushes == nil { + return 0 + } + return *r.TotalPushes +} + +// GetTotalRepos returns the TotalRepos field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetTotalRepos() int { + if r == nil || r.TotalRepos == nil { + return 0 + } + return *r.TotalRepos +} + +// GetTotalWikis returns the TotalWikis field if it's non-nil, zero value otherwise. +func (r *RepoStats) GetTotalWikis() int { + if r == nil || r.TotalWikis == nil { + return 0 + } + return *r.TotalWikis +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetAvatarURL() string { + if r == nil || r.AvatarURL == nil { + return "" + } + return *r.AvatarURL +} + +// GetContext returns the Context field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetContext() string { + if r == nil || r.Context == nil { + return "" + } + return *r.Context +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + +// GetCreator returns the Creator field. +func (r *RepoStatus) GetCreator() *User { + if r == nil { + return nil + } + return r.Creator +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetDescription() string { + if r == nil || r.Description == nil { + return "" + } + return *r.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetState() string { + if r == nil || r.State == nil { + return "" + } + return *r.State +} + +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetTargetURL() string { + if r == nil || r.TargetURL == nil { + return "" + } + return *r.TargetURL +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetUpdatedAt() Timestamp { + if r == nil || r.UpdatedAt == nil { + return Timestamp{} + } + return *r.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RepoStatus) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetIdentifier returns the Identifier field. +func (r *RequestedAction) GetIdentifier() string { + if r == nil { + return "" + } + return r.Identifier +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RequireCodeOwnerReviewChanges) GetFrom() bool { + if r == nil || r.From == nil { + return false + } + return *r.From +} + +// GetEnabled returns the Enabled field. +func (r *RequiredConversationResolution) GetEnabled() bool { + if r == nil { + return false + } + return r.Enabled +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RequiredConversationResolutionLevelChanges) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetParameters returns the Parameters field. +func (r *RequiredDeploymentsBranchRule) GetParameters() RequiredDeploymentsRuleParameters { + if r == nil { + return RequiredDeploymentsRuleParameters{} + } + return r.Parameters +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RequiredDeploymentsEnforcementLevelChanges) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetRequiredDeploymentEnvironments returns the RequiredDeploymentEnvironments slice if it's non-nil, nil otherwise. +func (r *RequiredDeploymentsRuleParameters) GetRequiredDeploymentEnvironments() []string { + if r == nil || r.RequiredDeploymentEnvironments == nil { + return nil + } + return r.RequiredDeploymentEnvironments +} + +// GetReviewer returns the Reviewer field. +func (r *RequiredReviewer) GetReviewer() any { + if r == nil { + return nil + } + return r.Reviewer +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RequiredReviewer) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetAppID returns the AppID field if it's non-nil, zero value otherwise. +func (r *RequiredStatusCheck) GetAppID() int64 { + if r == nil || r.AppID == nil { + return 0 + } + return *r.AppID +} + +// GetContext returns the Context field. +func (r *RequiredStatusCheck) GetContext() string { + if r == nil { + return "" + } + return r.Context +} + +// GetChecks returns the Checks field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecks) GetChecks() []*RequiredStatusCheck { + if r == nil || r.Checks == nil { + return nil + } + return *r.Checks +} + +// GetContexts returns the Contexts field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecks) GetContexts() []string { + if r == nil || r.Contexts == nil { + return nil + } + return *r.Contexts +} + +// GetContextsURL returns the ContextsURL field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecks) GetContextsURL() string { + if r == nil || r.ContextsURL == nil { + return "" + } + return *r.ContextsURL +} + +// GetStrict returns the Strict field. +func (r *RequiredStatusChecks) GetStrict() bool { + if r == nil { + return false + } + return r.Strict +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecks) GetURL() string { + if r == nil || r.URL == nil { + return "" + } + return *r.URL +} + +// GetParameters returns the Parameters field. +func (r *RequiredStatusChecksBranchRule) GetParameters() RequiredStatusChecksRuleParameters { + if r == nil { + return RequiredStatusChecksRuleParameters{} + } + return r.Parameters +} + +// GetFrom returns the From slice if it's non-nil, nil otherwise. +func (r *RequiredStatusChecksChanges) GetFrom() []string { + if r == nil || r.From == nil { + return nil + } + return r.From +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecksEnforcementLevelChanges) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetChecks returns the Checks slice if it's non-nil, nil otherwise. +func (r *RequiredStatusChecksRequest) GetChecks() []*RequiredStatusCheck { + if r == nil || r.Checks == nil { + return nil + } + return r.Checks +} + +// GetContexts returns the Contexts slice if it's non-nil, nil otherwise. +func (r *RequiredStatusChecksRequest) GetContexts() []string { + if r == nil || r.Contexts == nil { + return nil + } + return r.Contexts +} + +// GetStrict returns the Strict field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecksRequest) GetStrict() bool { + if r == nil || r.Strict == nil { + return false + } + return *r.Strict +} + +// GetDoNotEnforceOnCreate returns the DoNotEnforceOnCreate field if it's non-nil, zero value otherwise. +func (r *RequiredStatusChecksRuleParameters) GetDoNotEnforceOnCreate() bool { + if r == nil || r.DoNotEnforceOnCreate == nil { + return false + } + return *r.DoNotEnforceOnCreate +} + +// GetRequiredStatusChecks returns the RequiredStatusChecks slice if it's non-nil, nil otherwise. +func (r *RequiredStatusChecksRuleParameters) GetRequiredStatusChecks() []*RuleStatusCheck { + if r == nil || r.RequiredStatusChecks == nil { + return nil + } + return r.RequiredStatusChecks +} + +// GetStrictRequiredStatusChecksPolicy returns the StrictRequiredStatusChecksPolicy field. +func (r *RequiredStatusChecksRuleParameters) GetStrictRequiredStatusChecksPolicy() bool { + if r == nil { + return false + } + return r.StrictRequiredStatusChecksPolicy +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RequireLastPushApprovalChanges) GetFrom() bool { + if r == nil || r.From == nil { + return false + } + return *r.From +} + +// GetEnabled returns the Enabled field. +func (r *RequireLinearHistory) GetEnabled() bool { + if r == nil { + return false + } + return r.Enabled +} + +// GetAfter returns the After field. +func (r *Response) GetAfter() string { + if r == nil { + return "" + } + return r.After +} + +// GetBefore returns the Before field. +func (r *Response) GetBefore() string { + if r == nil { + return "" + } + return r.Before +} + +// GetCursor returns the Cursor field. +func (r *Response) GetCursor() string { + if r == nil { + return "" + } + return r.Cursor +} + +// GetFirstPage returns the FirstPage field. +func (r *Response) GetFirstPage() int { + if r == nil { + return 0 + } + return r.FirstPage +} + +// GetLastPage returns the LastPage field. +func (r *Response) GetLastPage() int { + if r == nil { + return 0 + } + return r.LastPage +} + +// GetNextPage returns the NextPage field. +func (r *Response) GetNextPage() int { + if r == nil { + return 0 + } + return r.NextPage +} + +// GetNextPageToken returns the NextPageToken field. +func (r *Response) GetNextPageToken() string { + if r == nil { + return "" + } + return r.NextPageToken +} + +// GetPrevPage returns the PrevPage field. +func (r *Response) GetPrevPage() int { + if r == nil { + return 0 + } + return r.PrevPage +} + +// GetRate returns the Rate field. +func (r *Response) GetRate() Rate { + if r == nil { + return Rate{} + } + return r.Rate +} + +// GetTokenExpiration returns the TokenExpiration field. +func (r *Response) GetTokenExpiration() Timestamp { + if r == nil { + return Timestamp{} + } + return r.TokenExpiration +} + +// GetComment returns the Comment field. +func (r *ReviewCustomDeploymentProtectionRuleRequest) GetComment() string { + if r == nil { + return "" + } + return r.Comment +} + +// GetEnvironmentName returns the EnvironmentName field. +func (r *ReviewCustomDeploymentProtectionRuleRequest) GetEnvironmentName() string { + if r == nil { + return "" + } + return r.EnvironmentName +} + +// GetState returns the State field. +func (r *ReviewCustomDeploymentProtectionRuleRequest) GetState() string { + if r == nil { + return "" + } + return r.State +} + +// GetTeams returns the Teams slice if it's non-nil, nil otherwise. +func (r *Reviewers) GetTeams() []*Team { + if r == nil || r.Teams == nil { + return nil + } + return r.Teams +} + +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (r *Reviewers) GetUsers() []*User { + if r == nil || r.Users == nil { + return nil + } + return r.Users +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *ReviewersRequest) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (r *ReviewersRequest) GetReviewers() []string { + if r == nil || r.Reviewers == nil { + return nil + } + return r.Reviewers +} + +// GetTeamReviewers returns the TeamReviewers slice if it's non-nil, nil otherwise. +func (r *ReviewersRequest) GetTeamReviewers() []string { + if r == nil || r.TeamReviewers == nil { + return nil + } + return r.TeamReviewers +} + +// GetAction returns the Action field. +func (r *ReviewPersonalAccessTokenRequestOptions) GetAction() string { + if r == nil { + return "" + } + return r.Action +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (r *ReviewPersonalAccessTokenRequestOptions) GetReason() string { + if r == nil || r.Reason == nil { + return "" + } + return *r.Reason +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (r *Rule) GetDescription() string { + if r == nil || r.Description == nil { + return "" + } + return *r.Description +} + +// GetFullDescription returns the FullDescription field if it's non-nil, zero value otherwise. +func (r *Rule) GetFullDescription() string { + if r == nil || r.FullDescription == nil { + return "" + } + return *r.FullDescription +} + +// GetHelp returns the Help field if it's non-nil, zero value otherwise. +func (r *Rule) GetHelp() string { + if r == nil || r.Help == nil { + return "" + } + return *r.Help +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *Rule) GetID() string { + if r == nil || r.ID == nil { + return "" + } + return *r.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *Rule) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetSecuritySeverityLevel returns the SecuritySeverityLevel field if it's non-nil, zero value otherwise. +func (r *Rule) GetSecuritySeverityLevel() string { + if r == nil || r.SecuritySeverityLevel == nil { + return "" + } + return *r.SecuritySeverityLevel +} + +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (r *Rule) GetSeverity() string { + if r == nil || r.Severity == nil { + return "" + } + return *r.Severity +} + +// GetTags returns the Tags slice if it's non-nil, nil otherwise. +func (r *Rule) GetTags() []string { + if r == nil || r.Tags == nil { + return nil + } + return r.Tags +} + +// GetAlertsThreshold returns the AlertsThreshold field. +func (r *RuleCodeScanningTool) GetAlertsThreshold() CodeScanningAlertsThreshold { + if r == nil { + return "" + } + return r.AlertsThreshold +} + +// GetSecurityAlertsThreshold returns the SecurityAlertsThreshold field. +func (r *RuleCodeScanningTool) GetSecurityAlertsThreshold() CodeScanningSecurityAlertsThreshold { + if r == nil { + return "" + } + return r.SecurityAlertsThreshold +} + +// GetTool returns the Tool field. +func (r *RuleCodeScanningTool) GetTool() string { + if r == nil { + return "" + } + return r.Tool +} + +// GetFilePatterns returns the FilePatterns slice if it's non-nil, nil otherwise. +func (r *RulesetRequiredReviewer) GetFilePatterns() []string { + if r == nil || r.FilePatterns == nil { + return nil + } + return r.FilePatterns +} + +// GetMinimumApprovals returns the MinimumApprovals field if it's non-nil, zero value otherwise. +func (r *RulesetRequiredReviewer) GetMinimumApprovals() int { + if r == nil || r.MinimumApprovals == nil { + return 0 + } + return *r.MinimumApprovals +} + +// GetReviewer returns the Reviewer field. +func (r *RulesetRequiredReviewer) GetReviewer() *RulesetReviewer { + if r == nil { + return nil + } + return r.Reviewer +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RulesetReviewer) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetType returns the Type field. +func (r *RulesetReviewer) GetType() *RulesetReviewerType { + if r == nil { + return nil + } + return r.Type +} + +// GetContext returns the Context field. +func (r *RuleStatusCheck) GetContext() string { + if r == nil { + return "" + } + return r.Context +} + +// GetIntegrationID returns the IntegrationID field if it's non-nil, zero value otherwise. +func (r *RuleStatusCheck) GetIntegrationID() int64 { + if r == nil || r.IntegrationID == nil { + return 0 + } + return *r.IntegrationID +} + +// GetPath returns the Path field. +func (r *RuleWorkflow) GetPath() string { + if r == nil { + return "" + } + return r.Path +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (r *RuleWorkflow) GetRef() string { + if r == nil || r.Ref == nil { + return "" + } + return *r.Ref +} + +// GetRepositoryID returns the RepositoryID field if it's non-nil, zero value otherwise. +func (r *RuleWorkflow) GetRepositoryID() int64 { + if r == nil || r.RepositoryID == nil { + return 0 + } + return *r.RepositoryID +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RuleWorkflow) GetSHA() string { + if r == nil || r.SHA == nil { + return "" + } + return *r.SHA +} + +// GetBusy returns the Busy field if it's non-nil, zero value otherwise. +func (r *Runner) GetBusy() bool { + if r == nil || r.Busy == nil { + return false + } + return *r.Busy +} + +// GetEphemeral returns the Ephemeral field if it's non-nil, zero value otherwise. +func (r *Runner) GetEphemeral() bool { + if r == nil || r.Ephemeral == nil { + return false + } + return *r.Ephemeral +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *Runner) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (r *Runner) GetLabels() []*RunnerLabels { + if r == nil || r.Labels == nil { + return nil + } + return r.Labels +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *Runner) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetOS returns the OS field if it's non-nil, zero value otherwise. +func (r *Runner) GetOS() string { + if r == nil || r.OS == nil { + return "" + } + return *r.OS +} + +// GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise. +func (r *Runner) GetRunnerGroupID() int64 { + if r == nil || r.RunnerGroupID == nil { + return 0 + } + return *r.RunnerGroupID +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (r *Runner) GetStatus() string { + if r == nil || r.Status == nil { + return "" + } + return *r.Status +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (r *Runner) GetVersion() string { + if r == nil || r.Version == nil { + return "" + } + return *r.Version +} + +// GetArchitecture returns the Architecture field if it's non-nil, zero value otherwise. +func (r *RunnerApplicationDownload) GetArchitecture() string { + if r == nil || r.Architecture == nil { + return "" + } + return *r.Architecture +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (r *RunnerApplicationDownload) GetDownloadURL() string { + if r == nil || r.DownloadURL == nil { + return "" + } + return *r.DownloadURL +} + +// GetFilename returns the Filename field if it's non-nil, zero value otherwise. +func (r *RunnerApplicationDownload) GetFilename() string { + if r == nil || r.Filename == nil { + return "" + } + return *r.Filename +} + +// GetOS returns the OS field if it's non-nil, zero value otherwise. +func (r *RunnerApplicationDownload) GetOS() string { + if r == nil || r.OS == nil { + return "" + } + return *r.OS +} + +// GetSHA256Checksum returns the SHA256Checksum field if it's non-nil, zero value otherwise. +func (r *RunnerApplicationDownload) GetSHA256Checksum() string { + if r == nil || r.SHA256Checksum == nil { + return "" + } + return *r.SHA256Checksum +} + +// GetTempDownloadToken returns the TempDownloadToken field if it's non-nil, zero value otherwise. +func (r *RunnerApplicationDownload) GetTempDownloadToken() string { + if r == nil || r.TempDownloadToken == nil { + return "" + } + return *r.TempDownloadToken +} + +// GetAllowsPublicRepositories returns the AllowsPublicRepositories field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetAllowsPublicRepositories() bool { + if r == nil || r.AllowsPublicRepositories == nil { + return false + } + return *r.AllowsPublicRepositories +} + +// GetDefault returns the Default field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetDefault() bool { + if r == nil || r.Default == nil { + return false + } + return *r.Default +} + +// GetHostedRunnersURL returns the HostedRunnersURL field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetHostedRunnersURL() string { + if r == nil || r.HostedRunnersURL == nil { + return "" + } + return *r.HostedRunnersURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetInherited returns the Inherited field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetInherited() bool { + if r == nil || r.Inherited == nil { + return false + } + return *r.Inherited +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetNetworkConfigurationID() string { + if r == nil || r.NetworkConfigurationID == nil { + return "" + } + return *r.NetworkConfigurationID +} + +// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetRestrictedToWorkflows() bool { + if r == nil || r.RestrictedToWorkflows == nil { + return false + } + return *r.RestrictedToWorkflows +} + +// GetRunnersURL returns the RunnersURL field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetRunnersURL() string { + if r == nil || r.RunnersURL == nil { + return "" + } + return *r.RunnersURL +} + +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetSelectedRepositoriesURL() string { + if r == nil || r.SelectedRepositoriesURL == nil { + return "" + } + return *r.SelectedRepositoriesURL +} + +// GetSelectedWorkflows returns the SelectedWorkflows slice if it's non-nil, nil otherwise. +func (r *RunnerGroup) GetSelectedWorkflows() []string { + if r == nil || r.SelectedWorkflows == nil { + return nil + } + return r.SelectedWorkflows +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetVisibility() string { + if r == nil || r.Visibility == nil { + return "" + } + return *r.Visibility +} + +// GetWorkflowRestrictionsReadOnly returns the WorkflowRestrictionsReadOnly field if it's non-nil, zero value otherwise. +func (r *RunnerGroup) GetWorkflowRestrictionsReadOnly() bool { + if r == nil || r.WorkflowRestrictionsReadOnly == nil { + return false + } + return *r.WorkflowRestrictionsReadOnly +} + +// GetRunnerGroups returns the RunnerGroups slice if it's non-nil, nil otherwise. +func (r *RunnerGroups) GetRunnerGroups() []*RunnerGroup { + if r == nil || r.RunnerGroups == nil { + return nil + } + return r.RunnerGroups +} + +// GetTotalCount returns the TotalCount field. +func (r *RunnerGroups) GetTotalCount() int { + if r == nil { + return 0 + } + return r.TotalCount +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RunnerLabels) GetID() int64 { + if r == nil || r.ID == nil { + return 0 + } + return *r.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RunnerLabels) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (r *RunnerLabels) GetType() string { + if r == nil || r.Type == nil { + return "" + } + return *r.Type +} + +// GetRunners returns the Runners slice if it's non-nil, nil otherwise. +func (r *Runners) GetRunners() []*Runner { + if r == nil || r.Runners == nil { + return nil + } + return r.Runners +} + +// GetTotalCount returns the TotalCount field. +func (r *Runners) GetTotalCount() int { + if r == nil { + return 0 + } + return r.TotalCount +} + +// GetCheckoutURI returns the CheckoutURI field if it's non-nil, zero value otherwise. +func (s *SarifAnalysis) GetCheckoutURI() string { + if s == nil || s.CheckoutURI == nil { + return "" + } + return *s.CheckoutURI +} + +// GetCommitSHA returns the CommitSHA field. +func (s *SarifAnalysis) GetCommitSHA() string { + if s == nil { + return "" + } + return s.CommitSHA +} + +// GetRef returns the Ref field. +func (s *SarifAnalysis) GetRef() string { + if s == nil { + return "" + } + return s.Ref +} + +// GetSarif returns the Sarif field. +func (s *SarifAnalysis) GetSarif() string { + if s == nil { + return "" + } + return s.Sarif +} + +// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. +func (s *SarifAnalysis) GetStartedAt() Timestamp { + if s == nil || s.StartedAt == nil { + return Timestamp{} + } + return *s.StartedAt +} + +// GetToolName returns the ToolName field if it's non-nil, zero value otherwise. +func (s *SarifAnalysis) GetToolName() string { + if s == nil || s.ToolName == nil { + return "" + } + return *s.ToolName +} + +// GetValidate returns the Validate field if it's non-nil, zero value otherwise. +func (s *SarifAnalysis) GetValidate() bool { + if s == nil || s.Validate == nil { + return false + } + return *s.Validate +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SarifID) GetID() string { + if s == nil || s.ID == nil { + return "" + } + return *s.ID +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SarifID) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + +// GetAnalysesURL returns the AnalysesURL field if it's non-nil, zero value otherwise. +func (s *SARIFUpload) GetAnalysesURL() string { + if s == nil || s.AnalysesURL == nil { + return "" + } + return *s.AnalysesURL +} + +// GetProcessingStatus returns the ProcessingStatus field if it's non-nil, zero value otherwise. +func (s *SARIFUpload) GetProcessingStatus() string { + if s == nil || s.ProcessingStatus == nil { + return "" + } + return *s.ProcessingStatus +} + +// GetSBOM returns the SBOM field. +func (s *SBOM) GetSBOM() *SBOMInfo { + if s == nil { + return nil + } + return s.SBOM +} + +// GetCreationInfo returns the CreationInfo field. +func (s *SBOMInfo) GetCreationInfo() *CreationInfo { + if s == nil { + return nil + } + return s.CreationInfo +} + +// GetDataLicense returns the DataLicense field if it's non-nil, zero value otherwise. +func (s *SBOMInfo) GetDataLicense() string { + if s == nil || s.DataLicense == nil { + return "" + } + return *s.DataLicense +} + +// GetDocumentDescribes returns the DocumentDescribes slice if it's non-nil, nil otherwise. +func (s *SBOMInfo) GetDocumentDescribes() []string { + if s == nil || s.DocumentDescribes == nil { + return nil + } + return s.DocumentDescribes +} + +// GetDocumentNamespace returns the DocumentNamespace field if it's non-nil, zero value otherwise. +func (s *SBOMInfo) GetDocumentNamespace() string { + if s == nil || s.DocumentNamespace == nil { + return "" + } + return *s.DocumentNamespace +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (s *SBOMInfo) GetName() string { + if s == nil || s.Name == nil { + return "" + } + return *s.Name +} + +// GetPackages returns the Packages slice if it's non-nil, nil otherwise. +func (s *SBOMInfo) GetPackages() []*RepoDependencies { + if s == nil || s.Packages == nil { + return nil + } + return s.Packages +} + +// GetRelationships returns the Relationships slice if it's non-nil, nil otherwise. +func (s *SBOMInfo) GetRelationships() []*SBOMRelationship { + if s == nil || s.Relationships == nil { + return nil + } + return s.Relationships +} + +// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. +func (s *SBOMInfo) GetSPDXID() string { + if s == nil || s.SPDXID == nil { + return "" + } + return *s.SPDXID +} + +// GetSPDXVersion returns the SPDXVersion field if it's non-nil, zero value otherwise. +func (s *SBOMInfo) GetSPDXVersion() string { + if s == nil || s.SPDXVersion == nil { + return "" + } + return *s.SPDXVersion +} + +// GetRelatedSPDXElement returns the RelatedSPDXElement field. +func (s *SBOMRelationship) GetRelatedSPDXElement() string { + if s == nil { + return "" + } + return s.RelatedSPDXElement +} + +// GetRelationshipType returns the RelationshipType field. +func (s *SBOMRelationship) GetRelationshipType() string { + if s == nil { + return "" + } + return s.RelationshipType +} + +// GetSPDXElementID returns the SPDXElementID field. +func (s *SBOMRelationship) GetSPDXElementID() string { + if s == nil { + return "" + } + return s.SPDXElementID +} + +// GetAnalysisKey returns the AnalysisKey field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetAnalysisKey() string { + if s == nil || s.AnalysisKey == nil { + return "" + } + return *s.AnalysisKey +} + +// GetCategory returns the Category field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetCategory() string { + if s == nil || s.Category == nil { + return "" + } + return *s.Category +} + +// GetCommitSHA returns the CommitSHA field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetCommitSHA() string { + if s == nil || s.CommitSHA == nil { + return "" + } + return *s.CommitSHA +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetDeletable returns the Deletable field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetDeletable() bool { + if s == nil || s.Deletable == nil { + return false + } + return *s.Deletable +} + +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetEnvironment() string { + if s == nil || s.Environment == nil { + return "" + } + return *s.Environment +} + +// GetError returns the Error field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetError() string { + if s == nil || s.Error == nil { + return "" + } + return *s.Error +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetID() int64 { + if s == nil || s.ID == nil { + return 0 + } + return *s.ID +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetRef() string { + if s == nil || s.Ref == nil { + return "" + } + return *s.Ref +} + +// GetResultsCount returns the ResultsCount field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetResultsCount() int { + if s == nil || s.ResultsCount == nil { + return 0 + } + return *s.ResultsCount +} + +// GetRulesCount returns the RulesCount field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetRulesCount() int { + if s == nil || s.RulesCount == nil { + return 0 + } + return *s.RulesCount +} + +// GetSarifID returns the SarifID field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetSarifID() string { + if s == nil || s.SarifID == nil { + return "" + } + return *s.SarifID +} + +// GetTool returns the Tool field. +func (s *ScanningAnalysis) GetTool() *Tool { + if s == nil { + return nil + } + return s.Tool +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + +// GetWarning returns the Warning field if it's non-nil, zero value otherwise. +func (s *ScanningAnalysis) GetWarning() string { + if s == nil || s.Warning == nil { + return "" + } + return *s.Warning +} + +// GetOperations returns the Operations slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseAttribute) GetOperations() []*SCIMEnterpriseAttributeOperation { + if s == nil || s.Operations == nil { + return nil + } + return s.Operations +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseAttribute) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetOp returns the Op field. +func (s *SCIMEnterpriseAttributeOperation) GetOp() string { + if s == nil { + return "" + } + return s.Op +} + +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseAttributeOperation) GetPath() string { + if s == nil || s.Path == nil { + return "" + } + return *s.Path +} + +// GetValue returns the Value field. +func (s *SCIMEnterpriseAttributeOperation) GetValue() any { + if s == nil { + return nil + } + return s.Value +} + +// GetDisplay returns the Display field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseDisplayReference) GetDisplay() string { + if s == nil || s.Display == nil { + return "" + } + return *s.Display +} + +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseDisplayReference) GetRef() string { + if s == nil || s.Ref == nil { + return "" + } + return *s.Ref +} + +// GetValue returns the Value field. +func (s *SCIMEnterpriseDisplayReference) GetValue() string { + if s == nil { + return "" + } + return s.Value +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseGroupAttributes) GetDisplayName() string { + if s == nil || s.DisplayName == nil { + return "" + } + return *s.DisplayName +} + +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseGroupAttributes) GetExternalID() string { + if s == nil || s.ExternalID == nil { + return "" + } + return *s.ExternalID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseGroupAttributes) GetID() string { + if s == nil || s.ID == nil { + return "" + } + return *s.ID +} + +// GetMembers returns the Members slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseGroupAttributes) GetMembers() []*SCIMEnterpriseDisplayReference { + if s == nil || s.Members == nil { + return nil + } + return s.Members +} + +// GetMeta returns the Meta field. +func (s *SCIMEnterpriseGroupAttributes) GetMeta() *SCIMEnterpriseMeta { + if s == nil { + return nil + } + return s.Meta +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseGroupAttributes) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetItemsPerPage returns the ItemsPerPage field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseGroups) GetItemsPerPage() int { + if s == nil || s.ItemsPerPage == nil { + return 0 + } + return *s.ItemsPerPage +} + +// GetResources returns the Resources slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseGroups) GetResources() []*SCIMEnterpriseGroupAttributes { + if s == nil || s.Resources == nil { + return nil + } + return s.Resources +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseGroups) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseGroups) GetStartIndex() int { + if s == nil || s.StartIndex == nil { + return 0 + } + return *s.StartIndex +} + +// GetTotalResults returns the TotalResults field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseGroups) GetTotalResults() int { + if s == nil || s.TotalResults == nil { + return 0 + } + return *s.TotalResults +} + +// GetCreated returns the Created field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseMeta) GetCreated() Timestamp { + if s == nil || s.Created == nil { + return Timestamp{} + } + return *s.Created +} + +// GetLastModified returns the LastModified field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseMeta) GetLastModified() Timestamp { + if s == nil || s.LastModified == nil { + return Timestamp{} + } + return *s.LastModified +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseMeta) GetLocation() string { + if s == nil || s.Location == nil { + return "" + } + return *s.Location +} + +// GetResourceType returns the ResourceType field. +func (s *SCIMEnterpriseMeta) GetResourceType() string { + if s == nil { + return "" + } + return s.ResourceType +} + +// GetActive returns the Active field. +func (s *SCIMEnterpriseUserAttributes) GetActive() bool { + if s == nil { + return false + } + return s.Active +} + +// GetDisplayName returns the DisplayName field. +func (s *SCIMEnterpriseUserAttributes) GetDisplayName() string { + if s == nil { + return "" + } + return s.DisplayName +} + +// GetEmails returns the Emails slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseUserAttributes) GetEmails() []*SCIMEnterpriseUserEmail { + if s == nil || s.Emails == nil { + return nil + } + return s.Emails +} + +// GetExternalID returns the ExternalID field. +func (s *SCIMEnterpriseUserAttributes) GetExternalID() string { + if s == nil { + return "" + } + return s.ExternalID +} + +// GetGroups returns the Groups slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseUserAttributes) GetGroups() []*SCIMEnterpriseDisplayReference { + if s == nil || s.Groups == nil { + return nil + } + return s.Groups +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUserAttributes) GetID() string { + if s == nil || s.ID == nil { + return "" + } + return *s.ID +} + +// GetMeta returns the Meta field. +func (s *SCIMEnterpriseUserAttributes) GetMeta() *SCIMEnterpriseMeta { + if s == nil { + return nil + } + return s.Meta +} + +// GetName returns the Name field. +func (s *SCIMEnterpriseUserAttributes) GetName() *SCIMEnterpriseUserName { + if s == nil { + return nil + } + return s.Name +} + +// GetRoles returns the Roles slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseUserAttributes) GetRoles() []*SCIMEnterpriseUserRole { + if s == nil || s.Roles == nil { + return nil + } + return s.Roles +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseUserAttributes) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetUserName returns the UserName field. +func (s *SCIMEnterpriseUserAttributes) GetUserName() string { + if s == nil { + return "" + } + return s.UserName +} + +// GetPrimary returns the Primary field. +func (s *SCIMEnterpriseUserEmail) GetPrimary() bool { + if s == nil { + return false + } + return s.Primary +} + +// GetType returns the Type field. +func (s *SCIMEnterpriseUserEmail) GetType() string { + if s == nil { + return "" + } + return s.Type +} + +// GetValue returns the Value field. +func (s *SCIMEnterpriseUserEmail) GetValue() string { + if s == nil { + return "" + } + return s.Value +} + +// GetFamilyName returns the FamilyName field. +func (s *SCIMEnterpriseUserName) GetFamilyName() string { + if s == nil { + return "" + } + return s.FamilyName +} + +// GetFormatted returns the Formatted field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUserName) GetFormatted() string { + if s == nil || s.Formatted == nil { + return "" + } + return *s.Formatted +} + +// GetGivenName returns the GivenName field. +func (s *SCIMEnterpriseUserName) GetGivenName() string { + if s == nil { + return "" + } + return s.GivenName +} + +// GetMiddleName returns the MiddleName field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUserName) GetMiddleName() string { + if s == nil || s.MiddleName == nil { + return "" + } + return *s.MiddleName +} + +// GetDisplay returns the Display field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUserRole) GetDisplay() string { + if s == nil || s.Display == nil { + return "" + } + return *s.Display +} + +// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUserRole) GetPrimary() bool { + if s == nil || s.Primary == nil { + return false + } + return *s.Primary +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUserRole) GetType() string { + if s == nil || s.Type == nil { + return "" + } + return *s.Type +} + +// GetValue returns the Value field. +func (s *SCIMEnterpriseUserRole) GetValue() string { + if s == nil { + return "" + } + return s.Value +} + +// GetItemsPerPage returns the ItemsPerPage field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUsers) GetItemsPerPage() int { + if s == nil || s.ItemsPerPage == nil { + return 0 + } + return *s.ItemsPerPage +} + +// GetResources returns the Resources slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseUsers) GetResources() []*SCIMEnterpriseUserAttributes { + if s == nil || s.Resources == nil { + return nil + } + return s.Resources +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMEnterpriseUsers) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUsers) GetStartIndex() int { + if s == nil || s.StartIndex == nil { + return 0 + } + return *s.StartIndex +} + +// GetTotalResults returns the TotalResults field if it's non-nil, zero value otherwise. +func (s *SCIMEnterpriseUsers) GetTotalResults() int { + if s == nil || s.TotalResults == nil { + return 0 + } + return *s.TotalResults +} + +// GetCreated returns the Created field if it's non-nil, zero value otherwise. +func (s *SCIMMeta) GetCreated() Timestamp { + if s == nil || s.Created == nil { + return Timestamp{} + } + return *s.Created +} + +// GetLastModified returns the LastModified field if it's non-nil, zero value otherwise. +func (s *SCIMMeta) GetLastModified() Timestamp { + if s == nil || s.LastModified == nil { + return Timestamp{} + } + return *s.LastModified +} + +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (s *SCIMMeta) GetLocation() string { + if s == nil || s.Location == nil { + return "" + } + return *s.Location +} + +// GetResourceType returns the ResourceType field if it's non-nil, zero value otherwise. +func (s *SCIMMeta) GetResourceType() string { + if s == nil || s.ResourceType == nil { + return "" + } + return *s.ResourceType +} + +// GetItemsPerPage returns the ItemsPerPage field if it's non-nil, zero value otherwise. +func (s *SCIMProvisionedIdentities) GetItemsPerPage() int { + if s == nil || s.ItemsPerPage == nil { + return 0 + } + return *s.ItemsPerPage +} + +// GetResources returns the Resources slice if it's non-nil, nil otherwise. +func (s *SCIMProvisionedIdentities) GetResources() []*SCIMUserAttributes { + if s == nil || s.Resources == nil { + return nil + } + return s.Resources +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMProvisionedIdentities) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (s *SCIMProvisionedIdentities) GetStartIndex() int { + if s == nil || s.StartIndex == nil { + return 0 + } + return *s.StartIndex +} + +// GetTotalResults returns the TotalResults field if it's non-nil, zero value otherwise. +func (s *SCIMProvisionedIdentities) GetTotalResults() int { + if s == nil || s.TotalResults == nil { + return 0 + } + return *s.TotalResults +} + +// GetActive returns the Active field if it's non-nil, zero value otherwise. +func (s *SCIMUserAttributes) GetActive() bool { + if s == nil || s.Active == nil { + return false + } + return *s.Active +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (s *SCIMUserAttributes) GetDisplayName() string { + if s == nil || s.DisplayName == nil { + return "" + } + return *s.DisplayName +} + +// GetEmails returns the Emails slice if it's non-nil, nil otherwise. +func (s *SCIMUserAttributes) GetEmails() []*SCIMUserEmail { + if s == nil || s.Emails == nil { + return nil + } + return s.Emails +} + +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (s *SCIMUserAttributes) GetExternalID() string { + if s == nil || s.ExternalID == nil { + return "" + } + return *s.ExternalID +} + +// GetGroups returns the Groups slice if it's non-nil, nil otherwise. +func (s *SCIMUserAttributes) GetGroups() []string { + if s == nil || s.Groups == nil { + return nil + } + return s.Groups +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SCIMUserAttributes) GetID() string { + if s == nil || s.ID == nil { + return "" + } + return *s.ID +} + +// GetMeta returns the Meta field. +func (s *SCIMUserAttributes) GetMeta() *SCIMMeta { + if s == nil { + return nil + } + return s.Meta +} + +// GetName returns the Name field. +func (s *SCIMUserAttributes) GetName() SCIMUserName { + if s == nil { + return SCIMUserName{} + } + return s.Name +} + +// GetRoles returns the Roles slice if it's non-nil, nil otherwise. +func (s *SCIMUserAttributes) GetRoles() []*SCIMUserRole { + if s == nil || s.Roles == nil { + return nil + } + return s.Roles +} + +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (s *SCIMUserAttributes) GetSchemas() []string { + if s == nil || s.Schemas == nil { + return nil + } + return s.Schemas +} + +// GetUserName returns the UserName field. +func (s *SCIMUserAttributes) GetUserName() string { + if s == nil { + return "" + } + return s.UserName +} + +// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. +func (s *SCIMUserEmail) GetPrimary() bool { + if s == nil || s.Primary == nil { + return false + } + return *s.Primary +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (s *SCIMUserEmail) GetType() string { + if s == nil || s.Type == nil { + return "" + } + return *s.Type +} + +// GetValue returns the Value field. +func (s *SCIMUserEmail) GetValue() string { + if s == nil { + return "" + } + return s.Value +} + +// GetFamilyName returns the FamilyName field. +func (s *SCIMUserName) GetFamilyName() string { + if s == nil { + return "" + } + return s.FamilyName +} + +// GetFormatted returns the Formatted field if it's non-nil, zero value otherwise. +func (s *SCIMUserName) GetFormatted() string { + if s == nil || s.Formatted == nil { + return "" + } + return *s.Formatted +} + +// GetGivenName returns the GivenName field. +func (s *SCIMUserName) GetGivenName() string { + if s == nil { + return "" + } + return s.GivenName +} + +// GetDisplay returns the Display field if it's non-nil, zero value otherwise. +func (s *SCIMUserRole) GetDisplay() string { + if s == nil || s.Display == nil { + return "" + } + return *s.Display +} + +// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. +func (s *SCIMUserRole) GetPrimary() bool { + if s == nil || s.Primary == nil { + return false + } + return *s.Primary +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (s *SCIMUserRole) GetType() string { + if s == nil || s.Type == nil { + return "" + } + return *s.Type +} + +// GetValue returns the Value field. +func (s *SCIMUserRole) GetValue() string { + if s == nil { + return "" + } + return s.Value +} + +// GetAdvancedSearch returns the AdvancedSearch field if it's non-nil, zero value otherwise. +func (s *SearchOptions) GetAdvancedSearch() bool { + if s == nil || s.AdvancedSearch == nil { + return false + } + return *s.AdvancedSearch +} + +// GetOrder returns the Order field. +func (s *SearchOptions) GetOrder() string { + if s == nil { + return "" + } + return s.Order +} + +// GetSearchType returns the SearchType field. +func (s *SearchOptions) GetSearchType() string { + if s == nil { + return "" + } + return s.SearchType +} + +// GetSort returns the Sort field. +func (s *SearchOptions) GetSort() string { + if s == nil { + return "" + } + return s.Sort +} + +// GetTextMatch returns the TextMatch field. +func (s *SearchOptions) GetTextMatch() bool { + if s == nil { + return false + } + return s.TextMatch +} + +// GetSeatsCreated returns the SeatsCreated field. +func (s *SeatAssignments) GetSeatsCreated() int { + if s == nil { + return 0 + } + return s.SeatsCreated +} + +// GetSeatsCancelled returns the SeatsCancelled field. +func (s *SeatCancellations) GetSeatsCancelled() int { + if s == nil { + return 0 + } + return s.SeatsCancelled +} + +// GetCreatedAt returns the CreatedAt field. +func (s *Secret) GetCreatedAt() Timestamp { + if s == nil { + return Timestamp{} + } + return s.CreatedAt +} + +// GetName returns the Name field. +func (s *Secret) GetName() string { + if s == nil { + return "" + } + return s.Name +} + +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field. +func (s *Secret) GetSelectedRepositoriesURL() string { + if s == nil { + return "" + } + return s.SelectedRepositoriesURL +} + +// GetUpdatedAt returns the UpdatedAt field. +func (s *Secret) GetUpdatedAt() Timestamp { + if s == nil { + return Timestamp{} + } + return s.UpdatedAt +} + +// GetVisibility returns the Visibility field. +func (s *Secret) GetVisibility() string { + if s == nil { + return "" + } + return s.Visibility +} + +// GetEncryptedValue returns the EncryptedValue field. +func (s *SecretOrgRequest) GetEncryptedValue() string { + if s == nil { + return "" + } + return s.EncryptedValue +} + +// GetKeyID returns the KeyID field. +func (s *SecretOrgRequest) GetKeyID() string { + if s == nil { + return "" + } + return s.KeyID +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (s *SecretOrgRequest) GetSelectedRepositoryIDs() []int64 { + if s == nil || s.SelectedRepositoryIDs == nil { + return nil + } + return s.SelectedRepositoryIDs +} + +// GetVisibility returns the Visibility field. +func (s *SecretOrgRequest) GetVisibility() string { + if s == nil { + return "" + } + return s.Visibility +} + +// GetEncryptedValue returns the EncryptedValue field. +func (s *SecretRequest) GetEncryptedValue() string { + if s == nil { + return "" + } + return s.EncryptedValue +} + +// GetKeyID returns the KeyID field. +func (s *SecretRequest) GetKeyID() string { + if s == nil { + return "" + } + return s.KeyID +} + +// GetSecrets returns the Secrets slice if it's non-nil, nil otherwise. +func (s *Secrets) GetSecrets() []*Secret { + if s == nil || s.Secrets == nil { + return nil + } + return s.Secrets +} + +// GetTotalCount returns the TotalCount field. +func (s *Secrets) GetTotalCount() int { + if s == nil { + return 0 + } + return s.TotalCount +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SecretScanning) GetStatus() string { + if s == nil || s.Status == nil { + return "" + } + return *s.Status +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetFirstLocationDetected returns the FirstLocationDetected field. +func (s *SecretScanningAlert) GetFirstLocationDetected() *SecretScanningAlertLocationDetails { + if s == nil { + return nil + } + return s.FirstLocationDetected +} + +// GetHasMoreLocations returns the HasMoreLocations field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetHasMoreLocations() bool { + if s == nil || s.HasMoreLocations == nil { + return false + } + return *s.HasMoreLocations +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetHTMLURL() string { + if s == nil || s.HTMLURL == nil { + return "" + } + return *s.HTMLURL +} + +// GetIsBase64Encoded returns the IsBase64Encoded field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetIsBase64Encoded() bool { + if s == nil || s.IsBase64Encoded == nil { + return false + } + return *s.IsBase64Encoded +} + +// GetLocationsURL returns the LocationsURL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetLocationsURL() string { + if s == nil || s.LocationsURL == nil { + return "" + } + return *s.LocationsURL +} + +// GetMultiRepo returns the MultiRepo field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetMultiRepo() bool { + if s == nil || s.MultiRepo == nil { + return false + } + return *s.MultiRepo +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetNumber() int { + if s == nil || s.Number == nil { + return 0 + } + return *s.Number +} + +// GetPubliclyLeaked returns the PubliclyLeaked field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetPubliclyLeaked() bool { + if s == nil || s.PubliclyLeaked == nil { + return false + } + return *s.PubliclyLeaked +} + +// GetPushProtectionBypassed returns the PushProtectionBypassed field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetPushProtectionBypassed() bool { + if s == nil || s.PushProtectionBypassed == nil { + return false + } + return *s.PushProtectionBypassed +} + +// GetPushProtectionBypassedAt returns the PushProtectionBypassedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetPushProtectionBypassedAt() Timestamp { + if s == nil || s.PushProtectionBypassedAt == nil { + return Timestamp{} + } + return *s.PushProtectionBypassedAt +} + +// GetPushProtectionBypassedBy returns the PushProtectionBypassedBy field. +func (s *SecretScanningAlert) GetPushProtectionBypassedBy() *User { + if s == nil { + return nil + } + return s.PushProtectionBypassedBy +} + +// GetPushProtectionBypassRequestComment returns the PushProtectionBypassRequestComment field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetPushProtectionBypassRequestComment() string { + if s == nil || s.PushProtectionBypassRequestComment == nil { + return "" + } + return *s.PushProtectionBypassRequestComment +} + +// GetPushProtectionBypassRequestHTMLURL returns the PushProtectionBypassRequestHTMLURL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetPushProtectionBypassRequestHTMLURL() string { + if s == nil || s.PushProtectionBypassRequestHTMLURL == nil { + return "" + } + return *s.PushProtectionBypassRequestHTMLURL +} + +// GetPushProtectionBypassRequestReviewer returns the PushProtectionBypassRequestReviewer field. +func (s *SecretScanningAlert) GetPushProtectionBypassRequestReviewer() *User { + if s == nil { + return nil + } + return s.PushProtectionBypassRequestReviewer +} + +// GetPushProtectionBypassRequestReviewerComment returns the PushProtectionBypassRequestReviewerComment field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetPushProtectionBypassRequestReviewerComment() string { + if s == nil || s.PushProtectionBypassRequestReviewerComment == nil { + return "" + } + return *s.PushProtectionBypassRequestReviewerComment +} + +// GetRepository returns the Repository field. +func (s *SecretScanningAlert) GetRepository() *Repository { + if s == nil { + return nil + } + return s.Repository +} + +// GetResolution returns the Resolution field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetResolution() string { + if s == nil || s.Resolution == nil { + return "" + } + return *s.Resolution +} + +// GetResolutionComment returns the ResolutionComment field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetResolutionComment() string { + if s == nil || s.ResolutionComment == nil { + return "" + } + return *s.ResolutionComment +} + +// GetResolvedAt returns the ResolvedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetResolvedAt() Timestamp { + if s == nil || s.ResolvedAt == nil { + return Timestamp{} + } + return *s.ResolvedAt +} + +// GetResolvedBy returns the ResolvedBy field. +func (s *SecretScanningAlert) GetResolvedBy() *User { + if s == nil { + return nil + } + return s.ResolvedBy +} + +// GetSecret returns the Secret field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetSecret() string { + if s == nil || s.Secret == nil { + return "" + } + return *s.Secret +} + +// GetSecretType returns the SecretType field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetSecretType() string { + if s == nil || s.SecretType == nil { + return "" + } + return *s.SecretType +} + +// GetSecretTypeDisplayName returns the SecretTypeDisplayName field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetSecretTypeDisplayName() string { + if s == nil || s.SecretTypeDisplayName == nil { + return "" + } + return *s.SecretTypeDisplayName +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetState() string { + if s == nil || s.State == nil { + return "" + } + return *s.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetUpdatedAt() Timestamp { + if s == nil || s.UpdatedAt == nil { + return Timestamp{} + } + return *s.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + +// GetValidity returns the Validity field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlert) GetValidity() string { + if s == nil || s.Validity == nil { + return "" + } + return *s.Validity +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertEvent) GetAction() string { + if s == nil || s.Action == nil { + return "" + } + return *s.Action +} + +// GetAlert returns the Alert field. +func (s *SecretScanningAlertEvent) GetAlert() *SecretScanningAlert { + if s == nil { + return nil + } + return s.Alert +} + +// GetEnterprise returns the Enterprise field. +func (s *SecretScanningAlertEvent) GetEnterprise() *Enterprise { + if s == nil { + return nil + } + return s.Enterprise +} + +// GetInstallation returns the Installation field. +func (s *SecretScanningAlertEvent) GetInstallation() *Installation { + if s == nil { + return nil + } + return s.Installation +} + +// GetOrganization returns the Organization field. +func (s *SecretScanningAlertEvent) GetOrganization() *Organization { + if s == nil { + return nil + } + return s.Organization +} + +// GetRepo returns the Repo field. +func (s *SecretScanningAlertEvent) GetRepo() *Repository { + if s == nil { + return nil + } + return s.Repo } // GetSender returns the Sender field. -func (p *PageBuildEvent) GetSender() *User { - if p == nil { +func (s *SecretScanningAlertEvent) GetSender() *User { + if s == nil { return nil } - return p.Sender + return s.Sender +} + +// GetDirection returns the Direction field. +func (s *SecretScanningAlertListOptions) GetDirection() string { + if s == nil { + return "" + } + return s.Direction +} + +// GetIsMultiRepo returns the IsMultiRepo field. +func (s *SecretScanningAlertListOptions) GetIsMultiRepo() bool { + if s == nil { + return false + } + return s.IsMultiRepo +} + +// GetIsPubliclyLeaked returns the IsPubliclyLeaked field. +func (s *SecretScanningAlertListOptions) GetIsPubliclyLeaked() bool { + if s == nil { + return false + } + return s.IsPubliclyLeaked +} + +// GetResolution returns the Resolution field. +func (s *SecretScanningAlertListOptions) GetResolution() string { + if s == nil { + return "" + } + return s.Resolution } -// GetCNAME returns the CNAME field if it's non-nil, zero value otherwise. -func (p *Pages) GetCNAME() string { - if p == nil || p.CNAME == nil { +// GetSecretType returns the SecretType field. +func (s *SecretScanningAlertListOptions) GetSecretType() string { + if s == nil { return "" } - return *p.CNAME + return s.SecretType } -// GetCustom404 returns the Custom404 field if it's non-nil, zero value otherwise. -func (p *Pages) GetCustom404() bool { - if p == nil || p.Custom404 == nil { - return false +// GetSort returns the Sort field. +func (s *SecretScanningAlertListOptions) GetSort() string { + if s == nil { + return "" } - return *p.Custom404 + return s.Sort } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *Pages) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { +// GetState returns the State field. +func (s *SecretScanningAlertListOptions) GetState() string { + if s == nil { return "" } - return *p.HTMLURL + return s.State } -// GetSource returns the Source field. -func (p *Pages) GetSource() *PagesSource { - if p == nil { +// GetValidity returns the Validity field. +func (s *SecretScanningAlertListOptions) GetValidity() string { + if s == nil { + return "" + } + return s.Validity +} + +// GetDetails returns the Details field. +func (s *SecretScanningAlertLocation) GetDetails() *SecretScanningAlertLocationDetails { + if s == nil { return nil } - return p.Source + return s.Details } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (p *Pages) GetStatus() string { - if p == nil || p.Status == nil { +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocation) GetType() string { + if s == nil || s.Type == nil { return "" } - return *p.Status + return *s.Type } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *Pages) GetURL() string { - if p == nil || p.URL == nil { +// GetBlobSHA returns the BlobSHA field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetBlobSHA() string { + if s == nil || s.BlobSHA == nil { return "" } - return *p.URL + return *s.BlobSHA } -// GetCommit returns the Commit field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetCommit() string { - if p == nil || p.Commit == nil { +// GetBlobURL returns the BlobURL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetBlobURL() string { + if s == nil || s.BlobURL == nil { return "" } - return *p.Commit + return *s.BlobURL } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} +// GetCommitSHA returns the CommitSHA field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetCommitSHA() string { + if s == nil || s.CommitSHA == nil { + return "" } - return *p.CreatedAt + return *s.CommitSHA } -// GetDuration returns the Duration field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetDuration() int { - if p == nil || p.Duration == nil { +// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetCommitURL() string { + if s == nil || s.CommitURL == nil { + return "" + } + return *s.CommitURL +} + +// GetEndColumn returns the EndColumn field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetEndColumn() int { + if s == nil || s.EndColumn == nil { return 0 } - return *p.Duration + return *s.EndColumn } -// GetError returns the Error field. -func (p *PagesBuild) GetError() *PagesError { - if p == nil { - return nil +// GetEndLine returns the EndLine field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetEndLine() int { + if s == nil || s.EndLine == nil { + return 0 } - return p.Error + return *s.EndLine } -// GetPusher returns the Pusher field. -func (p *PagesBuild) GetPusher() *User { - if p == nil { - return nil +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetPath() string { + if s == nil || s.Path == nil { + return "" } - return p.Pusher + return *s.Path } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetStatus() string { - if p == nil || p.Status == nil { +// GetPullRequestCommentURL returns the PullRequestCommentURL field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetPullRequestCommentURL() string { + if s == nil || s.PullRequestCommentURL == nil { return "" } - return *p.Status + return *s.PullRequestCommentURL } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} +// GetStartColumn returns the StartColumn field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetStartColumn() int { + if s == nil || s.StartColumn == nil { + return 0 } - return *p.UpdatedAt + return *s.StartColumn } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetURL() string { - if p == nil || p.URL == nil { - return "" +// GetStartline returns the Startline field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationDetails) GetStartline() int { + if s == nil || s.Startline == nil { + return 0 } - return *p.URL + return *s.Startline } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PagesError) GetMessage() string { - if p == nil || p.Message == nil { +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertLocationEvent) GetAction() string { + if s == nil || s.Action == nil { return "" } - return *p.Message + return *s.Action } -// GetBranch returns the Branch field if it's non-nil, zero value otherwise. -func (p *PagesSource) GetBranch() string { - if p == nil || p.Branch == nil { - return "" +// GetAlert returns the Alert field. +func (s *SecretScanningAlertLocationEvent) GetAlert() *SecretScanningAlert { + if s == nil { + return nil } - return *p.Branch + return s.Alert } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (p *PagesSource) GetPath() string { - if p == nil || p.Path == nil { - return "" +// GetInstallation returns the Installation field. +func (s *SecretScanningAlertLocationEvent) GetInstallation() *Installation { + if s == nil { + return nil } - return *p.Path + return s.Installation } -// GetTotalPages returns the TotalPages field if it's non-nil, zero value otherwise. -func (p *PageStats) GetTotalPages() int { - if p == nil || p.TotalPages == nil { - return 0 +// GetLocation returns the Location field. +func (s *SecretScanningAlertLocationEvent) GetLocation() *SecretScanningAlertLocation { + if s == nil { + return nil } - return *p.TotalPages + return s.Location } -// GetHook returns the Hook field. -func (p *PingEvent) GetHook() *Hook { - if p == nil { +// GetOrganization returns the Organization field. +func (s *SecretScanningAlertLocationEvent) GetOrganization() *Organization { + if s == nil { return nil } - return p.Hook + return s.Organization } -// GetHookID returns the HookID field if it's non-nil, zero value otherwise. -func (p *PingEvent) GetHookID() int64 { - if p == nil || p.HookID == nil { - return 0 +// GetRepo returns the Repo field. +func (s *SecretScanningAlertLocationEvent) GetRepo() *Repository { + if s == nil { + return nil } - return *p.HookID + return s.Repo } -// GetInstallation returns the Installation field. -func (p *PingEvent) GetInstallation() *Installation { - if p == nil { +// GetSender returns the Sender field. +func (s *SecretScanningAlertLocationEvent) GetSender() *User { + if s == nil { return nil } - return p.Installation + return s.Sender } -// GetZen returns the Zen field if it's non-nil, zero value otherwise. -func (p *PingEvent) GetZen() string { - if p == nil || p.Zen == nil { +// GetResolution returns the Resolution field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertUpdateOptions) GetResolution() string { + if s == nil || s.Resolution == nil { return "" } - return *p.Zen + return *s.Resolution } -// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. -func (p *Plan) GetCollaborators() int { - if p == nil || p.Collaborators == nil { - return 0 +// GetResolutionComment returns the ResolutionComment field if it's non-nil, zero value otherwise. +func (s *SecretScanningAlertUpdateOptions) GetResolutionComment() string { + if s == nil || s.ResolutionComment == nil { + return "" } - return *p.Collaborators + return *s.ResolutionComment } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *Plan) GetName() string { - if p == nil || p.Name == nil { +// GetState returns the State field. +func (s *SecretScanningAlertUpdateOptions) GetState() string { + if s == nil { return "" } - return *p.Name + return s.State } -// GetPrivateRepos returns the PrivateRepos field if it's non-nil, zero value otherwise. -func (p *Plan) GetPrivateRepos() int { - if p == nil || p.PrivateRepos == nil { - return 0 +// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningCreateCustomPatternsRequest) GetPatterns() []*SecretScanningCustomPatternRequest { + if s == nil || s.Patterns == nil { + return nil } - return *p.PrivateRepos + return s.Patterns } -// GetSpace returns the Space field if it's non-nil, zero value otherwise. -func (p *Plan) GetSpace() int { - if p == nil || p.Space == nil { - return 0 +// GetCreatedPatterns returns the CreatedPatterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningCreateCustomPatternsResponse) GetCreatedPatterns() []*SecretScanningCustomPattern { + if s == nil || s.CreatedPatterns == nil { + return nil } - return *p.Space + return s.CreatedPatterns } -// GetConfigURL returns the ConfigURL field if it's non-nil, zero value otherwise. -func (p *PreReceiveHook) GetConfigURL() string { - if p == nil || p.ConfigURL == nil { +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} + } + return *s.CreatedAt +} + +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { return "" } - return *p.ConfigURL + return *s.CustomPatternVersion } -// GetEnforcement returns the Enforcement field if it's non-nil, zero value otherwise. -func (p *PreReceiveHook) GetEnforcement() string { - if p == nil || p.Enforcement == nil { +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { return "" } - return *p.Enforcement + return *s.EndDelimiter } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PreReceiveHook) GetID() int64 { - if p == nil || p.ID == nil { +// GetID returns the ID field. +func (s *SecretScanningCustomPattern) GetID() int64 { + if s == nil { return 0 } - return *p.ID + return s.ID } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *PreReceiveHook) GetName() string { - if p == nil || p.Name == nil { +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPattern) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { + return nil + } + return s.MustMatch +} + +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPattern) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { + return nil + } + return s.MustNotMatch +} + +// GetName returns the Name field. +func (s *SecretScanningCustomPattern) GetName() string { + if s == nil { return "" } - return *p.Name + return s.Name } -// GetHRef returns the HRef field if it's non-nil, zero value otherwise. -func (p *PRLink) GetHRef() string { - if p == nil || p.HRef == nil { +// GetPattern returns the Pattern field. +func (s *SecretScanningCustomPattern) GetPattern() string { + if s == nil { return "" } - return *p.HRef + return s.Pattern } -// GetComments returns the Comments field. -func (p *PRLinks) GetComments() *PRLink { - if p == nil { - return nil +// GetPushProtectionEnabled returns the PushProtectionEnabled field. +func (s *SecretScanningCustomPattern) GetPushProtectionEnabled() bool { + if s == nil { + return false } - return p.Comments + return s.PushProtectionEnabled } -// GetCommits returns the Commits field. -func (p *PRLinks) GetCommits() *PRLink { - if p == nil { - return nil +// GetSlug returns the Slug field. +func (s *SecretScanningCustomPattern) GetSlug() string { + if s == nil { + return "" } - return p.Commits + return s.Slug } -// GetHTML returns the HTML field. -func (p *PRLinks) GetHTML() *PRLink { - if p == nil { - return nil +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { + return "" + } + return *s.StartDelimiter +} + +// GetState returns the State field. +func (s *SecretScanningCustomPattern) GetState() string { + if s == nil { + return "" + } + return s.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPattern) GetUpdatedAt() Timestamp { + if s == nil || s.UpdatedAt == nil { + return Timestamp{} + } + return *s.UpdatedAt +} + +// GetDirection returns the Direction field. +func (s *SecretScanningCustomPatternListOptions) GetDirection() string { + if s == nil { + return "" + } + return s.Direction +} + +// GetPushProtection returns the PushProtection field. +func (s *SecretScanningCustomPatternListOptions) GetPushProtection() string { + if s == nil { + return "" + } + return s.PushProtection +} + +// GetSort returns the Sort field. +func (s *SecretScanningCustomPatternListOptions) GetSort() string { + if s == nil { + return "" + } + return s.Sort +} + +// GetState returns the State field. +func (s *SecretScanningCustomPatternListOptions) GetState() string { + if s == nil { + return "" } - return p.HTML + return s.State } -// GetIssue returns the Issue field. -func (p *PRLinks) GetIssue() *PRLink { - if p == nil { - return nil +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternRequest) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { + return "" } - return p.Issue + return *s.EndDelimiter } -// GetReviewComment returns the ReviewComment field. -func (p *PRLinks) GetReviewComment() *PRLink { - if p == nil { +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternRequest) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { return nil } - return p.ReviewComment + return s.MustMatch } -// GetReviewComments returns the ReviewComments field. -func (p *PRLinks) GetReviewComments() *PRLink { - if p == nil { +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningCustomPatternRequest) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { return nil } - return p.ReviewComments + return s.MustNotMatch } -// GetSelf returns the Self field. -func (p *PRLinks) GetSelf() *PRLink { - if p == nil { - return nil +// GetName returns the Name field. +func (s *SecretScanningCustomPatternRequest) GetName() string { + if s == nil { + return "" } - return p.Self + return s.Name } -// GetStatuses returns the Statuses field. -func (p *PRLinks) GetStatuses() *PRLink { - if p == nil { - return nil +// GetPattern returns the Pattern field. +func (s *SecretScanningCustomPatternRequest) GetPattern() string { + if s == nil { + return "" } - return p.Statuses + return s.Pattern } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *Project) GetBody() string { - if p == nil || p.Body == nil { +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternRequest) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { return "" } - return *p.Body + return *s.StartDelimiter } -// GetColumnsURL returns the ColumnsURL field if it's non-nil, zero value otherwise. -func (p *Project) GetColumnsURL() string { - if p == nil || p.ColumnsURL == nil { +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternSetting) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { return "" } - return *p.ColumnsURL + return *s.CustomPatternVersion } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *Project) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} +// GetPushProtectionSetting returns the PushProtectionSetting field. +func (s *SecretScanningCustomPatternSetting) GetPushProtectionSetting() string { + if s == nil { + return "" } - return *p.CreatedAt + return s.PushProtectionSetting } -// GetCreator returns the Creator field. -func (p *Project) GetCreator() *User { - if p == nil { - return nil +// GetTokenType returns the TokenType field. +func (s *SecretScanningCustomPatternSetting) GetTokenType() string { + if s == nil { + return "" } - return p.Creator + return s.TokenType } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *Project) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningCustomPatternToDelete) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { return "" } - return *p.HTMLURL + return *s.CustomPatternVersion } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *Project) GetID() int64 { - if p == nil || p.ID == nil { +// GetPatternID returns the PatternID field. +func (s *SecretScanningCustomPatternToDelete) GetPatternID() int64 { + if s == nil { return 0 } - return *p.ID + return s.PatternID } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *Project) GetName() string { - if p == nil || p.Name == nil { - return "" +// GetReviewers returns the Reviewers slice if it's non-nil, nil otherwise. +func (s *SecretScanningDelegatedBypassOptions) GetReviewers() []*BypassReviewer { + if s == nil || s.Reviewers == nil { + return nil } - return *p.Name + return s.Reviewers } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *Project) GetNodeID() string { - if p == nil || p.NodeID == nil { +// GetPatterns returns the Patterns slice if it's non-nil, nil otherwise. +func (s *SecretScanningDeleteCustomPatternsRequest) GetPatterns() []*SecretScanningCustomPatternToDelete { + if s == nil || s.Patterns == nil { + return nil + } + return s.Patterns +} + +// GetPostDeleteAction returns the PostDeleteAction field if it's non-nil, zero value otherwise. +func (s *SecretScanningDeleteCustomPatternsRequest) GetPostDeleteAction() string { + if s == nil || s.PostDeleteAction == nil { return "" } - return *p.NodeID + return *s.PostDeleteAction } -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (p *Project) GetNumber() int { - if p == nil || p.Number == nil { - return 0 +// GetCustomPatternOverrides returns the CustomPatternOverrides slice if it's non-nil, nil otherwise. +func (s *SecretScanningPatternConfigs) GetCustomPatternOverrides() []*SecretScanningPatternOverride { + if s == nil || s.CustomPatternOverrides == nil { + return nil } - return *p.Number + return s.CustomPatternOverrides } -// GetOwnerURL returns the OwnerURL field if it's non-nil, zero value otherwise. -func (p *Project) GetOwnerURL() string { - if p == nil || p.OwnerURL == nil { +// GetPatternConfigVersion returns the PatternConfigVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternConfigs) GetPatternConfigVersion() string { + if s == nil || s.PatternConfigVersion == nil { return "" } - return *p.OwnerURL + return *s.PatternConfigVersion } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *Project) GetState() string { - if p == nil || p.State == nil { +// GetProviderPatternOverrides returns the ProviderPatternOverrides slice if it's non-nil, nil otherwise. +func (s *SecretScanningPatternConfigs) GetProviderPatternOverrides() []*SecretScanningPatternOverride { + if s == nil || s.ProviderPatternOverrides == nil { + return nil + } + return s.ProviderPatternOverrides +} + +// GetPatternConfigVersion returns the PatternConfigVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternConfigsUpdate) GetPatternConfigVersion() string { + if s == nil || s.PatternConfigVersion == nil { return "" } - return *p.State + return *s.PatternConfigVersion } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *Project) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} +// GetCustomPatternSettings returns the CustomPatternSettings slice if it's non-nil, nil otherwise. +func (s *SecretScanningPatternConfigsUpdateOptions) GetCustomPatternSettings() []*SecretScanningCustomPatternSetting { + if s == nil || s.CustomPatternSettings == nil { + return nil } - return *p.UpdatedAt + return s.CustomPatternSettings } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *Project) GetURL() string { - if p == nil || p.URL == nil { +// GetPatternConfigVersion returns the PatternConfigVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternConfigsUpdateOptions) GetPatternConfigVersion() string { + if s == nil || s.PatternConfigVersion == nil { return "" } - return *p.URL + return *s.PatternConfigVersion } -// GetArchived returns the Archived field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetArchived() bool { - if p == nil || p.Archived == nil { - return false +// GetProviderPatternSettings returns the ProviderPatternSettings slice if it's non-nil, nil otherwise. +func (s *SecretScanningPatternConfigsUpdateOptions) GetProviderPatternSettings() []*SecretScanningProviderPatternSetting { + if s == nil || s.ProviderPatternSettings == nil { + return nil } - return *p.Archived + return s.ProviderPatternSettings } -// GetColumnID returns the ColumnID field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetColumnID() int64 { - if p == nil || p.ColumnID == nil { +// GetAlertTotal returns the AlertTotal field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetAlertTotal() int { + if s == nil || s.AlertTotal == nil { return 0 } - return *p.ColumnID + return *s.AlertTotal } -// GetColumnName returns the ColumnName field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetColumnName() string { - if p == nil || p.ColumnName == nil { - return "" +// GetAlertTotalPercentage returns the AlertTotalPercentage field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetAlertTotalPercentage() int { + if s == nil || s.AlertTotalPercentage == nil { + return 0 } - return *p.ColumnName + return *s.AlertTotalPercentage } -// GetColumnURL returns the ColumnURL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetColumnURL() string { - if p == nil || p.ColumnURL == nil { - return "" +// GetBypassrate returns the Bypassrate field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetBypassrate() int { + if s == nil || s.Bypassrate == nil { + return 0 } - return *p.ColumnURL + return *s.Bypassrate } -// GetContentURL returns the ContentURL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetContentURL() string { - if p == nil || p.ContentURL == nil { +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { return "" } - return *p.ContentURL + return *s.CustomPatternVersion } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} +// GetDefaultSetting returns the DefaultSetting field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetDefaultSetting() string { + if s == nil || s.DefaultSetting == nil { + return "" } - return *p.CreatedAt + return *s.DefaultSetting } -// GetCreator returns the Creator field. -func (p *ProjectCard) GetCreator() *User { - if p == nil { - return nil +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetDisplayName() string { + if s == nil || s.DisplayName == nil { + return "" } - return p.Creator + return *s.DisplayName } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetID() int64 { - if p == nil || p.ID == nil { - return 0 +// GetEnterpriseSetting returns the EnterpriseSetting field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetEnterpriseSetting() string { + if s == nil || s.EnterpriseSetting == nil { + return "" } - return *p.ID + return *s.EnterpriseSetting } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetNodeID() string { - if p == nil || p.NodeID == nil { - return "" +// GetFalsePositiveRate returns the FalsePositiveRate field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetFalsePositiveRate() int { + if s == nil || s.FalsePositiveRate == nil { + return 0 } - return *p.NodeID + return *s.FalsePositiveRate } -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetNote() string { - if p == nil || p.Note == nil { - return "" +// GetFalsePositives returns the FalsePositives field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetFalsePositives() int { + if s == nil || s.FalsePositives == nil { + return 0 } - return *p.Note + return *s.FalsePositives } -// GetPreviousColumnName returns the PreviousColumnName field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetPreviousColumnName() string { - if p == nil || p.PreviousColumnName == nil { +// GetSetting returns the Setting field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetSetting() string { + if s == nil || s.Setting == nil { return "" } - return *p.PreviousColumnName + return *s.Setting } -// GetProjectID returns the ProjectID field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetProjectID() int64 { - if p == nil || p.ProjectID == nil { - return 0 +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetSlug() string { + if s == nil || s.Slug == nil { + return "" } - return *p.ProjectID + return *s.Slug } -// GetProjectURL returns the ProjectURL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetProjectURL() string { - if p == nil || p.ProjectURL == nil { +// GetTokenType returns the TokenType field if it's non-nil, zero value otherwise. +func (s *SecretScanningPatternOverride) GetTokenType() string { + if s == nil || s.TokenType == nil { return "" } - return *p.ProjectURL + return *s.TokenType } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} +// GetPushProtectionSetting returns the PushProtectionSetting field. +func (s *SecretScanningProviderPatternSetting) GetPushProtectionSetting() string { + if s == nil { + return "" } - return *p.UpdatedAt + return s.PushProtectionSetting } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetURL() string { - if p == nil || p.URL == nil { +// GetTokenType returns the TokenType field. +func (s *SecretScanningProviderPatternSetting) GetTokenType() string { + if s == nil { return "" } - return *p.URL + return s.TokenType } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *ProjectCardEvent) GetAction() string { - if p == nil || p.Action == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SecretScanningPushProtection) GetStatus() string { + if s == nil || s.Status == nil { return "" } - return *p.Action + return *s.Status } -// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. -func (p *ProjectCardEvent) GetAfterID() int64 { - if p == nil || p.AfterID == nil { - return 0 +// GetBackfillScans returns the BackfillScans slice if it's non-nil, nil otherwise. +func (s *SecretScanningScanHistory) GetBackfillScans() []*SecretsScan { + if s == nil || s.BackfillScans == nil { + return nil } - return *p.AfterID + return s.BackfillScans } -// GetChanges returns the Changes field. -func (p *ProjectCardEvent) GetChanges() *ProjectCardChange { - if p == nil { +// GetCustomPatternBackfillScans returns the CustomPatternBackfillScans slice if it's non-nil, nil otherwise. +func (s *SecretScanningScanHistory) GetCustomPatternBackfillScans() []*CustomPatternBackfillScan { + if s == nil || s.CustomPatternBackfillScans == nil { return nil } - return p.Changes + return s.CustomPatternBackfillScans } -// GetInstallation returns the Installation field. -func (p *ProjectCardEvent) GetInstallation() *Installation { - if p == nil { +// GetIncrementalScans returns the IncrementalScans slice if it's non-nil, nil otherwise. +func (s *SecretScanningScanHistory) GetIncrementalScans() []*SecretsScan { + if s == nil || s.IncrementalScans == nil { return nil } - return p.Installation + return s.IncrementalScans } -// GetOrg returns the Org field. -func (p *ProjectCardEvent) GetOrg() *Organization { - if p == nil { +// GetPatternUpdateScans returns the PatternUpdateScans slice if it's non-nil, nil otherwise. +func (s *SecretScanningScanHistory) GetPatternUpdateScans() []*SecretsScan { + if s == nil || s.PatternUpdateScans == nil { return nil } - return p.Org + return s.PatternUpdateScans } -// GetProjectCard returns the ProjectCard field. -func (p *ProjectCardEvent) GetProjectCard() *ProjectCard { - if p == nil { - return nil +// GetCustomPatternVersion returns the CustomPatternVersion field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetCustomPatternVersion() string { + if s == nil || s.CustomPatternVersion == nil { + return "" } - return p.ProjectCard + return *s.CustomPatternVersion } -// GetRepo returns the Repo field. -func (p *ProjectCardEvent) GetRepo() *Repository { - if p == nil { - return nil +// GetEndDelimiter returns the EndDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetEndDelimiter() string { + if s == nil || s.EndDelimiter == nil { + return "" } - return p.Repo + return *s.EndDelimiter } -// GetSender returns the Sender field. -func (p *ProjectCardEvent) GetSender() *User { - if p == nil { +// GetMustMatch returns the MustMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetMustMatch() []string { + if s == nil || s.MustMatch == nil { return nil } - return p.Sender + return s.MustMatch } -// GetArchivedState returns the ArchivedState field if it's non-nil, zero value otherwise. -func (p *ProjectCardListOptions) GetArchivedState() string { - if p == nil || p.ArchivedState == nil { - return "" +// GetMustNotMatch returns the MustNotMatch slice if it's non-nil, nil otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetMustNotMatch() []string { + if s == nil || s.MustNotMatch == nil { + return nil } - return *p.ArchivedState + return s.MustNotMatch } -// GetArchived returns the Archived field if it's non-nil, zero value otherwise. -func (p *ProjectCardOptions) GetArchived() bool { - if p == nil || p.Archived == nil { - return false +// GetPattern returns the Pattern field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetPattern() string { + if s == nil || s.Pattern == nil { + return "" } - return *p.Archived + return *s.Pattern } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (p *ProjectCollaboratorOptions) GetPermission() string { - if p == nil || p.Permission == nil { +// GetStartDelimiter returns the StartDelimiter field if it's non-nil, zero value otherwise. +func (s *SecretScanningUpdateCustomPatternRequest) GetStartDelimiter() string { + if s == nil || s.StartDelimiter == nil { return "" } - return *p.Permission + return *s.StartDelimiter } -// GetCardsURL returns the CardsURL field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetCardsURL() string { - if p == nil || p.CardsURL == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SecretScanningValidityChecks) GetStatus() string { + if s == nil || s.Status == nil { return "" } - return *p.CardsURL + return *s.Status } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (s *SecretsScan) GetCompletedAt() Timestamp { + if s == nil || s.CompletedAt == nil { return Timestamp{} } - return *p.CreatedAt + return *s.CompletedAt } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetID() int64 { - if p == nil || p.ID == nil { - return 0 +// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. +func (s *SecretsScan) GetStartedAt() Timestamp { + if s == nil || s.StartedAt == nil { + return Timestamp{} } - return *p.ID + return *s.StartedAt } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetName() string { - if p == nil || p.Name == nil { +// GetStatus returns the Status field. +func (s *SecretsScan) GetStatus() string { + if s == nil { return "" } - return *p.Name + return s.Status } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetNodeID() string { - if p == nil || p.NodeID == nil { +// GetType returns the Type field. +func (s *SecretsScan) GetType() string { + if s == nil { return "" } - return *p.NodeID + return s.Type } -// GetProjectURL returns the ProjectURL field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetProjectURL() string { - if p == nil || p.ProjectURL == nil { - return "" +// GetAuthor returns the Author field. +func (s *SecurityAdvisory) GetAuthor() *User { + if s == nil { + return nil } - return *p.ProjectURL + return s.Author } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { +// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetClosedAt() Timestamp { + if s == nil || s.ClosedAt == nil { return Timestamp{} } - return *p.UpdatedAt + return *s.ClosedAt } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetURL() string { - if p == nil || p.URL == nil { - return "" +// GetCollaboratingTeams returns the CollaboratingTeams slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetCollaboratingTeams() []*Team { + if s == nil || s.CollaboratingTeams == nil { + return nil } - return *p.URL + return s.CollaboratingTeams } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *ProjectColumnEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" +// GetCollaboratingUsers returns the CollaboratingUsers slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetCollaboratingUsers() []*User { + if s == nil || s.CollaboratingUsers == nil { + return nil } - return *p.Action + return s.CollaboratingUsers } -// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. -func (p *ProjectColumnEvent) GetAfterID() int64 { - if p == nil || p.AfterID == nil { - return 0 +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} } - return *p.AfterID + return *s.CreatedAt } -// GetChanges returns the Changes field. -func (p *ProjectColumnEvent) GetChanges() *ProjectColumnChange { - if p == nil { +// GetCredits returns the Credits slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetCredits() []*RepoAdvisoryCredit { + if s == nil || s.Credits == nil { return nil } - return p.Changes + return s.Credits } -// GetInstallation returns the Installation field. -func (p *ProjectColumnEvent) GetInstallation() *Installation { - if p == nil { +// GetCreditsDetailed returns the CreditsDetailed slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetCreditsDetailed() []*RepoAdvisoryCreditDetailed { + if s == nil || s.CreditsDetailed == nil { return nil } - return p.Installation + return s.CreditsDetailed } -// GetOrg returns the Org field. -func (p *ProjectColumnEvent) GetOrg() *Organization { - if p == nil { +// GetCVEID returns the CVEID field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetCVEID() string { + if s == nil || s.CVEID == nil { + return "" + } + return *s.CVEID +} + +// GetCVSS returns the CVSS field. +func (s *SecurityAdvisory) GetCVSS() *AdvisoryCVSS { + if s == nil { return nil } - return p.Org + return s.CVSS } -// GetProjectColumn returns the ProjectColumn field. -func (p *ProjectColumnEvent) GetProjectColumn() *ProjectColumn { - if p == nil { +// GetCVSSSeverities returns the CVSSSeverities field. +func (s *SecurityAdvisory) GetCVSSSeverities() *AdvisoryCVSSSeverities { + if s == nil { return nil } - return p.ProjectColumn + return s.CVSSSeverities } -// GetRepo returns the Repo field. -func (p *ProjectColumnEvent) GetRepo() *Repository { - if p == nil { +// GetCWEIDs returns the CWEIDs slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetCWEIDs() []string { + if s == nil || s.CWEIDs == nil { return nil } - return p.Repo + return s.CWEIDs } -// GetSender returns the Sender field. -func (p *ProjectColumnEvent) GetSender() *User { - if p == nil { +// GetCWEs returns the CWEs slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetCWEs() []*AdvisoryCWEs { + if s == nil || s.CWEs == nil { return nil } - return p.Sender + return s.CWEs } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *ProjectEvent) GetAction() string { - if p == nil || p.Action == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetDescription() string { + if s == nil || s.Description == nil { return "" } - return *p.Action + return *s.Description } -// GetChanges returns the Changes field. -func (p *ProjectEvent) GetChanges() *ProjectChange { - if p == nil { - return nil +// GetGHSAID returns the GHSAID field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetGHSAID() string { + if s == nil || s.GHSAID == nil { + return "" } - return p.Changes + return *s.GHSAID } -// GetInstallation returns the Installation field. -func (p *ProjectEvent) GetInstallation() *Installation { - if p == nil { - return nil +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetHTMLURL() string { + if s == nil || s.HTMLURL == nil { + return "" } - return p.Installation + return *s.HTMLURL } -// GetOrg returns the Org field. -func (p *ProjectEvent) GetOrg() *Organization { - if p == nil { +// GetIdentifiers returns the Identifiers slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetIdentifiers() []*AdvisoryIdentifier { + if s == nil || s.Identifiers == nil { return nil } - return p.Org + return s.Identifiers } -// GetProject returns the Project field. -func (p *ProjectEvent) GetProject() *Project { - if p == nil { +// GetPrivateFork returns the PrivateFork field. +func (s *SecurityAdvisory) GetPrivateFork() *Repository { + if s == nil { return nil } - return p.Project + return s.PrivateFork } -// GetRepo returns the Repo field. -func (p *ProjectEvent) GetRepo() *Repository { - if p == nil { - return nil +// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetPublishedAt() Timestamp { + if s == nil || s.PublishedAt == nil { + return Timestamp{} } - return p.Repo + return *s.PublishedAt } -// GetSender returns the Sender field. -func (p *ProjectEvent) GetSender() *User { - if p == nil { +// GetPublisher returns the Publisher field. +func (s *SecurityAdvisory) GetPublisher() *User { + if s == nil { return nil } - return p.Sender + return s.Publisher } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *ProjectOptions) GetBody() string { - if p == nil || p.Body == nil { - return "" +// GetReferences returns the References slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetReferences() []*AdvisoryReference { + if s == nil || s.References == nil { + return nil } - return *p.Body + return s.References } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *ProjectOptions) GetName() string { - if p == nil || p.Name == nil { +// GetSeverity returns the Severity field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetSeverity() string { + if s == nil || s.Severity == nil { return "" } - return *p.Name + return *s.Severity } -// GetOrganizationPermission returns the OrganizationPermission field if it's non-nil, zero value otherwise. -func (p *ProjectOptions) GetOrganizationPermission() string { - if p == nil || p.OrganizationPermission == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetState() string { + if s == nil || s.State == nil { return "" } - return *p.OrganizationPermission -} - -// GetPublic returns the Public field if it's non-nil, zero value otherwise. -func (p *ProjectOptions) GetPublic() bool { - if p == nil || p.Public == nil { - return false - } - return *p.Public + return *s.State } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *ProjectOptions) GetState() string { - if p == nil || p.State == nil { - return "" +// GetSubmission returns the Submission field. +func (s *SecurityAdvisory) GetSubmission() *SecurityAdvisorySubmission { + if s == nil { + return nil } - return *p.State + return s.Submission } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (p *ProjectPermissionLevel) GetPermission() string { - if p == nil || p.Permission == nil { +// GetSummary returns the Summary field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetSummary() string { + if s == nil || s.Summary == nil { return "" } - return *p.Permission + return *s.Summary } -// GetUser returns the User field. -func (p *ProjectPermissionLevel) GetUser() *User { - if p == nil { - return nil +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetUpdatedAt() Timestamp { + if s == nil || s.UpdatedAt == nil { + return Timestamp{} } - return p.User + return *s.UpdatedAt } -// GetEnforceAdmins returns the EnforceAdmins field. -func (p *Protection) GetEnforceAdmins() *AdminEnforcement { - if p == nil { - return nil +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetURL() string { + if s == nil || s.URL == nil { + return "" } - return p.EnforceAdmins + return *s.URL } -// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. -func (p *Protection) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcement { - if p == nil { +// GetVulnerabilities returns the Vulnerabilities slice if it's non-nil, nil otherwise. +func (s *SecurityAdvisory) GetVulnerabilities() []*AdvisoryVulnerability { + if s == nil || s.Vulnerabilities == nil { return nil } - return p.RequiredPullRequestReviews + return s.Vulnerabilities } -// GetRequiredStatusChecks returns the RequiredStatusChecks field. -func (p *Protection) GetRequiredStatusChecks() *RequiredStatusChecks { - if p == nil { - return nil +// GetWithdrawnAt returns the WithdrawnAt field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisory) GetWithdrawnAt() Timestamp { + if s == nil || s.WithdrawnAt == nil { + return Timestamp{} } - return p.RequiredStatusChecks + return *s.WithdrawnAt } -// GetRestrictions returns the Restrictions field. -func (p *Protection) GetRestrictions() *BranchRestrictions { - if p == nil { - return nil +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisoryEvent) GetAction() string { + if s == nil || s.Action == nil { + return "" } - return p.Restrictions + return *s.Action } -// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. -func (p *ProtectionRequest) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcementRequest { - if p == nil { +// GetEnterprise returns the Enterprise field. +func (s *SecurityAdvisoryEvent) GetEnterprise() *Enterprise { + if s == nil { return nil } - return p.RequiredPullRequestReviews + return s.Enterprise } -// GetRequiredStatusChecks returns the RequiredStatusChecks field. -func (p *ProtectionRequest) GetRequiredStatusChecks() *RequiredStatusChecks { - if p == nil { +// GetInstallation returns the Installation field. +func (s *SecurityAdvisoryEvent) GetInstallation() *Installation { + if s == nil { return nil } - return p.RequiredStatusChecks + return s.Installation } -// GetRestrictions returns the Restrictions field. -func (p *ProtectionRequest) GetRestrictions() *BranchRestrictionsRequest { - if p == nil { +// GetOrganization returns the Organization field. +func (s *SecurityAdvisoryEvent) GetOrganization() *Organization { + if s == nil { return nil } - return p.Restrictions + return s.Organization } -// GetInstallation returns the Installation field. -func (p *PublicEvent) GetInstallation() *Installation { - if p == nil { +// GetRepository returns the Repository field. +func (s *SecurityAdvisoryEvent) GetRepository() *Repository { + if s == nil { return nil } - return p.Installation + return s.Repository } -// GetRepo returns the Repo field. -func (p *PublicEvent) GetRepo() *Repository { - if p == nil { +// GetSecurityAdvisory returns the SecurityAdvisory field. +func (s *SecurityAdvisoryEvent) GetSecurityAdvisory() *SecurityAdvisory { + if s == nil { return nil } - return p.Repo + return s.SecurityAdvisory } // GetSender returns the Sender field. -func (p *PublicEvent) GetSender() *User { - if p == nil { +func (s *SecurityAdvisoryEvent) GetSender() *User { + if s == nil { return nil } - return p.Sender + return s.Sender } -// GetExpectedHeadSHA returns the ExpectedHeadSHA field if it's non-nil, zero value otherwise. -func (p *PullReqestBranchUpdateOptions) GetExpectedHeadSHA() string { - if p == nil || p.ExpectedHeadSHA == nil { - return "" +// GetAccepted returns the Accepted field if it's non-nil, zero value otherwise. +func (s *SecurityAdvisorySubmission) GetAccepted() bool { + if s == nil || s.Accepted == nil { + return false } - return *p.ExpectedHeadSHA + return *s.Accepted } -// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetActiveLockReason() string { - if p == nil || p.ActiveLockReason == nil { - return "" +// GetAdvancedSecurity returns the AdvancedSecurity field. +func (s *SecurityAndAnalysis) GetAdvancedSecurity() *AdvancedSecurity { + if s == nil { + return nil } - return *p.ActiveLockReason + return s.AdvancedSecurity } -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetAdditions() int { - if p == nil || p.Additions == nil { - return 0 +// GetCodeSecurity returns the CodeSecurity field. +func (s *SecurityAndAnalysis) GetCodeSecurity() *CodeSecurity { + if s == nil { + return nil } - return *p.Additions + return s.CodeSecurity } -// GetAssignee returns the Assignee field. -func (p *PullRequest) GetAssignee() *User { - if p == nil { +// GetDependabotSecurityUpdates returns the DependabotSecurityUpdates field. +func (s *SecurityAndAnalysis) GetDependabotSecurityUpdates() *DependabotSecurityUpdates { + if s == nil { return nil } - return p.Assignee + return s.DependabotSecurityUpdates } -// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetAuthorAssociation() string { - if p == nil || p.AuthorAssociation == nil { - return "" +// GetSecretScanning returns the SecretScanning field. +func (s *SecurityAndAnalysis) GetSecretScanning() *SecretScanning { + if s == nil { + return nil } - return *p.AuthorAssociation + return s.SecretScanning } -// GetBase returns the Base field. -func (p *PullRequest) GetBase() *PullRequestBranch { - if p == nil { +// GetSecretScanningPushProtection returns the SecretScanningPushProtection field. +func (s *SecurityAndAnalysis) GetSecretScanningPushProtection() *SecretScanningPushProtection { + if s == nil { return nil } - return p.Base + return s.SecretScanningPushProtection } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetBody() string { - if p == nil || p.Body == nil { - return "" +// GetSecretScanningValidityChecks returns the SecretScanningValidityChecks field. +func (s *SecurityAndAnalysis) GetSecretScanningValidityChecks() *SecretScanningValidityChecks { + if s == nil { + return nil } - return *p.Body + return s.SecretScanningValidityChecks } -// GetChangedFiles returns the ChangedFiles field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetChangedFiles() int { - if p == nil || p.ChangedFiles == nil { - return 0 +// GetFrom returns the From field. +func (s *SecurityAndAnalysisChange) GetFrom() *SecurityAndAnalysisChangeFrom { + if s == nil { + return nil } - return *p.ChangedFiles + return s.From } -// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetClosedAt() time.Time { - if p == nil || p.ClosedAt == nil { - return time.Time{} +// GetSecurityAndAnalysis returns the SecurityAndAnalysis field. +func (s *SecurityAndAnalysisChangeFrom) GetSecurityAndAnalysis() *SecurityAndAnalysis { + if s == nil { + return nil } - return *p.ClosedAt + return s.SecurityAndAnalysis } -// GetComments returns the Comments field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetComments() int { - if p == nil || p.Comments == nil { - return 0 +// GetChanges returns the Changes field. +func (s *SecurityAndAnalysisEvent) GetChanges() *SecurityAndAnalysisChange { + if s == nil { + return nil } - return *p.Comments + return s.Changes } -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCommentsURL() string { - if p == nil || p.CommentsURL == nil { - return "" +// GetEnterprise returns the Enterprise field. +func (s *SecurityAndAnalysisEvent) GetEnterprise() *Enterprise { + if s == nil { + return nil } - return *p.CommentsURL + return s.Enterprise } -// GetCommits returns the Commits field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCommits() int { - if p == nil || p.Commits == nil { - return 0 +// GetInstallation returns the Installation field. +func (s *SecurityAndAnalysisEvent) GetInstallation() *Installation { + if s == nil { + return nil } - return *p.Commits + return s.Installation } -// GetCommitsURL returns the CommitsURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCommitsURL() string { - if p == nil || p.CommitsURL == nil { - return "" +// GetOrganization returns the Organization field. +func (s *SecurityAndAnalysisEvent) GetOrganization() *Organization { + if s == nil { + return nil } - return *p.CommitsURL + return s.Organization } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCreatedAt() time.Time { - if p == nil || p.CreatedAt == nil { - return time.Time{} +// GetRepository returns the Repository field. +func (s *SecurityAndAnalysisEvent) GetRepository() *Repository { + if s == nil { + return nil } - return *p.CreatedAt + return s.Repository } -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetDeletions() int { - if p == nil || p.Deletions == nil { - return 0 +// GetSender returns the Sender field. +func (s *SecurityAndAnalysisEvent) GetSender() *User { + if s == nil { + return nil } - return *p.Deletions + return s.Sender } -// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetDiffURL() string { - if p == nil || p.DiffURL == nil { - return "" +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (s *SelectedReposList) GetRepositories() []*Repository { + if s == nil || s.Repositories == nil { + return nil } - return *p.DiffURL + return s.Repositories } -// GetDraft returns the Draft field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetDraft() bool { - if p == nil || p.Draft == nil { - return false +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (s *SelectedReposList) GetTotalCount() int { + if s == nil || s.TotalCount == nil { + return 0 } - return *p.Draft + return *s.TotalCount } -// GetHead returns the Head field. -func (p *PullRequest) GetHead() *PullRequestBranch { - if p == nil { +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (s *SelfHostedRunnersAllowedRepos) GetRepositories() []*Repository { + if s == nil || s.Repositories == nil { return nil } - return p.Head -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL + return s.Repositories } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetID() int64 { - if p == nil || p.ID == nil { +// GetTotalCount returns the TotalCount field. +func (s *SelfHostedRunnersAllowedRepos) GetTotalCount() int { + if s == nil { return 0 } - return *p.ID + return s.TotalCount +} + +// GetEnabledRepositories returns the EnabledRepositories field if it's non-nil, zero value otherwise. +func (s *SelfHostedRunnersSettingsOrganization) GetEnabledRepositories() string { + if s == nil || s.EnabledRepositories == nil { + return "" + } + return *s.EnabledRepositories } -// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetIssueURL() string { - if p == nil || p.IssueURL == nil { +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field if it's non-nil, zero value otherwise. +func (s *SelfHostedRunnersSettingsOrganization) GetSelectedRepositoriesURL() string { + if s == nil || s.SelectedRepositoriesURL == nil { return "" } - return *p.IssueURL + return *s.SelectedRepositoriesURL } -// GetLinks returns the Links field. -func (p *PullRequest) GetLinks() *PRLinks { - if p == nil { - return nil +// GetEnabledRepositories returns the EnabledRepositories field if it's non-nil, zero value otherwise. +func (s *SelfHostedRunnersSettingsOrganizationOpt) GetEnabledRepositories() string { + if s == nil || s.EnabledRepositories == nil { + return "" } - return p.Links + return *s.EnabledRepositories } -// GetLocked returns the Locked field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetLocked() bool { - if p == nil || p.Locked == nil { +// GetDisableSelfHostedRunnersForAllOrgs returns the DisableSelfHostedRunnersForAllOrgs field if it's non-nil, zero value otherwise. +func (s *SelfHostRunnerPermissionsEnterprise) GetDisableSelfHostedRunnersForAllOrgs() bool { + if s == nil || s.DisableSelfHostedRunnersForAllOrgs == nil { return false } - return *p.Locked + return *s.DisableSelfHostedRunnersForAllOrgs } -// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMaintainerCanModify() bool { - if p == nil || p.MaintainerCanModify == nil { - return false +// GetServerInstances returns the ServerInstances field. +func (s *ServerInstanceProperties) GetServerInstances() *ServerInstances { + if s == nil { + return nil } - return *p.MaintainerCanModify + return s.ServerInstances } -// GetMergeable returns the Mergeable field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergeable() bool { - if p == nil || p.Mergeable == nil { - return false +// GetItems returns the Items field. +func (s *ServerInstances) GetItems() *ServiceInstanceItems { + if s == nil { + return nil } - return *p.Mergeable + return s.Items } -// GetMergeableState returns the MergeableState field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergeableState() string { - if p == nil || p.MergeableState == nil { +// GetType returns the Type field. +func (s *ServerInstances) GetType() string { + if s == nil { return "" } - return *p.MergeableState + return s.Type } -// GetMergeCommitSHA returns the MergeCommitSHA field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergeCommitSHA() string { - if p == nil || p.MergeCommitSHA == nil { +// GetHostname returns the Hostname field. +func (s *ServerItemProperties) GetHostname() string { + if s == nil { return "" } - return *p.MergeCommitSHA + return s.Hostname } -// GetMerged returns the Merged field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMerged() bool { - if p == nil || p.Merged == nil { - return false +// GetLastSync returns the LastSync field. +func (s *ServerItemProperties) GetLastSync() *LastLicenseSync { + if s == nil { + return nil } - return *p.Merged + return s.LastSync } -// GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergedAt() time.Time { - if p == nil || p.MergedAt == nil { - return time.Time{} +// GetServerID returns the ServerID field. +func (s *ServerItemProperties) GetServerID() string { + if s == nil { + return "" } - return *p.MergedAt + return s.ServerID } -// GetMergedBy returns the MergedBy field. -func (p *PullRequest) GetMergedBy() *User { - if p == nil { +// GetProperties returns the Properties field. +func (s *ServiceInstanceItems) GetProperties() *ServerItemProperties { + if s == nil { return nil } - return p.MergedBy + return s.Properties } -// GetMilestone returns the Milestone field. -func (p *PullRequest) GetMilestone() *Milestone { - if p == nil { +// GetType returns the Type field. +func (s *ServiceInstanceItems) GetType() string { + if s == nil { + return "" + } + return s.Type +} + +// GetSelectedOrganizationIDs returns the SelectedOrganizationIDs slice if it's non-nil, nil otherwise. +func (s *SetOrgAccessRunnerGroupRequest) GetSelectedOrganizationIDs() []int64 { + if s == nil || s.SelectedOrganizationIDs == nil { return nil } - return p.Milestone + return s.SelectedOrganizationIDs } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetNodeID() string { - if p == nil || p.NodeID == nil { - return "" +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (s *SetRepoAccessRunnerGroupRequest) GetSelectedRepositoryIDs() []int64 { + if s == nil || s.SelectedRepositoryIDs == nil { + return nil } - return *p.NodeID + return s.SelectedRepositoryIDs } -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetNumber() int { - if p == nil || p.Number == nil { - return 0 +// GetRunners returns the Runners slice if it's non-nil, nil otherwise. +func (s *SetRunnerGroupRunnersRequest) GetRunners() []int64 { + if s == nil || s.Runners == nil { + return nil } - return *p.Number + return s.Runners } -// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetPatchURL() string { - if p == nil || p.PatchURL == nil { +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (s *SignatureRequirementEnforcementLevelChanges) GetFrom() string { + if s == nil || s.From == nil { return "" } - return *p.PatchURL + return *s.From } -// GetRebaseable returns the Rebaseable field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetRebaseable() bool { - if p == nil || p.Rebaseable == nil { +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (s *SignaturesProtectedBranch) GetEnabled() bool { + if s == nil || s.Enabled == nil { return false } - return *p.Rebaseable + return *s.Enabled } -// GetReviewComments returns the ReviewComments field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetReviewComments() int { - if p == nil || p.ReviewComments == nil { - return 0 +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SignaturesProtectedBranch) GetURL() string { + if s == nil || s.URL == nil { + return "" } - return *p.ReviewComments + return *s.URL } -// GetReviewCommentsURL returns the ReviewCommentsURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetReviewCommentsURL() string { - if p == nil || p.ReviewCommentsURL == nil { +// GetPayload returns the Payload field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetPayload() string { + if s == nil || s.Payload == nil { return "" } - return *p.ReviewCommentsURL + return *s.Payload } -// GetReviewCommentURL returns the ReviewCommentURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetReviewCommentURL() string { - if p == nil || p.ReviewCommentURL == nil { +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetReason() string { + if s == nil || s.Reason == nil { return "" } - return *p.ReviewCommentURL + return *s.Reason } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetState() string { - if p == nil || p.State == nil { +// GetSignature returns the Signature field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetSignature() string { + if s == nil || s.Signature == nil { return "" } - return *p.State + return *s.Signature } -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetStatusesURL() string { - if p == nil || p.StatusesURL == nil { - return "" +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (s *SignatureVerification) GetVerified() bool { + if s == nil || s.Verified == nil { + return false } - return *p.StatusesURL + return *s.Verified } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetTitle() string { - if p == nil || p.Title == nil { +// GetNegate returns the Negate field. +func (s *SimplePatternRuleParameters) GetNegate() bool { + if s == nil { + return false + } + return s.Negate +} + +// GetPattern returns the Pattern field. +func (s *SimplePatternRuleParameters) GetPattern() string { + if s == nil { return "" } - return *p.Title + return s.Pattern } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetUpdatedAt() time.Time { - if p == nil || p.UpdatedAt == nil { - return time.Time{} +// GetProvider returns the Provider field if it's non-nil, zero value otherwise. +func (s *SocialAccount) GetProvider() string { + if s == nil || s.Provider == nil { + return "" } - return *p.UpdatedAt + return *s.Provider } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetURL() string { - if p == nil || p.URL == nil { +func (s *SocialAccount) GetURL() string { + if s == nil || s.URL == nil { return "" } - return *p.URL + return *s.URL } -// GetUser returns the User field. -func (p *PullRequest) GetUser() *User { - if p == nil { +// GetActor returns the Actor field. +func (s *Source) GetActor() *User { + if s == nil { return nil } - return p.User + return s.Actor } -// GetLabel returns the Label field if it's non-nil, zero value otherwise. -func (p *PullRequestBranch) GetLabel() string { - if p == nil || p.Label == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *Source) GetID() int64 { + if s == nil || s.ID == nil { + return 0 } - return *p.Label + return *s.ID } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (p *PullRequestBranch) GetRef() string { - if p == nil || p.Ref == nil { +// GetIssue returns the Issue field. +func (s *Source) GetIssue() *Issue { + if s == nil { + return nil + } + return s.Issue +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (s *Source) GetType() string { + if s == nil || s.Type == nil { return "" } - return *p.Ref + return *s.Type } -// GetRepo returns the Repo field. -func (p *PullRequestBranch) GetRepo() *Repository { - if p == nil { - return nil +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *Source) GetURL() string { + if s == nil || s.URL == nil { + return "" } - return p.Repo + return *s.URL } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *PullRequestBranch) GetSHA() string { - if p == nil || p.SHA == nil { +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetEmail() string { + if s == nil || s.Email == nil { return "" } - return *p.SHA + return *s.Email } -// GetUser returns the User field. -func (p *PullRequestBranch) GetUser() *User { - if p == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetID() int64 { + if s == nil || s.ID == nil { + return 0 } - return p.User + return *s.ID } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PullRequestBranchUpdateResponse) GetMessage() string { - if p == nil || p.Message == nil { +// GetImportURL returns the ImportURL field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetImportURL() string { + if s == nil || s.ImportURL == nil { return "" } - return *p.Message + return *s.ImportURL } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequestBranchUpdateResponse) GetURL() string { - if p == nil || p.URL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetName() string { + if s == nil || s.Name == nil { return "" } - return *p.URL + return *s.Name } -// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetAuthorAssociation() string { - if p == nil || p.AuthorAssociation == nil { +// GetRemoteID returns the RemoteID field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetRemoteID() string { + if s == nil || s.RemoteID == nil { return "" } - return *p.AuthorAssociation + return *s.RemoteID } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetBody() string { - if p == nil || p.Body == nil { +// GetRemoteName returns the RemoteName field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetRemoteName() string { + if s == nil || s.RemoteName == nil { return "" } - return *p.Body + return *s.RemoteName } -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetCommitID() string { - if p == nil || p.CommitID == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SourceImportAuthor) GetURL() string { + if s == nil || s.URL == nil { return "" } - return *p.CommitID + return *s.URL } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetCreatedAt() time.Time { - if p == nil || p.CreatedAt == nil { - return time.Time{} +// GetDomain returns the Domain field. +func (s *SplunkConfig) GetDomain() string { + if s == nil { + return "" } - return *p.CreatedAt + return s.Domain } -// GetDiffHunk returns the DiffHunk field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetDiffHunk() string { - if p == nil || p.DiffHunk == nil { +// GetEncryptedToken returns the EncryptedToken field. +func (s *SplunkConfig) GetEncryptedToken() string { + if s == nil { return "" } - return *p.DiffHunk + return s.EncryptedToken } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { +// GetKeyID returns the KeyID field. +func (s *SplunkConfig) GetKeyID() string { + if s == nil { return "" } - return *p.HTMLURL + return s.KeyID } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetID() int64 { - if p == nil || p.ID == nil { +// GetPort returns the Port field. +func (s *SplunkConfig) GetPort() uint16 { + if s == nil { return 0 } - return *p.ID + return s.Port } -// GetInReplyTo returns the InReplyTo field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetInReplyTo() int64 { - if p == nil || p.InReplyTo == nil { - return 0 +// GetSSLVerify returns the SSLVerify field. +func (s *SplunkConfig) GetSSLVerify() bool { + if s == nil { + return false } - return *p.InReplyTo + return s.SSLVerify } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetNodeID() string { - if p == nil || p.NodeID == nil { +// GetPrivacyLevel returns the PrivacyLevel field if it's non-nil, zero value otherwise. +func (s *SponsorshipChanges) GetPrivacyLevel() string { + if s == nil || s.PrivacyLevel == nil { return "" } - return *p.NodeID + return *s.PrivacyLevel } -// GetOriginalCommitID returns the OriginalCommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetOriginalCommitID() string { - if p == nil || p.OriginalCommitID == nil { +// GetTier returns the Tier field. +func (s *SponsorshipChanges) GetTier() *SponsorshipTier { + if s == nil { + return nil + } + return s.Tier +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (s *SponsorshipEvent) GetAction() string { + if s == nil || s.Action == nil { return "" } - return *p.OriginalCommitID + return *s.Action } -// GetOriginalPosition returns the OriginalPosition field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetOriginalPosition() int { - if p == nil || p.OriginalPosition == nil { - return 0 +// GetChanges returns the Changes field. +func (s *SponsorshipEvent) GetChanges() *SponsorshipChanges { + if s == nil { + return nil } - return *p.OriginalPosition + return s.Changes } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPath() string { - if p == nil || p.Path == nil { +// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. +func (s *SponsorshipEvent) GetEffectiveDate() string { + if s == nil || s.EffectiveDate == nil { return "" } - return *p.Path + return *s.EffectiveDate } -// GetPosition returns the Position field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPosition() int { - if p == nil || p.Position == nil { - return 0 +// GetInstallation returns the Installation field. +func (s *SponsorshipEvent) GetInstallation() *Installation { + if s == nil { + return nil } - return *p.Position + return s.Installation } -// GetPullRequestReviewID returns the PullRequestReviewID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPullRequestReviewID() int64 { - if p == nil || p.PullRequestReviewID == nil { - return 0 +// GetOrganization returns the Organization field. +func (s *SponsorshipEvent) GetOrganization() *Organization { + if s == nil { + return nil } - return *p.PullRequestReviewID + return s.Organization } -// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPullRequestURL() string { - if p == nil || p.PullRequestURL == nil { - return "" +// GetRepository returns the Repository field. +func (s *SponsorshipEvent) GetRepository() *Repository { + if s == nil { + return nil } - return *p.PullRequestURL + return s.Repository } -// GetReactions returns the Reactions field. -func (p *PullRequestComment) GetReactions() *Reactions { - if p == nil { +// GetSender returns the Sender field. +func (s *SponsorshipEvent) GetSender() *User { + if s == nil { return nil } - return p.Reactions + return s.Sender } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetUpdatedAt() time.Time { - if p == nil || p.UpdatedAt == nil { - return time.Time{} +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (s *SponsorshipTier) GetFrom() string { + if s == nil || s.From == nil { + return "" } - return *p.UpdatedAt + return *s.From } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetURL() string { - if p == nil || p.URL == nil { +// GetKey returns the Key field. +func (s *SSHKeyOptions) GetKey() string { + if s == nil { return "" } - return *p.URL + return s.Key } -// GetUser returns the User field. -func (p *PullRequestComment) GetUser() *User { - if p == nil { - return nil +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetHostname() string { + if s == nil || s.Hostname == nil { + return "" } - return p.User + return *s.Hostname } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *PullRequestEvent) GetAction() string { - if p == nil || p.Action == nil { +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetMessage() string { + if s == nil || s.Message == nil { return "" } - return *p.Action + return *s.Message } -// GetAssignee returns the Assignee field. -func (p *PullRequestEvent) GetAssignee() *User { - if p == nil { - return nil +// GetModified returns the Modified field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetModified() bool { + if s == nil || s.Modified == nil { + return false } - return p.Assignee + return *s.Modified } -// GetChanges returns the Changes field. -func (p *PullRequestEvent) GetChanges() *EditChange { - if p == nil { - return nil +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetUUID() string { + if s == nil || s.UUID == nil { + return "" } - return p.Changes + return *s.UUID } -// GetInstallation returns the Installation field. -func (p *PullRequestEvent) GetInstallation() *Installation { - if p == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} } - return p.Installation + return *s.CreatedAt } -// GetLabel returns the Label field. -func (p *PullRequestEvent) GetLabel() *Label { - if p == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetID() int64 { + if s == nil || s.ID == nil { + return 0 } - return p.Label + return *s.ID } -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (p *PullRequestEvent) GetNumber() int { - if p == nil || p.Number == nil { - return 0 +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetKey() string { + if s == nil || s.Key == nil { + return "" } - return *p.Number + return *s.Key } -// GetOrganization returns the Organization field. -func (p *PullRequestEvent) GetOrganization() *Organization { - if p == nil { - return nil +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (s *SSHSigningKey) GetTitle() string { + if s == nil || s.Title == nil { + return "" } - return p.Organization + return *s.Title } -// GetPullRequest returns the PullRequest field. -func (p *PullRequestEvent) GetPullRequest() *PullRequest { - if p == nil { - return nil +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (s *StarEvent) GetAction() string { + if s == nil || s.Action == nil { + return "" } - return p.PullRequest + return *s.Action } -// GetRepo returns the Repo field. -func (p *PullRequestEvent) GetRepo() *Repository { - if p == nil { +// GetInstallation returns the Installation field. +func (s *StarEvent) GetInstallation() *Installation { + if s == nil { return nil } - return p.Repo + return s.Installation } -// GetRequestedReviewer returns the RequestedReviewer field. -func (p *PullRequestEvent) GetRequestedReviewer() *User { - if p == nil { +// GetOrg returns the Org field. +func (s *StarEvent) GetOrg() *Organization { + if s == nil { return nil } - return p.RequestedReviewer + return s.Org } -// GetRequestedTeam returns the RequestedTeam field. -func (p *PullRequestEvent) GetRequestedTeam() *Team { - if p == nil { +// GetRepo returns the Repo field. +func (s *StarEvent) GetRepo() *Repository { + if s == nil { return nil } - return p.RequestedTeam + return s.Repo } // GetSender returns the Sender field. -func (p *PullRequestEvent) GetSender() *User { - if p == nil { +func (s *StarEvent) GetSender() *User { + if s == nil { return nil } - return p.Sender + return s.Sender } -// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetDiffURL() string { - if p == nil || p.DiffURL == nil { - return "" +// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. +func (s *StarEvent) GetStarredAt() Timestamp { + if s == nil || s.StarredAt == nil { + return Timestamp{} } - return *p.DiffURL + return *s.StarredAt } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" +// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. +func (s *Stargazer) GetStarredAt() Timestamp { + if s == nil || s.StarredAt == nil { + return Timestamp{} } - return *p.HTMLURL + return *s.StarredAt } -// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetPatchURL() string { - if p == nil || p.PatchURL == nil { - return "" +// GetUser returns the User field. +func (s *Stargazer) GetUser() *User { + if s == nil { + return nil } - return *p.PatchURL + return s.User } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetURL() string { - if p == nil || p.URL == nil { - return "" +// GetRepository returns the Repository field. +func (s *StarredRepository) GetRepository() *Repository { + if s == nil { + return nil } - return *p.URL + return s.Repository } -// GetMerged returns the Merged field if it's non-nil, zero value otherwise. -func (p *PullRequestMergeResult) GetMerged() bool { - if p == nil || p.Merged == nil { - return false +// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. +func (s *StarredRepository) GetStarredAt() Timestamp { + if s == nil || s.StarredAt == nil { + return Timestamp{} } - return *p.Merged + return *s.StarredAt } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PullRequestMergeResult) GetMessage() string { - if p == nil || p.Message == nil { - return "" +// GetBranches returns the Branches slice if it's non-nil, nil otherwise. +func (s *StatusEvent) GetBranches() []*Branch { + if s == nil || s.Branches == nil { + return nil } - return *p.Message + return s.Branches } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *PullRequestMergeResult) GetSHA() string { - if p == nil || p.SHA == nil { - return "" +// GetCommit returns the Commit field. +func (s *StatusEvent) GetCommit() *RepositoryCommit { + if s == nil { + return nil } - return *p.SHA + return s.Commit } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetBody() string { - if p == nil || p.Body == nil { +// GetContext returns the Context field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetContext() string { + if s == nil || s.Context == nil { return "" } - return *p.Body + return *s.Context } -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetCommitID() string { - if p == nil || p.CommitID == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} } - return *p.CommitID + return *s.CreatedAt } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetDescription() string { + if s == nil || s.Description == nil { return "" } - return *p.HTMLURL + return *s.Description } // GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetID() int64 { - if p == nil || p.ID == nil { +func (s *StatusEvent) GetID() int64 { + if s == nil || s.ID == nil { return 0 } - return *p.ID + return *s.ID } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetNodeID() string { - if p == nil || p.NodeID == nil { - return "" +// GetInstallation returns the Installation field. +func (s *StatusEvent) GetInstallation() *Installation { + if s == nil { + return nil } - return *p.NodeID + return s.Installation } -// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetPullRequestURL() string { - if p == nil || p.PullRequestURL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetName() string { + if s == nil || s.Name == nil { return "" } - return *p.PullRequestURL + return *s.Name } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetState() string { - if p == nil || p.State == nil { - return "" +// GetOrg returns the Org field. +func (s *StatusEvent) GetOrg() *Organization { + if s == nil { + return nil } - return *p.State + return s.Org } -// GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetSubmittedAt() time.Time { - if p == nil || p.SubmittedAt == nil { - return time.Time{} +// GetRepo returns the Repo field. +func (s *StatusEvent) GetRepo() *Repository { + if s == nil { + return nil } - return *p.SubmittedAt + return s.Repo } -// GetUser returns the User field. -func (p *PullRequestReview) GetUser() *User { - if p == nil { +// GetSender returns the Sender field. +func (s *StatusEvent) GetSender() *User { + if s == nil { return nil } - return p.User + return s.Sender } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewCommentEvent) GetAction() string { - if p == nil || p.Action == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetSHA() string { + if s == nil || s.SHA == nil { return "" } - return *p.Action + return *s.SHA } -// GetChanges returns the Changes field. -func (p *PullRequestReviewCommentEvent) GetChanges() *EditChange { - if p == nil { - return nil +// GetState returns the State field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetState() string { + if s == nil || s.State == nil { + return "" } - return p.Changes + return *s.State } -// GetComment returns the Comment field. -func (p *PullRequestReviewCommentEvent) GetComment() *PullRequestComment { - if p == nil { - return nil +// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetTargetURL() string { + if s == nil || s.TargetURL == nil { + return "" } - return p.Comment + return *s.TargetURL } -// GetInstallation returns the Installation field. -func (p *PullRequestReviewCommentEvent) GetInstallation() *Installation { - if p == nil { - return nil +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (s *StatusEvent) GetUpdatedAt() Timestamp { + if s == nil || s.UpdatedAt == nil { + return Timestamp{} } - return p.Installation + return *s.UpdatedAt } -// GetPullRequest returns the PullRequest field. -func (p *PullRequestReviewCommentEvent) GetPullRequest() *PullRequest { - if p == nil { - return nil +// GetDaysLeftInBillingCycle returns the DaysLeftInBillingCycle field. +func (s *StorageBilling) GetDaysLeftInBillingCycle() int { + if s == nil { + return 0 } - return p.PullRequest + return s.DaysLeftInBillingCycle } -// GetRepo returns the Repo field. -func (p *PullRequestReviewCommentEvent) GetRepo() *Repository { - if p == nil { - return nil +// GetEstimatedPaidStorageForMonth returns the EstimatedPaidStorageForMonth field. +func (s *StorageBilling) GetEstimatedPaidStorageForMonth() int { + if s == nil { + return 0 } - return p.Repo + return s.EstimatedPaidStorageForMonth } -// GetSender returns the Sender field. -func (p *PullRequestReviewCommentEvent) GetSender() *User { - if p == nil { - return nil +// GetEstimatedStorageForMonth returns the EstimatedStorageForMonth field. +func (s *StorageBilling) GetEstimatedStorageForMonth() int { + if s == nil { + return 0 } - return p.Sender + return s.EstimatedStorageForMonth } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewDismissalRequest) GetMessage() string { - if p == nil || p.Message == nil { - return "" +// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. +func (s *SubIssueRequest) GetAfterID() int64 { + if s == nil || s.AfterID == nil { + return 0 } - return *p.Message + return *s.AfterID } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" +// GetBeforeID returns the BeforeID field if it's non-nil, zero value otherwise. +func (s *SubIssueRequest) GetBeforeID() int64 { + if s == nil || s.BeforeID == nil { + return 0 } - return *p.Action + return *s.BeforeID } -// GetInstallation returns the Installation field. -func (p *PullRequestReviewEvent) GetInstallation() *Installation { - if p == nil { - return nil +// GetReplaceParent returns the ReplaceParent field if it's non-nil, zero value otherwise. +func (s *SubIssueRequest) GetReplaceParent() bool { + if s == nil || s.ReplaceParent == nil { + return false } - return p.Installation + return *s.ReplaceParent } -// GetOrganization returns the Organization field. -func (p *PullRequestReviewEvent) GetOrganization() *Organization { - if p == nil { - return nil +// GetSubIssueID returns the SubIssueID field. +func (s *SubIssueRequest) GetSubIssueID() int64 { + if s == nil { + return 0 } - return p.Organization + return s.SubIssueID } -// GetPullRequest returns the PullRequest field. -func (p *PullRequestReviewEvent) GetPullRequest() *PullRequest { - if p == nil { - return nil +// GetCompleted returns the Completed field if it's non-nil, zero value otherwise. +func (s *SubIssuesSummary) GetCompleted() int { + if s == nil || s.Completed == nil { + return 0 } - return p.PullRequest + return *s.Completed } -// GetRepo returns the Repo field. -func (p *PullRequestReviewEvent) GetRepo() *Repository { - if p == nil { - return nil +// GetPercentCompleted returns the PercentCompleted field if it's non-nil, zero value otherwise. +func (s *SubIssuesSummary) GetPercentCompleted() int { + if s == nil || s.PercentCompleted == nil { + return 0 } - return p.Repo + return *s.PercentCompleted } -// GetReview returns the Review field. -func (p *PullRequestReviewEvent) GetReview() *PullRequestReview { - if p == nil { - return nil +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (s *SubIssuesSummary) GetTotal() int { + if s == nil || s.Total == nil { + return 0 } - return p.Review + return *s.Total } -// GetSender returns the Sender field. -func (p *PullRequestReviewEvent) GetSender() *User { - if p == nil { - return nil +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (s *Subscription) GetCreatedAt() Timestamp { + if s == nil || s.CreatedAt == nil { + return Timestamp{} } - return p.Sender + return *s.CreatedAt } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetBody() string { - if p == nil || p.Body == nil { +// GetIgnored returns the Ignored field if it's non-nil, zero value otherwise. +func (s *Subscription) GetIgnored() bool { + if s == nil || s.Ignored == nil { + return false + } + return *s.Ignored +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (s *Subscription) GetReason() string { + if s == nil || s.Reason == nil { return "" } - return *p.Body + return *s.Reason } -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetCommitID() string { - if p == nil || p.CommitID == nil { +// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. +func (s *Subscription) GetRepositoryURL() string { + if s == nil || s.RepositoryURL == nil { return "" } - return *p.CommitID + return *s.RepositoryURL } -// GetEvent returns the Event field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetEvent() string { - if p == nil || p.Event == nil { +// GetSubscribed returns the Subscribed field if it's non-nil, zero value otherwise. +func (s *Subscription) GetSubscribed() bool { + if s == nil || s.Subscribed == nil { + return false + } + return *s.Subscribed +} + +// GetThreadURL returns the ThreadURL field if it's non-nil, zero value otherwise. +func (s *Subscription) GetThreadURL() string { + if s == nil || s.ThreadURL == nil { return "" } - return *p.Event + return *s.ThreadURL } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetNodeID() string { - if p == nil || p.NodeID == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *Subscription) GetURL() string { + if s == nil || s.URL == nil { return "" } - return *p.NodeID + return *s.URL } -// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. -func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { - if p == nil { +// GetNodes returns the Nodes slice if it's non-nil, nil otherwise. +func (s *SystemRequirements) GetNodes() []*SystemRequirementsNode { + if s == nil || s.Nodes == nil { return nil } - return p.DismissalRestrictionsRequest + return s.Nodes } -// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. -func (p *PullRequestReviewsEnforcementUpdate) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { - if p == nil { - return nil +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SystemRequirements) GetStatus() string { + if s == nil || s.Status == nil { + return "" + } + return *s.Status +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNode) GetHostname() string { + if s == nil || s.Hostname == nil { + return "" } - return p.DismissalRestrictionsRequest + return *s.Hostname } -// GetDismissStaleReviews returns the DismissStaleReviews field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewsEnforcementUpdate) GetDismissStaleReviews() bool { - if p == nil || p.DismissStaleReviews == nil { - return false +// GetRolesStatus returns the RolesStatus slice if it's non-nil, nil otherwise. +func (s *SystemRequirementsNode) GetRolesStatus() []*SystemRequirementsNodeRoleStatus { + if s == nil || s.RolesStatus == nil { + return nil } - return *p.DismissStaleReviews + return s.RolesStatus } -// GetMergablePulls returns the MergablePulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetMergablePulls() int { - if p == nil || p.MergablePulls == nil { - return 0 +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNode) GetStatus() string { + if s == nil || s.Status == nil { + return "" } - return *p.MergablePulls + return *s.Status } -// GetMergedPulls returns the MergedPulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetMergedPulls() int { - if p == nil || p.MergedPulls == nil { - return 0 +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNodeRoleStatus) GetRole() string { + if s == nil || s.Role == nil { + return "" } - return *p.MergedPulls + return *s.Role } -// GetTotalPulls returns the TotalPulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetTotalPulls() int { - if p == nil || p.TotalPulls == nil { - return 0 +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNodeRoleStatus) GetStatus() string { + if s == nil || s.Status == nil { + return "" } - return *p.TotalPulls + return *s.Status } -// GetUnmergablePulls returns the UnmergablePulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetUnmergablePulls() int { - if p == nil || p.UnmergablePulls == nil { - return 0 +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (t *Tag) GetMessage() string { + if t == nil || t.Message == nil { + return "" } - return *p.UnmergablePulls + return *t.Message } -// GetCommits returns the Commits field if it's non-nil, zero value otherwise. -func (p *PunchCard) GetCommits() int { - if p == nil || p.Commits == nil { - return 0 +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (t *Tag) GetNodeID() string { + if t == nil || t.NodeID == nil { + return "" } - return *p.Commits + return *t.NodeID } -// GetDay returns the Day field if it's non-nil, zero value otherwise. -func (p *PunchCard) GetDay() int { - if p == nil || p.Day == nil { - return 0 +// GetObject returns the Object field. +func (t *Tag) GetObject() *GitObject { + if t == nil { + return nil } - return *p.Day + return t.Object } -// GetHour returns the Hour field if it's non-nil, zero value otherwise. -func (p *PunchCard) GetHour() int { - if p == nil || p.Hour == nil { - return 0 +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *Tag) GetSHA() string { + if t == nil || t.SHA == nil { + return "" } - return *p.Hour + return *t.SHA } -// GetAfter returns the After field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetAfter() string { - if p == nil || p.After == nil { +// GetTag returns the Tag field if it's non-nil, zero value otherwise. +func (t *Tag) GetTag() string { + if t == nil || t.Tag == nil { return "" } - return *p.After + return *t.Tag } -// GetBaseRef returns the BaseRef field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetBaseRef() string { - if p == nil || p.BaseRef == nil { - return "" +// GetTagger returns the Tagger field. +func (t *Tag) GetTagger() *CommitAuthor { + if t == nil { + return nil } - return *p.BaseRef + return t.Tagger } -// GetBefore returns the Before field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetBefore() string { - if p == nil || p.Before == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *Tag) GetURL() string { + if t == nil || t.URL == nil { return "" } - return *p.Before + return *t.URL } -// GetCompare returns the Compare field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetCompare() string { - if p == nil || p.Compare == nil { - return "" +// GetVerification returns the Verification field. +func (t *Tag) GetVerification() *SignatureVerification { + if t == nil { + return nil } - return *p.Compare + return t.Verification } -// GetCreated returns the Created field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetCreated() bool { - if p == nil || p.Created == nil { - return false +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *TagProtection) GetID() int64 { + if t == nil || t.ID == nil { + return 0 } - return *p.Created + return *t.ID } -// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetDeleted() bool { - if p == nil || p.Deleted == nil { - return false +// GetPattern returns the Pattern field if it's non-nil, zero value otherwise. +func (t *TagProtection) GetPattern() string { + if t == nil || t.Pattern == nil { + return "" } - return *p.Deleted + return *t.Pattern } -// GetDistinctSize returns the DistinctSize field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetDistinctSize() int { - if p == nil || p.DistinctSize == nil { - return 0 +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (t *TaskStep) GetCompletedAt() Timestamp { + if t == nil || t.CompletedAt == nil { + return Timestamp{} } - return *p.DistinctSize + return *t.CompletedAt } -// GetForced returns the Forced field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetForced() bool { - if p == nil || p.Forced == nil { - return false +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (t *TaskStep) GetConclusion() string { + if t == nil || t.Conclusion == nil { + return "" } - return *p.Forced + return *t.Conclusion } -// GetHead returns the Head field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetHead() string { - if p == nil || p.Head == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *TaskStep) GetName() string { + if t == nil || t.Name == nil { return "" } - return *p.Head + return *t.Name } -// GetHeadCommit returns the HeadCommit field. -func (p *PushEvent) GetHeadCommit() *PushEventCommit { - if p == nil { - return nil +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (t *TaskStep) GetNumber() int64 { + if t == nil || t.Number == nil { + return 0 } - return p.HeadCommit + return *t.Number } -// GetInstallation returns the Installation field. -func (p *PushEvent) GetInstallation() *Installation { - if p == nil { - return nil +// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. +func (t *TaskStep) GetStartedAt() Timestamp { + if t == nil || t.StartedAt == nil { + return Timestamp{} } - return p.Installation + return *t.StartedAt } -// GetPusher returns the Pusher field. -func (p *PushEvent) GetPusher() *User { - if p == nil { - return nil +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (t *TaskStep) GetStatus() string { + if t == nil || t.Status == nil { + return "" } - return p.Pusher + return *t.Status } -// GetPushID returns the PushID field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetPushID() int64 { - if p == nil || p.PushID == nil { - return 0 +// GetAccessSource returns the AccessSource field if it's non-nil, zero value otherwise. +func (t *Team) GetAccessSource() string { + if t == nil || t.AccessSource == nil { + return "" } - return *p.PushID + return *t.AccessSource } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetRef() string { - if p == nil || p.Ref == nil { +// GetAssignment returns the Assignment field if it's non-nil, zero value otherwise. +func (t *Team) GetAssignment() string { + if t == nil || t.Assignment == nil { return "" } - return *p.Ref + return *t.Assignment } -// GetRepo returns the Repo field. -func (p *PushEvent) GetRepo() *PushEventRepository { - if p == nil { - return nil +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (t *Team) GetDescription() string { + if t == nil || t.Description == nil { + return "" } - return p.Repo + return *t.Description } -// GetSender returns the Sender field. -func (p *PushEvent) GetSender() *User { - if p == nil { - return nil +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (t *Team) GetHTMLURL() string { + if t == nil || t.HTMLURL == nil { + return "" } - return p.Sender + return *t.HTMLURL } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetSize() int { - if p == nil || p.Size == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *Team) GetID() int64 { + if t == nil || t.ID == nil { return 0 } - return *p.Size + return *t.ID } -// GetAuthor returns the Author field. -func (p *PushEventCommit) GetAuthor() *CommitAuthor { - if p == nil { - return nil +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (t *Team) GetLDAPDN() string { + if t == nil || t.LDAPDN == nil { + return "" } - return p.Author + return *t.LDAPDN } -// GetCommitter returns the Committer field. -func (p *PushEventCommit) GetCommitter() *CommitAuthor { - if p == nil { - return nil +// GetMembersCount returns the MembersCount field if it's non-nil, zero value otherwise. +func (t *Team) GetMembersCount() int { + if t == nil || t.MembersCount == nil { + return 0 } - return p.Committer + return *t.MembersCount } -// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetDistinct() bool { - if p == nil || p.Distinct == nil { - return false +// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. +func (t *Team) GetMembersURL() string { + if t == nil || t.MembersURL == nil { + return "" } - return *p.Distinct + return *t.MembersURL } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetID() string { - if p == nil || p.ID == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *Team) GetName() string { + if t == nil || t.Name == nil { return "" } - return *p.ID + return *t.Name } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetMessage() string { - if p == nil || p.Message == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (t *Team) GetNodeID() string { + if t == nil || t.NodeID == nil { return "" } - return *p.Message + return *t.NodeID } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetSHA() string { - if p == nil || p.SHA == nil { +// GetNotificationSetting returns the NotificationSetting field if it's non-nil, zero value otherwise. +func (t *Team) GetNotificationSetting() string { + if t == nil || t.NotificationSetting == nil { return "" } - return *p.SHA + return *t.NotificationSetting } -// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetTimestamp() Timestamp { - if p == nil || p.Timestamp == nil { - return Timestamp{} +// GetOrganization returns the Organization field. +func (t *Team) GetOrganization() *Organization { + if t == nil { + return nil + } + return t.Organization +} + +// GetParent returns the Parent field. +func (t *Team) GetParent() *Team { + if t == nil { + return nil + } + return t.Parent +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (t *Team) GetPermission() string { + if t == nil || t.Permission == nil { + return "" + } + return *t.Permission +} + +// GetPermissions returns the Permissions map if it's non-nil, an empty map otherwise. +func (t *Team) GetPermissions() map[string]bool { + if t == nil || t.Permissions == nil { + return map[string]bool{} } - return *p.Timestamp + return t.Permissions } -// GetTreeID returns the TreeID field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetTreeID() string { - if p == nil || p.TreeID == nil { +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (t *Team) GetPrivacy() string { + if t == nil || t.Privacy == nil { return "" } - return *p.TreeID + return *t.Privacy } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetURL() string { - if p == nil || p.URL == nil { - return "" +// GetReposCount returns the ReposCount field if it's non-nil, zero value otherwise. +func (t *Team) GetReposCount() int { + if t == nil || t.ReposCount == nil { + return 0 } - return *p.URL + return *t.ReposCount } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (p *PushEventRepoOwner) GetEmail() string { - if p == nil || p.Email == nil { +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (t *Team) GetRepositoriesURL() string { + if t == nil || t.RepositoriesURL == nil { return "" } - return *p.Email + return *t.RepositoriesURL } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *PushEventRepoOwner) GetName() string { - if p == nil || p.Name == nil { +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (t *Team) GetSlug() string { + if t == nil || t.Slug == nil { return "" } - return *p.Name + return *t.Slug } -// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetArchiveURL() string { - if p == nil || p.ArchiveURL == nil { +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (t *Team) GetType() string { + if t == nil || t.Type == nil { return "" } - return *p.ArchiveURL + return *t.Type } -// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetCloneURL() string { - if p == nil || p.CloneURL == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *Team) GetURL() string { + if t == nil || t.URL == nil { return "" } - return *p.CloneURL + return *t.URL } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} +// GetInstallation returns the Installation field. +func (t *TeamAddEvent) GetInstallation() *Installation { + if t == nil { + return nil } - return *p.CreatedAt + return t.Installation } -// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetDefaultBranch() string { - if p == nil || p.DefaultBranch == nil { - return "" +// GetOrg returns the Org field. +func (t *TeamAddEvent) GetOrg() *Organization { + if t == nil { + return nil } - return *p.DefaultBranch + return t.Org } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetDescription() string { - if p == nil || p.Description == nil { - return "" +// GetRepo returns the Repo field. +func (t *TeamAddEvent) GetRepo() *Repository { + if t == nil { + return nil } - return *p.Description + return t.Repo } -// GetFork returns the Fork field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetFork() bool { - if p == nil || p.Fork == nil { - return false +// GetSender returns the Sender field. +func (t *TeamAddEvent) GetSender() *User { + if t == nil { + return nil } - return *p.Fork + return t.Sender } -// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetForksCount() int { - if p == nil || p.ForksCount == nil { - return 0 +// GetTeam returns the Team field. +func (t *TeamAddEvent) GetTeam() *Team { + if t == nil { + return nil } - return *p.ForksCount + return t.Team } -// GetFullName returns the FullName field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetFullName() string { - if p == nil || p.FullName == nil { +// GetRole returns the Role field. +func (t *TeamAddTeamMembershipOptions) GetRole() string { + if t == nil { return "" } - return *p.FullName + return t.Role } -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetGitURL() string { - if p == nil || p.GitURL == nil { +// GetPermission returns the Permission field. +func (t *TeamAddTeamRepoOptions) GetPermission() string { + if t == nil { return "" } - return *p.GitURL + return t.Permission } -// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasDownloads() bool { - if p == nil || p.HasDownloads == nil { - return false +// GetDescription returns the Description field. +func (t *TeamChange) GetDescription() *TeamDescription { + if t == nil { + return nil } - return *p.HasDownloads + return t.Description } -// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasIssues() bool { - if p == nil || p.HasIssues == nil { - return false +// GetName returns the Name field. +func (t *TeamChange) GetName() *TeamName { + if t == nil { + return nil } - return *p.HasIssues + return t.Name } -// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasPages() bool { - if p == nil || p.HasPages == nil { - return false +// GetPrivacy returns the Privacy field. +func (t *TeamChange) GetPrivacy() *TeamPrivacy { + if t == nil { + return nil } - return *p.HasPages + return t.Privacy } -// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasWiki() bool { - if p == nil || p.HasWiki == nil { - return false +// GetRepository returns the Repository field. +func (t *TeamChange) GetRepository() *TeamRepository { + if t == nil { + return nil } - return *p.HasWiki + return t.Repository } -// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHomepage() string { - if p == nil || p.Homepage == nil { +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (t *TeamDescription) GetFrom() string { + if t == nil || t.From == nil { return "" } - return *p.Homepage + return *t.From } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" +// GetAuthor returns the Author field. +func (t *TeamDiscussion) GetAuthor() *User { + if t == nil { + return nil } - return *p.HTMLURL + return t.Author } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetID() int64 { - if p == nil || p.ID == nil { - return 0 +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetBody() string { + if t == nil || t.Body == nil { + return "" } - return *p.ID + return *t.Body } -// GetLanguage returns the Language field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetLanguage() string { - if p == nil || p.Language == nil { +// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetBodyHTML() string { + if t == nil || t.BodyHTML == nil { return "" } - return *p.Language + return *t.BodyHTML } -// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetMasterBranch() string { - if p == nil || p.MasterBranch == nil { +// GetBodyVersion returns the BodyVersion field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetBodyVersion() string { + if t == nil || t.BodyVersion == nil { return "" } - return *p.MasterBranch + return *t.BodyVersion } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetName() string { - if p == nil || p.Name == nil { - return "" +// GetCommentsCount returns the CommentsCount field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetCommentsCount() int { + if t == nil || t.CommentsCount == nil { + return 0 } - return *p.Name + return *t.CommentsCount } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetNodeID() string { - if p == nil || p.NodeID == nil { +// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetCommentsURL() string { + if t == nil || t.CommentsURL == nil { return "" } - return *p.NodeID + return *t.CommentsURL } -// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetOpenIssuesCount() int { - if p == nil || p.OpenIssuesCount == nil { - return 0 +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetCreatedAt() Timestamp { + if t == nil || t.CreatedAt == nil { + return Timestamp{} } - return *p.OpenIssuesCount + return *t.CreatedAt } -// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetOrganization() string { - if p == nil || p.Organization == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetHTMLURL() string { + if t == nil || t.HTMLURL == nil { return "" } - return *p.Organization -} - -// GetOwner returns the Owner field. -func (p *PushEventRepository) GetOwner() *User { - if p == nil { - return nil - } - return p.Owner + return *t.HTMLURL } -// GetPrivate returns the Private field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetPrivate() bool { - if p == nil || p.Private == nil { - return false +// GetLastEditedAt returns the LastEditedAt field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetLastEditedAt() Timestamp { + if t == nil || t.LastEditedAt == nil { + return Timestamp{} } - return *p.Private + return *t.LastEditedAt } -// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetPullsURL() string { - if p == nil || p.PullsURL == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetNodeID() string { + if t == nil || t.NodeID == nil { return "" } - return *p.PullsURL + return *t.NodeID } -// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetPushedAt() Timestamp { - if p == nil || p.PushedAt == nil { - return Timestamp{} +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetNumber() int { + if t == nil || t.Number == nil { + return 0 } - return *p.PushedAt + return *t.Number } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetSize() int { - if p == nil || p.Size == nil { - return 0 +// GetPinned returns the Pinned field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetPinned() bool { + if t == nil || t.Pinned == nil { + return false } - return *p.Size + return *t.Pinned } -// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetSSHURL() string { - if p == nil || p.SSHURL == nil { - return "" +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetPrivate() bool { + if t == nil || t.Private == nil { + return false } - return *p.SSHURL + return *t.Private } -// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetStargazersCount() int { - if p == nil || p.StargazersCount == nil { - return 0 +// GetReactions returns the Reactions field. +func (t *TeamDiscussion) GetReactions() *Reactions { + if t == nil { + return nil } - return *p.StargazersCount + return t.Reactions } -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetStatusesURL() string { - if p == nil || p.StatusesURL == nil { +// GetTeamURL returns the TeamURL field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetTeamURL() string { + if t == nil || t.TeamURL == nil { return "" } - return *p.StatusesURL + return *t.TeamURL } -// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetSVNURL() string { - if p == nil || p.SVNURL == nil { +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (t *TeamDiscussion) GetTitle() string { + if t == nil || t.Title == nil { return "" } - return *p.SVNURL + return *t.Title } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { +func (t *TeamDiscussion) GetUpdatedAt() Timestamp { + if t == nil || t.UpdatedAt == nil { return Timestamp{} } - return *p.UpdatedAt + return *t.UpdatedAt } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetURL() string { - if p == nil || p.URL == nil { +func (t *TeamDiscussion) GetURL() string { + if t == nil || t.URL == nil { return "" } - return *p.URL + return *t.URL } -// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetWatchersCount() int { - if p == nil || p.WatchersCount == nil { - return 0 +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (t *TeamEvent) GetAction() string { + if t == nil || t.Action == nil { + return "" } - return *p.WatchersCount + return *t.Action } -// GetCore returns the Core field. -func (r *RateLimits) GetCore() *Rate { - if r == nil { +// GetChanges returns the Changes field. +func (t *TeamEvent) GetChanges() *TeamChange { + if t == nil { return nil } - return r.Core + return t.Changes } -// GetSearch returns the Search field. -func (r *RateLimits) GetSearch() *Rate { - if r == nil { +// GetInstallation returns the Installation field. +func (t *TeamEvent) GetInstallation() *Installation { + if t == nil { return nil } - return r.Search + return t.Installation } -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (r *Reaction) GetContent() string { - if r == nil || r.Content == nil { - return "" +// GetOrg returns the Org field. +func (t *TeamEvent) GetOrg() *Organization { + if t == nil { + return nil } - return *r.Content + return t.Org } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *Reaction) GetID() int64 { - if r == nil || r.ID == nil { - return 0 +// GetRepo returns the Repo field. +func (t *TeamEvent) GetRepo() *Repository { + if t == nil { + return nil } - return *r.ID + return t.Repo } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *Reaction) GetNodeID() string { - if r == nil || r.NodeID == nil { - return "" +// GetSender returns the Sender field. +func (t *TeamEvent) GetSender() *User { + if t == nil { + return nil } - return *r.NodeID + return t.Sender } -// GetUser returns the User field. -func (r *Reaction) GetUser() *User { - if r == nil { +// GetTeam returns the Team field. +func (t *TeamEvent) GetTeam() *Team { + if t == nil { return nil } - return r.User + return t.Team } -// GetConfused returns the Confused field if it's non-nil, zero value otherwise. -func (r *Reactions) GetConfused() int { - if r == nil || r.Confused == nil { - return 0 +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetDescription() string { + if t == nil || t.Description == nil { + return "" } - return *r.Confused + return *t.Description } -// GetHeart returns the Heart field if it's non-nil, zero value otherwise. -func (r *Reactions) GetHeart() int { - if r == nil || r.Heart == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetID() int64 { + if t == nil || t.ID == nil { return 0 } - return *r.Heart + return *t.ID } -// GetHooray returns the Hooray field if it's non-nil, zero value otherwise. -func (r *Reactions) GetHooray() int { - if r == nil || r.Hooray == nil { - return 0 +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetLDAPDN() string { + if t == nil || t.LDAPDN == nil { + return "" } - return *r.Hooray + return *t.LDAPDN } -// GetLaugh returns the Laugh field if it's non-nil, zero value otherwise. -func (r *Reactions) GetLaugh() int { - if r == nil || r.Laugh == nil { - return 0 +// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetMembersURL() string { + if t == nil || t.MembersURL == nil { + return "" } - return *r.Laugh + return *t.MembersURL } -// GetMinusOne returns the MinusOne field if it's non-nil, zero value otherwise. -func (r *Reactions) GetMinusOne() int { - if r == nil || r.MinusOne == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetName() string { + if t == nil || t.Name == nil { + return "" } - return *r.MinusOne + return *t.Name } -// GetPlusOne returns the PlusOne field if it's non-nil, zero value otherwise. -func (r *Reactions) GetPlusOne() int { - if r == nil || r.PlusOne == nil { - return 0 +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetPermission() string { + if t == nil || t.Permission == nil { + return "" } - return *r.PlusOne + return *t.Permission } -// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. -func (r *Reactions) GetTotalCount() int { - if r == nil || r.TotalCount == nil { - return 0 +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetPrivacy() string { + if t == nil || t.Privacy == nil { + return "" } - return *r.TotalCount + return *t.Privacy } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *Reactions) GetURL() string { - if r == nil || r.URL == nil { +// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetRepositoriesURL() string { + if t == nil || t.RepositoriesURL == nil { return "" } - return *r.URL + return *t.RepositoriesURL } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *Reference) GetNodeID() string { - if r == nil || r.NodeID == nil { +// GetSlug returns the Slug field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetSlug() string { + if t == nil || t.Slug == nil { return "" } - return *r.NodeID + return *t.Slug } -// GetObject returns the Object field. -func (r *Reference) GetObject() *GitObject { - if r == nil { - return nil +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *TeamLDAPMapping) GetURL() string { + if t == nil || t.URL == nil { + return "" } - return r.Object + return *t.URL } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (r *Reference) GetRef() string { - if r == nil || r.Ref == nil { +// GetRole returns the Role field. +func (t *TeamListTeamMembersOptions) GetRole() string { + if t == nil { return "" } - return *r.Ref + return t.Role } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *Reference) GetURL() string { - if r == nil || r.URL == nil { +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (t *TeamName) GetFrom() string { + if t == nil || t.From == nil { return "" } - return *r.URL + return *t.From } -// GetBrowserDownloadURL returns the BrowserDownloadURL field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetBrowserDownloadURL() string { - if r == nil || r.BrowserDownloadURL == nil { - return "" +// GetFrom returns the From field. +func (t *TeamPermissions) GetFrom() *TeamPermissionsFrom { + if t == nil { + return nil } - return *r.BrowserDownloadURL + return t.From } -// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetContentType() string { - if r == nil || r.ContentType == nil { - return "" +// GetAdmin returns the Admin field if it's non-nil, zero value otherwise. +func (t *TeamPermissionsFrom) GetAdmin() bool { + if t == nil || t.Admin == nil { + return false } - return *r.ContentType + return *t.Admin } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} +// GetPull returns the Pull field if it's non-nil, zero value otherwise. +func (t *TeamPermissionsFrom) GetPull() bool { + if t == nil || t.Pull == nil { + return false } - return *r.CreatedAt + return *t.Pull } -// GetDownloadCount returns the DownloadCount field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetDownloadCount() int { - if r == nil || r.DownloadCount == nil { - return 0 +// GetPush returns the Push field if it's non-nil, zero value otherwise. +func (t *TeamPermissionsFrom) GetPush() bool { + if t == nil || t.Push == nil { + return false } - return *r.DownloadCount + return *t.Push +} + +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (t *TeamPrivacy) GetFrom() string { + if t == nil || t.From == nil { + return "" + } + return *t.From +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (t *TeamProjectOptions) GetPermission() string { + if t == nil || t.Permission == nil { + return "" + } + return *t.Permission } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetID() int64 { - if r == nil || r.ID == nil { - return 0 +// GetPermissions returns the Permissions field. +func (t *TeamRepository) GetPermissions() *TeamPermissions { + if t == nil { + return nil } - return *r.ID + return t.Permissions } -// GetLabel returns the Label field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetLabel() string { - if r == nil || r.Label == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (t *TemplateRepoRequest) GetDescription() string { + if t == nil || t.Description == nil { return "" } - return *r.Label + return *t.Description } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetName() string { - if r == nil || r.Name == nil { +// GetIncludeAllBranches returns the IncludeAllBranches field if it's non-nil, zero value otherwise. +func (t *TemplateRepoRequest) GetIncludeAllBranches() bool { + if t == nil || t.IncludeAllBranches == nil { + return false + } + return *t.IncludeAllBranches +} + +// GetName returns the Name field. +func (t *TemplateRepoRequest) GetName() string { + if t == nil { return "" } - return *r.Name + return t.Name } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetNodeID() string { - if r == nil || r.NodeID == nil { +// GetOwner returns the Owner field if it's non-nil, zero value otherwise. +func (t *TemplateRepoRequest) GetOwner() string { + if t == nil || t.Owner == nil { return "" } - return *r.NodeID + return *t.Owner } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetSize() int { - if r == nil || r.Size == nil { - return 0 +// GetPrivate returns the Private field if it's non-nil, zero value otherwise. +func (t *TemplateRepoRequest) GetPrivate() bool { + if t == nil || t.Private == nil { + return false } - return *r.Size + return *t.Private } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetState() string { - if r == nil || r.State == nil { +// GetFragment returns the Fragment field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetFragment() string { + if t == nil || t.Fragment == nil { return "" } - return *r.State + return *t.Fragment } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetUpdatedAt() Timestamp { - if r == nil || r.UpdatedAt == nil { - return Timestamp{} +// GetMatches returns the Matches slice if it's non-nil, nil otherwise. +func (t *TextMatch) GetMatches() []*Match { + if t == nil || t.Matches == nil { + return nil } - return *r.UpdatedAt + return t.Matches } -// GetUploader returns the Uploader field. -func (r *ReleaseAsset) GetUploader() *User { - if r == nil { - return nil +// GetObjectType returns the ObjectType field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetObjectType() string { + if t == nil || t.ObjectType == nil { + return "" } - return r.Uploader + return *t.ObjectType } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetURL() string { - if r == nil || r.URL == nil { +// GetObjectURL returns the ObjectURL field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetObjectURL() string { + if t == nil || t.ObjectURL == nil { return "" } - return *r.URL + return *t.ObjectURL } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (r *ReleaseEvent) GetAction() string { - if r == nil || r.Action == nil { +// GetProperty returns the Property field if it's non-nil, zero value otherwise. +func (t *TextMatch) GetProperty() string { + if t == nil || t.Property == nil { return "" } - return *r.Action + return *t.Property } -// GetInstallation returns the Installation field. -func (r *ReleaseEvent) GetInstallation() *Installation { - if r == nil { +// GetActor returns the Actor field. +func (t *Timeline) GetActor() *User { + if t == nil { return nil } - return r.Installation + return t.Actor } -// GetRelease returns the Release field. -func (r *ReleaseEvent) GetRelease() *RepositoryRelease { - if r == nil { +// GetAssignee returns the Assignee field. +func (t *Timeline) GetAssignee() *User { + if t == nil { return nil } - return r.Release + return t.Assignee } -// GetRepo returns the Repo field. -func (r *ReleaseEvent) GetRepo() *Repository { - if r == nil { +// GetAssigner returns the Assigner field. +func (t *Timeline) GetAssigner() *User { + if t == nil { return nil } - return r.Repo + return t.Assigner } -// GetSender returns the Sender field. -func (r *ReleaseEvent) GetSender() *User { - if r == nil { +// GetAuthor returns the Author field. +func (t *Timeline) GetAuthor() *CommitAuthor { + if t == nil { return nil } - return r.Sender + return t.Author } -// GetFrom returns the From field if it's non-nil, zero value otherwise. -func (r *Rename) GetFrom() string { - if r == nil || r.From == nil { +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (t *Timeline) GetBody() string { + if t == nil || t.Body == nil { return "" } - return *r.From + return *t.Body } -// GetTo returns the To field if it's non-nil, zero value otherwise. -func (r *Rename) GetTo() string { - if r == nil || r.To == nil { +// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. +func (t *Timeline) GetCommitID() string { + if t == nil || t.CommitID == nil { return "" } - return *r.To + return *t.CommitID } -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (r *RepositoriesSearchResult) GetIncompleteResults() bool { - if r == nil || r.IncompleteResults == nil { - return false +// GetCommitter returns the Committer field. +func (t *Timeline) GetCommitter() *CommitAuthor { + if t == nil { + return nil } - return *r.IncompleteResults + return t.Committer } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (r *RepositoriesSearchResult) GetTotal() int { - if r == nil || r.Total == nil { - return 0 +// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. +func (t *Timeline) GetCommitURL() string { + if t == nil || t.CommitURL == nil { + return "" } - return *r.Total + return *t.CommitURL } -// GetAllowMergeCommit returns the AllowMergeCommit field if it's non-nil, zero value otherwise. -func (r *Repository) GetAllowMergeCommit() bool { - if r == nil || r.AllowMergeCommit == nil { - return false +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (t *Timeline) GetCreatedAt() Timestamp { + if t == nil || t.CreatedAt == nil { + return Timestamp{} } - return *r.AllowMergeCommit + return *t.CreatedAt } -// GetAllowRebaseMerge returns the AllowRebaseMerge field if it's non-nil, zero value otherwise. -func (r *Repository) GetAllowRebaseMerge() bool { - if r == nil || r.AllowRebaseMerge == nil { - return false +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (t *Timeline) GetEvent() string { + if t == nil || t.Event == nil { + return "" } - return *r.AllowRebaseMerge + return *t.Event } -// GetAllowSquashMerge returns the AllowSquashMerge field if it's non-nil, zero value otherwise. -func (r *Repository) GetAllowSquashMerge() bool { - if r == nil || r.AllowSquashMerge == nil { - return false +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *Timeline) GetID() int64 { + if t == nil || t.ID == nil { + return 0 } - return *r.AllowSquashMerge + return *t.ID } -// GetArchived returns the Archived field if it's non-nil, zero value otherwise. -func (r *Repository) GetArchived() bool { - if r == nil || r.Archived == nil { - return false +// GetLabel returns the Label field. +func (t *Timeline) GetLabel() *Label { + if t == nil { + return nil } - return *r.Archived + return t.Label } -// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetArchiveURL() string { - if r == nil || r.ArchiveURL == nil { +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (t *Timeline) GetMessage() string { + if t == nil || t.Message == nil { return "" } - return *r.ArchiveURL + return *t.Message } -// GetAssigneesURL returns the AssigneesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetAssigneesURL() string { - if r == nil || r.AssigneesURL == nil { - return "" +// GetMilestone returns the Milestone field. +func (t *Timeline) GetMilestone() *Milestone { + if t == nil { + return nil } - return *r.AssigneesURL + return t.Milestone } -// GetAutoInit returns the AutoInit field if it's non-nil, zero value otherwise. -func (r *Repository) GetAutoInit() bool { - if r == nil || r.AutoInit == nil { - return false +// GetParents returns the Parents slice if it's non-nil, nil otherwise. +func (t *Timeline) GetParents() []*Commit { + if t == nil || t.Parents == nil { + return nil } - return *r.AutoInit + return t.Parents } -// GetBlobsURL returns the BlobsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetBlobsURL() string { - if r == nil || r.BlobsURL == nil { - return "" +// GetPerformedViaGithubApp returns the PerformedViaGithubApp field. +func (t *Timeline) GetPerformedViaGithubApp() *App { + if t == nil { + return nil } - return *r.BlobsURL + return t.PerformedViaGithubApp } -// GetBranchesURL returns the BranchesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetBranchesURL() string { - if r == nil || r.BranchesURL == nil { - return "" +// GetRename returns the Rename field. +func (t *Timeline) GetRename() *Rename { + if t == nil { + return nil } - return *r.BranchesURL + return t.Rename } -// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCloneURL() string { - if r == nil || r.CloneURL == nil { - return "" +// GetRequestedTeam returns the RequestedTeam field. +func (t *Timeline) GetRequestedTeam() *Team { + if t == nil { + return nil } - return *r.CloneURL + return t.RequestedTeam } -// GetCodeOfConduct returns the CodeOfConduct field. -func (r *Repository) GetCodeOfConduct() *CodeOfConduct { - if r == nil { +// GetRequester returns the Requester field. +func (t *Timeline) GetRequester() *User { + if t == nil { return nil } - return r.CodeOfConduct + return t.Requester } -// GetCollaboratorsURL returns the CollaboratorsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCollaboratorsURL() string { - if r == nil || r.CollaboratorsURL == nil { +// GetReviewer returns the Reviewer field. +func (t *Timeline) GetReviewer() *User { + if t == nil { + return nil + } + return t.Reviewer +} + +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *Timeline) GetSHA() string { + if t == nil || t.SHA == nil { return "" } - return *r.CollaboratorsURL + return *t.SHA +} + +// GetSource returns the Source field. +func (t *Timeline) GetSource() *Source { + if t == nil { + return nil + } + return t.Source } -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCommentsURL() string { - if r == nil || r.CommentsURL == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (t *Timeline) GetState() string { + if t == nil || t.State == nil { return "" } - return *r.CommentsURL + return *t.State } -// GetCommitsURL returns the CommitsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCommitsURL() string { - if r == nil || r.CommitsURL == nil { - return "" +// GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. +func (t *Timeline) GetSubmittedAt() Timestamp { + if t == nil || t.SubmittedAt == nil { + return Timestamp{} } - return *r.CommitsURL + return *t.SubmittedAt } -// GetCompareURL returns the CompareURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCompareURL() string { - if r == nil || r.CompareURL == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *Timeline) GetURL() string { + if t == nil || t.URL == nil { return "" } - return *r.CompareURL + return *t.URL } -// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetContentsURL() string { - if r == nil || r.ContentsURL == nil { - return "" +// GetUser returns the User field. +func (t *Timeline) GetUser() *User { + if t == nil { + return nil } - return *r.ContentsURL + return t.User } -// GetContributorsURL returns the ContributorsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetContributorsURL() string { - if r == nil || r.ContributorsURL == nil { +// GetGUID returns the GUID field if it's non-nil, zero value otherwise. +func (t *Tool) GetGUID() string { + if t == nil || t.GUID == nil { return "" } - return *r.ContributorsURL + return *t.GUID } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *Repository) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *Tool) GetName() string { + if t == nil || t.Name == nil { + return "" } - return *r.CreatedAt + return *t.Name } -// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. -func (r *Repository) GetDefaultBranch() string { - if r == nil || r.DefaultBranch == nil { +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (t *Tool) GetVersion() string { + if t == nil || t.Version == nil { return "" } - return *r.DefaultBranch + return *t.Version } -// GetDeploymentsURL returns the DeploymentsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetDeploymentsURL() string { - if r == nil || r.DeploymentsURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetCreatedAt() Timestamp { + if t == nil || t.CreatedAt == nil { + return Timestamp{} } - return *r.DeploymentsURL + return *t.CreatedAt } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (r *Repository) GetDescription() string { - if r == nil || r.Description == nil { +// GetCreatedBy returns the CreatedBy field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetCreatedBy() string { + if t == nil || t.CreatedBy == nil { return "" } - return *r.Description + return *t.CreatedBy } -// GetDisabled returns the Disabled field if it's non-nil, zero value otherwise. -func (r *Repository) GetDisabled() bool { - if r == nil || r.Disabled == nil { +// GetCurated returns the Curated field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetCurated() bool { + if t == nil || t.Curated == nil { return false } - return *r.Disabled + return *t.Curated } -// GetDownloadsURL returns the DownloadsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetDownloadsURL() string { - if r == nil || r.DownloadsURL == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetDescription() string { + if t == nil || t.Description == nil { return "" } - return *r.DownloadsURL + return *t.Description } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetEventsURL() string { - if r == nil || r.EventsURL == nil { +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetDisplayName() string { + if t == nil || t.DisplayName == nil { return "" } - return *r.EventsURL + return *t.DisplayName } -// GetFork returns the Fork field if it's non-nil, zero value otherwise. -func (r *Repository) GetFork() bool { - if r == nil || r.Fork == nil { +// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetFeatured() bool { + if t == nil || t.Featured == nil { return false } - return *r.Fork + return *t.Featured } -// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetForksCount() int { - if r == nil || r.ForksCount == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetName() string { + if t == nil || t.Name == nil { + return "" } - return *r.ForksCount + return *t.Name } -// GetForksURL returns the ForksURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetForksURL() string { - if r == nil || r.ForksURL == nil { - return "" +// GetScore returns the Score field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetScore() float64 { + if t == nil || t.Score == nil { + return 0 } - return *r.ForksURL + return *t.Score } -// GetFullName returns the FullName field if it's non-nil, zero value otherwise. -func (r *Repository) GetFullName() string { - if r == nil || r.FullName == nil { +// GetShortDescription returns the ShortDescription field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetShortDescription() string { + if t == nil || t.ShortDescription == nil { return "" } - return *r.FullName + return *t.ShortDescription } -// GetGitCommitsURL returns the GitCommitsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitCommitsURL() string { - if r == nil || r.GitCommitsURL == nil { +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (t *TopicResult) GetUpdatedAt() string { + if t == nil || t.UpdatedAt == nil { return "" } - return *r.GitCommitsURL + return *t.UpdatedAt } -// GetGitignoreTemplate returns the GitignoreTemplate field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitignoreTemplate() string { - if r == nil || r.GitignoreTemplate == nil { - return "" +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (t *TopicsSearchResult) GetIncompleteResults() bool { + if t == nil || t.IncompleteResults == nil { + return false } - return *r.GitignoreTemplate + return *t.IncompleteResults } -// GetGitRefsURL returns the GitRefsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitRefsURL() string { - if r == nil || r.GitRefsURL == nil { - return "" +// GetTopics returns the Topics slice if it's non-nil, nil otherwise. +func (t *TopicsSearchResult) GetTopics() []*TopicResult { + if t == nil || t.Topics == nil { + return nil } - return *r.GitRefsURL + return t.Topics } -// GetGitTagsURL returns the GitTagsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitTagsURL() string { - if r == nil || r.GitTagsURL == nil { - return "" +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (t *TopicsSearchResult) GetTotal() int { + if t == nil || t.Total == nil { + return 0 } - return *r.GitTagsURL + return *t.Total } -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitURL() string { - if r == nil || r.GitURL == nil { - return "" +// GetTotalActiveCachesCount returns the TotalActiveCachesCount field. +func (t *TotalCacheUsage) GetTotalActiveCachesCount() int { + if t == nil { + return 0 } - return *r.GitURL + return t.TotalActiveCachesCount } -// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasDownloads() bool { - if r == nil || r.HasDownloads == nil { - return false +// GetTotalActiveCachesUsageSizeInBytes returns the TotalActiveCachesUsageSizeInBytes field. +func (t *TotalCacheUsage) GetTotalActiveCachesUsageSizeInBytes() int64 { + if t == nil { + return 0 } - return *r.HasDownloads + return t.TotalActiveCachesUsageSizeInBytes } -// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasIssues() bool { - if r == nil || r.HasIssues == nil { - return false +// GetPer returns the Per field. +func (t *TrafficBreakdownOptions) GetPer() string { + if t == nil { + return "" } - return *r.HasIssues + return t.Per } -// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasPages() bool { - if r == nil || r.HasPages == nil { - return false +// GetClones returns the Clones slice if it's non-nil, nil otherwise. +func (t *TrafficClones) GetClones() []*TrafficData { + if t == nil || t.Clones == nil { + return nil } - return *r.HasPages + return t.Clones } -// GetHasProjects returns the HasProjects field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasProjects() bool { - if r == nil || r.HasProjects == nil { - return false +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficClones) GetCount() int { + if t == nil || t.Count == nil { + return 0 } - return *r.HasProjects + return *t.Count } -// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasWiki() bool { - if r == nil || r.HasWiki == nil { - return false +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficClones) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 } - return *r.HasWiki + return *t.Uniques } -// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. -func (r *Repository) GetHomepage() string { - if r == nil || r.Homepage == nil { - return "" +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficData) GetCount() int { + if t == nil || t.Count == nil { + return 0 } - return *r.Homepage + return *t.Count } -// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetHooksURL() string { - if r == nil || r.HooksURL == nil { - return "" +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (t *TrafficData) GetTimestamp() Timestamp { + if t == nil || t.Timestamp == nil { + return Timestamp{} } - return *r.HooksURL + return *t.Timestamp } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficData) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 } - return *r.HTMLURL + return *t.Uniques } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *Repository) GetID() int64 { - if r == nil || r.ID == nil { +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetCount() int { + if t == nil || t.Count == nil { return 0 } - return *r.ID + return *t.Count } -// GetIssueCommentURL returns the IssueCommentURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetIssueCommentURL() string { - if r == nil || r.IssueCommentURL == nil { +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetPath() string { + if t == nil || t.Path == nil { return "" } - return *r.IssueCommentURL + return *t.Path } -// GetIssueEventsURL returns the IssueEventsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetIssueEventsURL() string { - if r == nil || r.IssueEventsURL == nil { +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetTitle() string { + if t == nil || t.Title == nil { return "" } - return *r.IssueEventsURL + return *t.Title } -// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetIssuesURL() string { - if r == nil || r.IssuesURL == nil { - return "" +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficPath) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 } - return *r.IssuesURL + return *t.Uniques } -// GetIsTemplate returns the IsTemplate field if it's non-nil, zero value otherwise. -func (r *Repository) GetIsTemplate() bool { - if r == nil || r.IsTemplate == nil { - return false +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficReferrer) GetCount() int { + if t == nil || t.Count == nil { + return 0 } - return *r.IsTemplate + return *t.Count } -// GetKeysURL returns the KeysURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetKeysURL() string { - if r == nil || r.KeysURL == nil { +// GetReferrer returns the Referrer field if it's non-nil, zero value otherwise. +func (t *TrafficReferrer) GetReferrer() string { + if t == nil || t.Referrer == nil { return "" } - return *r.KeysURL + return *t.Referrer } -// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetLabelsURL() string { - if r == nil || r.LabelsURL == nil { - return "" +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficReferrer) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 } - return *r.LabelsURL + return *t.Uniques } -// GetLanguage returns the Language field if it's non-nil, zero value otherwise. -func (r *Repository) GetLanguage() string { - if r == nil || r.Language == nil { - return "" +// GetCount returns the Count field if it's non-nil, zero value otherwise. +func (t *TrafficViews) GetCount() int { + if t == nil || t.Count == nil { + return 0 } - return *r.Language + return *t.Count } -// GetLanguagesURL returns the LanguagesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetLanguagesURL() string { - if r == nil || r.LanguagesURL == nil { - return "" +// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. +func (t *TrafficViews) GetUniques() int { + if t == nil || t.Uniques == nil { + return 0 } - return *r.LanguagesURL + return *t.Uniques } -// GetLicense returns the License field. -func (r *Repository) GetLicense() *License { - if r == nil { +// GetViews returns the Views slice if it's non-nil, nil otherwise. +func (t *TrafficViews) GetViews() []*TrafficData { + if t == nil || t.Views == nil { return nil } - return r.License + return t.Views } -// GetLicenseTemplate returns the LicenseTemplate field if it's non-nil, zero value otherwise. -func (r *Repository) GetLicenseTemplate() string { - if r == nil || r.LicenseTemplate == nil { +// GetNewName returns the NewName field if it's non-nil, zero value otherwise. +func (t *TransferRequest) GetNewName() string { + if t == nil || t.NewName == nil { return "" } - return *r.LicenseTemplate + return *t.NewName } -// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. -func (r *Repository) GetMasterBranch() string { - if r == nil || r.MasterBranch == nil { +// GetNewOwner returns the NewOwner field. +func (t *TransferRequest) GetNewOwner() string { + if t == nil { return "" } - return *r.MasterBranch + return t.NewOwner } -// GetMergesURL returns the MergesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetMergesURL() string { - if r == nil || r.MergesURL == nil { - return "" +// GetTeamID returns the TeamID slice if it's non-nil, nil otherwise. +func (t *TransferRequest) GetTeamID() []int64 { + if t == nil || t.TeamID == nil { + return nil } - return *r.MergesURL + return t.TeamID } -// GetMilestonesURL returns the MilestonesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetMilestonesURL() string { - if r == nil || r.MilestonesURL == nil { - return "" +// GetEntries returns the Entries slice if it's non-nil, nil otherwise. +func (t *Tree) GetEntries() []*TreeEntry { + if t == nil || t.Entries == nil { + return nil } - return *r.MilestonesURL + return t.Entries } -// GetMirrorURL returns the MirrorURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetMirrorURL() string { - if r == nil || r.MirrorURL == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *Tree) GetSHA() string { + if t == nil || t.SHA == nil { return "" } - return *r.MirrorURL + return *t.SHA } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *Repository) GetName() string { - if r == nil || r.Name == nil { +// GetTruncated returns the Truncated field if it's non-nil, zero value otherwise. +func (t *Tree) GetTruncated() bool { + if t == nil || t.Truncated == nil { + return false + } + return *t.Truncated +} + +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetContent() string { + if t == nil || t.Content == nil { return "" } - return *r.Name + return *t.Content } -// GetNetworkCount returns the NetworkCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetNetworkCount() int { - if r == nil || r.NetworkCount == nil { - return 0 +// GetMode returns the Mode field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetMode() string { + if t == nil || t.Mode == nil { + return "" } - return *r.NetworkCount + return *t.Mode } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *Repository) GetNodeID() string { - if r == nil || r.NodeID == nil { +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetPath() string { + if t == nil || t.Path == nil { return "" } - return *r.NodeID + return *t.Path } -// GetNotificationsURL returns the NotificationsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetNotificationsURL() string { - if r == nil || r.NotificationsURL == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetSHA() string { + if t == nil || t.SHA == nil { return "" } - return *r.NotificationsURL + return *t.SHA } -// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetOpenIssuesCount() int { - if r == nil || r.OpenIssuesCount == nil { +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetSize() int { + if t == nil || t.Size == nil { return 0 } - return *r.OpenIssuesCount + return *t.Size } -// GetOrganization returns the Organization field. -func (r *Repository) GetOrganization() *Organization { - if r == nil { - return nil +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetType() string { + if t == nil || t.Type == nil { + return "" } - return r.Organization + return *t.Type } -// GetOwner returns the Owner field. -func (r *Repository) GetOwner() *User { - if r == nil { - return nil +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (t *TreeEntry) GetURL() string { + if t == nil || t.URL == nil { + return "" } - return r.Owner + return *t.URL } -// GetParent returns the Parent field. -func (r *Repository) GetParent() *Repository { - if r == nil { - return nil +// GetClientID returns the ClientID field. +func (u *UnauthenticatedRateLimitedTransport) GetClientID() string { + if u == nil { + return "" } - return r.Parent + return u.ClientID } -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (r *Repository) GetPermissions() map[string]bool { - if r == nil || r.Permissions == nil { - return map[string]bool{} +// GetClientSecret returns the ClientSecret field. +func (u *UnauthenticatedRateLimitedTransport) GetClientSecret() string { + if u == nil { + return "" } - return *r.Permissions + return u.ClientSecret } -// GetPrivate returns the Private field if it's non-nil, zero value otherwise. -func (r *Repository) GetPrivate() bool { - if r == nil || r.Private == nil { - return false +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (u *UpdateAppInstallationRepositoriesRequest) GetRepositories() []string { + if u == nil || u.Repositories == nil { + return nil } - return *r.Private + return u.Repositories } -// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetPullsURL() string { - if r == nil || r.PullsURL == nil { +// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. +func (u *UpdateAppInstallationRepositoriesRequest) GetRepositorySelection() string { + if u == nil || u.RepositorySelection == nil { return "" } - return *r.PullsURL + return *u.RepositorySelection } -// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. -func (r *Repository) GetPushedAt() Timestamp { - if r == nil || r.PushedAt == nil { - return Timestamp{} +// GetOp returns the Op field. +func (u *UpdateAttributeForSCIMUserOperations) GetOp() string { + if u == nil { + return "" } - return *r.PushedAt + return u.Op } -// GetReleasesURL returns the ReleasesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetReleasesURL() string { - if r == nil || r.ReleasesURL == nil { +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (u *UpdateAttributeForSCIMUserOperations) GetPath() string { + if u == nil || u.Path == nil { return "" } - return *r.ReleasesURL + return *u.Path } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *Repository) GetSize() int { - if r == nil || r.Size == nil { - return 0 +// GetValue returns the Value field. +func (u *UpdateAttributeForSCIMUserOperations) GetValue() json.RawMessage { + if u == nil { + return json.RawMessage{} } - return *r.Size + return u.Value } -// GetSource returns the Source field. -func (r *Repository) GetSource() *Repository { - if r == nil { +// GetOperations returns the Operations slice if it's non-nil, nil otherwise. +func (u *UpdateAttributeForSCIMUserRequest) GetOperations() []*UpdateAttributeForSCIMUserOperations { + if u == nil || u.Operations == nil { return nil } - return r.Source + return u.Operations } -// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSSHURL() string { - if r == nil || r.SSHURL == nil { - return "" +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (u *UpdateAttributeForSCIMUserRequest) GetSchemas() []string { + if u == nil || u.Schemas == nil { + return nil } - return *r.SSHURL + return u.Schemas } -// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetStargazersCount() int { - if r == nil || r.StargazersCount == nil { - return 0 +// GetParameters returns the Parameters field. +func (u *UpdateBranchRule) GetParameters() UpdateRuleParameters { + if u == nil { + return UpdateRuleParameters{} } - return *r.StargazersCount + return u.Parameters } -// GetStargazersURL returns the StargazersURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetStargazersURL() string { - if r == nil || r.StargazersURL == nil { - return "" +// GetActions returns the Actions slice if it's non-nil, nil otherwise. +func (u *UpdateCheckRunOptions) GetActions() []*CheckRunAction { + if u == nil || u.Actions == nil { + return nil } - return *r.StargazersURL + return u.Actions } -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetStatusesURL() string { - if r == nil || r.StatusesURL == nil { - return "" +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (u *UpdateCheckRunOptions) GetCompletedAt() Timestamp { + if u == nil || u.CompletedAt == nil { + return Timestamp{} } - return *r.StatusesURL + return *u.CompletedAt } -// GetSubscribersCount returns the SubscribersCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetSubscribersCount() int { - if r == nil || r.SubscribersCount == nil { - return 0 +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (u *UpdateCheckRunOptions) GetConclusion() string { + if u == nil || u.Conclusion == nil { + return "" } - return *r.SubscribersCount + return *u.Conclusion } -// GetSubscribersURL returns the SubscribersURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSubscribersURL() string { - if r == nil || r.SubscribersURL == nil { +// GetDetailsURL returns the DetailsURL field if it's non-nil, zero value otherwise. +func (u *UpdateCheckRunOptions) GetDetailsURL() string { + if u == nil || u.DetailsURL == nil { return "" } - return *r.SubscribersURL + return *u.DetailsURL } -// GetSubscriptionURL returns the SubscriptionURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSubscriptionURL() string { - if r == nil || r.SubscriptionURL == nil { +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (u *UpdateCheckRunOptions) GetExternalID() string { + if u == nil || u.ExternalID == nil { return "" } - return *r.SubscriptionURL + return *u.ExternalID } -// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSVNURL() string { - if r == nil || r.SVNURL == nil { +// GetName returns the Name field. +func (u *UpdateCheckRunOptions) GetName() string { + if u == nil { return "" } - return *r.SVNURL + return u.Name } -// GetTagsURL returns the TagsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetTagsURL() string { - if r == nil || r.TagsURL == nil { - return "" +// GetOutput returns the Output field. +func (u *UpdateCheckRunOptions) GetOutput() *CheckRunOutput { + if u == nil { + return nil } - return *r.TagsURL + return u.Output } - -// GetTeamID returns the TeamID field if it's non-nil, zero value otherwise. -func (r *Repository) GetTeamID() int64 { - if r == nil || r.TeamID == nil { - return 0 + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (u *UpdateCheckRunOptions) GetStatus() string { + if u == nil || u.Status == nil { + return "" } - return *r.TeamID + return *u.Status } -// GetTeamsURL returns the TeamsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetTeamsURL() string { - if r == nil || r.TeamsURL == nil { +// GetMachine returns the Machine field if it's non-nil, zero value otherwise. +func (u *UpdateCodespaceOptions) GetMachine() string { + if u == nil || u.Machine == nil { return "" } - return *r.TeamsURL + return *u.Machine } -// GetTemplateRepository returns the TemplateRepository field. -func (r *Repository) GetTemplateRepository() *Repository { - if r == nil { +// GetRecentFolders returns the RecentFolders slice if it's non-nil, nil otherwise. +func (u *UpdateCodespaceOptions) GetRecentFolders() []string { + if u == nil || u.RecentFolders == nil { return nil } - return r.TemplateRepository + return u.RecentFolders } -// GetTreesURL returns the TreesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetTreesURL() string { - if r == nil || r.TreesURL == nil { - return "" +// GetGroupID returns the GroupID field. +func (u *UpdateConnectedExternalGroupRequest) GetGroupID() int64 { + if u == nil { + return 0 } - return *r.TreesURL + return u.GroupID } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *Repository) GetUpdatedAt() Timestamp { - if r == nil || r.UpdatedAt == nil { - return Timestamp{} +// GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. +func (u *UpdateCustomOrgRoleRequest) GetBaseRole() string { + if u == nil || u.BaseRole == nil { + return "" } - return *r.UpdatedAt + return *u.BaseRole } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *Repository) GetURL() string { - if r == nil || r.URL == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (u *UpdateCustomOrgRoleRequest) GetDescription() string { + if u == nil || u.Description == nil { return "" } - return *r.URL + return *u.Description } -// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetWatchersCount() int { - if r == nil || r.WatchersCount == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UpdateCustomOrgRoleRequest) GetName() string { + if u == nil || u.Name == nil { + return "" } - return *r.WatchersCount + return *u.Name } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetBody() string { - if r == nil || r.Body == nil { - return "" +// GetPermissions returns the Permissions slice if it's non-nil, nil otherwise. +func (u *UpdateCustomOrgRoleRequest) GetPermissions() []string { + if u == nil || u.Permissions == nil { + return nil } - return *r.Body + return u.Permissions } -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetCommitID() string { - if r == nil || r.CommitID == nil { +// GetBaseRole returns the BaseRole field if it's non-nil, zero value otherwise. +func (u *UpdateCustomRepoRoleRequest) GetBaseRole() string { + if u == nil || u.BaseRole == nil { return "" } - return *r.CommitID + return *u.BaseRole } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetCreatedAt() time.Time { - if r == nil || r.CreatedAt == nil { - return time.Time{} +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (u *UpdateCustomRepoRoleRequest) GetDescription() string { + if u == nil || u.Description == nil { + return "" } - return *r.CreatedAt + return *u.Description } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UpdateCustomRepoRoleRequest) GetName() string { + if u == nil || u.Name == nil { return "" } - return *r.HTMLURL + return *u.Name } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetID() int64 { - if r == nil || r.ID == nil { - return 0 +// GetPermissions returns the Permissions slice if it's non-nil, nil otherwise. +func (u *UpdateCustomRepoRoleRequest) GetPermissions() []string { + if u == nil || u.Permissions == nil { + return nil } - return *r.ID + return u.Permissions } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetNodeID() string { - if r == nil || r.NodeID == nil { - return "" +// GetLanguages returns the Languages slice if it's non-nil, nil otherwise. +func (u *UpdateDefaultSetupConfigurationOptions) GetLanguages() []string { + if u == nil || u.Languages == nil { + return nil } - return *r.NodeID + return u.Languages } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetPath() string { - if r == nil || r.Path == nil { +// GetQuerySuite returns the QuerySuite field if it's non-nil, zero value otherwise. +func (u *UpdateDefaultSetupConfigurationOptions) GetQuerySuite() string { + if u == nil || u.QuerySuite == nil { return "" } - return *r.Path + return *u.QuerySuite } -// GetPosition returns the Position field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetPosition() int { - if r == nil || r.Position == nil { - return 0 +// GetState returns the State field. +func (u *UpdateDefaultSetupConfigurationOptions) GetState() string { + if u == nil { + return "" } - return *r.Position + return u.State } -// GetReactions returns the Reactions field. -func (r *RepositoryComment) GetReactions() *Reactions { - if r == nil { - return nil +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (u *UpdateDefaultSetupConfigurationResponse) GetRunID() int64 { + if u == nil || u.RunID == nil { + return 0 } - return r.Reactions + return *u.RunID } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetUpdatedAt() time.Time { - if r == nil || r.UpdatedAt == nil { - return time.Time{} +// GetRunURL returns the RunURL field if it's non-nil, zero value otherwise. +func (u *UpdateDefaultSetupConfigurationResponse) GetRunURL() string { + if u == nil || u.RunURL == nil { + return "" } - return *r.UpdatedAt + return *u.RunURL } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetURL() string { - if r == nil || r.URL == nil { +// GetName returns the Name field. +func (u *UpdateDeploymentBranchPolicyRequest) GetName() string { + if u == nil { return "" } - return *r.URL + return u.Name } -// GetUser returns the User field. -func (r *RepositoryComment) GetUser() *User { - if r == nil { - return nil +// GetAllowsPublicRepositories returns the AllowsPublicRepositories field if it's non-nil, zero value otherwise. +func (u *UpdateEnterpriseRunnerGroupRequest) GetAllowsPublicRepositories() bool { + if u == nil || u.AllowsPublicRepositories == nil { + return false } - return r.User + return *u.AllowsPublicRepositories } -// GetAuthor returns the Author field. -func (r *RepositoryCommit) GetAuthor() *User { - if r == nil { - return nil +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UpdateEnterpriseRunnerGroupRequest) GetName() string { + if u == nil || u.Name == nil { + return "" } - return r.Author + return *u.Name } -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetCommentsURL() string { - if r == nil || r.CommentsURL == nil { +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (u *UpdateEnterpriseRunnerGroupRequest) GetNetworkConfigurationID() string { + if u == nil || u.NetworkConfigurationID == nil { return "" } - return *r.CommentsURL + return *u.NetworkConfigurationID } -// GetCommit returns the Commit field. -func (r *RepositoryCommit) GetCommit() *Commit { - if r == nil { - return nil +// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise. +func (u *UpdateEnterpriseRunnerGroupRequest) GetRestrictedToWorkflows() bool { + if u == nil || u.RestrictedToWorkflows == nil { + return false } - return r.Commit + return *u.RestrictedToWorkflows } -// GetCommitter returns the Committer field. -func (r *RepositoryCommit) GetCommitter() *User { - if r == nil { +// GetSelectedWorkflows returns the SelectedWorkflows slice if it's non-nil, nil otherwise. +func (u *UpdateEnterpriseRunnerGroupRequest) GetSelectedWorkflows() []string { + if u == nil || u.SelectedWorkflows == nil { return nil } - return r.Committer + return u.SelectedWorkflows } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (u *UpdateEnterpriseRunnerGroupRequest) GetVisibility() string { + if u == nil || u.Visibility == nil { return "" } - return *r.HTMLURL + return *u.Visibility } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetNodeID() string { - if r == nil || r.NodeID == nil { +// GetBody returns the Body field. +func (u *UpdateGistCommentRequest) GetBody() string { + if u == nil { return "" } - return *r.NodeID + return u.Body } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetSHA() string { - if r == nil || r.SHA == nil { +// GetContent returns the Content field if it's non-nil, zero value otherwise. +func (u *UpdateGistFile) GetContent() string { + if u == nil || u.Content == nil { return "" } - return *r.SHA + return *u.Content } -// GetStats returns the Stats field. -func (r *RepositoryCommit) GetStats() *CommitStats { - if r == nil { - return nil +// GetFilename returns the Filename field if it's non-nil, zero value otherwise. +func (u *UpdateGistFile) GetFilename() string { + if u == nil || u.Filename == nil { + return "" } - return r.Stats + return *u.Filename } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetURL() string { - if r == nil || r.URL == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (u *UpdateGistRequest) GetDescription() string { + if u == nil || u.Description == nil { return "" } - return *r.URL + return *u.Description } -// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetDownloadURL() string { - if r == nil || r.DownloadURL == nil { - return "" +// GetEnableStaticIP returns the EnableStaticIP field if it's non-nil, zero value otherwise. +func (u *UpdateHostedRunnerRequest) GetEnableStaticIP() bool { + if u == nil || u.EnableStaticIP == nil { + return false } - return *r.DownloadURL + return *u.EnableStaticIP } -// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetEncoding() string { - if r == nil || r.Encoding == nil { +// GetImageID returns the ImageID field if it's non-nil, zero value otherwise. +func (u *UpdateHostedRunnerRequest) GetImageID() string { + if u == nil || u.ImageID == nil { return "" } - return *r.Encoding + return *u.ImageID } -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetGitURL() string { - if r == nil || r.GitURL == nil { +// GetImageVersion returns the ImageVersion field if it's non-nil, zero value otherwise. +func (u *UpdateHostedRunnerRequest) GetImageVersion() string { + if u == nil || u.ImageVersion == nil { return "" } - return *r.GitURL + return *u.ImageVersion } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" +// GetMaximumRunners returns the MaximumRunners field if it's non-nil, zero value otherwise. +func (u *UpdateHostedRunnerRequest) GetMaximumRunners() int64 { + if u == nil || u.MaximumRunners == nil { + return 0 } - return *r.HTMLURL + return *u.MaximumRunners } // GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetName() string { - if r == nil || r.Name == nil { +func (u *UpdateHostedRunnerRequest) GetName() string { + if u == nil || u.Name == nil { return "" } - return *r.Name + return *u.Name } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetPath() string { - if r == nil || r.Path == nil { - return "" +// GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise. +func (u *UpdateHostedRunnerRequest) GetRunnerGroupID() int64 { + if u == nil || u.RunnerGroupID == nil { + return 0 } - return *r.Path + return *u.RunnerGroupID } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetSHA() string { - if r == nil || r.SHA == nil { +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (u *UpdateHostedRunnerRequest) GetSize() string { + if u == nil || u.Size == nil { return "" } - return *r.SHA + return *u.Size } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetSize() int { - if r == nil || r.Size == nil { - return 0 +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (u *UpdateIssueLabelRequest) GetColor() string { + if u == nil || u.Color == nil { + return "" } - return *r.Size + return *u.Color } -// GetTarget returns the Target field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetTarget() string { - if r == nil || r.Target == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (u *UpdateIssueLabelRequest) GetDescription() string { + if u == nil || u.Description == nil { return "" } - return *r.Target + return *u.Description } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetType() string { - if r == nil || r.Type == nil { +// GetNewName returns the NewName field if it's non-nil, zero value otherwise. +func (u *UpdateIssueLabelRequest) GetNewName() string { + if u == nil || u.NewName == nil { return "" } - return *r.Type + return *u.NewName } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetURL() string { - if r == nil || r.URL == nil { +// GetAssignee returns the Assignee field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetAssignee() string { + if u == nil || u.Assignee == nil { return "" } - return *r.URL + return *u.Assignee } -// GetAuthor returns the Author field. -func (r *RepositoryContentFileOptions) GetAuthor() *CommitAuthor { - if r == nil { +// GetAssignees returns the Assignees slice if it's non-nil, nil otherwise. +func (u *UpdateIssueRequest) GetAssignees() []string { + if u == nil || u.Assignees == nil { return nil } - return r.Author + return u.Assignees } -// GetBranch returns the Branch field if it's non-nil, zero value otherwise. -func (r *RepositoryContentFileOptions) GetBranch() string { - if r == nil || r.Branch == nil { +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetBody() string { + if u == nil || u.Body == nil { return "" } - return *r.Branch + return *u.Body } -// GetCommitter returns the Committer field. -func (r *RepositoryContentFileOptions) GetCommitter() *CommitAuthor { - if r == nil { +// GetDuplicateIssueID returns the DuplicateIssueID field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetDuplicateIssueID() int { + if u == nil || u.DuplicateIssueID == nil { + return 0 + } + return *u.DuplicateIssueID +} + +// GetIssueFieldValues returns the IssueFieldValues slice if it's non-nil, nil otherwise. +func (u *UpdateIssueRequest) GetIssueFieldValues() []*IssueRequestFieldValue { + if u == nil || u.IssueFieldValues == nil { return nil } - return r.Committer + return u.IssueFieldValues } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (r *RepositoryContentFileOptions) GetMessage() string { - if r == nil || r.Message == nil { +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (u *UpdateIssueRequest) GetLabels() []string { + if u == nil || u.Labels == nil { + return nil + } + return u.Labels +} + +// GetMilestone returns the Milestone field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetMilestone() int { + if u == nil || u.Milestone == nil { + return 0 + } + return *u.Milestone +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetState() string { + if u == nil || u.State == nil { + return "" + } + return *u.State +} + +// GetStateReason returns the StateReason field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetStateReason() string { + if u == nil || u.StateReason == nil { return "" } - return *r.Message + return *u.StateReason } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryContentFileOptions) GetSHA() string { - if r == nil || r.SHA == nil { +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetTitle() string { + if u == nil || u.Title == nil { return "" } - return *r.SHA + return *u.Title } -// GetContent returns the Content field. -func (r *RepositoryContentResponse) GetContent() *RepositoryContent { - if r == nil { - return nil +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (u *UpdateIssueRequest) GetType() string { + if u == nil || u.Type == nil { + return "" } - return r.Content + return *u.Type } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (r *RepositoryEvent) GetAction() string { - if r == nil || r.Action == nil { +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (u *UpdateMilestoneRequest) GetDescription() string { + if u == nil || u.Description == nil { return "" } - return *r.Action + return *u.Description } -// GetInstallation returns the Installation field. -func (r *RepositoryEvent) GetInstallation() *Installation { - if r == nil { - return nil +// GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. +func (u *UpdateMilestoneRequest) GetDueOn() Timestamp { + if u == nil || u.DueOn == nil { + return Timestamp{} } - return r.Installation + return *u.DueOn } -// GetOrg returns the Org field. -func (r *RepositoryEvent) GetOrg() *Organization { - if r == nil { - return nil +// GetState returns the State field if it's non-nil, zero value otherwise. +func (u *UpdateMilestoneRequest) GetState() string { + if u == nil || u.State == nil { + return "" } - return r.Org + return *u.State } -// GetRepo returns the Repo field. -func (r *RepositoryEvent) GetRepo() *Repository { - if r == nil { - return nil +// GetTitle returns the Title field if it's non-nil, zero value otherwise. +func (u *UpdateMilestoneRequest) GetTitle() string { + if u == nil || u.Title == nil { + return "" } - return r.Repo + return *u.Title } -// GetSender returns the Sender field. -func (r *RepositoryEvent) GetSender() *User { - if r == nil { - return nil +// GetAccountID returns the AccountID field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetAccountID() string { + if u == nil || u.AccountID == nil { + return "" } - return r.Sender + return *u.AccountID } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} +// GetAudience returns the Audience field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetAudience() string { + if u == nil || u.Audience == nil { + return "" } - return *r.CreatedAt + return *u.Audience } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { +// GetAuthType returns the AuthType field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetAuthType() string { + if u == nil || u.AuthType == nil { return "" } - return *r.HTMLURL + return *u.AuthType } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetID() int64 { - if r == nil || r.ID == nil { - return 0 +// GetAWSRegion returns the AWSRegion field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetAWSRegion() string { + if u == nil || u.AWSRegion == nil { + return "" } - return *r.ID + return *u.AWSRegion } -// GetInvitee returns the Invitee field. -func (r *RepositoryInvitation) GetInvitee() *User { - if r == nil { - return nil +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetClientID() string { + if u == nil || u.ClientID == nil { + return "" } - return r.Invitee + return *u.ClientID } -// GetInviter returns the Inviter field. -func (r *RepositoryInvitation) GetInviter() *User { - if r == nil { - return nil +// GetDomain returns the Domain field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetDomain() string { + if u == nil || u.Domain == nil { + return "" } - return r.Inviter + return *u.Domain } -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetPermissions() string { - if r == nil || r.Permissions == nil { +// GetDomainOwner returns the DomainOwner field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetDomainOwner() string { + if u == nil || u.DomainOwner == nil { return "" } - return *r.Permissions + return *u.DomainOwner } -// GetRepo returns the Repo field. -func (r *RepositoryInvitation) GetRepo() *Repository { - if r == nil { - return nil +// GetEncryptedValue returns the EncryptedValue field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetEncryptedValue() string { + if u == nil || u.EncryptedValue == nil { + return "" } - return r.Repo + return *u.EncryptedValue } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetURL() string { - if r == nil || r.URL == nil { +// GetIdentityMappingName returns the IdentityMappingName field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetIdentityMappingName() string { + if u == nil || u.IdentityMappingName == nil { return "" } - return *r.URL + return *u.IdentityMappingName } -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetContent() string { - if r == nil || r.Content == nil { +// GetJFrogOIDCProviderName returns the JFrogOIDCProviderName field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetJFrogOIDCProviderName() string { + if u == nil || u.JFrogOIDCProviderName == nil { return "" } - return *r.Content + return *u.JFrogOIDCProviderName } -// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetDownloadURL() string { - if r == nil || r.DownloadURL == nil { +// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetKeyID() string { + if u == nil || u.KeyID == nil { return "" } - return *r.DownloadURL + return *u.KeyID } -// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetEncoding() string { - if r == nil || r.Encoding == nil { - return "" +// GetRegistryType returns the RegistryType field. +func (u *UpdateOrganizationPrivateRegistry) GetRegistryType() *PrivateRegistryType { + if u == nil { + return nil } - return *r.Encoding + return u.RegistryType } -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetGitURL() string { - if r == nil || r.GitURL == nil { - return "" +// GetReplacesBase returns the ReplacesBase field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetReplacesBase() bool { + if u == nil || u.ReplacesBase == nil { + return false } - return *r.GitURL + return *u.ReplacesBase } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { +// GetRoleName returns the RoleName field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetRoleName() string { + if u == nil || u.RoleName == nil { return "" } - return *r.HTMLURL + return *u.RoleName } -// GetLicense returns the License field. -func (r *RepositoryLicense) GetLicense() *License { - if r == nil { +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs slice if it's non-nil, nil otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetSelectedRepositoryIDs() []int64 { + if u == nil || u.SelectedRepositoryIDs == nil { return nil } - return r.License + return u.SelectedRepositoryIDs } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetName() string { - if r == nil || r.Name == nil { +// GetTenantID returns the TenantID field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetTenantID() string { + if u == nil || u.TenantID == nil { return "" } - return *r.Name + return *u.TenantID } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetPath() string { - if r == nil || r.Path == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetURL() string { + if u == nil || u.URL == nil { return "" } - return *r.Path + return *u.URL } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetSHA() string { - if r == nil || r.SHA == nil { +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (u *UpdateOrganizationPrivateRegistry) GetUsername() string { + if u == nil || u.Username == nil { return "" } - return *r.SHA + return *u.Username } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetSize() int { - if r == nil || r.Size == nil { - return 0 +// GetVisibility returns the Visibility field. +func (u *UpdateOrganizationPrivateRegistry) GetVisibility() *PrivateRegistryVisibility { + if u == nil { + return nil } - return *r.Size + return u.Visibility } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetType() string { - if r == nil || r.Type == nil { +// GetEnforcement returns the Enforcement field if it's non-nil, zero value otherwise. +func (u *UpdatePreReceiveHookRequest) GetEnforcement() string { + if u == nil || u.Enforcement == nil { return "" } - return *r.Type + return *u.Enforcement } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetURL() string { - if r == nil || r.URL == nil { - return "" +// GetArchived returns the Archived field if it's non-nil, zero value otherwise. +func (u *UpdateProjectItemOptions) GetArchived() bool { + if u == nil || u.Archived == nil { + return false } - return *r.URL + return *u.Archived } -// GetBase returns the Base field if it's non-nil, zero value otherwise. -func (r *RepositoryMergeRequest) GetBase() string { - if r == nil || r.Base == nil { - return "" +// GetFields returns the Fields slice if it's non-nil, nil otherwise. +func (u *UpdateProjectItemOptions) GetFields() []*UpdateProjectV2Field { + if u == nil || u.Fields == nil { + return nil } - return *r.Base + return u.Fields } -// GetCommitMessage returns the CommitMessage field if it's non-nil, zero value otherwise. -func (r *RepositoryMergeRequest) GetCommitMessage() string { - if r == nil || r.CommitMessage == nil { - return "" +// GetID returns the ID field. +func (u *UpdateProjectV2Field) GetID() int64 { + if u == nil { + return 0 } - return *r.CommitMessage + return u.ID } -// GetHead returns the Head field if it's non-nil, zero value otherwise. -func (r *RepositoryMergeRequest) GetHead() string { - if r == nil || r.Head == nil { - return "" +// GetValue returns the Value field. +func (u *UpdateProjectV2Field) GetValue() any { + if u == nil { + return nil } - return *r.Head + return u.Value } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (r *RepositoryPermissionLevel) GetPermission() string { - if r == nil || r.Permission == nil { +// GetActive returns the Active field if it's non-nil, zero value otherwise. +func (u *UpdateProvisionedOrgMembershipRequest) GetActive() bool { + if u == nil || u.Active == nil { + return false + } + return *u.Active +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (u *UpdateProvisionedOrgMembershipRequest) GetDisplayName() string { + if u == nil || u.DisplayName == nil { return "" } - return *r.Permission + return *u.DisplayName } -// GetUser returns the User field. -func (r *RepositoryPermissionLevel) GetUser() *User { - if r == nil { +// GetEmails returns the Emails slice if it's non-nil, nil otherwise. +func (u *UpdateProvisionedOrgMembershipRequest) GetEmails() []*SCIMUserEmail { + if u == nil || u.Emails == nil { return nil } - return r.User + return u.Emails } -// GetAssetsURL returns the AssetsURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetAssetsURL() string { - if r == nil || r.AssetsURL == nil { +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (u *UpdateProvisionedOrgMembershipRequest) GetExternalID() string { + if u == nil || u.ExternalID == nil { return "" } - return *r.AssetsURL + return *u.ExternalID } -// GetAuthor returns the Author field. -func (r *RepositoryRelease) GetAuthor() *User { - if r == nil { +// GetGroups returns the Groups slice if it's non-nil, nil otherwise. +func (u *UpdateProvisionedOrgMembershipRequest) GetGroups() []string { + if u == nil || u.Groups == nil { return nil } - return r.Author + return u.Groups } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetBody() string { - if r == nil || r.Body == nil { - return "" +// GetName returns the Name field. +func (u *UpdateProvisionedOrgMembershipRequest) GetName() SCIMUserName { + if u == nil { + return SCIMUserName{} } - return *r.Body + return u.Name } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} +// GetSchemas returns the Schemas slice if it's non-nil, nil otherwise. +func (u *UpdateProvisionedOrgMembershipRequest) GetSchemas() []string { + if u == nil || u.Schemas == nil { + return nil } - return *r.CreatedAt + return u.Schemas } -// GetDraft returns the Draft field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetDraft() bool { - if r == nil || r.Draft == nil { +// GetUserName returns the UserName field. +func (u *UpdateProvisionedOrgMembershipRequest) GetUserName() string { + if u == nil { + return "" + } + return u.UserName +} + +// GetForce returns the Force field if it's non-nil, zero value otherwise. +func (u *UpdateRef) GetForce() bool { + if u == nil || u.Force == nil { return false } - return *r.Draft + return *u.Force } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { +// GetSHA returns the SHA field. +func (u *UpdateRef) GetSHA() string { + if u == nil { return "" } - return *r.HTMLURL + return u.SHA } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetID() int64 { - if r == nil || r.ID == nil { - return 0 +// GetLabel returns the Label field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseAssetRequest) GetLabel() string { + if u == nil || u.Label == nil { + return "" } - return *r.ID + return *u.Label } // GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetName() string { - if r == nil || r.Name == nil { +func (u *UpdateReleaseAssetRequest) GetName() string { + if u == nil || u.Name == nil { return "" } - return *r.Name + return *u.Name } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetNodeID() string { - if r == nil || r.NodeID == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseAssetRequest) GetState() string { + if u == nil || u.State == nil { return "" } - return *r.NodeID + return *u.State } -// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetPrerelease() bool { - if r == nil || r.Prerelease == nil { - return false +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetBody() string { + if u == nil || u.Body == nil { + return "" } - return *r.Prerelease + return *u.Body } -// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetPublishedAt() Timestamp { - if r == nil || r.PublishedAt == nil { - return Timestamp{} +// GetDiscussionCategoryName returns the DiscussionCategoryName field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetDiscussionCategoryName() string { + if u == nil || u.DiscussionCategoryName == nil { + return "" } - return *r.PublishedAt + return *u.DiscussionCategoryName } -// GetTagName returns the TagName field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetTagName() string { - if r == nil || r.TagName == nil { - return "" +// GetDraft returns the Draft field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetDraft() bool { + if u == nil || u.Draft == nil { + return false } - return *r.TagName + return *u.Draft } -// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetTarballURL() string { - if r == nil || r.TarballURL == nil { +// GetMakeLatest returns the MakeLatest field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetMakeLatest() string { + if u == nil || u.MakeLatest == nil { return "" } - return *r.TarballURL + return *u.MakeLatest } -// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetTargetCommitish() string { - if r == nil || r.TargetCommitish == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetName() string { + if u == nil || u.Name == nil { return "" } - return *r.TargetCommitish + return *u.Name } -// GetUploadURL returns the UploadURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetUploadURL() string { - if r == nil || r.UploadURL == nil { - return "" +// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetPrerelease() bool { + if u == nil || u.Prerelease == nil { + return false } - return *r.UploadURL + return *u.Prerelease } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetURL() string { - if r == nil || r.URL == nil { +// GetTagName returns the TagName field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetTagName() string { + if u == nil || u.TagName == nil { return "" } - return *r.URL + return *u.TagName } -// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetZipballURL() string { - if r == nil || r.ZipballURL == nil { +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (u *UpdateReleaseRequest) GetTargetCommitish() string { + if u == nil || u.TargetCommitish == nil { return "" } - return *r.ZipballURL + return *u.TargetCommitish } -// GetCommit returns the Commit field. -func (r *RepositoryTag) GetCommit() *Commit { - if r == nil { - return nil +// GetUpdateAllowsFetchAndMerge returns the UpdateAllowsFetchAndMerge field. +func (u *UpdateRuleParameters) GetUpdateAllowsFetchAndMerge() bool { + if u == nil { + return false } - return r.Commit + return u.UpdateAllowsFetchAndMerge +} + +// GetAllowsPublicRepositories returns the AllowsPublicRepositories field if it's non-nil, zero value otherwise. +func (u *UpdateRunnerGroupRequest) GetAllowsPublicRepositories() bool { + if u == nil || u.AllowsPublicRepositories == nil { + return false + } + return *u.AllowsPublicRepositories } // GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryTag) GetName() string { - if r == nil || r.Name == nil { +func (u *UpdateRunnerGroupRequest) GetName() string { + if u == nil || u.Name == nil { return "" } - return *r.Name + return *u.Name } -// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryTag) GetTarballURL() string { - if r == nil || r.TarballURL == nil { +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (u *UpdateRunnerGroupRequest) GetNetworkConfigurationID() string { + if u == nil || u.NetworkConfigurationID == nil { return "" } - return *r.TarballURL + return *u.NetworkConfigurationID } -// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryTag) GetZipballURL() string { - if r == nil || r.ZipballURL == nil { - return "" +// GetRestrictedToWorkflows returns the RestrictedToWorkflows field if it's non-nil, zero value otherwise. +func (u *UpdateRunnerGroupRequest) GetRestrictedToWorkflows() bool { + if u == nil || u.RestrictedToWorkflows == nil { + return false } - return *r.ZipballURL + return *u.RestrictedToWorkflows } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (r *RepositoryVulnerabilityAlertEvent) GetAction() string { - if r == nil || r.Action == nil { +// GetSelectedWorkflows returns the SelectedWorkflows slice if it's non-nil, nil otherwise. +func (u *UpdateRunnerGroupRequest) GetSelectedWorkflows() []string { + if u == nil || u.SelectedWorkflows == nil { + return nil + } + return u.SelectedWorkflows +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (u *UpdateRunnerGroupRequest) GetVisibility() string { + if u == nil || u.Visibility == nil { return "" } - return *r.Action + return *u.Visibility } -// GetForkRepos returns the ForkRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetForkRepos() int { - if r == nil || r.ForkRepos == nil { - return 0 +// GetLDAPDN returns the LDAPDN field. +func (u *UpdateTeamLDAPMappingRequest) GetLDAPDN() string { + if u == nil { + return "" } - return *r.ForkRepos + return u.LDAPDN } -// GetOrgRepos returns the OrgRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetOrgRepos() int { - if r == nil || r.OrgRepos == nil { - return 0 +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetDescription() string { + if u == nil || u.Description == nil { + return "" } - return *r.OrgRepos + return *u.Description } -// GetRootRepos returns the RootRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetRootRepos() int { - if r == nil || r.RootRepos == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetName() string { + if u == nil || u.Name == nil { + return "" } - return *r.RootRepos + return *u.Name } -// GetTotalPushes returns the TotalPushes field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetTotalPushes() int { - if r == nil || r.TotalPushes == nil { - return 0 +// GetNotificationSetting returns the NotificationSetting field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetNotificationSetting() string { + if u == nil || u.NotificationSetting == nil { + return "" } - return *r.TotalPushes + return *u.NotificationSetting } -// GetTotalRepos returns the TotalRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetTotalRepos() int { - if r == nil || r.TotalRepos == nil { +// GetParentTeamID returns the ParentTeamID field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetParentTeamID() int64 { + if u == nil || u.ParentTeamID == nil { return 0 } - return *r.TotalRepos + return *u.ParentTeamID } -// GetTotalWikis returns the TotalWikis field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetTotalWikis() int { - if r == nil || r.TotalWikis == nil { - return 0 +// GetParentTeamSlug returns the ParentTeamSlug field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetParentTeamSlug() string { + if u == nil || u.ParentTeamSlug == nil { + return "" } - return *r.TotalWikis + return *u.ParentTeamSlug } -// GetContext returns the Context field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetContext() string { - if r == nil || r.Context == nil { +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetPermission() string { + if u == nil || u.Permission == nil { return "" } - return *r.Context + return *u.Permission } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetCreatedAt() time.Time { - if r == nil || r.CreatedAt == nil { - return time.Time{} +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (u *UpdateTeamRequest) GetPrivacy() string { + if u == nil || u.Privacy == nil { + return "" } - return *r.CreatedAt + return *u.Privacy } -// GetCreator returns the Creator field. -func (r *RepoStatus) GetCreator() *User { - if r == nil { - return nil +// GetRemoveParentTeam returns the RemoveParentTeam field. +func (u *UpdateTeamRequest) GetRemoveParentTeam() bool { + if u == nil { + return false } - return r.Creator + return u.RemoveParentTeam } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetDescription() string { - if r == nil || r.Description == nil { +// GetLDAPDN returns the LDAPDN field. +func (u *UpdateUserLDAPMappingRequest) GetLDAPDN() string { + if u == nil { return "" } - return *r.Description + return u.LDAPDN } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetID() int64 { - if r == nil || r.ID == nil { - return 0 +// GetLicense returns the License field. +func (u *UploadLicenseOptions) GetLicense() string { + if u == nil { + return "" } - return *r.ID + return u.License } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetNodeID() string { - if r == nil || r.NodeID == nil { +// GetLabel returns the Label field. +func (u *UploadOptions) GetLabel() string { + if u == nil { return "" } - return *r.NodeID + return u.Label } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetState() string { - if r == nil || r.State == nil { +// GetMediaType returns the MediaType field. +func (u *UploadOptions) GetMediaType() string { + if u == nil { return "" } - return *r.State + return u.MediaType } -// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetTargetURL() string { - if r == nil || r.TargetURL == nil { +// GetName returns the Name field. +func (u *UploadOptions) GetName() string { + if u == nil { return "" } - return *r.TargetURL + return u.Name } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetUpdatedAt() time.Time { - if r == nil || r.UpdatedAt == nil { - return time.Time{} +// GetDate returns the Date field. +func (u *UsageItem) GetDate() string { + if u == nil { + return "" } - return *r.UpdatedAt + return u.Date } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetURL() string { - if r == nil || r.URL == nil { - return "" +// GetDiscountAmount returns the DiscountAmount field. +func (u *UsageItem) GetDiscountAmount() float64 { + if u == nil { + return 0 } - return *r.URL + return u.DiscountAmount } -// GetStrict returns the Strict field if it's non-nil, zero value otherwise. -func (r *RequiredStatusChecksRequest) GetStrict() bool { - if r == nil || r.Strict == nil { - return false +// GetGrossAmount returns the GrossAmount field. +func (u *UsageItem) GetGrossAmount() float64 { + if u == nil { + return 0 } - return *r.Strict + return u.GrossAmount } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *ReviewersRequest) GetNodeID() string { - if r == nil || r.NodeID == nil { - return "" +// GetNetAmount returns the NetAmount field. +func (u *UsageItem) GetNetAmount() float64 { + if u == nil { + return 0 } - return *r.NodeID + return u.NetAmount } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (s *ServiceHook) GetName() string { - if s == nil || s.Name == nil { +// GetOrganizationName returns the OrganizationName field if it's non-nil, zero value otherwise. +func (u *UsageItem) GetOrganizationName() string { + if u == nil || u.OrganizationName == nil { return "" } - return *s.Name + return *u.OrganizationName } -// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. -func (s *SignaturesProtectedBranch) GetEnabled() bool { - if s == nil || s.Enabled == nil { - return false +// GetPricePerUnit returns the PricePerUnit field. +func (u *UsageItem) GetPricePerUnit() float64 { + if u == nil { + return 0 } - return *s.Enabled + return u.PricePerUnit } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *SignaturesProtectedBranch) GetURL() string { - if s == nil || s.URL == nil { +// GetProduct returns the Product field. +func (u *UsageItem) GetProduct() string { + if u == nil { return "" } - return *s.URL + return u.Product } -// GetPayload returns the Payload field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetPayload() string { - if s == nil || s.Payload == nil { - return "" +// GetQuantity returns the Quantity field. +func (u *UsageItem) GetQuantity() float64 { + if u == nil { + return 0 } - return *s.Payload + return u.Quantity } -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetReason() string { - if s == nil || s.Reason == nil { +// GetRepositoryName returns the RepositoryName field if it's non-nil, zero value otherwise. +func (u *UsageItem) GetRepositoryName() string { + if u == nil || u.RepositoryName == nil { return "" } - return *s.Reason + return *u.RepositoryName } -// GetSignature returns the Signature field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetSignature() string { - if s == nil || s.Signature == nil { +// GetSKU returns the SKU field. +func (u *UsageItem) GetSKU() string { + if u == nil { return "" } - return *s.Signature + return u.SKU } -// GetVerified returns the Verified field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetVerified() bool { - if s == nil || s.Verified == nil { - return false +// GetUnitType returns the UnitType field. +func (u *UsageItem) GetUnitType() string { + if u == nil { + return "" } - return *s.Verified + return u.UnitType } -// GetActor returns the Actor field. -func (s *Source) GetActor() *User { - if s == nil { +// GetUsageItems returns the UsageItems slice if it's non-nil, nil otherwise. +func (u *UsageReport) GetUsageItems() []*UsageItem { + if u == nil || u.UsageItems == nil { return nil } - return s.Actor + return u.UsageItems } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (s *Source) GetID() int64 { - if s == nil || s.ID == nil { +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (u *UsageReportOptions) GetDay() int { + if u == nil || u.Day == nil { return 0 } - return *s.ID + return *u.Day } -// GetIssue returns the Issue field. -func (s *Source) GetIssue() *Issue { - if s == nil { - return nil +// GetHour returns the Hour field if it's non-nil, zero value otherwise. +func (u *UsageReportOptions) GetHour() int { + if u == nil || u.Hour == nil { + return 0 } - return s.Issue + return *u.Hour } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (s *Source) GetType() string { - if s == nil || s.Type == nil { - return "" +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (u *UsageReportOptions) GetMonth() int { + if u == nil || u.Month == nil { + return 0 } - return *s.Type + return *u.Month } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *Source) GetURL() string { - if s == nil || s.URL == nil { - return "" +// GetYear returns the Year field if it's non-nil, zero value otherwise. +func (u *UsageReportOptions) GetYear() int { + if u == nil || u.Year == nil { + return 0 } - return *s.URL + return *u.Year } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetEmail() string { - if s == nil || s.Email == nil { +// GetAssignment returns the Assignment field if it's non-nil, zero value otherwise. +func (u *User) GetAssignment() string { + if u == nil || u.Assignment == nil { return "" } - return *s.Email -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetID() int64 { - if s == nil || s.ID == nil { - return 0 - } - return *s.ID + return *u.Assignment } -// GetImportURL returns the ImportURL field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetImportURL() string { - if s == nil || s.ImportURL == nil { +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (u *User) GetAvatarURL() string { + if u == nil || u.AvatarURL == nil { return "" } - return *s.ImportURL + return *u.AvatarURL } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetName() string { - if s == nil || s.Name == nil { +// GetBio returns the Bio field if it's non-nil, zero value otherwise. +func (u *User) GetBio() string { + if u == nil || u.Bio == nil { return "" } - return *s.Name + return *u.Bio } -// GetRemoteID returns the RemoteID field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetRemoteID() string { - if s == nil || s.RemoteID == nil { +// GetBlog returns the Blog field if it's non-nil, zero value otherwise. +func (u *User) GetBlog() string { + if u == nil || u.Blog == nil { return "" } - return *s.RemoteID + return *u.Blog } -// GetRemoteName returns the RemoteName field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetRemoteName() string { - if s == nil || s.RemoteName == nil { - return "" +// GetBusinessPlus returns the BusinessPlus field if it's non-nil, zero value otherwise. +func (u *User) GetBusinessPlus() bool { + if u == nil || u.BusinessPlus == nil { + return false } - return *s.RemoteName + return *u.BusinessPlus } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetURL() string { - if s == nil || s.URL == nil { - return "" +// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. +func (u *User) GetCollaborators() int { + if u == nil || u.Collaborators == nil { + return 0 } - return *s.URL + return *u.Collaborators } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (s *StarEvent) GetAction() string { - if s == nil || s.Action == nil { +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (u *User) GetCompany() string { + if u == nil || u.Company == nil { return "" } - return *s.Action + return *u.Company } -// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. -func (s *StarEvent) GetStarredAt() Timestamp { - if s == nil || s.StarredAt == nil { +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (u *User) GetCreatedAt() Timestamp { + if u == nil || u.CreatedAt == nil { return Timestamp{} } - return *s.StarredAt + return *u.CreatedAt } -// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. -func (s *Stargazer) GetStarredAt() Timestamp { - if s == nil || s.StarredAt == nil { - return Timestamp{} +// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. +func (u *User) GetDiskUsage() int { + if u == nil || u.DiskUsage == nil { + return 0 } - return *s.StarredAt + return *u.DiskUsage } -// GetUser returns the User field. -func (s *Stargazer) GetUser() *User { - if s == nil { - return nil +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (u *User) GetEmail() string { + if u == nil || u.Email == nil { + return "" } - return s.User + return *u.Email } -// GetRepository returns the Repository field. -func (s *StarredRepository) GetRepository() *Repository { - if s == nil { - return nil +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (u *User) GetEventsURL() string { + if u == nil || u.EventsURL == nil { + return "" } - return s.Repository + return *u.EventsURL } -// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. -func (s *StarredRepository) GetStarredAt() Timestamp { - if s == nil || s.StarredAt == nil { - return Timestamp{} +// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. +func (u *User) GetFollowers() int { + if u == nil || u.Followers == nil { + return 0 } - return *s.StarredAt + return *u.Followers } -// GetCommit returns the Commit field. -func (s *StatusEvent) GetCommit() *RepositoryCommit { - if s == nil { - return nil +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (u *User) GetFollowersURL() string { + if u == nil || u.FollowersURL == nil { + return "" } - return s.Commit + return *u.FollowersURL } -// GetContext returns the Context field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetContext() string { - if s == nil || s.Context == nil { - return "" +// GetFollowing returns the Following field if it's non-nil, zero value otherwise. +func (u *User) GetFollowing() int { + if u == nil || u.Following == nil { + return 0 } - return *s.Context + return *u.Following } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetCreatedAt() Timestamp { - if s == nil || s.CreatedAt == nil { - return Timestamp{} +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (u *User) GetFollowingURL() string { + if u == nil || u.FollowingURL == nil { + return "" } - return *s.CreatedAt + return *u.FollowingURL } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetDescription() string { - if s == nil || s.Description == nil { +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (u *User) GetGistsURL() string { + if u == nil || u.GistsURL == nil { return "" } - return *s.Description + return *u.GistsURL } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetID() int64 { - if s == nil || s.ID == nil { - return 0 +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (u *User) GetGravatarID() string { + if u == nil || u.GravatarID == nil { + return "" } - return *s.ID + return *u.GravatarID } -// GetInstallation returns the Installation field. -func (s *StatusEvent) GetInstallation() *Installation { - if s == nil { - return nil +// GetHireable returns the Hireable field if it's non-nil, zero value otherwise. +func (u *User) GetHireable() bool { + if u == nil || u.Hireable == nil { + return false } - return s.Installation + return *u.Hireable } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetName() string { - if s == nil || s.Name == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (u *User) GetHTMLURL() string { + if u == nil || u.HTMLURL == nil { return "" } - return *s.Name + return *u.HTMLURL } -// GetRepo returns the Repo field. -func (s *StatusEvent) GetRepo() *Repository { - if s == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (u *User) GetID() int64 { + if u == nil || u.ID == nil { + return 0 } - return s.Repo + return *u.ID } -// GetSender returns the Sender field. -func (s *StatusEvent) GetSender() *User { - if s == nil { +// GetInherited returns the Inherited field if it's non-nil, zero value otherwise. +func (u *User) GetInherited() bool { + if u == nil || u.Inherited == nil { + return false + } + return *u.Inherited +} + +// GetInheritedFrom returns the InheritedFrom slice if it's non-nil, nil otherwise. +func (u *User) GetInheritedFrom() []*Team { + if u == nil || u.InheritedFrom == nil { return nil } - return s.Sender + return u.InheritedFrom } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetSHA() string { - if s == nil || s.SHA == nil { +// GetLdapDn returns the LdapDn field if it's non-nil, zero value otherwise. +func (u *User) GetLdapDn() string { + if u == nil || u.LdapDn == nil { return "" } - return *s.SHA + return *u.LdapDn } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetState() string { - if s == nil || s.State == nil { +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (u *User) GetLocation() string { + if u == nil || u.Location == nil { return "" } - return *s.State + return *u.Location } -// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetTargetURL() string { - if s == nil || s.TargetURL == nil { +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (u *User) GetLogin() string { + if u == nil || u.Login == nil { return "" } - return *s.TargetURL + return *u.Login } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetUpdatedAt() Timestamp { - if s == nil || s.UpdatedAt == nil { - return Timestamp{} +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *User) GetName() string { + if u == nil || u.Name == nil { + return "" } - return *s.UpdatedAt + return *u.Name } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (s *Subscription) GetCreatedAt() Timestamp { - if s == nil || s.CreatedAt == nil { - return Timestamp{} +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (u *User) GetNodeID() string { + if u == nil || u.NodeID == nil { + return "" } - return *s.CreatedAt + return *u.NodeID } -// GetIgnored returns the Ignored field if it's non-nil, zero value otherwise. -func (s *Subscription) GetIgnored() bool { - if s == nil || s.Ignored == nil { - return false +// GetNotificationEmail returns the NotificationEmail field if it's non-nil, zero value otherwise. +func (u *User) GetNotificationEmail() string { + if u == nil || u.NotificationEmail == nil { + return "" } - return *s.Ignored + return *u.NotificationEmail } -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (s *Subscription) GetReason() string { - if s == nil || s.Reason == nil { +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (u *User) GetOrganizationsURL() string { + if u == nil || u.OrganizationsURL == nil { return "" } - return *s.Reason + return *u.OrganizationsURL } -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (s *Subscription) GetRepositoryURL() string { - if s == nil || s.RepositoryURL == nil { - return "" +// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. +func (u *User) GetOwnedPrivateRepos() int64 { + if u == nil || u.OwnedPrivateRepos == nil { + return 0 } - return *s.RepositoryURL + return *u.OwnedPrivateRepos } -// GetSubscribed returns the Subscribed field if it's non-nil, zero value otherwise. -func (s *Subscription) GetSubscribed() bool { - if s == nil || s.Subscribed == nil { - return false +// GetPermissions returns the Permissions field. +func (u *User) GetPermissions() *RepositoryPermissions { + if u == nil { + return nil } - return *s.Subscribed + return u.Permissions } -// GetThreadURL returns the ThreadURL field if it's non-nil, zero value otherwise. -func (s *Subscription) GetThreadURL() string { - if s == nil || s.ThreadURL == nil { - return "" +// GetPlan returns the Plan field. +func (u *User) GetPlan() *Plan { + if u == nil { + return nil } - return *s.ThreadURL + return u.Plan } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *Subscription) GetURL() string { - if s == nil || s.URL == nil { - return "" +// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. +func (u *User) GetPrivateGists() int { + if u == nil || u.PrivateGists == nil { + return 0 } - return *s.URL + return *u.PrivateGists } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (t *Tag) GetMessage() string { - if t == nil || t.Message == nil { - return "" +// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. +func (u *User) GetPublicGists() int { + if u == nil || u.PublicGists == nil { + return 0 } - return *t.Message + return *u.PublicGists } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (t *Tag) GetNodeID() string { - if t == nil || t.NodeID == nil { - return "" +// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. +func (u *User) GetPublicRepos() int { + if u == nil || u.PublicRepos == nil { + return 0 } - return *t.NodeID + return *u.PublicRepos } -// GetObject returns the Object field. -func (t *Tag) GetObject() *GitObject { - if t == nil { - return nil +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (u *User) GetReceivedEventsURL() string { + if u == nil || u.ReceivedEventsURL == nil { + return "" } - return t.Object + return *u.ReceivedEventsURL } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (t *Tag) GetSHA() string { - if t == nil || t.SHA == nil { +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (u *User) GetReposURL() string { + if u == nil || u.ReposURL == nil { return "" } - return *t.SHA + return *u.ReposURL } -// GetTag returns the Tag field if it's non-nil, zero value otherwise. -func (t *Tag) GetTag() string { - if t == nil || t.Tag == nil { +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (u *User) GetRole() string { + if u == nil || u.Role == nil { return "" } - return *t.Tag + return *u.Role } -// GetTagger returns the Tagger field. -func (t *Tag) GetTagger() *CommitAuthor { - if t == nil { - return nil +// GetRoleName returns the RoleName field if it's non-nil, zero value otherwise. +func (u *User) GetRoleName() string { + if u == nil || u.RoleName == nil { + return "" } - return t.Tagger + return *u.RoleName } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *Tag) GetURL() string { - if t == nil || t.URL == nil { - return "" +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (u *User) GetSiteAdmin() bool { + if u == nil || u.SiteAdmin == nil { + return false } - return *t.URL + return *u.SiteAdmin } -// GetVerification returns the Verification field. -func (t *Tag) GetVerification() *SignatureVerification { - if t == nil { - return nil +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (u *User) GetStarredURL() string { + if u == nil || u.StarredURL == nil { + return "" } - return t.Verification + return *u.StarredURL } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (t *Team) GetDescription() string { - if t == nil || t.Description == nil { +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (u *User) GetSubscriptionsURL() string { + if u == nil || u.SubscriptionsURL == nil { return "" } - return *t.Description + return *u.SubscriptionsURL } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (t *Team) GetID() int64 { - if t == nil || t.ID == nil { - return 0 +// GetSuspendedAt returns the SuspendedAt field if it's non-nil, zero value otherwise. +func (u *User) GetSuspendedAt() Timestamp { + if u == nil || u.SuspendedAt == nil { + return Timestamp{} } - return *t.ID + return *u.SuspendedAt } -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (t *Team) GetLDAPDN() string { - if t == nil || t.LDAPDN == nil { - return "" +// GetTextMatches returns the TextMatches slice if it's non-nil, nil otherwise. +func (u *User) GetTextMatches() []*TextMatch { + if u == nil || u.TextMatches == nil { + return nil } - return *t.LDAPDN + return u.TextMatches } -// GetMembersCount returns the MembersCount field if it's non-nil, zero value otherwise. -func (t *Team) GetMembersCount() int { - if t == nil || t.MembersCount == nil { +// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. +func (u *User) GetTotalPrivateRepos() int64 { + if u == nil || u.TotalPrivateRepos == nil { return 0 } - return *t.MembersCount + return *u.TotalPrivateRepos } -// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. -func (t *Team) GetMembersURL() string { - if t == nil || t.MembersURL == nil { +// GetTwitterUsername returns the TwitterUsername field if it's non-nil, zero value otherwise. +func (u *User) GetTwitterUsername() string { + if u == nil || u.TwitterUsername == nil { return "" } - return *t.MembersURL + return *u.TwitterUsername } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (t *Team) GetName() string { - if t == nil || t.Name == nil { - return "" +// GetTwoFactorAuthentication returns the TwoFactorAuthentication field if it's non-nil, zero value otherwise. +func (u *User) GetTwoFactorAuthentication() bool { + if u == nil || u.TwoFactorAuthentication == nil { + return false } - return *t.Name + return *u.TwoFactorAuthentication } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (t *Team) GetNodeID() string { - if t == nil || t.NodeID == nil { +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (u *User) GetType() string { + if u == nil || u.Type == nil { return "" } - return *t.NodeID -} - -// GetOrganization returns the Organization field. -func (t *Team) GetOrganization() *Organization { - if t == nil { - return nil - } - return t.Organization + return *u.Type } -// GetParent returns the Parent field. -func (t *Team) GetParent() *Team { - if t == nil { - return nil +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (u *User) GetUpdatedAt() Timestamp { + if u == nil || u.UpdatedAt == nil { + return Timestamp{} } - return t.Parent + return *u.UpdatedAt } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (t *Team) GetPermission() string { - if t == nil || t.Permission == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *User) GetURL() string { + if u == nil || u.URL == nil { return "" } - return *t.Permission + return *u.URL } -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (t *Team) GetPrivacy() string { - if t == nil || t.Privacy == nil { +// GetUserViewType returns the UserViewType field if it's non-nil, zero value otherwise. +func (u *User) GetUserViewType() string { + if u == nil || u.UserViewType == nil { return "" } - return *t.Privacy + return *u.UserViewType } -// GetReposCount returns the ReposCount field if it's non-nil, zero value otherwise. -func (t *Team) GetReposCount() int { - if t == nil || t.ReposCount == nil { - return 0 +// GetApp returns the App field. +func (u *UserAuthorization) GetApp() *OAuthAPP { + if u == nil { + return nil } - return *t.ReposCount + return u.App } -// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. -func (t *Team) GetRepositoriesURL() string { - if t == nil || t.RepositoriesURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetCreatedAt() Timestamp { + if u == nil || u.CreatedAt == nil { + return Timestamp{} } - return *t.RepositoriesURL + return *u.CreatedAt } -// GetSlug returns the Slug field if it's non-nil, zero value otherwise. -func (t *Team) GetSlug() string { - if t == nil || t.Slug == nil { +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetFingerprint() string { + if u == nil || u.Fingerprint == nil { return "" } - return *t.Slug + return *u.Fingerprint } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *Team) GetURL() string { - if t == nil || t.URL == nil { +// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetHashedToken() string { + if u == nil || u.HashedToken == nil { return "" } - return *t.URL + return *u.HashedToken } -// GetInstallation returns the Installation field. -func (t *TeamAddEvent) GetInstallation() *Installation { - if t == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetID() int64 { + if u == nil || u.ID == nil { + return 0 } - return t.Installation + return *u.ID } -// GetOrg returns the Org field. -func (t *TeamAddEvent) GetOrg() *Organization { - if t == nil { - return nil +// GetNote returns the Note field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetNote() string { + if u == nil || u.Note == nil { + return "" } - return t.Org + return *u.Note } -// GetRepo returns the Repo field. -func (t *TeamAddEvent) GetRepo() *Repository { - if t == nil { - return nil +// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetNoteURL() string { + if u == nil || u.NoteURL == nil { + return "" } - return t.Repo + return *u.NoteURL } -// GetSender returns the Sender field. -func (t *TeamAddEvent) GetSender() *User { - if t == nil { +// GetScopes returns the Scopes slice if it's non-nil, nil otherwise. +func (u *UserAuthorization) GetScopes() []string { + if u == nil || u.Scopes == nil { return nil } - return t.Sender + return u.Scopes } -// GetTeam returns the Team field. -func (t *TeamAddEvent) GetTeam() *Team { - if t == nil { - return nil +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetToken() string { + if u == nil || u.Token == nil { + return "" } - return t.Team + return *u.Token } -// GetAuthor returns the Author field. -func (t *TeamDiscussion) GetAuthor() *User { - if t == nil { - return nil +// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetTokenLastEight() string { + if u == nil || u.TokenLastEight == nil { + return "" } - return t.Author + return *u.TokenLastEight } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetBody() string { - if t == nil || t.Body == nil { - return "" +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetUpdatedAt() Timestamp { + if u == nil || u.UpdatedAt == nil { + return Timestamp{} } - return *t.Body + return *u.UpdatedAt } -// GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetBodyHTML() string { - if t == nil || t.BodyHTML == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *UserAuthorization) GetURL() string { + if u == nil || u.URL == nil { return "" } - return *t.BodyHTML + return *u.URL } -// GetBodyVersion returns the BodyVersion field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetBodyVersion() string { - if t == nil || t.BodyVersion == nil { +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (u *UserContext) GetMessage() string { + if u == nil || u.Message == nil { return "" } - return *t.BodyVersion + return *u.Message } -// GetCommentsCount returns the CommentsCount field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetCommentsCount() int { - if t == nil || t.CommentsCount == nil { - return 0 +// GetOcticon returns the Octicon field if it's non-nil, zero value otherwise. +func (u *UserContext) GetOcticon() string { + if u == nil || u.Octicon == nil { + return "" } - return *t.CommentsCount + return *u.Octicon } -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetCommentsURL() string { - if t == nil || t.CommentsURL == nil { +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetEmail() string { + if u == nil || u.Email == nil { return "" } - return *t.CommentsURL + return *u.Email } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetCreatedAt() Timestamp { - if t == nil || t.CreatedAt == nil { - return Timestamp{} +// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetPrimary() bool { + if u == nil || u.Primary == nil { + return false } - return *t.CreatedAt + return *u.Primary } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetHTMLURL() string { - if t == nil || t.HTMLURL == nil { - return "" +// GetVerified returns the Verified field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetVerified() bool { + if u == nil || u.Verified == nil { + return false } - return *t.HTMLURL + return *u.Verified } -// GetLastEditedAt returns the LastEditedAt field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetLastEditedAt() Timestamp { - if t == nil || t.LastEditedAt == nil { - return Timestamp{} +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (u *UserEmail) GetVisibility() string { + if u == nil || u.Visibility == nil { + return "" } - return *t.LastEditedAt + return *u.Visibility } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetNodeID() string { - if t == nil || t.NodeID == nil { +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (u *UserEvent) GetAction() string { + if u == nil || u.Action == nil { return "" } - return *t.NodeID + return *u.Action } -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetNumber() int { - if t == nil || t.Number == nil { - return 0 +// GetEnterprise returns the Enterprise field. +func (u *UserEvent) GetEnterprise() *Enterprise { + if u == nil { + return nil } - return *t.Number + return u.Enterprise } -// GetPinned returns the Pinned field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetPinned() bool { - if t == nil || t.Pinned == nil { - return false +// GetInstallation returns the Installation field. +func (u *UserEvent) GetInstallation() *Installation { + if u == nil { + return nil } - return *t.Pinned + return u.Installation } -// GetPrivate returns the Private field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetPrivate() bool { - if t == nil || t.Private == nil { - return false +// GetSender returns the Sender field. +func (u *UserEvent) GetSender() *User { + if u == nil { + return nil } - return *t.Private + return u.Sender } -// GetReactions returns the Reactions field. -func (t *TeamDiscussion) GetReactions() *Reactions { - if t == nil { +// GetUser returns the User field. +func (u *UserEvent) GetUser() *User { + if u == nil { return nil } - return t.Reactions + return u.User } -// GetTeamURL returns the TeamURL field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetTeamURL() string { - if t == nil || t.TeamURL == nil { +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetAvatarURL() string { + if u == nil || u.AvatarURL == nil { return "" } - return *t.TeamURL + return *u.AvatarURL } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetTitle() string { - if t == nil || t.Title == nil { +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetEventsURL() string { + if u == nil || u.EventsURL == nil { return "" } - return *t.Title + return *u.EventsURL } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetUpdatedAt() Timestamp { - if t == nil || t.UpdatedAt == nil { - return Timestamp{} +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetFollowersURL() string { + if u == nil || u.FollowersURL == nil { + return "" } - return *t.UpdatedAt + return *u.FollowersURL } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *TeamDiscussion) GetURL() string { - if t == nil || t.URL == nil { +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetFollowingURL() string { + if u == nil || u.FollowingURL == nil { return "" } - return *t.URL + return *u.FollowingURL } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (t *TeamEvent) GetAction() string { - if t == nil || t.Action == nil { +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetGistsURL() string { + if u == nil || u.GistsURL == nil { return "" } - return *t.Action + return *u.GistsURL } -// GetChanges returns the Changes field. -func (t *TeamEvent) GetChanges() *TeamChange { - if t == nil { - return nil +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetGravatarID() string { + if u == nil || u.GravatarID == nil { + return "" } - return t.Changes + return *u.GravatarID } -// GetInstallation returns the Installation field. -func (t *TeamEvent) GetInstallation() *Installation { - if t == nil { - return nil +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetID() int64 { + if u == nil || u.ID == nil { + return 0 } - return t.Installation + return *u.ID } -// GetOrg returns the Org field. -func (t *TeamEvent) GetOrg() *Organization { - if t == nil { - return nil +// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetLDAPDN() string { + if u == nil || u.LDAPDN == nil { + return "" } - return t.Org + return *u.LDAPDN } -// GetRepo returns the Repo field. -func (t *TeamEvent) GetRepo() *Repository { - if t == nil { - return nil +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetLogin() string { + if u == nil || u.Login == nil { + return "" } - return t.Repo + return *u.Login } -// GetSender returns the Sender field. -func (t *TeamEvent) GetSender() *User { - if t == nil { - return nil +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetOrganizationsURL() string { + if u == nil || u.OrganizationsURL == nil { + return "" } - return t.Sender + return *u.OrganizationsURL } -// GetTeam returns the Team field. -func (t *TeamEvent) GetTeam() *Team { - if t == nil { - return nil +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetReceivedEventsURL() string { + if u == nil || u.ReceivedEventsURL == nil { + return "" } - return t.Team + return *u.ReceivedEventsURL } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetDescription() string { - if t == nil || t.Description == nil { +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetReposURL() string { + if u == nil || u.ReposURL == nil { return "" } - return *t.Description + return *u.ReposURL } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetID() int64 { - if t == nil || t.ID == nil { - return 0 +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetSiteAdmin() bool { + if u == nil || u.SiteAdmin == nil { + return false } - return *t.ID + return *u.SiteAdmin } -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetLDAPDN() string { - if t == nil || t.LDAPDN == nil { +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetStarredURL() string { + if u == nil || u.StarredURL == nil { return "" } - return *t.LDAPDN + return *u.StarredURL } -// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetMembersURL() string { - if t == nil || t.MembersURL == nil { +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetSubscriptionsURL() string { + if u == nil || u.SubscriptionsURL == nil { return "" } - return *t.MembersURL + return *u.SubscriptionsURL } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetName() string { - if t == nil || t.Name == nil { +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetType() string { + if u == nil || u.Type == nil { return "" } - return *t.Name + return *u.Type } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetPermission() string { - if t == nil || t.Permission == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *UserLDAPMapping) GetURL() string { + if u == nil || u.URL == nil { return "" } - return *t.Permission + return *u.URL } -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetPrivacy() string { - if t == nil || t.Privacy == nil { +// GetPerPage returns the PerPage field. +func (u *UserListOptions) GetPerPage() int { + if u == nil { + return 0 + } + return u.PerPage +} + +// GetSince returns the Since field. +func (u *UserListOptions) GetSince() int64 { + if u == nil { + return 0 + } + return u.Since +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetCreatedAt() string { + if u == nil || u.CreatedAt == nil { return "" } - return *t.Privacy + return *u.CreatedAt +} + +// GetExcludeAttachments returns the ExcludeAttachments field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetExcludeAttachments() bool { + if u == nil || u.ExcludeAttachments == nil { + return false + } + return *u.ExcludeAttachments } -// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetRepositoriesURL() string { - if t == nil || t.RepositoriesURL == nil { +// GetGUID returns the GUID field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetGUID() string { + if u == nil || u.GUID == nil { return "" } - return *t.RepositoriesURL + return *u.GUID } -// GetSlug returns the Slug field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetSlug() string { - if t == nil || t.Slug == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetID() int64 { + if u == nil || u.ID == nil { + return 0 } - return *t.Slug + return *u.ID } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetURL() string { - if t == nil || t.URL == nil { - return "" +// GetLockRepositories returns the LockRepositories field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetLockRepositories() bool { + if u == nil || u.LockRepositories == nil { + return false } - return *t.URL + return *u.LockRepositories } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (t *TeamProjectOptions) GetPermission() string { - if t == nil || t.Permission == nil { - return "" +// GetRepositories returns the Repositories slice if it's non-nil, nil otherwise. +func (u *UserMigration) GetRepositories() []*Repository { + if u == nil || u.Repositories == nil { + return nil } - return *t.Permission + return u.Repositories } -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (t *TemplateRepoRequest) GetDescription() string { - if t == nil || t.Description == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetState() string { + if u == nil || u.State == nil { return "" } - return *t.Description + return *u.State } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (t *TemplateRepoRequest) GetName() string { - if t == nil || t.Name == nil { +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetUpdatedAt() string { + if u == nil || u.UpdatedAt == nil { return "" } - return *t.Name + return *u.UpdatedAt } -// GetOwner returns the Owner field if it's non-nil, zero value otherwise. -func (t *TemplateRepoRequest) GetOwner() string { - if t == nil || t.Owner == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (u *UserMigration) GetURL() string { + if u == nil || u.URL == nil { return "" } - return *t.Owner + return *u.URL } -// GetPrivate returns the Private field if it's non-nil, zero value otherwise. -func (t *TemplateRepoRequest) GetPrivate() bool { - if t == nil || t.Private == nil { +// GetExcludeAttachments returns the ExcludeAttachments field. +func (u *UserMigrationOptions) GetExcludeAttachments() bool { + if u == nil { return false } - return *t.Private -} - -// GetFragment returns the Fragment field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetFragment() string { - if t == nil || t.Fragment == nil { - return "" - } - return *t.Fragment + return u.ExcludeAttachments } -// GetObjectType returns the ObjectType field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetObjectType() string { - if t == nil || t.ObjectType == nil { - return "" +// GetLockRepositories returns the LockRepositories field. +func (u *UserMigrationOptions) GetLockRepositories() bool { + if u == nil { + return false } - return *t.ObjectType + return u.LockRepositories } -// GetObjectURL returns the ObjectURL field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetObjectURL() string { - if t == nil || t.ObjectURL == nil { - return "" +// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. +func (u *UsersSearchResult) GetIncompleteResults() bool { + if u == nil || u.IncompleteResults == nil { + return false } - return *t.ObjectURL + return *u.IncompleteResults } -// GetProperty returns the Property field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetProperty() string { - if t == nil || t.Property == nil { - return "" +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (u *UsersSearchResult) GetTotal() int { + if u == nil || u.Total == nil { + return 0 } - return *t.Property + return *u.Total } -// GetActor returns the Actor field. -func (t *Timeline) GetActor() *User { - if t == nil { +// GetUsers returns the Users slice if it's non-nil, nil otherwise. +func (u *UsersSearchResult) GetUsers() []*User { + if u == nil || u.Users == nil { return nil } - return t.Actor + return u.Users } -// GetAssignee returns the Assignee field. -func (t *Timeline) GetAssignee() *User { - if t == nil { - return nil +// GetAdminUsers returns the AdminUsers field if it's non-nil, zero value otherwise. +func (u *UserStats) GetAdminUsers() int { + if u == nil || u.AdminUsers == nil { + return 0 } - return t.Assignee + return *u.AdminUsers } -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCommitID() string { - if t == nil || t.CommitID == nil { - return "" +// GetSuspendedUsers returns the SuspendedUsers field if it's non-nil, zero value otherwise. +func (u *UserStats) GetSuspendedUsers() int { + if u == nil || u.SuspendedUsers == nil { + return 0 } - return *t.CommitID + return *u.SuspendedUsers } -// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCommitURL() string { - if t == nil || t.CommitURL == nil { - return "" +// GetTotalUsers returns the TotalUsers field if it's non-nil, zero value otherwise. +func (u *UserStats) GetTotalUsers() int { + if u == nil || u.TotalUsers == nil { + return 0 } - return *t.CommitURL + return *u.TotalUsers } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCreatedAt() time.Time { - if t == nil || t.CreatedAt == nil { - return time.Time{} +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (u *UserSuspendOptions) GetReason() string { + if u == nil || u.Reason == nil { + return "" } - return *t.CreatedAt + return *u.Reason } -// GetEvent returns the Event field if it's non-nil, zero value otherwise. -func (t *Timeline) GetEvent() string { - if t == nil || t.Event == nil { +// GetBio returns the Bio field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetBio() string { + if u == nil || u.Bio == nil { return "" } - return *t.Event + return *u.Bio } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (t *Timeline) GetID() int64 { - if t == nil || t.ID == nil { - return 0 +// GetBlog returns the Blog field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetBlog() string { + if u == nil || u.Blog == nil { + return "" } - return *t.ID + return *u.Blog } -// GetLabel returns the Label field. -func (t *Timeline) GetLabel() *Label { - if t == nil { - return nil +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetCompany() string { + if u == nil || u.Company == nil { + return "" } - return t.Label + return *u.Company } -// GetMilestone returns the Milestone field. -func (t *Timeline) GetMilestone() *Milestone { - if t == nil { - return nil +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetEmail() string { + if u == nil || u.Email == nil { + return "" } - return t.Milestone + return *u.Email } -// GetProjectCard returns the ProjectCard field. -func (t *Timeline) GetProjectCard() *ProjectCard { - if t == nil { - return nil +// GetHireable returns the Hireable field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetHireable() bool { + if u == nil || u.Hireable == nil { + return false } - return t.ProjectCard + return *u.Hireable } -// GetRename returns the Rename field. -func (t *Timeline) GetRename() *Rename { - if t == nil { - return nil +// GetLocation returns the Location field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetLocation() string { + if u == nil || u.Location == nil { + return "" } - return t.Rename + return *u.Location } -// GetSource returns the Source field. -func (t *Timeline) GetSource() *Source { - if t == nil { - return nil +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetName() string { + if u == nil || u.Name == nil { + return "" } - return t.Source + return *u.Name } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *Timeline) GetURL() string { - if t == nil || t.URL == nil { +// GetTwitterUsername returns the TwitterUsername field if it's non-nil, zero value otherwise. +func (u *UserUpdateRequest) GetTwitterUsername() string { + if u == nil || u.TwitterUsername == nil { return "" } - return *t.URL + return *u.TwitterUsername } -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficClones) GetCount() int { - if t == nil || t.Count == nil { - return 0 +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (v *VulnerabilityPackage) GetEcosystem() string { + if v == nil || v.Ecosystem == nil { + return "" } - return *t.Count + return *v.Ecosystem } -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficClones) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (v *VulnerabilityPackage) GetName() string { + if v == nil || v.Name == nil { + return "" } - return *t.Uniques + return *v.Name } -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficData) GetCount() int { - if t == nil || t.Count == nil { - return 0 +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (w *WatchEvent) GetAction() string { + if w == nil || w.Action == nil { + return "" } - return *t.Count + return *w.Action } -// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. -func (t *TrafficData) GetTimestamp() Timestamp { - if t == nil || t.Timestamp == nil { - return Timestamp{} +// GetInstallation returns the Installation field. +func (w *WatchEvent) GetInstallation() *Installation { + if w == nil { + return nil } - return *t.Timestamp + return w.Installation } -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficData) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 +// GetOrg returns the Org field. +func (w *WatchEvent) GetOrg() *Organization { + if w == nil { + return nil } - return *t.Uniques + return w.Org } -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetCount() int { - if t == nil || t.Count == nil { - return 0 +// GetRepo returns the Repo field. +func (w *WatchEvent) GetRepo() *Repository { + if w == nil { + return nil } - return *t.Count + return w.Repo } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetPath() string { - if t == nil || t.Path == nil { - return "" +// GetSender returns the Sender field. +func (w *WatchEvent) GetSender() *User { + if w == nil { + return nil } - return *t.Path + return w.Sender } -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetTitle() string { - if t == nil || t.Title == nil { - return "" +// GetDays returns the Days slice if it's non-nil, nil otherwise. +func (w *WeeklyCommitActivity) GetDays() []int { + if w == nil || w.Days == nil { + return nil } - return *t.Title + return w.Days } -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetUniques() int { - if t == nil || t.Uniques == nil { +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (w *WeeklyCommitActivity) GetTotal() int { + if w == nil || w.Total == nil { return 0 } - return *t.Uniques + return *w.Total } -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficReferrer) GetCount() int { - if t == nil || t.Count == nil { - return 0 +// GetWeek returns the Week field if it's non-nil, zero value otherwise. +func (w *WeeklyCommitActivity) GetWeek() Timestamp { + if w == nil || w.Week == nil { + return Timestamp{} } - return *t.Count + return *w.Week } -// GetReferrer returns the Referrer field if it's non-nil, zero value otherwise. -func (t *TrafficReferrer) GetReferrer() string { - if t == nil || t.Referrer == nil { - return "" +// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetAdditions() int { + if w == nil || w.Additions == nil { + return 0 } - return *t.Referrer + return *w.Additions } -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficReferrer) GetUniques() int { - if t == nil || t.Uniques == nil { +// GetCommits returns the Commits field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetCommits() int { + if w == nil || w.Commits == nil { return 0 } - return *t.Uniques + return *w.Commits } -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficViews) GetCount() int { - if t == nil || t.Count == nil { +// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetDeletions() int { + if w == nil || w.Deletions == nil { return 0 } - return *t.Count + return *w.Deletions } -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficViews) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 +// GetWeek returns the Week field if it's non-nil, zero value otherwise. +func (w *WeeklyStats) GetWeek() Timestamp { + if w == nil || w.Week == nil { + return Timestamp{} } - return *t.Uniques + return *w.Week } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (t *Tree) GetSHA() string { - if t == nil || t.SHA == nil { +// GetBadgeURL returns the BadgeURL field if it's non-nil, zero value otherwise. +func (w *Workflow) GetBadgeURL() string { + if w == nil || w.BadgeURL == nil { return "" } - return *t.SHA + return *w.BadgeURL } -// GetTruncated returns the Truncated field if it's non-nil, zero value otherwise. -func (t *Tree) GetTruncated() bool { - if t == nil || t.Truncated == nil { - return false +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (w *Workflow) GetCreatedAt() Timestamp { + if w == nil || w.CreatedAt == nil { + return Timestamp{} } - return *t.Truncated + return *w.CreatedAt } -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetContent() string { - if t == nil || t.Content == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (w *Workflow) GetHTMLURL() string { + if w == nil || w.HTMLURL == nil { return "" } - return *t.Content + return *w.HTMLURL } -// GetMode returns the Mode field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetMode() string { - if t == nil || t.Mode == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (w *Workflow) GetID() int64 { + if w == nil || w.ID == nil { + return 0 } - return *t.Mode + return *w.ID } -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetPath() string { - if t == nil || t.Path == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (w *Workflow) GetName() string { + if w == nil || w.Name == nil { return "" } - return *t.Path + return *w.Name } -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetSHA() string { - if t == nil || t.SHA == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (w *Workflow) GetNodeID() string { + if w == nil || w.NodeID == nil { return "" } - return *t.SHA + return *w.NodeID } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetSize() int { - if t == nil || t.Size == nil { - return 0 +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (w *Workflow) GetPath() string { + if w == nil || w.Path == nil { + return "" } - return *t.Size + return *w.Path } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetType() string { - if t == nil || t.Type == nil { +// GetState returns the State field if it's non-nil, zero value otherwise. +func (w *Workflow) GetState() string { + if w == nil || w.State == nil { return "" } - return *t.Type + return *w.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (w *Workflow) GetUpdatedAt() Timestamp { + if w == nil || w.UpdatedAt == nil { + return Timestamp{} + } + return *w.UpdatedAt } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetURL() string { - if t == nil || t.URL == nil { +func (w *Workflow) GetURL() string { + if w == nil || w.URL == nil { return "" } - return *t.URL + return *w.URL } -// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetCompletedAt() Timestamp { - if u == nil || u.CompletedAt == nil { - return Timestamp{} +// GetTotalMS returns the TotalMS field if it's non-nil, zero value otherwise. +func (w *WorkflowBill) GetTotalMS() int64 { + if w == nil || w.TotalMS == nil { + return 0 } - return *u.CompletedAt + return *w.TotalMS } -// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetConclusion() string { - if u == nil || u.Conclusion == nil { - return "" +// GetInputs returns the Inputs field. +func (w *WorkflowDispatchEvent) GetInputs() json.RawMessage { + if w == nil { + return json.RawMessage{} } - return *u.Conclusion + return w.Inputs } -// GetDetailsURL returns the DetailsURL field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetDetailsURL() string { - if u == nil || u.DetailsURL == nil { - return "" +// GetInstallation returns the Installation field. +func (w *WorkflowDispatchEvent) GetInstallation() *Installation { + if w == nil { + return nil } - return *u.DetailsURL + return w.Installation } -// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetExternalID() string { - if u == nil || u.ExternalID == nil { - return "" +// GetOrg returns the Org field. +func (w *WorkflowDispatchEvent) GetOrg() *Organization { + if w == nil { + return nil } - return *u.ExternalID + return w.Org } -// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetHeadBranch() string { - if u == nil || u.HeadBranch == nil { +// GetRef returns the Ref field if it's non-nil, zero value otherwise. +func (w *WorkflowDispatchEvent) GetRef() string { + if w == nil || w.Ref == nil { return "" } - return *u.HeadBranch + return *w.Ref } -// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetHeadSHA() string { - if u == nil || u.HeadSHA == nil { - return "" +// GetRepo returns the Repo field. +func (w *WorkflowDispatchEvent) GetRepo() *Repository { + if w == nil { + return nil } - return *u.HeadSHA + return w.Repo } -// GetOutput returns the Output field. -func (u *UpdateCheckRunOptions) GetOutput() *CheckRunOutput { - if u == nil { +// GetSender returns the Sender field. +func (w *WorkflowDispatchEvent) GetSender() *User { + if w == nil { return nil } - return u.Output + return w.Sender } -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (u *UpdateCheckRunOptions) GetStatus() string { - if u == nil || u.Status == nil { +// GetWorkflow returns the Workflow field if it's non-nil, zero value otherwise. +func (w *WorkflowDispatchEvent) GetWorkflow() string { + if w == nil || w.Workflow == nil { return "" } - return *u.Status + return *w.Workflow } -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (u *User) GetAvatarURL() string { - if u == nil || u.AvatarURL == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (w *WorkflowDispatchRunDetails) GetHTMLURL() string { + if w == nil || w.HTMLURL == nil { return "" } - return *u.AvatarURL + return *w.HTMLURL } -// GetBio returns the Bio field if it's non-nil, zero value otherwise. -func (u *User) GetBio() string { - if u == nil || u.Bio == nil { +// GetRunURL returns the RunURL field if it's non-nil, zero value otherwise. +func (w *WorkflowDispatchRunDetails) GetRunURL() string { + if w == nil || w.RunURL == nil { return "" } - return *u.Bio + return *w.RunURL } -// GetBlog returns the Blog field if it's non-nil, zero value otherwise. -func (u *User) GetBlog() string { - if u == nil || u.Blog == nil { +// GetWorkflowRunID returns the WorkflowRunID field if it's non-nil, zero value otherwise. +func (w *WorkflowDispatchRunDetails) GetWorkflowRunID() int64 { + if w == nil || w.WorkflowRunID == nil { + return 0 + } + return *w.WorkflowRunID +} + +// GetCheckRunURL returns the CheckRunURL field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetCheckRunURL() string { + if w == nil || w.CheckRunURL == nil { return "" } - return *u.Blog + return *w.CheckRunURL } -// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. -func (u *User) GetCollaborators() int { - if u == nil || u.Collaborators == nil { - return 0 +// GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetCompletedAt() Timestamp { + if w == nil || w.CompletedAt == nil { + return Timestamp{} } - return *u.Collaborators + return *w.CompletedAt } -// GetCompany returns the Company field if it's non-nil, zero value otherwise. -func (u *User) GetCompany() string { - if u == nil || u.Company == nil { +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetConclusion() string { + if w == nil || w.Conclusion == nil { return "" } - return *u.Company + return *w.Conclusion } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (u *User) GetCreatedAt() Timestamp { - if u == nil || u.CreatedAt == nil { +func (w *WorkflowJob) GetCreatedAt() Timestamp { + if w == nil || w.CreatedAt == nil { return Timestamp{} } - return *u.CreatedAt + return *w.CreatedAt } -// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. -func (u *User) GetDiskUsage() int { - if u == nil || u.DiskUsage == nil { - return 0 +// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetHeadBranch() string { + if w == nil || w.HeadBranch == nil { + return "" } - return *u.DiskUsage + return *w.HeadBranch } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (u *User) GetEmail() string { - if u == nil || u.Email == nil { +// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetHeadSHA() string { + if w == nil || w.HeadSHA == nil { return "" } - return *u.Email + return *w.HeadSHA } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (u *User) GetEventsURL() string { - if u == nil || u.EventsURL == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetHTMLURL() string { + if w == nil || w.HTMLURL == nil { return "" } - return *u.EventsURL + return *w.HTMLURL } -// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. -func (u *User) GetFollowers() int { - if u == nil || u.Followers == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetID() int64 { + if w == nil || w.ID == nil { return 0 } - return *u.Followers + return *w.ID } -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (u *User) GetFollowersURL() string { - if u == nil || u.FollowersURL == nil { - return "" +// GetLabels returns the Labels slice if it's non-nil, nil otherwise. +func (w *WorkflowJob) GetLabels() []string { + if w == nil || w.Labels == nil { + return nil } - return *u.FollowersURL + return w.Labels } -// GetFollowing returns the Following field if it's non-nil, zero value otherwise. -func (u *User) GetFollowing() int { - if u == nil || u.Following == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetName() string { + if w == nil || w.Name == nil { + return "" } - return *u.Following + return *w.Name } -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (u *User) GetFollowingURL() string { - if u == nil || u.FollowingURL == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetNodeID() string { + if w == nil || w.NodeID == nil { return "" } - return *u.FollowingURL + return *w.NodeID } -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (u *User) GetGistsURL() string { - if u == nil || u.GistsURL == nil { - return "" +// GetRunAttempt returns the RunAttempt field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunAttempt() int64 { + if w == nil || w.RunAttempt == nil { + return 0 } - return *u.GistsURL + return *w.RunAttempt } -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (u *User) GetGravatarID() string { - if u == nil || u.GravatarID == nil { - return "" +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunID() int64 { + if w == nil || w.RunID == nil { + return 0 } - return *u.GravatarID + return *w.RunID } -// GetHireable returns the Hireable field if it's non-nil, zero value otherwise. -func (u *User) GetHireable() bool { - if u == nil || u.Hireable == nil { - return false +// GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunnerGroupID() int64 { + if w == nil || w.RunnerGroupID == nil { + return 0 } - return *u.Hireable + return *w.RunnerGroupID } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (u *User) GetHTMLURL() string { - if u == nil || u.HTMLURL == nil { +// GetRunnerGroupName returns the RunnerGroupName field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunnerGroupName() string { + if w == nil || w.RunnerGroupName == nil { return "" } - return *u.HTMLURL + return *w.RunnerGroupName } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (u *User) GetID() int64 { - if u == nil || u.ID == nil { +// GetRunnerID returns the RunnerID field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunnerID() int64 { + if w == nil || w.RunnerID == nil { return 0 } - return *u.ID + return *w.RunnerID } -// GetLdapDn returns the LdapDn field if it's non-nil, zero value otherwise. -func (u *User) GetLdapDn() string { - if u == nil || u.LdapDn == nil { +// GetRunnerName returns the RunnerName field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunnerName() string { + if w == nil || w.RunnerName == nil { return "" } - return *u.LdapDn + return *w.RunnerName } -// GetLocation returns the Location field if it's non-nil, zero value otherwise. -func (u *User) GetLocation() string { - if u == nil || u.Location == nil { +// GetRunURL returns the RunURL field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetRunURL() string { + if w == nil || w.RunURL == nil { return "" } - return *u.Location + return *w.RunURL } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (u *User) GetLogin() string { - if u == nil || u.Login == nil { - return "" +// GetStartedAt returns the StartedAt field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetStartedAt() Timestamp { + if w == nil || w.StartedAt == nil { + return Timestamp{} } - return *u.Login + return *w.StartedAt } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (u *User) GetName() string { - if u == nil || u.Name == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetStatus() string { + if w == nil || w.Status == nil { return "" } - return *u.Name + return *w.Status } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (u *User) GetNodeID() string { - if u == nil || u.NodeID == nil { - return "" +// GetSteps returns the Steps slice if it's non-nil, nil otherwise. +func (w *WorkflowJob) GetSteps() []*TaskStep { + if w == nil || w.Steps == nil { + return nil } - return *u.NodeID + return w.Steps } -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (u *User) GetOrganizationsURL() string { - if u == nil || u.OrganizationsURL == nil { +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetURL() string { + if w == nil || w.URL == nil { return "" } - return *u.OrganizationsURL + return *w.URL } -// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. -func (u *User) GetOwnedPrivateRepos() int { - if u == nil || u.OwnedPrivateRepos == nil { - return 0 +// GetWorkflowName returns the WorkflowName field if it's non-nil, zero value otherwise. +func (w *WorkflowJob) GetWorkflowName() string { + if w == nil || w.WorkflowName == nil { + return "" } - return *u.OwnedPrivateRepos + return *w.WorkflowName } -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (u *User) GetPermissions() map[string]bool { - if u == nil || u.Permissions == nil { - return map[string]bool{} +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (w *WorkflowJobEvent) GetAction() string { + if w == nil || w.Action == nil { + return "" } - return *u.Permissions + return *w.Action } -// GetPlan returns the Plan field. -func (u *User) GetPlan() *Plan { - if u == nil { +// GetDeployment returns the Deployment field. +func (w *WorkflowJobEvent) GetDeployment() *Deployment { + if w == nil { return nil } - return u.Plan + return w.Deployment } -// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. -func (u *User) GetPrivateGists() int { - if u == nil || u.PrivateGists == nil { - return 0 +// GetInstallation returns the Installation field. +func (w *WorkflowJobEvent) GetInstallation() *Installation { + if w == nil { + return nil } - return *u.PrivateGists + return w.Installation } -// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. -func (u *User) GetPublicGists() int { - if u == nil || u.PublicGists == nil { - return 0 +// GetOrg returns the Org field. +func (w *WorkflowJobEvent) GetOrg() *Organization { + if w == nil { + return nil } - return *u.PublicGists + return w.Org } -// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. -func (u *User) GetPublicRepos() int { - if u == nil || u.PublicRepos == nil { - return 0 +// GetRepo returns the Repo field. +func (w *WorkflowJobEvent) GetRepo() *Repository { + if w == nil { + return nil } - return *u.PublicRepos + return w.Repo } -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (u *User) GetReceivedEventsURL() string { - if u == nil || u.ReceivedEventsURL == nil { - return "" +// GetSender returns the Sender field. +func (w *WorkflowJobEvent) GetSender() *User { + if w == nil { + return nil } - return *u.ReceivedEventsURL + return w.Sender } -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (u *User) GetReposURL() string { - if u == nil || u.ReposURL == nil { - return "" +// GetWorkflowJob returns the WorkflowJob field. +func (w *WorkflowJobEvent) GetWorkflowJob() *WorkflowJob { + if w == nil { + return nil } - return *u.ReposURL + return w.WorkflowJob } -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (u *User) GetSiteAdmin() bool { - if u == nil || u.SiteAdmin == nil { - return false +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetConclusion() string { + if w == nil || w.Conclusion == nil { + return "" } - return *u.SiteAdmin + return *w.Conclusion } -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (u *User) GetStarredURL() string { - if u == nil || u.StarredURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetCreatedAt() Timestamp { + if w == nil || w.CreatedAt == nil { + return Timestamp{} } - return *u.StarredURL + return *w.CreatedAt } -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (u *User) GetSubscriptionsURL() string { - if u == nil || u.SubscriptionsURL == nil { +// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetEnvironment() string { + if w == nil || w.Environment == nil { return "" } - return *u.SubscriptionsURL + return *w.Environment } -// GetSuspendedAt returns the SuspendedAt field if it's non-nil, zero value otherwise. -func (u *User) GetSuspendedAt() Timestamp { - if u == nil || u.SuspendedAt == nil { - return Timestamp{} +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetHTMLURL() string { + if w == nil || w.HTMLURL == nil { + return "" } - return *u.SuspendedAt + return *w.HTMLURL } -// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. -func (u *User) GetTotalPrivateRepos() int { - if u == nil || u.TotalPrivateRepos == nil { +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetID() int64 { + if w == nil || w.ID == nil { return 0 } - return *u.TotalPrivateRepos + return *w.ID } -// GetTwoFactorAuthentication returns the TwoFactorAuthentication field if it's non-nil, zero value otherwise. -func (u *User) GetTwoFactorAuthentication() bool { - if u == nil || u.TwoFactorAuthentication == nil { - return false +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetName() string { + if w == nil || w.Name == nil { + return "" } - return *u.TwoFactorAuthentication + return *w.Name } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (u *User) GetType() string { - if u == nil || u.Type == nil { +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (w *WorkflowJobRun) GetStatus() string { + if w == nil || w.Status == nil { return "" } - return *u.Type + return *w.Status } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (u *User) GetUpdatedAt() Timestamp { - if u == nil || u.UpdatedAt == nil { +func (w *WorkflowJobRun) GetUpdatedAt() Timestamp { + if w == nil || w.UpdatedAt == nil { return Timestamp{} } - return *u.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (u *User) GetURL() string { - if u == nil || u.URL == nil { - return "" - } - return *u.URL + return *w.UpdatedAt } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (u *UserContext) GetMessage() string { - if u == nil || u.Message == nil { - return "" +// GetActor returns the Actor field. +func (w *WorkflowRun) GetActor() *User { + if w == nil { + return nil } - return *u.Message + return w.Actor } -// GetOcticon returns the Octicon field if it's non-nil, zero value otherwise. -func (u *UserContext) GetOcticon() string { - if u == nil || u.Octicon == nil { +// GetArtifactsURL returns the ArtifactsURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetArtifactsURL() string { + if w == nil || w.ArtifactsURL == nil { return "" } - return *u.Octicon + return *w.ArtifactsURL } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetEmail() string { - if u == nil || u.Email == nil { +// GetCancelURL returns the CancelURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetCancelURL() string { + if w == nil || w.CancelURL == nil { return "" } - return *u.Email + return *w.CancelURL } -// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetPrimary() bool { - if u == nil || u.Primary == nil { - return false +// GetCheckSuiteID returns the CheckSuiteID field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetCheckSuiteID() int64 { + if w == nil || w.CheckSuiteID == nil { + return 0 } - return *u.Primary + return *w.CheckSuiteID } -// GetVerified returns the Verified field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetVerified() bool { - if u == nil || u.Verified == nil { - return false +// GetCheckSuiteNodeID returns the CheckSuiteNodeID field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetCheckSuiteNodeID() string { + if w == nil || w.CheckSuiteNodeID == nil { + return "" } - return *u.Verified + return *w.CheckSuiteNodeID } -// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetVisibility() string { - if u == nil || u.Visibility == nil { +// GetCheckSuiteURL returns the CheckSuiteURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetCheckSuiteURL() string { + if w == nil || w.CheckSuiteURL == nil { return "" } - return *u.Visibility + return *w.CheckSuiteURL } -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetAvatarURL() string { - if u == nil || u.AvatarURL == nil { +// GetConclusion returns the Conclusion field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetConclusion() string { + if w == nil || w.Conclusion == nil { return "" } - return *u.AvatarURL + return *w.Conclusion } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetEventsURL() string { - if u == nil || u.EventsURL == nil { - return "" +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetCreatedAt() Timestamp { + if w == nil || w.CreatedAt == nil { + return Timestamp{} } - return *u.EventsURL + return *w.CreatedAt } -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetFollowersURL() string { - if u == nil || u.FollowersURL == nil { +// GetDisplayTitle returns the DisplayTitle field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetDisplayTitle() string { + if w == nil || w.DisplayTitle == nil { return "" } - return *u.FollowersURL + return *w.DisplayTitle } -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetFollowingURL() string { - if u == nil || u.FollowingURL == nil { +// GetEvent returns the Event field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetEvent() string { + if w == nil || w.Event == nil { return "" } - return *u.FollowingURL + return *w.Event } -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetGistsURL() string { - if u == nil || u.GistsURL == nil { +// GetHeadBranch returns the HeadBranch field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetHeadBranch() string { + if w == nil || w.HeadBranch == nil { return "" } - return *u.GistsURL + return *w.HeadBranch } -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetGravatarID() string { - if u == nil || u.GravatarID == nil { - return "" +// GetHeadCommit returns the HeadCommit field. +func (w *WorkflowRun) GetHeadCommit() *HeadCommit { + if w == nil { + return nil } - return *u.GravatarID + return w.HeadCommit } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetID() int64 { - if u == nil || u.ID == nil { - return 0 +// GetHeadRepository returns the HeadRepository field. +func (w *WorkflowRun) GetHeadRepository() *Repository { + if w == nil { + return nil } - return *u.ID + return w.HeadRepository } -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetLDAPDN() string { - if u == nil || u.LDAPDN == nil { +// GetHeadSHA returns the HeadSHA field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetHeadSHA() string { + if w == nil || w.HeadSHA == nil { return "" } - return *u.LDAPDN + return *w.HeadSHA } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetLogin() string { - if u == nil || u.Login == nil { +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetHTMLURL() string { + if w == nil || w.HTMLURL == nil { return "" } - return *u.Login + return *w.HTMLURL } -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetOrganizationsURL() string { - if u == nil || u.OrganizationsURL == nil { - return "" +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetID() int64 { + if w == nil || w.ID == nil { + return 0 } - return *u.OrganizationsURL + return *w.ID } -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetReceivedEventsURL() string { - if u == nil || u.ReceivedEventsURL == nil { +// GetJobsURL returns the JobsURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetJobsURL() string { + if w == nil || w.JobsURL == nil { return "" } - return *u.ReceivedEventsURL + return *w.JobsURL } -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetReposURL() string { - if u == nil || u.ReposURL == nil { +// GetLogsURL returns the LogsURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetLogsURL() string { + if w == nil || w.LogsURL == nil { return "" } - return *u.ReposURL + return *w.LogsURL } -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetSiteAdmin() bool { - if u == nil || u.SiteAdmin == nil { - return false +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetName() string { + if w == nil || w.Name == nil { + return "" } - return *u.SiteAdmin + return *w.Name } -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetStarredURL() string { - if u == nil || u.StarredURL == nil { +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetNodeID() string { + if w == nil || w.NodeID == nil { return "" } - return *u.StarredURL + return *w.NodeID } -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetSubscriptionsURL() string { - if u == nil || u.SubscriptionsURL == nil { +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetPath() string { + if w == nil || w.Path == nil { return "" } - return *u.SubscriptionsURL + return *w.Path } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetType() string { - if u == nil || u.Type == nil { +// GetPreviousAttemptURL returns the PreviousAttemptURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetPreviousAttemptURL() string { + if w == nil || w.PreviousAttemptURL == nil { return "" } - return *u.Type + return *w.PreviousAttemptURL } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetURL() string { - if u == nil || u.URL == nil { - return "" +// GetPullRequests returns the PullRequests slice if it's non-nil, nil otherwise. +func (w *WorkflowRun) GetPullRequests() []*PullRequest { + if w == nil || w.PullRequests == nil { + return nil } - return *u.URL + return w.PullRequests } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetCreatedAt() string { - if u == nil || u.CreatedAt == nil { - return "" +// GetReferencedWorkflows returns the ReferencedWorkflows slice if it's non-nil, nil otherwise. +func (w *WorkflowRun) GetReferencedWorkflows() []*ReferencedWorkflow { + if w == nil || w.ReferencedWorkflows == nil { + return nil } - return *u.CreatedAt + return w.ReferencedWorkflows } -// GetExcludeAttachments returns the ExcludeAttachments field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetExcludeAttachments() bool { - if u == nil || u.ExcludeAttachments == nil { - return false +// GetRepository returns the Repository field. +func (w *WorkflowRun) GetRepository() *Repository { + if w == nil { + return nil } - return *u.ExcludeAttachments + return w.Repository } -// GetGUID returns the GUID field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetGUID() string { - if u == nil || u.GUID == nil { +// GetRerunURL returns the RerunURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetRerunURL() string { + if w == nil || w.RerunURL == nil { return "" } - return *u.GUID + return *w.RerunURL } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetID() int64 { - if u == nil || u.ID == nil { +// GetRunAttempt returns the RunAttempt field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetRunAttempt() int { + if w == nil || w.RunAttempt == nil { return 0 } - return *u.ID + return *w.RunAttempt } -// GetLockRepositories returns the LockRepositories field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetLockRepositories() bool { - if u == nil || u.LockRepositories == nil { - return false +// GetRunNumber returns the RunNumber field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetRunNumber() int { + if w == nil || w.RunNumber == nil { + return 0 } - return *u.LockRepositories + return *w.RunNumber } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetState() string { - if u == nil || u.State == nil { +// GetRunStartedAt returns the RunStartedAt field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetRunStartedAt() Timestamp { + if w == nil || w.RunStartedAt == nil { + return Timestamp{} + } + return *w.RunStartedAt +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetStatus() string { + if w == nil || w.Status == nil { return "" } - return *u.State + return *w.Status +} + +// GetTriggeringActor returns the TriggeringActor field. +func (w *WorkflowRun) GetTriggeringActor() *User { + if w == nil { + return nil + } + return w.TriggeringActor } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetUpdatedAt() string { - if u == nil || u.UpdatedAt == nil { - return "" +func (w *WorkflowRun) GetUpdatedAt() Timestamp { + if w == nil || w.UpdatedAt == nil { + return Timestamp{} } - return *u.UpdatedAt + return *w.UpdatedAt } // GetURL returns the URL field if it's non-nil, zero value otherwise. -func (u *UserMigration) GetURL() string { - if u == nil || u.URL == nil { +func (w *WorkflowRun) GetURL() string { + if w == nil || w.URL == nil { return "" } - return *u.URL + return *w.URL } -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (u *UsersSearchResult) GetIncompleteResults() bool { - if u == nil || u.IncompleteResults == nil { - return false +// GetWorkflowID returns the WorkflowID field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetWorkflowID() int64 { + if w == nil || w.WorkflowID == nil { + return 0 } - return *u.IncompleteResults + return *w.WorkflowID } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (u *UsersSearchResult) GetTotal() int { - if u == nil || u.Total == nil { - return 0 +// GetWorkflowURL returns the WorkflowURL field if it's non-nil, zero value otherwise. +func (w *WorkflowRun) GetWorkflowURL() string { + if w == nil || w.WorkflowURL == nil { + return "" } - return *u.Total + return *w.WorkflowURL } -// GetAdminUsers returns the AdminUsers field if it's non-nil, zero value otherwise. -func (u *UserStats) GetAdminUsers() int { - if u == nil || u.AdminUsers == nil { - return 0 +// GetExcludePullRequests returns the ExcludePullRequests field if it's non-nil, zero value otherwise. +func (w *WorkflowRunAttemptOptions) GetExcludePullRequests() bool { + if w == nil || w.ExcludePullRequests == nil { + return false } - return *u.AdminUsers + return *w.ExcludePullRequests } -// GetSuspendedUsers returns the SuspendedUsers field if it's non-nil, zero value otherwise. -func (u *UserStats) GetSuspendedUsers() int { - if u == nil || u.SuspendedUsers == nil { - return 0 +// GetJobRuns returns the JobRuns slice if it's non-nil, nil otherwise. +func (w *WorkflowRunBill) GetJobRuns() []*WorkflowRunJobRun { + if w == nil || w.JobRuns == nil { + return nil } - return *u.SuspendedUsers + return w.JobRuns } -// GetTotalUsers returns the TotalUsers field if it's non-nil, zero value otherwise. -func (u *UserStats) GetTotalUsers() int { - if u == nil || u.TotalUsers == nil { +// GetJobs returns the Jobs field if it's non-nil, zero value otherwise. +func (w *WorkflowRunBill) GetJobs() int { + if w == nil || w.Jobs == nil { return 0 } - return *u.TotalUsers + return *w.Jobs } -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (u *UserSuspendOptions) GetReason() string { - if u == nil || u.Reason == nil { - return "" +// GetTotalMS returns the TotalMS field if it's non-nil, zero value otherwise. +func (w *WorkflowRunBill) GetTotalMS() int64 { + if w == nil || w.TotalMS == nil { + return 0 } - return *u.Reason + return *w.TotalMS } // GetAction returns the Action field if it's non-nil, zero value otherwise. -func (w *WatchEvent) GetAction() string { +func (w *WorkflowRunEvent) GetAction() string { if w == nil || w.Action == nil { return "" } @@ -12797,15 +46007,23 @@ func (w *WatchEvent) GetAction() string { } // GetInstallation returns the Installation field. -func (w *WatchEvent) GetInstallation() *Installation { +func (w *WorkflowRunEvent) GetInstallation() *Installation { if w == nil { return nil } return w.Installation } +// GetOrg returns the Org field. +func (w *WorkflowRunEvent) GetOrg() *Organization { + if w == nil { + return nil + } + return w.Org +} + // GetRepo returns the Repo field. -func (w *WatchEvent) GetRepo() *Repository { +func (w *WorkflowRunEvent) GetRepo() *Repository { if w == nil { return nil } @@ -12813,217 +46031,185 @@ func (w *WatchEvent) GetRepo() *Repository { } // GetSender returns the Sender field. -func (w *WatchEvent) GetSender() *User { +func (w *WorkflowRunEvent) GetSender() *User { if w == nil { return nil } return w.Sender } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (w *WebHookAuthor) GetEmail() string { - if w == nil || w.Email == nil { - return "" - } - return *w.Email -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (w *WebHookAuthor) GetName() string { - if w == nil || w.Name == nil { - return "" - } - return *w.Name -} - -// GetUsername returns the Username field if it's non-nil, zero value otherwise. -func (w *WebHookAuthor) GetUsername() string { - if w == nil || w.Username == nil { - return "" - } - return *w.Username -} - -// GetAuthor returns the Author field. -func (w *WebHookCommit) GetAuthor() *WebHookAuthor { +// GetWorkflow returns the Workflow field. +func (w *WorkflowRunEvent) GetWorkflow() *Workflow { if w == nil { return nil } - return w.Author + return w.Workflow } -// GetCommitter returns the Committer field. -func (w *WebHookCommit) GetCommitter() *WebHookAuthor { +// GetWorkflowRun returns the WorkflowRun field. +func (w *WorkflowRunEvent) GetWorkflowRun() *WorkflowRun { if w == nil { return nil } - return w.Committer + return w.WorkflowRun } -// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetDistinct() bool { - if w == nil || w.Distinct == nil { - return false +// GetDurationMS returns the DurationMS field if it's non-nil, zero value otherwise. +func (w *WorkflowRunJobRun) GetDurationMS() int64 { + if w == nil || w.DurationMS == nil { + return 0 } - return *w.Distinct + return *w.DurationMS } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetID() string { - if w == nil || w.ID == nil { - return "" +// GetJobID returns the JobID field if it's non-nil, zero value otherwise. +func (w *WorkflowRunJobRun) GetJobID() int { + if w == nil || w.JobID == nil { + return 0 } - return *w.ID + return *w.JobID } -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetMessage() string { - if w == nil || w.Message == nil { - return "" +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (w *WorkflowRuns) GetTotalCount() int { + if w == nil || w.TotalCount == nil { + return 0 } - return *w.Message + return *w.TotalCount } -// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetTimestamp() time.Time { - if w == nil || w.Timestamp == nil { - return time.Time{} +// GetWorkflowRuns returns the WorkflowRuns slice if it's non-nil, nil otherwise. +func (w *WorkflowRuns) GetWorkflowRuns() []*WorkflowRun { + if w == nil || w.WorkflowRuns == nil { + return nil } - return *w.Timestamp + return w.WorkflowRuns } -// GetAfter returns the After field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetAfter() string { - if w == nil || w.After == nil { - return "" +// GetBillable returns the Billable field. +func (w *WorkflowRunUsage) GetBillable() *WorkflowRunBillMap { + if w == nil { + return nil } - return *w.After + return w.Billable } -// GetBefore returns the Before field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetBefore() string { - if w == nil || w.Before == nil { - return "" +// GetRunDurationMS returns the RunDurationMS field if it's non-nil, zero value otherwise. +func (w *WorkflowRunUsage) GetRunDurationMS() int64 { + if w == nil || w.RunDurationMS == nil { + return 0 } - return *w.Before + return *w.RunDurationMS } -// GetCompare returns the Compare field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetCompare() string { - if w == nil || w.Compare == nil { - return "" +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (w *Workflows) GetTotalCount() int { + if w == nil || w.TotalCount == nil { + return 0 } - return *w.Compare + return *w.TotalCount } -// GetCreated returns the Created field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetCreated() bool { - if w == nil || w.Created == nil { - return false +// GetWorkflows returns the Workflows slice if it's non-nil, nil otherwise. +func (w *Workflows) GetWorkflows() []*Workflow { + if w == nil || w.Workflows == nil { + return nil } - return *w.Created + return w.Workflows } -// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetDeleted() bool { - if w == nil || w.Deleted == nil { - return false +// GetParameters returns the Parameters field. +func (w *WorkflowsBranchRule) GetParameters() WorkflowsRuleParameters { + if w == nil { + return WorkflowsRuleParameters{} } - return *w.Deleted + return w.Parameters } -// GetForced returns the Forced field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetForced() bool { - if w == nil || w.Forced == nil { +// GetRequireApprovalForForkPRWorkflows returns the RequireApprovalForForkPRWorkflows field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissions) GetRequireApprovalForForkPRWorkflows() bool { + if w == nil || w.RequireApprovalForForkPRWorkflows == nil { return false } - return *w.Forced + return *w.RequireApprovalForForkPRWorkflows } -// GetHeadCommit returns the HeadCommit field. -func (w *WebHookPayload) GetHeadCommit() *WebHookCommit { - if w == nil { - return nil +// GetRunWorkflowsFromForkPullRequests returns the RunWorkflowsFromForkPullRequests field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissions) GetRunWorkflowsFromForkPullRequests() bool { + if w == nil || w.RunWorkflowsFromForkPullRequests == nil { + return false } - return w.HeadCommit + return *w.RunWorkflowsFromForkPullRequests } -// GetPusher returns the Pusher field. -func (w *WebHookPayload) GetPusher() *User { - if w == nil { - return nil +// GetSendSecretsAndVariables returns the SendSecretsAndVariables field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissions) GetSendSecretsAndVariables() bool { + if w == nil || w.SendSecretsAndVariables == nil { + return false } - return w.Pusher + return *w.SendSecretsAndVariables } -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetRef() string { - if w == nil || w.Ref == nil { - return "" +// GetSendWriteTokensToWorkflows returns the SendWriteTokensToWorkflows field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissions) GetSendWriteTokensToWorkflows() bool { + if w == nil || w.SendWriteTokensToWorkflows == nil { + return false } - return *w.Ref + return *w.SendWriteTokensToWorkflows } -// GetRepo returns the Repo field. -func (w *WebHookPayload) GetRepo() *Repository { - if w == nil { - return nil +// GetRequireApprovalForForkPRWorkflows returns the RequireApprovalForForkPRWorkflows field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissionsOpt) GetRequireApprovalForForkPRWorkflows() bool { + if w == nil || w.RequireApprovalForForkPRWorkflows == nil { + return false } - return w.Repo + return *w.RequireApprovalForForkPRWorkflows } -// GetSender returns the Sender field. -func (w *WebHookPayload) GetSender() *User { +// GetRunWorkflowsFromForkPullRequests returns the RunWorkflowsFromForkPullRequests field. +func (w *WorkflowsPermissionsOpt) GetRunWorkflowsFromForkPullRequests() bool { if w == nil { - return nil - } - return w.Sender -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (w *WeeklyCommitActivity) GetTotal() int { - if w == nil || w.Total == nil { - return 0 + return false } - return *w.Total + return w.RunWorkflowsFromForkPullRequests } -// GetWeek returns the Week field if it's non-nil, zero value otherwise. -func (w *WeeklyCommitActivity) GetWeek() Timestamp { - if w == nil || w.Week == nil { - return Timestamp{} +// GetSendSecretsAndVariables returns the SendSecretsAndVariables field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissionsOpt) GetSendSecretsAndVariables() bool { + if w == nil || w.SendSecretsAndVariables == nil { + return false } - return *w.Week + return *w.SendSecretsAndVariables } -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetAdditions() int { - if w == nil || w.Additions == nil { - return 0 +// GetSendWriteTokensToWorkflows returns the SendWriteTokensToWorkflows field if it's non-nil, zero value otherwise. +func (w *WorkflowsPermissionsOpt) GetSendWriteTokensToWorkflows() bool { + if w == nil || w.SendWriteTokensToWorkflows == nil { + return false } - return *w.Additions + return *w.SendWriteTokensToWorkflows } -// GetCommits returns the Commits field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetCommits() int { - if w == nil || w.Commits == nil { - return 0 +// GetDoNotEnforceOnCreate returns the DoNotEnforceOnCreate field if it's non-nil, zero value otherwise. +func (w *WorkflowsRuleParameters) GetDoNotEnforceOnCreate() bool { + if w == nil || w.DoNotEnforceOnCreate == nil { + return false } - return *w.Commits + return *w.DoNotEnforceOnCreate } -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetDeletions() int { - if w == nil || w.Deletions == nil { - return 0 +// GetWorkflows returns the Workflows slice if it's non-nil, nil otherwise. +func (w *WorkflowsRuleParameters) GetWorkflows() []*RuleWorkflow { + if w == nil || w.Workflows == nil { + return nil } - return *w.Deletions + return w.Workflows } -// GetWeek returns the Week field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetWeek() Timestamp { - if w == nil || w.Week == nil { - return Timestamp{} +// GetBillable returns the Billable field. +func (w *WorkflowUsage) GetBillable() *WorkflowBillMap { + if w == nil { + return nil } - return *w.Week + return w.Billable } diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go new file mode 100644 index 00000000000..fdc048b3a0d --- /dev/null +++ b/github/github-accessors_test.go @@ -0,0 +1,58030 @@ +// Code generated by gen-accessors; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + +// Copyright 2017 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "testing" + "time" +) + +func TestAbuseRateLimitError_GetMessage(tt *testing.T) { + tt.Parallel() + a := &AbuseRateLimitError{} + a.GetMessage() + a = nil + a.GetMessage() +} + +func TestAbuseRateLimitError_GetRetryAfter(tt *testing.T) { + tt.Parallel() + var zeroValue time.Duration + a := &AbuseRateLimitError{RetryAfter: &zeroValue} + a.GetRetryAfter() + a = &AbuseRateLimitError{} + a.GetRetryAfter() + a = nil + a.GetRetryAfter() +} + +func TestAcceptedAssignment_GetAssignment(tt *testing.T) { + tt.Parallel() + a := &AcceptedAssignment{} + a.GetAssignment() + a = nil + a.GetAssignment() +} + +func TestAcceptedAssignment_GetCommitCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &AcceptedAssignment{CommitCount: &zeroValue} + a.GetCommitCount() + a = &AcceptedAssignment{} + a.GetCommitCount() + a = nil + a.GetCommitCount() +} + +func TestAcceptedAssignment_GetGrade(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AcceptedAssignment{Grade: &zeroValue} + a.GetGrade() + a = &AcceptedAssignment{} + a.GetGrade() + a = nil + a.GetGrade() +} + +func TestAcceptedAssignment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AcceptedAssignment{ID: &zeroValue} + a.GetID() + a = &AcceptedAssignment{} + a.GetID() + a = nil + a.GetID() +} + +func TestAcceptedAssignment_GetPassing(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AcceptedAssignment{Passing: &zeroValue} + a.GetPassing() + a = &AcceptedAssignment{} + a.GetPassing() + a = nil + a.GetPassing() +} + +func TestAcceptedAssignment_GetRepository(tt *testing.T) { + tt.Parallel() + a := &AcceptedAssignment{} + a.GetRepository() + a = nil + a.GetRepository() +} + +func TestAcceptedAssignment_GetStudents(tt *testing.T) { + tt.Parallel() + zeroValue := []*ClassroomUser{} + a := &AcceptedAssignment{Students: zeroValue} + a.GetStudents() + a = &AcceptedAssignment{} + a.GetStudents() + a = nil + a.GetStudents() +} + +func TestAcceptedAssignment_GetSubmitted(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AcceptedAssignment{Submitted: &zeroValue} + a.GetSubmitted() + a = &AcceptedAssignment{} + a.GetSubmitted() + a = nil + a.GetSubmitted() +} + +func TestAcceptedError_GetRaw(tt *testing.T) { + tt.Parallel() + zeroValue := []byte{} + a := &AcceptedError{Raw: zeroValue} + a.GetRaw() + a = &AcceptedError{} + a.GetRaw() + a = nil + a.GetRaw() +} + +func TestAccessibleRepository_GetFullName(tt *testing.T) { + tt.Parallel() + a := &AccessibleRepository{} + a.GetFullName() + a = nil + a.GetFullName() +} + +func TestAccessibleRepository_GetID(tt *testing.T) { + tt.Parallel() + a := &AccessibleRepository{} + a.GetID() + a = nil + a.GetID() +} + +func TestAccessibleRepository_GetName(tt *testing.T) { + tt.Parallel() + a := &AccessibleRepository{} + a.GetName() + a = nil + a.GetName() +} + +func TestActionsAllowed_GetGithubOwnedAllowed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &ActionsAllowed{GithubOwnedAllowed: &zeroValue} + a.GetGithubOwnedAllowed() + a = &ActionsAllowed{} + a.GetGithubOwnedAllowed() + a = nil + a.GetGithubOwnedAllowed() +} + +func TestActionsAllowed_GetPatternsAllowed(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &ActionsAllowed{PatternsAllowed: zeroValue} + a.GetPatternsAllowed() + a = &ActionsAllowed{} + a.GetPatternsAllowed() + a = nil + a.GetPatternsAllowed() +} + +func TestActionsAllowed_GetVerifiedAllowed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &ActionsAllowed{VerifiedAllowed: &zeroValue} + a.GetVerifiedAllowed() + a = &ActionsAllowed{} + a.GetVerifiedAllowed() + a = nil + a.GetVerifiedAllowed() +} + +func TestActionsCache_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ActionsCache{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &ActionsCache{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestActionsCache_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ActionsCache{ID: &zeroValue} + a.GetID() + a = &ActionsCache{} + a.GetID() + a = nil + a.GetID() +} + +func TestActionsCache_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCache{Key: &zeroValue} + a.GetKey() + a = &ActionsCache{} + a.GetKey() + a = nil + a.GetKey() +} + +func TestActionsCache_GetLastAccessedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ActionsCache{LastAccessedAt: &zeroValue} + a.GetLastAccessedAt() + a = &ActionsCache{} + a.GetLastAccessedAt() + a = nil + a.GetLastAccessedAt() +} + +func TestActionsCache_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCache{Ref: &zeroValue} + a.GetRef() + a = &ActionsCache{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestActionsCache_GetSizeInBytes(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ActionsCache{SizeInBytes: &zeroValue} + a.GetSizeInBytes() + a = &ActionsCache{} + a.GetSizeInBytes() + a = nil + a.GetSizeInBytes() +} + +func TestActionsCache_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCache{Version: &zeroValue} + a.GetVersion() + a = &ActionsCache{} + a.GetVersion() + a = nil + a.GetVersion() +} + +func TestActionsCacheList_GetActionsCaches(tt *testing.T) { + tt.Parallel() + zeroValue := []*ActionsCache{} + a := &ActionsCacheList{ActionsCaches: zeroValue} + a.GetActionsCaches() + a = &ActionsCacheList{} + a.GetActionsCaches() + a = nil + a.GetActionsCaches() +} + +func TestActionsCacheList_GetTotalCount(tt *testing.T) { + tt.Parallel() + a := &ActionsCacheList{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestActionsCacheListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCacheListOptions{Direction: &zeroValue} + a.GetDirection() + a = &ActionsCacheListOptions{} + a.GetDirection() + a = nil + a.GetDirection() +} + +func TestActionsCacheListOptions_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCacheListOptions{Key: &zeroValue} + a.GetKey() + a = &ActionsCacheListOptions{} + a.GetKey() + a = nil + a.GetKey() +} + +func TestActionsCacheListOptions_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCacheListOptions{Ref: &zeroValue} + a.GetRef() + a = &ActionsCacheListOptions{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestActionsCacheListOptions_GetSort(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsCacheListOptions{Sort: &zeroValue} + a.GetSort() + a = &ActionsCacheListOptions{} + a.GetSort() + a = nil + a.GetSort() +} + +func TestActionsCacheUsage_GetActiveCachesCount(tt *testing.T) { + tt.Parallel() + a := &ActionsCacheUsage{} + a.GetActiveCachesCount() + a = nil + a.GetActiveCachesCount() +} + +func TestActionsCacheUsage_GetActiveCachesSizeInBytes(tt *testing.T) { + tt.Parallel() + a := &ActionsCacheUsage{} + a.GetActiveCachesSizeInBytes() + a = nil + a.GetActiveCachesSizeInBytes() +} + +func TestActionsCacheUsage_GetFullName(tt *testing.T) { + tt.Parallel() + a := &ActionsCacheUsage{} + a.GetFullName() + a = nil + a.GetFullName() +} + +func TestActionsCacheUsageList_GetRepoCacheUsage(tt *testing.T) { + tt.Parallel() + zeroValue := []*ActionsCacheUsage{} + a := &ActionsCacheUsageList{RepoCacheUsage: zeroValue} + a.GetRepoCacheUsage() + a = &ActionsCacheUsageList{} + a.GetRepoCacheUsage() + a = nil + a.GetRepoCacheUsage() +} + +func TestActionsCacheUsageList_GetTotalCount(tt *testing.T) { + tt.Parallel() + a := &ActionsCacheUsageList{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestActionsCreateOrgVariableRequest_GetName(tt *testing.T) { + tt.Parallel() + a := &ActionsCreateOrgVariableRequest{} + a.GetName() + a = nil + a.GetName() +} + +func TestActionsCreateOrgVariableRequest_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + a := &ActionsCreateOrgVariableRequest{SelectedRepositoryIDs: zeroValue} + a.GetSelectedRepositoryIDs() + a = &ActionsCreateOrgVariableRequest{} + a.GetSelectedRepositoryIDs() + a = nil + a.GetSelectedRepositoryIDs() +} + +func TestActionsCreateOrgVariableRequest_GetValue(tt *testing.T) { + tt.Parallel() + a := &ActionsCreateOrgVariableRequest{} + a.GetValue() + a = nil + a.GetValue() +} + +func TestActionsCreateOrgVariableRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + a := &ActionsCreateOrgVariableRequest{} + a.GetVisibility() + a = nil + a.GetVisibility() +} + +func TestActionsCreateVariableRequest_GetName(tt *testing.T) { + tt.Parallel() + a := &ActionsCreateVariableRequest{} + a.GetName() + a = nil + a.GetName() +} + +func TestActionsCreateVariableRequest_GetValue(tt *testing.T) { + tt.Parallel() + a := &ActionsCreateVariableRequest{} + a.GetValue() + a = nil + a.GetValue() +} + +func TestActionsEnabledOnEnterpriseRepos_GetOrganizations(tt *testing.T) { + tt.Parallel() + zeroValue := []*Organization{} + a := &ActionsEnabledOnEnterpriseRepos{Organizations: zeroValue} + a.GetOrganizations() + a = &ActionsEnabledOnEnterpriseRepos{} + a.GetOrganizations() + a = nil + a.GetOrganizations() +} + +func TestActionsEnabledOnEnterpriseRepos_GetTotalCount(tt *testing.T) { + tt.Parallel() + a := &ActionsEnabledOnEnterpriseRepos{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestActionsEnabledOnOrgRepos_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + a := &ActionsEnabledOnOrgRepos{Repositories: zeroValue} + a.GetRepositories() + a = &ActionsEnabledOnOrgRepos{} + a.GetRepositories() + a = nil + a.GetRepositories() +} + +func TestActionsEnabledOnOrgRepos_GetTotalCount(tt *testing.T) { + tt.Parallel() + a := &ActionsEnabledOnOrgRepos{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestActionsInboundDomains_GetFullDomains(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &ActionsInboundDomains{FullDomains: zeroValue} + a.GetFullDomains() + a = &ActionsInboundDomains{} + a.GetFullDomains() + a = nil + a.GetFullDomains() +} + +func TestActionsInboundDomains_GetWildcardDomains(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &ActionsInboundDomains{WildcardDomains: zeroValue} + a.GetWildcardDomains() + a = &ActionsInboundDomains{} + a.GetWildcardDomains() + a = nil + a.GetWildcardDomains() +} + +func TestActionsPermissions_GetAllowedActions(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissions{AllowedActions: &zeroValue} + a.GetAllowedActions() + a = &ActionsPermissions{} + a.GetAllowedActions() + a = nil + a.GetAllowedActions() +} + +func TestActionsPermissions_GetEnabledRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissions{EnabledRepositories: &zeroValue} + a.GetEnabledRepositories() + a = &ActionsPermissions{} + a.GetEnabledRepositories() + a = nil + a.GetEnabledRepositories() +} + +func TestActionsPermissions_GetSelectedActionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissions{SelectedActionsURL: &zeroValue} + a.GetSelectedActionsURL() + a = &ActionsPermissions{} + a.GetSelectedActionsURL() + a = nil + a.GetSelectedActionsURL() +} + +func TestActionsPermissions_GetSHAPinningRequired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &ActionsPermissions{SHAPinningRequired: &zeroValue} + a.GetSHAPinningRequired() + a = &ActionsPermissions{} + a.GetSHAPinningRequired() + a = nil + a.GetSHAPinningRequired() +} + +func TestActionsPermissionsEnterprise_GetAllowedActions(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissionsEnterprise{AllowedActions: &zeroValue} + a.GetAllowedActions() + a = &ActionsPermissionsEnterprise{} + a.GetAllowedActions() + a = nil + a.GetAllowedActions() +} + +func TestActionsPermissionsEnterprise_GetEnabledOrganizations(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissionsEnterprise{EnabledOrganizations: &zeroValue} + a.GetEnabledOrganizations() + a = &ActionsPermissionsEnterprise{} + a.GetEnabledOrganizations() + a = nil + a.GetEnabledOrganizations() +} + +func TestActionsPermissionsEnterprise_GetSelectedActionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissionsEnterprise{SelectedActionsURL: &zeroValue} + a.GetSelectedActionsURL() + a = &ActionsPermissionsEnterprise{} + a.GetSelectedActionsURL() + a = nil + a.GetSelectedActionsURL() +} + +func TestActionsPermissionsRepository_GetAllowedActions(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissionsRepository{AllowedActions: &zeroValue} + a.GetAllowedActions() + a = &ActionsPermissionsRepository{} + a.GetAllowedActions() + a = nil + a.GetAllowedActions() +} + +func TestActionsPermissionsRepository_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &ActionsPermissionsRepository{Enabled: &zeroValue} + a.GetEnabled() + a = &ActionsPermissionsRepository{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestActionsPermissionsRepository_GetSelectedActionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsPermissionsRepository{SelectedActionsURL: &zeroValue} + a.GetSelectedActionsURL() + a = &ActionsPermissionsRepository{} + a.GetSelectedActionsURL() + a = nil + a.GetSelectedActionsURL() +} + +func TestActionsPermissionsRepository_GetSHAPinningRequired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &ActionsPermissionsRepository{SHAPinningRequired: &zeroValue} + a.GetSHAPinningRequired() + a = &ActionsPermissionsRepository{} + a.GetSHAPinningRequired() + a = nil + a.GetSHAPinningRequired() +} + +func TestActionsUpdateOrgVariableRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsUpdateOrgVariableRequest{Name: &zeroValue} + a.GetName() + a = &ActionsUpdateOrgVariableRequest{} + a.GetName() + a = nil + a.GetName() +} + +func TestActionsUpdateOrgVariableRequest_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + a := &ActionsUpdateOrgVariableRequest{SelectedRepositoryIDs: zeroValue} + a.GetSelectedRepositoryIDs() + a = &ActionsUpdateOrgVariableRequest{} + a.GetSelectedRepositoryIDs() + a = nil + a.GetSelectedRepositoryIDs() +} + +func TestActionsUpdateOrgVariableRequest_GetValue(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsUpdateOrgVariableRequest{Value: &zeroValue} + a.GetValue() + a = &ActionsUpdateOrgVariableRequest{} + a.GetValue() + a = nil + a.GetValue() +} + +func TestActionsUpdateOrgVariableRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsUpdateOrgVariableRequest{Visibility: &zeroValue} + a.GetVisibility() + a = &ActionsUpdateOrgVariableRequest{} + a.GetVisibility() + a = nil + a.GetVisibility() +} + +func TestActionsUpdateVariableRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsUpdateVariableRequest{Name: &zeroValue} + a.GetName() + a = &ActionsUpdateVariableRequest{} + a.GetName() + a = nil + a.GetName() +} + +func TestActionsUpdateVariableRequest_GetValue(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsUpdateVariableRequest{Value: &zeroValue} + a.GetValue() + a = &ActionsUpdateVariableRequest{} + a.GetValue() + a = nil + a.GetValue() +} + +func TestActionsVariable_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ActionsVariable{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &ActionsVariable{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestActionsVariable_GetName(tt *testing.T) { + tt.Parallel() + a := &ActionsVariable{} + a.GetName() + a = nil + a.GetName() +} + +func TestActionsVariable_GetSelectedRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsVariable{SelectedRepositoriesURL: &zeroValue} + a.GetSelectedRepositoriesURL() + a = &ActionsVariable{} + a.GetSelectedRepositoriesURL() + a = nil + a.GetSelectedRepositoriesURL() +} + +func TestActionsVariable_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ActionsVariable{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &ActionsVariable{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestActionsVariable_GetValue(tt *testing.T) { + tt.Parallel() + a := &ActionsVariable{} + a.GetValue() + a = nil + a.GetValue() +} + +func TestActionsVariable_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActionsVariable{Visibility: &zeroValue} + a.GetVisibility() + a = &ActionsVariable{} + a.GetVisibility() + a = nil + a.GetVisibility() +} + +func TestActionsVariables_GetTotalCount(tt *testing.T) { + tt.Parallel() + a := &ActionsVariables{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestActionsVariables_GetVariables(tt *testing.T) { + tt.Parallel() + zeroValue := []*ActionsVariable{} + a := &ActionsVariables{Variables: zeroValue} + a.GetVariables() + a = &ActionsVariables{} + a.GetVariables() + a = nil + a.GetVariables() +} + +func TestActiveCommitters_GetMaximumAdvancedSecurityCommitters(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ActiveCommitters{MaximumAdvancedSecurityCommitters: &zeroValue} + a.GetMaximumAdvancedSecurityCommitters() + a = &ActiveCommitters{} + a.GetMaximumAdvancedSecurityCommitters() + a = nil + a.GetMaximumAdvancedSecurityCommitters() +} + +func TestActiveCommitters_GetPurchasedAdvancedSecurityCommitters(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ActiveCommitters{PurchasedAdvancedSecurityCommitters: &zeroValue} + a.GetPurchasedAdvancedSecurityCommitters() + a = &ActiveCommitters{} + a.GetPurchasedAdvancedSecurityCommitters() + a = nil + a.GetPurchasedAdvancedSecurityCommitters() +} + +func TestActiveCommitters_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryActiveCommitters{} + a := &ActiveCommitters{Repositories: zeroValue} + a.GetRepositories() + a = &ActiveCommitters{} + a.GetRepositories() + a = nil + a.GetRepositories() +} + +func TestActiveCommitters_GetTotalAdvancedSecurityCommitters(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ActiveCommitters{TotalAdvancedSecurityCommitters: &zeroValue} + a.GetTotalAdvancedSecurityCommitters() + a = &ActiveCommitters{} + a.GetTotalAdvancedSecurityCommitters() + a = nil + a.GetTotalAdvancedSecurityCommitters() +} + +func TestActiveCommitters_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ActiveCommitters{TotalCount: &zeroValue} + a.GetTotalCount() + a = &ActiveCommitters{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestActiveCommittersListOptions_GetAdvancedSecurityProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActiveCommittersListOptions{AdvancedSecurityProduct: &zeroValue} + a.GetAdvancedSecurityProduct() + a = &ActiveCommittersListOptions{} + a.GetAdvancedSecurityProduct() + a = nil + a.GetAdvancedSecurityProduct() +} + +func TestActivityListStarredOptions_GetDirection(tt *testing.T) { + tt.Parallel() + a := &ActivityListStarredOptions{} + a.GetDirection() + a = nil + a.GetDirection() +} + +func TestActivityListStarredOptions_GetSort(tt *testing.T) { + tt.Parallel() + a := &ActivityListStarredOptions{} + a.GetSort() + a = nil + a.GetSort() +} + +func TestActorLocation_GetCountryCode(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ActorLocation{CountryCode: &zeroValue} + a.GetCountryCode() + a = &ActorLocation{} + a.GetCountryCode() + a = nil + a.GetCountryCode() +} + +func TestAddProjectItemOptions_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AddProjectItemOptions{ID: &zeroValue} + a.GetID() + a = &AddProjectItemOptions{} + a.GetID() + a = nil + a.GetID() +} + +func TestAddProjectItemOptions_GetType(tt *testing.T) { + tt.Parallel() + a := &AddProjectItemOptions{} + a.GetType() + a = nil + a.GetType() +} + +func TestAddProjectV2FieldRequest_GetDataType(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AddProjectV2FieldRequest{DataType: &zeroValue} + a.GetDataType() + a = &AddProjectV2FieldRequest{} + a.GetDataType() + a = nil + a.GetDataType() +} + +func TestAddProjectV2FieldRequest_GetIssueFieldID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AddProjectV2FieldRequest{IssueFieldID: &zeroValue} + a.GetIssueFieldID() + a = &AddProjectV2FieldRequest{} + a.GetIssueFieldID() + a = nil + a.GetIssueFieldID() +} + +func TestAddProjectV2FieldRequest_GetIterationConfiguration(tt *testing.T) { + tt.Parallel() + a := &AddProjectV2FieldRequest{} + a.GetIterationConfiguration() + a = nil + a.GetIterationConfiguration() +} + +func TestAddProjectV2FieldRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AddProjectV2FieldRequest{Name: &zeroValue} + a.GetName() + a = &AddProjectV2FieldRequest{} + a.GetName() + a = nil + a.GetName() +} + +func TestAddProjectV2FieldRequest_GetSingleSelectOptions(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProjectV2FieldSingleSelectOption{} + a := &AddProjectV2FieldRequest{SingleSelectOptions: zeroValue} + a.GetSingleSelectOptions() + a = &AddProjectV2FieldRequest{} + a.GetSingleSelectOptions() + a = nil + a.GetSingleSelectOptions() +} + +func TestAddResourcesToCostCenterResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AddResourcesToCostCenterResponse{Message: &zeroValue} + a.GetMessage() + a = &AddResourcesToCostCenterResponse{} + a.GetMessage() + a = nil + a.GetMessage() +} + +func TestAddResourcesToCostCenterResponse_GetReassignedResources(tt *testing.T) { + tt.Parallel() + zeroValue := []*ReassignedResource{} + a := &AddResourcesToCostCenterResponse{ReassignedResources: zeroValue} + a.GetReassignedResources() + a = &AddResourcesToCostCenterResponse{} + a.GetReassignedResources() + a = nil + a.GetReassignedResources() +} + +func TestAdminEnforcedChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AdminEnforcedChanges{From: &zeroValue} + a.GetFrom() + a = &AdminEnforcedChanges{} + a.GetFrom() + a = nil + a.GetFrom() +} + +func TestAdminEnforcement_GetEnabled(tt *testing.T) { + tt.Parallel() + a := &AdminEnforcement{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAdminEnforcement_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdminEnforcement{URL: &zeroValue} + a.GetURL() + a = &AdminEnforcement{} + a.GetURL() + a = nil + a.GetURL() +} + +func TestAdminStats_GetComments(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetComments() + a = nil + a.GetComments() +} + +func TestAdminStats_GetGists(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetGists() + a = nil + a.GetGists() +} + +func TestAdminStats_GetHooks(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetHooks() + a = nil + a.GetHooks() +} + +func TestAdminStats_GetIssues(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetIssues() + a = nil + a.GetIssues() +} + +func TestAdminStats_GetMilestones(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetMilestones() + a = nil + a.GetMilestones() +} + +func TestAdminStats_GetOrgs(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetOrgs() + a = nil + a.GetOrgs() +} + +func TestAdminStats_GetPages(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetPages() + a = nil + a.GetPages() +} + +func TestAdminStats_GetPulls(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetPulls() + a = nil + a.GetPulls() +} + +func TestAdminStats_GetRepos(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetRepos() + a = nil + a.GetRepos() +} + +func TestAdminStats_GetUsers(tt *testing.T) { + tt.Parallel() + a := &AdminStats{} + a.GetUsers() + a = nil + a.GetUsers() +} + +func TestAdvancedSecurity_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvancedSecurity{Status: &zeroValue} + a.GetStatus() + a = &AdvancedSecurity{} + a.GetStatus() + a = nil + a.GetStatus() +} + +func TestAdvancedSecurityCommittersBreakdown_GetLastPushedDate(tt *testing.T) { + tt.Parallel() + a := &AdvancedSecurityCommittersBreakdown{} + a.GetLastPushedDate() + a = nil + a.GetLastPushedDate() +} + +func TestAdvancedSecurityCommittersBreakdown_GetLastPushedEmail(tt *testing.T) { + tt.Parallel() + a := &AdvancedSecurityCommittersBreakdown{} + a.GetLastPushedEmail() + a = nil + a.GetLastPushedEmail() +} + +func TestAdvancedSecurityCommittersBreakdown_GetUserLogin(tt *testing.T) { + tt.Parallel() + a := &AdvancedSecurityCommittersBreakdown{} + a.GetUserLogin() + a = nil + a.GetUserLogin() +} + +func TestAdvisoryCVSS_GetScore(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + a := &AdvisoryCVSS{Score: &zeroValue} + a.GetScore() + a = &AdvisoryCVSS{} + a.GetScore() + a = nil + a.GetScore() +} + +func TestAdvisoryCVSS_GetVectorString(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryCVSS{VectorString: &zeroValue} + a.GetVectorString() + a = &AdvisoryCVSS{} + a.GetVectorString() + a = nil + a.GetVectorString() +} + +func TestAdvisoryCVSSSeverities_GetCVSSV3(tt *testing.T) { + tt.Parallel() + a := &AdvisoryCVSSSeverities{} + a.GetCVSSV3() + a = nil + a.GetCVSSV3() +} + +func TestAdvisoryCVSSSeverities_GetCVSSV4(tt *testing.T) { + tt.Parallel() + a := &AdvisoryCVSSSeverities{} + a.GetCVSSV4() + a = nil + a.GetCVSSV4() +} + +func TestAdvisoryCWEs_GetCWEID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryCWEs{CWEID: &zeroValue} + a.GetCWEID() + a = &AdvisoryCWEs{} + a.GetCWEID() + a = nil + a.GetCWEID() +} + +func TestAdvisoryCWEs_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryCWEs{Name: &zeroValue} + a.GetName() + a = &AdvisoryCWEs{} + a.GetName() + a = nil + a.GetName() +} + +func TestAdvisoryEPSS_GetPercentage(tt *testing.T) { + tt.Parallel() + a := &AdvisoryEPSS{} + a.GetPercentage() + a = nil + a.GetPercentage() +} + +func TestAdvisoryEPSS_GetPercentile(tt *testing.T) { + tt.Parallel() + a := &AdvisoryEPSS{} + a.GetPercentile() + a = nil + a.GetPercentile() +} + +func TestAdvisoryIdentifier_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryIdentifier{Type: &zeroValue} + a.GetType() + a = &AdvisoryIdentifier{} + a.GetType() + a = nil + a.GetType() +} + +func TestAdvisoryIdentifier_GetValue(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryIdentifier{Value: &zeroValue} + a.GetValue() + a = &AdvisoryIdentifier{} + a.GetValue() + a = nil + a.GetValue() +} + +func TestAdvisoryReference_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryReference{URL: &zeroValue} + a.GetURL() + a = &AdvisoryReference{} + a.GetURL() + a = nil + a.GetURL() +} + +func TestAdvisoryVulnerability_GetFirstPatchedVersion(tt *testing.T) { + tt.Parallel() + a := &AdvisoryVulnerability{} + a.GetFirstPatchedVersion() + a = nil + a.GetFirstPatchedVersion() +} + +func TestAdvisoryVulnerability_GetPackage(tt *testing.T) { + tt.Parallel() + a := &AdvisoryVulnerability{} + a.GetPackage() + a = nil + a.GetPackage() +} + +func TestAdvisoryVulnerability_GetPatchedVersions(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryVulnerability{PatchedVersions: &zeroValue} + a.GetPatchedVersions() + a = &AdvisoryVulnerability{} + a.GetPatchedVersions() + a = nil + a.GetPatchedVersions() +} + +func TestAdvisoryVulnerability_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryVulnerability{Severity: &zeroValue} + a.GetSeverity() + a = &AdvisoryVulnerability{} + a.GetSeverity() + a = nil + a.GetSeverity() +} + +func TestAdvisoryVulnerability_GetVulnerableFunctions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &AdvisoryVulnerability{VulnerableFunctions: zeroValue} + a.GetVulnerableFunctions() + a = &AdvisoryVulnerability{} + a.GetVulnerableFunctions() + a = nil + a.GetVulnerableFunctions() +} + +func TestAdvisoryVulnerability_GetVulnerableVersionRange(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AdvisoryVulnerability{VulnerableVersionRange: &zeroValue} + a.GetVulnerableVersionRange() + a = &AdvisoryVulnerability{} + a.GetVulnerableVersionRange() + a = nil + a.GetVulnerableVersionRange() +} + +func TestAlert_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Alert{ClosedAt: &zeroValue} + a.GetClosedAt() + a = &Alert{} + a.GetClosedAt() + a = nil + a.GetClosedAt() +} + +func TestAlert_GetClosedBy(tt *testing.T) { + tt.Parallel() + a := &Alert{} + a.GetClosedBy() + a = nil + a.GetClosedBy() +} + +func TestAlert_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Alert{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &Alert{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestAlert_GetDismissedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Alert{DismissedAt: &zeroValue} + a.GetDismissedAt() + a = &Alert{} + a.GetDismissedAt() + a = nil + a.GetDismissedAt() +} + +func TestAlert_GetDismissedBy(tt *testing.T) { + tt.Parallel() + a := &Alert{} + a.GetDismissedBy() + a = nil + a.GetDismissedBy() +} + +func TestAlert_GetDismissedComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{DismissedComment: &zeroValue} + a.GetDismissedComment() + a = &Alert{} + a.GetDismissedComment() + a = nil + a.GetDismissedComment() +} + +func TestAlert_GetDismissedReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{DismissedReason: &zeroValue} + a.GetDismissedReason() + a = &Alert{} + a.GetDismissedReason() + a = nil + a.GetDismissedReason() +} + +func TestAlert_GetFixedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Alert{FixedAt: &zeroValue} + a.GetFixedAt() + a = &Alert{} + a.GetFixedAt() + a = nil + a.GetFixedAt() +} + +func TestAlert_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{HTMLURL: &zeroValue} + a.GetHTMLURL() + a = &Alert{} + a.GetHTMLURL() + a = nil + a.GetHTMLURL() +} + +func TestAlert_GetInstances(tt *testing.T) { + tt.Parallel() + zeroValue := []*MostRecentInstance{} + a := &Alert{Instances: zeroValue} + a.GetInstances() + a = &Alert{} + a.GetInstances() + a = nil + a.GetInstances() +} + +func TestAlert_GetInstancesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{InstancesURL: &zeroValue} + a.GetInstancesURL() + a = &Alert{} + a.GetInstancesURL() + a = nil + a.GetInstancesURL() +} + +func TestAlert_GetMostRecentInstance(tt *testing.T) { + tt.Parallel() + a := &Alert{} + a.GetMostRecentInstance() + a = nil + a.GetMostRecentInstance() +} + +func TestAlert_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &Alert{Number: &zeroValue} + a.GetNumber() + a = &Alert{} + a.GetNumber() + a = nil + a.GetNumber() +} + +func TestAlert_GetRepository(tt *testing.T) { + tt.Parallel() + a := &Alert{} + a.GetRepository() + a = nil + a.GetRepository() +} + +func TestAlert_GetRule(tt *testing.T) { + tt.Parallel() + a := &Alert{} + a.GetRule() + a = nil + a.GetRule() +} + +func TestAlert_GetRuleDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{RuleDescription: &zeroValue} + a.GetRuleDescription() + a = &Alert{} + a.GetRuleDescription() + a = nil + a.GetRuleDescription() +} + +func TestAlert_GetRuleID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{RuleID: &zeroValue} + a.GetRuleID() + a = &Alert{} + a.GetRuleID() + a = nil + a.GetRuleID() +} + +func TestAlert_GetRuleSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{RuleSeverity: &zeroValue} + a.GetRuleSeverity() + a = &Alert{} + a.GetRuleSeverity() + a = nil + a.GetRuleSeverity() +} + +func TestAlert_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{State: &zeroValue} + a.GetState() + a = &Alert{} + a.GetState() + a = nil + a.GetState() +} + +func TestAlert_GetTool(tt *testing.T) { + tt.Parallel() + a := &Alert{} + a.GetTool() + a = nil + a.GetTool() +} + +func TestAlert_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Alert{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &Alert{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestAlert_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Alert{URL: &zeroValue} + a.GetURL() + a = &Alert{} + a.GetURL() + a = nil + a.GetURL() +} + +func TestAlertInstancesListOptions_GetRef(tt *testing.T) { + tt.Parallel() + a := &AlertInstancesListOptions{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestAlertListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetDirection() + a = nil + a.GetDirection() +} + +func TestAlertListOptions_GetRef(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestAlertListOptions_GetSeverity(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetSeverity() + a = nil + a.GetSeverity() +} + +func TestAlertListOptions_GetSort(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetSort() + a = nil + a.GetSort() +} + +func TestAlertListOptions_GetState(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetState() + a = nil + a.GetState() +} + +func TestAlertListOptions_GetToolGUID(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetToolGUID() + a = nil + a.GetToolGUID() +} + +func TestAlertListOptions_GetToolName(tt *testing.T) { + tt.Parallel() + a := &AlertListOptions{} + a.GetToolName() + a = nil + a.GetToolName() +} + +func TestAllowDeletions_GetEnabled(tt *testing.T) { + tt.Parallel() + a := &AllowDeletions{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAllowDeletionsEnforcementLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AllowDeletionsEnforcementLevelChanges{From: &zeroValue} + a.GetFrom() + a = &AllowDeletionsEnforcementLevelChanges{} + a.GetFrom() + a = nil + a.GetFrom() +} + +func TestAllowForcePushes_GetEnabled(tt *testing.T) { + tt.Parallel() + a := &AllowForcePushes{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAllowForkSyncing_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AllowForkSyncing{Enabled: &zeroValue} + a.GetEnabled() + a = &AllowForkSyncing{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAmazonS3AccessKeysConfig_GetAuthenticationType(tt *testing.T) { + tt.Parallel() + a := &AmazonS3AccessKeysConfig{} + a.GetAuthenticationType() + a = nil + a.GetAuthenticationType() +} + +func TestAmazonS3AccessKeysConfig_GetBucket(tt *testing.T) { + tt.Parallel() + a := &AmazonS3AccessKeysConfig{} + a.GetBucket() + a = nil + a.GetBucket() +} + +func TestAmazonS3AccessKeysConfig_GetEncryptedAccessKeyID(tt *testing.T) { + tt.Parallel() + a := &AmazonS3AccessKeysConfig{} + a.GetEncryptedAccessKeyID() + a = nil + a.GetEncryptedAccessKeyID() +} + +func TestAmazonS3AccessKeysConfig_GetEncryptedSecretKey(tt *testing.T) { + tt.Parallel() + a := &AmazonS3AccessKeysConfig{} + a.GetEncryptedSecretKey() + a = nil + a.GetEncryptedSecretKey() +} + +func TestAmazonS3AccessKeysConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + a := &AmazonS3AccessKeysConfig{} + a.GetKeyID() + a = nil + a.GetKeyID() +} + +func TestAmazonS3AccessKeysConfig_GetRegion(tt *testing.T) { + tt.Parallel() + a := &AmazonS3AccessKeysConfig{} + a.GetRegion() + a = nil + a.GetRegion() +} + +func TestAmazonS3OIDCConfig_GetArnRole(tt *testing.T) { + tt.Parallel() + a := &AmazonS3OIDCConfig{} + a.GetArnRole() + a = nil + a.GetArnRole() +} + +func TestAmazonS3OIDCConfig_GetAuthenticationType(tt *testing.T) { + tt.Parallel() + a := &AmazonS3OIDCConfig{} + a.GetAuthenticationType() + a = nil + a.GetAuthenticationType() +} + +func TestAmazonS3OIDCConfig_GetBucket(tt *testing.T) { + tt.Parallel() + a := &AmazonS3OIDCConfig{} + a.GetBucket() + a = nil + a.GetBucket() +} + +func TestAmazonS3OIDCConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + a := &AmazonS3OIDCConfig{} + a.GetKeyID() + a = nil + a.GetKeyID() +} + +func TestAmazonS3OIDCConfig_GetRegion(tt *testing.T) { + tt.Parallel() + a := &AmazonS3OIDCConfig{} + a.GetRegion() + a = nil + a.GetRegion() +} + +func TestAnalysesListOptions_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AnalysesListOptions{Ref: &zeroValue} + a.GetRef() + a = &AnalysesListOptions{} + a.GetRef() + a = nil + a.GetRef() +} + +func TestAnalysesListOptions_GetSarifID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AnalysesListOptions{SarifID: &zeroValue} + a.GetSarifID() + a = &AnalysesListOptions{} + a.GetSarifID() + a = nil + a.GetSarifID() +} + +func TestAPIMeta_GetActions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Actions: zeroValue} + a.GetActions() + a = &APIMeta{} + a.GetActions() + a = nil + a.GetActions() +} + +func TestAPIMeta_GetActionsMacos(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{ActionsMacos: zeroValue} + a.GetActionsMacos() + a = &APIMeta{} + a.GetActionsMacos() + a = nil + a.GetActionsMacos() +} + +func TestAPIMeta_GetAPI(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{API: zeroValue} + a.GetAPI() + a = &APIMeta{} + a.GetAPI() + a = nil + a.GetAPI() +} + +func TestAPIMeta_GetCodespaces(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Codespaces: zeroValue} + a.GetCodespaces() + a = &APIMeta{} + a.GetCodespaces() + a = nil + a.GetCodespaces() +} + +func TestAPIMeta_GetCopilot(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Copilot: zeroValue} + a.GetCopilot() + a = &APIMeta{} + a.GetCopilot() + a = nil + a.GetCopilot() +} + +func TestAPIMeta_GetDependabot(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Dependabot: zeroValue} + a.GetDependabot() + a = &APIMeta{} + a.GetDependabot() + a = nil + a.GetDependabot() +} + +func TestAPIMeta_GetDomains(tt *testing.T) { + tt.Parallel() + a := &APIMeta{} + a.GetDomains() + a = nil + a.GetDomains() +} + +func TestAPIMeta_GetGit(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Git: zeroValue} + a.GetGit() + a = &APIMeta{} + a.GetGit() + a = nil + a.GetGit() +} + +func TestAPIMeta_GetGithubEnterpriseImporter(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{GithubEnterpriseImporter: zeroValue} + a.GetGithubEnterpriseImporter() + a = &APIMeta{} + a.GetGithubEnterpriseImporter() + a = nil + a.GetGithubEnterpriseImporter() +} + +func TestAPIMeta_GetHooks(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Hooks: zeroValue} + a.GetHooks() + a = &APIMeta{} + a.GetHooks() + a = nil + a.GetHooks() +} + +func TestAPIMeta_GetImporter(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Importer: zeroValue} + a.GetImporter() + a = &APIMeta{} + a.GetImporter() + a = nil + a.GetImporter() +} + +func TestAPIMeta_GetPackages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Packages: zeroValue} + a.GetPackages() + a = &APIMeta{} + a.GetPackages() + a = nil + a.GetPackages() +} + +func TestAPIMeta_GetPages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Pages: zeroValue} + a.GetPages() + a = &APIMeta{} + a.GetPages() + a = nil + a.GetPages() +} + +func TestAPIMeta_GetSSHKeyFingerprints(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + a := &APIMeta{SSHKeyFingerprints: zeroValue} + a.GetSSHKeyFingerprints() + a = &APIMeta{} + a.GetSSHKeyFingerprints() + a = nil + a.GetSSHKeyFingerprints() +} + +func TestAPIMeta_GetSSHKeys(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{SSHKeys: zeroValue} + a.GetSSHKeys() + a = &APIMeta{} + a.GetSSHKeys() + a = nil + a.GetSSHKeys() +} + +func TestAPIMeta_GetVerifiablePasswordAuthentication(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &APIMeta{VerifiablePasswordAuthentication: &zeroValue} + a.GetVerifiablePasswordAuthentication() + a = &APIMeta{} + a.GetVerifiablePasswordAuthentication() + a = nil + a.GetVerifiablePasswordAuthentication() +} + +func TestAPIMeta_GetWeb(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMeta{Web: zeroValue} + a.GetWeb() + a = &APIMeta{} + a.GetWeb() + a = nil + a.GetWeb() +} + +func TestAPIMetaArtifactAttestations_GetServices(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMetaArtifactAttestations{Services: zeroValue} + a.GetServices() + a = &APIMetaArtifactAttestations{} + a.GetServices() + a = nil + a.GetServices() +} + +func TestAPIMetaArtifactAttestations_GetTrustDomain(tt *testing.T) { + tt.Parallel() + a := &APIMetaArtifactAttestations{} + a.GetTrustDomain() + a = nil + a.GetTrustDomain() +} + +func TestAPIMetaDomains_GetActions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMetaDomains{Actions: zeroValue} + a.GetActions() + a = &APIMetaDomains{} + a.GetActions() + a = nil + a.GetActions() +} + +func TestAPIMetaDomains_GetActionsInbound(tt *testing.T) { + tt.Parallel() + a := &APIMetaDomains{} + a.GetActionsInbound() + a = nil + a.GetActionsInbound() +} + +func TestAPIMetaDomains_GetArtifactAttestations(tt *testing.T) { + tt.Parallel() + a := &APIMetaDomains{} + a.GetArtifactAttestations() + a = nil + a.GetArtifactAttestations() +} + +func TestAPIMetaDomains_GetCodespaces(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMetaDomains{Codespaces: zeroValue} + a.GetCodespaces() + a = &APIMetaDomains{} + a.GetCodespaces() + a = nil + a.GetCodespaces() +} + +func TestAPIMetaDomains_GetCopilot(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMetaDomains{Copilot: zeroValue} + a.GetCopilot() + a = &APIMetaDomains{} + a.GetCopilot() + a = nil + a.GetCopilot() +} + +func TestAPIMetaDomains_GetPackages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMetaDomains{Packages: zeroValue} + a.GetPackages() + a = &APIMetaDomains{} + a.GetPackages() + a = nil + a.GetPackages() +} + +func TestAPIMetaDomains_GetWebsite(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &APIMetaDomains{Website: zeroValue} + a.GetWebsite() + a = &APIMetaDomains{} + a.GetWebsite() + a = nil + a.GetWebsite() +} + +func TestApp_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{ClientID: &zeroValue} + a.GetClientID() + a = &App{} + a.GetClientID() + a = nil + a.GetClientID() +} + +func TestApp_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &App{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &App{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestApp_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{Description: &zeroValue} + a.GetDescription() + a = &App{} + a.GetDescription() + a = nil + a.GetDescription() +} + +func TestApp_GetEvents(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &App{Events: zeroValue} + a.GetEvents() + a = &App{} + a.GetEvents() + a = nil + a.GetEvents() +} + +func TestApp_GetExternalURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{ExternalURL: &zeroValue} + a.GetExternalURL() + a = &App{} + a.GetExternalURL() + a = nil + a.GetExternalURL() +} + +func TestApp_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{HTMLURL: &zeroValue} + a.GetHTMLURL() + a = &App{} + a.GetHTMLURL() + a = nil + a.GetHTMLURL() +} + +func TestApp_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &App{ID: &zeroValue} + a.GetID() + a = &App{} + a.GetID() + a = nil + a.GetID() +} + +func TestApp_GetInstallationsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &App{InstallationsCount: &zeroValue} + a.GetInstallationsCount() + a = &App{} + a.GetInstallationsCount() + a = nil + a.GetInstallationsCount() +} + +func TestApp_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{Name: &zeroValue} + a.GetName() + a = &App{} + a.GetName() + a = nil + a.GetName() +} + +func TestApp_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{NodeID: &zeroValue} + a.GetNodeID() + a = &App{} + a.GetNodeID() + a = nil + a.GetNodeID() +} + +func TestApp_GetOwner(tt *testing.T) { + tt.Parallel() + a := &App{} + a.GetOwner() + a = nil + a.GetOwner() +} + +func TestApp_GetPermissions(tt *testing.T) { + tt.Parallel() + a := &App{} + a.GetPermissions() + a = nil + a.GetPermissions() +} + +func TestApp_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &App{Slug: &zeroValue} + a.GetSlug() + a = &App{} + a.GetSlug() + a = nil + a.GetSlug() +} + +func TestApp_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &App{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &App{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestAppConfig_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{ClientID: &zeroValue} + a.GetClientID() + a = &AppConfig{} + a.GetClientID() + a = nil + a.GetClientID() +} + +func TestAppConfig_GetClientSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{ClientSecret: &zeroValue} + a.GetClientSecret() + a = &AppConfig{} + a.GetClientSecret() + a = nil + a.GetClientSecret() +} + +func TestAppConfig_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &AppConfig{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &AppConfig{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestAppConfig_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{Description: &zeroValue} + a.GetDescription() + a = &AppConfig{} + a.GetDescription() + a = nil + a.GetDescription() +} + +func TestAppConfig_GetExternalURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{ExternalURL: &zeroValue} + a.GetExternalURL() + a = &AppConfig{} + a.GetExternalURL() + a = nil + a.GetExternalURL() +} + +func TestAppConfig_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{HTMLURL: &zeroValue} + a.GetHTMLURL() + a = &AppConfig{} + a.GetHTMLURL() + a = nil + a.GetHTMLURL() +} + +func TestAppConfig_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AppConfig{ID: &zeroValue} + a.GetID() + a = &AppConfig{} + a.GetID() + a = nil + a.GetID() +} + +func TestAppConfig_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{Name: &zeroValue} + a.GetName() + a = &AppConfig{} + a.GetName() + a = nil + a.GetName() +} + +func TestAppConfig_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{NodeID: &zeroValue} + a.GetNodeID() + a = &AppConfig{} + a.GetNodeID() + a = nil + a.GetNodeID() +} + +func TestAppConfig_GetOwner(tt *testing.T) { + tt.Parallel() + a := &AppConfig{} + a.GetOwner() + a = nil + a.GetOwner() +} + +func TestAppConfig_GetPEM(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{PEM: &zeroValue} + a.GetPEM() + a = &AppConfig{} + a.GetPEM() + a = nil + a.GetPEM() +} + +func TestAppConfig_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{Slug: &zeroValue} + a.GetSlug() + a = &AppConfig{} + a.GetSlug() + a = nil + a.GetSlug() +} + +func TestAppConfig_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &AppConfig{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &AppConfig{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestAppConfig_GetWebhookSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AppConfig{WebhookSecret: &zeroValue} + a.GetWebhookSecret() + a = &AppConfig{} + a.GetWebhookSecret() + a = nil + a.GetWebhookSecret() +} + +func TestAppInstallationRepositoriesRequest_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &AppInstallationRepositoriesRequest{Repositories: zeroValue} + a.GetRepositories() + a = &AppInstallationRepositoriesRequest{} + a.GetRepositories() + a = nil + a.GetRepositories() +} + +func TestArchivedAt_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ArchivedAt{From: &zeroValue} + a.GetFrom() + a = &ArchivedAt{} + a.GetFrom() + a = nil + a.GetFrom() +} + +func TestArchivedAt_GetTo(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ArchivedAt{To: &zeroValue} + a.GetTo() + a = &ArchivedAt{} + a.GetTo() + a = nil + a.GetTo() +} + +func TestArtifact_GetArchiveDownloadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Artifact{ArchiveDownloadURL: &zeroValue} + a.GetArchiveDownloadURL() + a = &Artifact{} + a.GetArchiveDownloadURL() + a = nil + a.GetArchiveDownloadURL() +} + +func TestArtifact_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Artifact{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &Artifact{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestArtifact_GetDigest(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Artifact{Digest: &zeroValue} + a.GetDigest() + a = &Artifact{} + a.GetDigest() + a = nil + a.GetDigest() +} + +func TestArtifact_GetExpired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &Artifact{Expired: &zeroValue} + a.GetExpired() + a = &Artifact{} + a.GetExpired() + a = nil + a.GetExpired() +} + +func TestArtifact_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Artifact{ExpiresAt: &zeroValue} + a.GetExpiresAt() + a = &Artifact{} + a.GetExpiresAt() + a = nil + a.GetExpiresAt() +} + +func TestArtifact_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &Artifact{ID: &zeroValue} + a.GetID() + a = &Artifact{} + a.GetID() + a = nil + a.GetID() +} + +func TestArtifact_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Artifact{Name: &zeroValue} + a.GetName() + a = &Artifact{} + a.GetName() + a = nil + a.GetName() +} + +func TestArtifact_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Artifact{NodeID: &zeroValue} + a.GetNodeID() + a = &Artifact{} + a.GetNodeID() + a = nil + a.GetNodeID() +} + +func TestArtifact_GetSizeInBytes(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &Artifact{SizeInBytes: &zeroValue} + a.GetSizeInBytes() + a = &Artifact{} + a.GetSizeInBytes() + a = nil + a.GetSizeInBytes() +} + +func TestArtifact_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Artifact{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &Artifact{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestArtifact_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Artifact{URL: &zeroValue} + a.GetURL() + a = &Artifact{} + a.GetURL() + a = nil + a.GetURL() +} + +func TestArtifact_GetWorkflowRun(tt *testing.T) { + tt.Parallel() + a := &Artifact{} + a.GetWorkflowRun() + a = nil + a.GetWorkflowRun() +} + +func TestArtifactDeploymentRecord_GetAttestationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactDeploymentRecord{AttestationID: &zeroValue} + a.GetAttestationID() + a = &ArtifactDeploymentRecord{} + a.GetAttestationID() + a = nil + a.GetAttestationID() +} + +func TestArtifactDeploymentRecord_GetCluster(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactDeploymentRecord{Cluster: &zeroValue} + a.GetCluster() + a = &ArtifactDeploymentRecord{} + a.GetCluster() + a = nil + a.GetCluster() +} + +func TestArtifactDeploymentRecord_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ArtifactDeploymentRecord{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &ArtifactDeploymentRecord{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestArtifactDeploymentRecord_GetDeploymentName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactDeploymentRecord{DeploymentName: &zeroValue} + a.GetDeploymentName() + a = &ArtifactDeploymentRecord{} + a.GetDeploymentName() + a = nil + a.GetDeploymentName() +} + +func TestArtifactDeploymentRecord_GetDigest(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactDeploymentRecord{Digest: &zeroValue} + a.GetDigest() + a = &ArtifactDeploymentRecord{} + a.GetDigest() + a = nil + a.GetDigest() +} + +func TestArtifactDeploymentRecord_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactDeploymentRecord{ID: &zeroValue} + a.GetID() + a = &ArtifactDeploymentRecord{} + a.GetID() + a = nil + a.GetID() +} + +func TestArtifactDeploymentRecord_GetLogicalEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactDeploymentRecord{LogicalEnvironment: &zeroValue} + a.GetLogicalEnvironment() + a = &ArtifactDeploymentRecord{} + a.GetLogicalEnvironment() + a = nil + a.GetLogicalEnvironment() +} + +func TestArtifactDeploymentRecord_GetPhysicalEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactDeploymentRecord{PhysicalEnvironment: &zeroValue} + a.GetPhysicalEnvironment() + a = &ArtifactDeploymentRecord{} + a.GetPhysicalEnvironment() + a = nil + a.GetPhysicalEnvironment() +} + +func TestArtifactDeploymentRecord_GetRuntimeRisks(tt *testing.T) { + tt.Parallel() + zeroValue := []DeploymentRuntimeRisk{} + a := &ArtifactDeploymentRecord{RuntimeRisks: zeroValue} + a.GetRuntimeRisks() + a = &ArtifactDeploymentRecord{} + a.GetRuntimeRisks() + a = nil + a.GetRuntimeRisks() +} + +func TestArtifactDeploymentRecord_GetTags(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + a := &ArtifactDeploymentRecord{Tags: zeroValue} + a.GetTags() + a = &ArtifactDeploymentRecord{} + a.GetTags() + a = nil + a.GetTags() +} + +func TestArtifactDeploymentRecord_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ArtifactDeploymentRecord{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &ArtifactDeploymentRecord{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestArtifactDeploymentResponse_GetDeploymentRecords(tt *testing.T) { + tt.Parallel() + zeroValue := []*ArtifactDeploymentRecord{} + a := &ArtifactDeploymentResponse{DeploymentRecords: zeroValue} + a.GetDeploymentRecords() + a = &ArtifactDeploymentResponse{} + a.GetDeploymentRecords() + a = nil + a.GetDeploymentRecords() +} + +func TestArtifactDeploymentResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ArtifactDeploymentResponse{TotalCount: &zeroValue} + a.GetTotalCount() + a = &ArtifactDeploymentResponse{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestArtifactList_GetArtifacts(tt *testing.T) { + tt.Parallel() + zeroValue := []*Artifact{} + a := &ArtifactList{Artifacts: zeroValue} + a.GetArtifacts() + a = &ArtifactList{} + a.GetArtifacts() + a = nil + a.GetArtifacts() +} + +func TestArtifactList_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactList{TotalCount: &zeroValue} + a.GetTotalCount() + a = &ArtifactList{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestArtifactPeriod_GetDays(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ArtifactPeriod{Days: &zeroValue} + a.GetDays() + a = &ArtifactPeriod{} + a.GetDays() + a = nil + a.GetDays() +} + +func TestArtifactPeriod_GetMaximumAllowedDays(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ArtifactPeriod{MaximumAllowedDays: &zeroValue} + a.GetMaximumAllowedDays() + a = &ArtifactPeriod{} + a.GetMaximumAllowedDays() + a = nil + a.GetMaximumAllowedDays() +} + +func TestArtifactPeriodOpt_GetDays(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ArtifactPeriodOpt{Days: &zeroValue} + a.GetDays() + a = &ArtifactPeriodOpt{} + a.GetDays() + a = nil + a.GetDays() +} + +func TestArtifactStorageRecord_GetArtifactURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactStorageRecord{ArtifactURL: &zeroValue} + a.GetArtifactURL() + a = &ArtifactStorageRecord{} + a.GetArtifactURL() + a = nil + a.GetArtifactURL() +} + +func TestArtifactStorageRecord_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ArtifactStorageRecord{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &ArtifactStorageRecord{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestArtifactStorageRecord_GetDigest(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactStorageRecord{Digest: &zeroValue} + a.GetDigest() + a = &ArtifactStorageRecord{} + a.GetDigest() + a = nil + a.GetDigest() +} + +func TestArtifactStorageRecord_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactStorageRecord{ID: &zeroValue} + a.GetID() + a = &ArtifactStorageRecord{} + a.GetID() + a = nil + a.GetID() +} + +func TestArtifactStorageRecord_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactStorageRecord{Name: &zeroValue} + a.GetName() + a = &ArtifactStorageRecord{} + a.GetName() + a = nil + a.GetName() +} + +func TestArtifactStorageRecord_GetRegistryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactStorageRecord{RegistryURL: &zeroValue} + a.GetRegistryURL() + a = &ArtifactStorageRecord{} + a.GetRegistryURL() + a = nil + a.GetRegistryURL() +} + +func TestArtifactStorageRecord_GetRepository(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactStorageRecord{Repository: &zeroValue} + a.GetRepository() + a = &ArtifactStorageRecord{} + a.GetRepository() + a = nil + a.GetRepository() +} + +func TestArtifactStorageRecord_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactStorageRecord{Status: &zeroValue} + a.GetStatus() + a = &ArtifactStorageRecord{} + a.GetStatus() + a = nil + a.GetStatus() +} + +func TestArtifactStorageRecord_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &ArtifactStorageRecord{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &ArtifactStorageRecord{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestArtifactStorageResponse_GetStorageRecords(tt *testing.T) { + tt.Parallel() + zeroValue := []*ArtifactStorageRecord{} + a := &ArtifactStorageResponse{StorageRecords: zeroValue} + a.GetStorageRecords() + a = &ArtifactStorageResponse{} + a.GetStorageRecords() + a = nil + a.GetStorageRecords() +} + +func TestArtifactStorageResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &ArtifactStorageResponse{TotalCount: &zeroValue} + a.GetTotalCount() + a = &ArtifactStorageResponse{} + a.GetTotalCount() + a = nil + a.GetTotalCount() +} + +func TestArtifactWorkflowRun_GetHeadBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactWorkflowRun{HeadBranch: &zeroValue} + a.GetHeadBranch() + a = &ArtifactWorkflowRun{} + a.GetHeadBranch() + a = nil + a.GetHeadBranch() +} + +func TestArtifactWorkflowRun_GetHeadRepositoryID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactWorkflowRun{HeadRepositoryID: &zeroValue} + a.GetHeadRepositoryID() + a = &ArtifactWorkflowRun{} + a.GetHeadRepositoryID() + a = nil + a.GetHeadRepositoryID() +} + +func TestArtifactWorkflowRun_GetHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &ArtifactWorkflowRun{HeadSHA: &zeroValue} + a.GetHeadSHA() + a = &ArtifactWorkflowRun{} + a.GetHeadSHA() + a = nil + a.GetHeadSHA() +} + +func TestArtifactWorkflowRun_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactWorkflowRun{ID: &zeroValue} + a.GetID() + a = &ArtifactWorkflowRun{} + a.GetID() + a = nil + a.GetID() +} + +func TestArtifactWorkflowRun_GetRepositoryID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &ArtifactWorkflowRun{RepositoryID: &zeroValue} + a.GetRepositoryID() + a = &ArtifactWorkflowRun{} + a.GetRepositoryID() + a = nil + a.GetRepositoryID() +} + +func TestAssignmentGrade_GetAssignmentName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{AssignmentName: &zeroValue} + a.GetAssignmentName() + a = &AssignmentGrade{} + a.GetAssignmentName() + a = nil + a.GetAssignmentName() +} + +func TestAssignmentGrade_GetAssignmentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{AssignmentURL: &zeroValue} + a.GetAssignmentURL() + a = &AssignmentGrade{} + a.GetAssignmentURL() + a = nil + a.GetAssignmentURL() +} + +func TestAssignmentGrade_GetGithubUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{GithubUsername: &zeroValue} + a.GetGithubUsername() + a = &AssignmentGrade{} + a.GetGithubUsername() + a = nil + a.GetGithubUsername() +} + +func TestAssignmentGrade_GetGroupName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{GroupName: &zeroValue} + a.GetGroupName() + a = &AssignmentGrade{} + a.GetGroupName() + a = nil + a.GetGroupName() +} + +func TestAssignmentGrade_GetPointsAvailable(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &AssignmentGrade{PointsAvailable: &zeroValue} + a.GetPointsAvailable() + a = &AssignmentGrade{} + a.GetPointsAvailable() + a = nil + a.GetPointsAvailable() +} + +func TestAssignmentGrade_GetPointsAwarded(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &AssignmentGrade{PointsAwarded: &zeroValue} + a.GetPointsAwarded() + a = &AssignmentGrade{} + a.GetPointsAwarded() + a = nil + a.GetPointsAwarded() +} + +func TestAssignmentGrade_GetRosterIdentifier(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{RosterIdentifier: &zeroValue} + a.GetRosterIdentifier() + a = &AssignmentGrade{} + a.GetRosterIdentifier() + a = nil + a.GetRosterIdentifier() +} + +func TestAssignmentGrade_GetStarterCodeURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{StarterCodeURL: &zeroValue} + a.GetStarterCodeURL() + a = &AssignmentGrade{} + a.GetStarterCodeURL() + a = nil + a.GetStarterCodeURL() +} + +func TestAssignmentGrade_GetStudentRepositoryName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{StudentRepositoryName: &zeroValue} + a.GetStudentRepositoryName() + a = &AssignmentGrade{} + a.GetStudentRepositoryName() + a = nil + a.GetStudentRepositoryName() +} + +func TestAssignmentGrade_GetStudentRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AssignmentGrade{StudentRepositoryURL: &zeroValue} + a.GetStudentRepositoryURL() + a = &AssignmentGrade{} + a.GetStudentRepositoryURL() + a = nil + a.GetStudentRepositoryURL() +} + +func TestAssignmentGrade_GetSubmissionTimestamp(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &AssignmentGrade{SubmissionTimestamp: &zeroValue} + a.GetSubmissionTimestamp() + a = &AssignmentGrade{} + a.GetSubmissionTimestamp() + a = nil + a.GetSubmissionTimestamp() +} + +func TestAttachment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Attachment{Body: &zeroValue} + a.GetBody() + a = &Attachment{} + a.GetBody() + a = nil + a.GetBody() +} + +func TestAttachment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &Attachment{ID: &zeroValue} + a.GetID() + a = &Attachment{} + a.GetID() + a = nil + a.GetID() +} + +func TestAttachment_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Attachment{Title: &zeroValue} + a.GetTitle() + a = &Attachment{} + a.GetTitle() + a = nil + a.GetTitle() +} + +func TestAttestation_GetBundle(tt *testing.T) { + tt.Parallel() + a := &Attestation{} + a.GetBundle() + a = nil + a.GetBundle() +} + +func TestAttestation_GetRepositoryID(tt *testing.T) { + tt.Parallel() + a := &Attestation{} + a.GetRepositoryID() + a = nil + a.GetRepositoryID() +} + +func TestAttestationsResponse_GetAttestations(tt *testing.T) { + tt.Parallel() + zeroValue := []*Attestation{} + a := &AttestationsResponse{Attestations: zeroValue} + a.GetAttestations() + a = &AttestationsResponse{} + a.GetAttestations() + a = nil + a.GetAttestations() +} + +func TestAuditEntry_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{Action: &zeroValue} + a.GetAction() + a = &AuditEntry{} + a.GetAction() + a = nil + a.GetAction() +} + +func TestAuditEntry_GetActor(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{Actor: &zeroValue} + a.GetActor() + a = &AuditEntry{} + a.GetActor() + a = nil + a.GetActor() +} + +func TestAuditEntry_GetActorID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AuditEntry{ActorID: &zeroValue} + a.GetActorID() + a = &AuditEntry{} + a.GetActorID() + a = nil + a.GetActorID() +} + +func TestAuditEntry_GetActorLocation(tt *testing.T) { + tt.Parallel() + a := &AuditEntry{} + a.GetActorLocation() + a = nil + a.GetActorLocation() +} + +func TestAuditEntry_GetAdditionalFields(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + a := &AuditEntry{AdditionalFields: zeroValue} + a.GetAdditionalFields() + a = &AuditEntry{} + a.GetAdditionalFields() + a = nil + a.GetAdditionalFields() +} + +func TestAuditEntry_GetBusiness(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{Business: &zeroValue} + a.GetBusiness() + a = &AuditEntry{} + a.GetBusiness() + a = nil + a.GetBusiness() +} + +func TestAuditEntry_GetBusinessID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AuditEntry{BusinessID: &zeroValue} + a.GetBusinessID() + a = &AuditEntry{} + a.GetBusinessID() + a = nil + a.GetBusinessID() +} + +func TestAuditEntry_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &AuditEntry{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &AuditEntry{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestAuditEntry_GetData(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + a := &AuditEntry{Data: zeroValue} + a.GetData() + a = &AuditEntry{} + a.GetData() + a = nil + a.GetData() +} + +func TestAuditEntry_GetDocumentID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{DocumentID: &zeroValue} + a.GetDocumentID() + a = &AuditEntry{} + a.GetDocumentID() + a = nil + a.GetDocumentID() +} + +func TestAuditEntry_GetExternalIdentityNameID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{ExternalIdentityNameID: &zeroValue} + a.GetExternalIdentityNameID() + a = &AuditEntry{} + a.GetExternalIdentityNameID() + a = nil + a.GetExternalIdentityNameID() +} + +func TestAuditEntry_GetExternalIdentityUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{ExternalIdentityUsername: &zeroValue} + a.GetExternalIdentityUsername() + a = &AuditEntry{} + a.GetExternalIdentityUsername() + a = nil + a.GetExternalIdentityUsername() +} + +func TestAuditEntry_GetHashedToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{HashedToken: &zeroValue} + a.GetHashedToken() + a = &AuditEntry{} + a.GetHashedToken() + a = nil + a.GetHashedToken() +} + +func TestAuditEntry_GetOrg(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{Org: &zeroValue} + a.GetOrg() + a = &AuditEntry{} + a.GetOrg() + a = nil + a.GetOrg() +} + +func TestAuditEntry_GetOrgID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AuditEntry{OrgID: &zeroValue} + a.GetOrgID() + a = &AuditEntry{} + a.GetOrgID() + a = nil + a.GetOrgID() +} + +func TestAuditEntry_GetTimestamp(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &AuditEntry{Timestamp: &zeroValue} + a.GetTimestamp() + a = &AuditEntry{} + a.GetTimestamp() + a = nil + a.GetTimestamp() +} + +func TestAuditEntry_GetTokenID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AuditEntry{TokenID: &zeroValue} + a.GetTokenID() + a = &AuditEntry{} + a.GetTokenID() + a = nil + a.GetTokenID() +} + +func TestAuditEntry_GetTokenScopes(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{TokenScopes: &zeroValue} + a.GetTokenScopes() + a = &AuditEntry{} + a.GetTokenScopes() + a = nil + a.GetTokenScopes() +} + +func TestAuditEntry_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuditEntry{User: &zeroValue} + a.GetUser() + a = &AuditEntry{} + a.GetUser() + a = nil + a.GetUser() +} + +func TestAuditEntry_GetUserID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AuditEntry{UserID: &zeroValue} + a.GetUserID() + a = &AuditEntry{} + a.GetUserID() + a = nil + a.GetUserID() +} + +func TestAuditLogStream_GetCreatedAt(tt *testing.T) { + tt.Parallel() + a := &AuditLogStream{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestAuditLogStream_GetEnabled(tt *testing.T) { + tt.Parallel() + a := &AuditLogStream{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAuditLogStream_GetID(tt *testing.T) { + tt.Parallel() + a := &AuditLogStream{} + a.GetID() + a = nil + a.GetID() +} + +func TestAuditLogStream_GetPausedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &AuditLogStream{PausedAt: &zeroValue} + a.GetPausedAt() + a = &AuditLogStream{} + a.GetPausedAt() + a = nil + a.GetPausedAt() +} + +func TestAuditLogStream_GetStreamDetails(tt *testing.T) { + tt.Parallel() + a := &AuditLogStream{} + a.GetStreamDetails() + a = nil + a.GetStreamDetails() +} + +func TestAuditLogStream_GetStreamType(tt *testing.T) { + tt.Parallel() + a := &AuditLogStream{} + a.GetStreamType() + a = nil + a.GetStreamType() +} + +func TestAuditLogStream_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + a := &AuditLogStream{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestAuditLogStreamConfig_GetEnabled(tt *testing.T) { + tt.Parallel() + a := &AuditLogStreamConfig{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAuditLogStreamConfig_GetStreamType(tt *testing.T) { + tt.Parallel() + a := &AuditLogStreamConfig{} + a.GetStreamType() + a = nil + a.GetStreamType() +} + +func TestAuditLogStreamConfig_GetVendorSpecific(tt *testing.T) { + tt.Parallel() + a := &AuditLogStreamConfig{} + a.GetVendorSpecific() + a = nil + a.GetVendorSpecific() +} + +func TestAuditLogStreamKey_GetKey(tt *testing.T) { + tt.Parallel() + a := &AuditLogStreamKey{} + a.GetKey() + a = nil + a.GetKey() +} + +func TestAuditLogStreamKey_GetKeyID(tt *testing.T) { + tt.Parallel() + a := &AuditLogStreamKey{} + a.GetKeyID() + a = nil + a.GetKeyID() +} + +func TestAuthorization_GetApp(tt *testing.T) { + tt.Parallel() + a := &Authorization{} + a.GetApp() + a = nil + a.GetApp() +} + +func TestAuthorization_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Authorization{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &Authorization{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestAuthorization_GetFingerprint(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{Fingerprint: &zeroValue} + a.GetFingerprint() + a = &Authorization{} + a.GetFingerprint() + a = nil + a.GetFingerprint() +} + +func TestAuthorization_GetHashedToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{HashedToken: &zeroValue} + a.GetHashedToken() + a = &Authorization{} + a.GetHashedToken() + a = nil + a.GetHashedToken() +} + +func TestAuthorization_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &Authorization{ID: &zeroValue} + a.GetID() + a = &Authorization{} + a.GetID() + a = nil + a.GetID() +} + +func TestAuthorization_GetNote(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{Note: &zeroValue} + a.GetNote() + a = &Authorization{} + a.GetNote() + a = nil + a.GetNote() +} + +func TestAuthorization_GetNoteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{NoteURL: &zeroValue} + a.GetNoteURL() + a = &Authorization{} + a.GetNoteURL() + a = nil + a.GetNoteURL() +} + +func TestAuthorization_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []Scope{} + a := &Authorization{Scopes: zeroValue} + a.GetScopes() + a = &Authorization{} + a.GetScopes() + a = nil + a.GetScopes() +} + +func TestAuthorization_GetToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{Token: &zeroValue} + a.GetToken() + a = &Authorization{} + a.GetToken() + a = nil + a.GetToken() +} + +func TestAuthorization_GetTokenLastEight(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{TokenLastEight: &zeroValue} + a.GetTokenLastEight() + a = &Authorization{} + a.GetTokenLastEight() + a = nil + a.GetTokenLastEight() +} + +func TestAuthorization_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + a := &Authorization{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &Authorization{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestAuthorization_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Authorization{URL: &zeroValue} + a.GetURL() + a = &Authorization{} + a.GetURL() + a = nil + a.GetURL() +} + +func TestAuthorization_GetUser(tt *testing.T) { + tt.Parallel() + a := &Authorization{} + a.GetUser() + a = nil + a.GetUser() +} + +func TestAuthorizationApp_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationApp{ClientID: &zeroValue} + a.GetClientID() + a = &AuthorizationApp{} + a.GetClientID() + a = nil + a.GetClientID() +} + +func TestAuthorizationApp_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationApp{Name: &zeroValue} + a.GetName() + a = &AuthorizationApp{} + a.GetName() + a = nil + a.GetName() +} + +func TestAuthorizationApp_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationApp{URL: &zeroValue} + a.GetURL() + a = &AuthorizationApp{} + a.GetURL() + a = nil + a.GetURL() +} + +func TestAuthorizationRequest_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationRequest{ClientID: &zeroValue} + a.GetClientID() + a = &AuthorizationRequest{} + a.GetClientID() + a = nil + a.GetClientID() +} + +func TestAuthorizationRequest_GetClientSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationRequest{ClientSecret: &zeroValue} + a.GetClientSecret() + a = &AuthorizationRequest{} + a.GetClientSecret() + a = nil + a.GetClientSecret() +} + +func TestAuthorizationRequest_GetFingerprint(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationRequest{Fingerprint: &zeroValue} + a.GetFingerprint() + a = &AuthorizationRequest{} + a.GetFingerprint() + a = nil + a.GetFingerprint() +} + +func TestAuthorizationRequest_GetNote(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationRequest{Note: &zeroValue} + a.GetNote() + a = &AuthorizationRequest{} + a.GetNote() + a = nil + a.GetNote() +} + +func TestAuthorizationRequest_GetNoteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationRequest{NoteURL: &zeroValue} + a.GetNoteURL() + a = &AuthorizationRequest{} + a.GetNoteURL() + a = nil + a.GetNoteURL() +} + +func TestAuthorizationRequest_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []Scope{} + a := &AuthorizationRequest{Scopes: zeroValue} + a.GetScopes() + a = &AuthorizationRequest{} + a.GetScopes() + a = nil + a.GetScopes() +} + +func TestAuthorizationUpdateRequest_GetAddScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &AuthorizationUpdateRequest{AddScopes: zeroValue} + a.GetAddScopes() + a = &AuthorizationUpdateRequest{} + a.GetAddScopes() + a = nil + a.GetAddScopes() +} + +func TestAuthorizationUpdateRequest_GetFingerprint(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationUpdateRequest{Fingerprint: &zeroValue} + a.GetFingerprint() + a = &AuthorizationUpdateRequest{} + a.GetFingerprint() + a = nil + a.GetFingerprint() +} + +func TestAuthorizationUpdateRequest_GetNote(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationUpdateRequest{Note: &zeroValue} + a.GetNote() + a = &AuthorizationUpdateRequest{} + a.GetNote() + a = nil + a.GetNote() +} + +func TestAuthorizationUpdateRequest_GetNoteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AuthorizationUpdateRequest{NoteURL: &zeroValue} + a.GetNoteURL() + a = &AuthorizationUpdateRequest{} + a.GetNoteURL() + a = nil + a.GetNoteURL() +} + +func TestAuthorizationUpdateRequest_GetRemoveScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &AuthorizationUpdateRequest{RemoveScopes: zeroValue} + a.GetRemoveScopes() + a = &AuthorizationUpdateRequest{} + a.GetRemoveScopes() + a = nil + a.GetRemoveScopes() +} + +func TestAuthorizationUpdateRequest_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &AuthorizationUpdateRequest{Scopes: zeroValue} + a.GetScopes() + a = &AuthorizationUpdateRequest{} + a.GetScopes() + a = nil + a.GetScopes() +} + +func TestAuthorizedActorNames_GetFrom(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + a := &AuthorizedActorNames{From: zeroValue} + a.GetFrom() + a = &AuthorizedActorNames{} + a.GetFrom() + a = nil + a.GetFrom() +} + +func TestAuthorizedActorsOnly_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AuthorizedActorsOnly{From: &zeroValue} + a.GetFrom() + a = &AuthorizedActorsOnly{} + a.GetFrom() + a = nil + a.GetFrom() +} + +func TestAuthorizedDismissalActorsOnlyChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AuthorizedDismissalActorsOnlyChanges{From: &zeroValue} + a.GetFrom() + a = &AuthorizedDismissalActorsOnlyChanges{} + a.GetFrom() + a = nil + a.GetFrom() +} + +func TestAutolink_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &Autolink{ID: &zeroValue} + a.GetID() + a = &Autolink{} + a.GetID() + a = nil + a.GetID() +} + +func TestAutolink_GetIsAlphanumeric(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &Autolink{IsAlphanumeric: &zeroValue} + a.GetIsAlphanumeric() + a = &Autolink{} + a.GetIsAlphanumeric() + a = nil + a.GetIsAlphanumeric() +} + +func TestAutolink_GetKeyPrefix(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Autolink{KeyPrefix: &zeroValue} + a.GetKeyPrefix() + a = &Autolink{} + a.GetKeyPrefix() + a = nil + a.GetKeyPrefix() +} + +func TestAutolink_GetURLTemplate(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &Autolink{URLTemplate: &zeroValue} + a.GetURLTemplate() + a = &Autolink{} + a.GetURLTemplate() + a = nil + a.GetURLTemplate() +} + +func TestAutomatedSecurityFixes_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AutomatedSecurityFixes{Enabled: &zeroValue} + a.GetEnabled() + a = &AutomatedSecurityFixes{} + a.GetEnabled() + a = nil + a.GetEnabled() +} + +func TestAutomatedSecurityFixes_GetPaused(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AutomatedSecurityFixes{Paused: &zeroValue} + a.GetPaused() + a = &AutomatedSecurityFixes{} + a.GetPaused() + a = nil + a.GetPaused() +} + +func TestAutoTriggerCheck_GetAppID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + a := &AutoTriggerCheck{AppID: &zeroValue} + a.GetAppID() + a = &AutoTriggerCheck{} + a.GetAppID() + a = nil + a.GetAppID() +} + +func TestAutoTriggerCheck_GetSetting(tt *testing.T) { + tt.Parallel() + var zeroValue bool + a := &AutoTriggerCheck{Setting: &zeroValue} + a.GetSetting() + a = &AutoTriggerCheck{} + a.GetSetting() + a = nil + a.GetSetting() +} + +func TestAzureBlobConfig_GetContainer(tt *testing.T) { + tt.Parallel() + a := &AzureBlobConfig{} + a.GetContainer() + a = nil + a.GetContainer() +} + +func TestAzureBlobConfig_GetEncryptedSASURL(tt *testing.T) { + tt.Parallel() + a := &AzureBlobConfig{} + a.GetEncryptedSASURL() + a = nil + a.GetEncryptedSASURL() +} + +func TestAzureBlobConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + a := &AzureBlobConfig{} + a.GetKeyID() + a = nil + a.GetKeyID() +} + +func TestAzureHubConfig_GetEncryptedConnstring(tt *testing.T) { + tt.Parallel() + a := &AzureHubConfig{} + a.GetEncryptedConnstring() + a = nil + a.GetEncryptedConnstring() +} + +func TestAzureHubConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + a := &AzureHubConfig{} + a.GetKeyID() + a = nil + a.GetKeyID() +} + +func TestAzureHubConfig_GetName(tt *testing.T) { + tt.Parallel() + a := &AzureHubConfig{} + a.GetName() + a = nil + a.GetName() +} + +func TestBasicAuthTransport_GetOTP(tt *testing.T) { + tt.Parallel() + b := &BasicAuthTransport{} + b.GetOTP() + b = nil + b.GetOTP() +} + +func TestBasicAuthTransport_GetPassword(tt *testing.T) { + tt.Parallel() + b := &BasicAuthTransport{} + b.GetPassword() + b = nil + b.GetPassword() +} + +func TestBasicAuthTransport_GetUsername(tt *testing.T) { + tt.Parallel() + b := &BasicAuthTransport{} + b.GetUsername() + b = nil + b.GetUsername() +} + +func TestBillingCostCenter_GetID(tt *testing.T) { + tt.Parallel() + b := &BillingCostCenter{} + b.GetID() + b = nil + b.GetID() +} + +func TestBillingCostCenter_GetName(tt *testing.T) { + tt.Parallel() + b := &BillingCostCenter{} + b.GetName() + b = nil + b.GetName() +} + +func TestBlob_GetContent(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Blob{Content: &zeroValue} + b.GetContent() + b = &Blob{} + b.GetContent() + b = nil + b.GetContent() +} + +func TestBlob_GetEncoding(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Blob{Encoding: &zeroValue} + b.GetEncoding() + b = &Blob{} + b.GetEncoding() + b = nil + b.GetEncoding() +} + +func TestBlob_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Blob{NodeID: &zeroValue} + b.GetNodeID() + b = &Blob{} + b.GetNodeID() + b = nil + b.GetNodeID() +} + +func TestBlob_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Blob{SHA: &zeroValue} + b.GetSHA() + b = &Blob{} + b.GetSHA() + b = nil + b.GetSHA() +} + +func TestBlob_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + b := &Blob{Size: &zeroValue} + b.GetSize() + b = &Blob{} + b.GetSize() + b = nil + b.GetSize() +} + +func TestBlob_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Blob{URL: &zeroValue} + b.GetURL() + b = &Blob{} + b.GetURL() + b = nil + b.GetURL() +} + +func TestBlockCreations_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BlockCreations{Enabled: &zeroValue} + b.GetEnabled() + b = &BlockCreations{} + b.GetEnabled() + b = nil + b.GetEnabled() +} + +func TestBranch_GetCommit(tt *testing.T) { + tt.Parallel() + b := &Branch{} + b.GetCommit() + b = nil + b.GetCommit() +} + +func TestBranch_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Branch{Name: &zeroValue} + b.GetName() + b = &Branch{} + b.GetName() + b = nil + b.GetName() +} + +func TestBranch_GetProtected(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &Branch{Protected: &zeroValue} + b.GetProtected() + b = &Branch{} + b.GetProtected() + b = nil + b.GetProtected() +} + +func TestBranch_GetProtection(tt *testing.T) { + tt.Parallel() + b := &Branch{} + b.GetProtection() + b = nil + b.GetProtection() +} + +func TestBranch_GetProtectionURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &Branch{ProtectionURL: &zeroValue} + b.GetProtectionURL() + b = &Branch{} + b.GetProtectionURL() + b = nil + b.GetProtectionURL() +} + +func TestBranchCommit_GetCommit(tt *testing.T) { + tt.Parallel() + b := &BranchCommit{} + b.GetCommit() + b = nil + b.GetCommit() +} + +func TestBranchCommit_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchCommit{Name: &zeroValue} + b.GetName() + b = &BranchCommit{} + b.GetName() + b = nil + b.GetName() +} + +func TestBranchCommit_GetProtected(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchCommit{Protected: &zeroValue} + b.GetProtected() + b = &BranchCommit{} + b.GetProtected() + b = nil + b.GetProtected() +} + +func TestBranchListOptions_GetProtected(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchListOptions{Protected: &zeroValue} + b.GetProtected() + b = &BranchListOptions{} + b.GetProtected() + b = nil + b.GetProtected() +} + +func TestBranchPolicy_GetCustomBranchPolicies(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchPolicy{CustomBranchPolicies: &zeroValue} + b.GetCustomBranchPolicies() + b = &BranchPolicy{} + b.GetCustomBranchPolicies() + b = nil + b.GetCustomBranchPolicies() +} + +func TestBranchPolicy_GetProtectedBranches(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchPolicy{ProtectedBranches: &zeroValue} + b.GetProtectedBranches() + b = &BranchPolicy{} + b.GetProtectedBranches() + b = nil + b.GetProtectedBranches() +} + +func TestBranchProtectionConfigurationEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionConfigurationEvent{Action: &zeroValue} + b.GetAction() + b = &BranchProtectionConfigurationEvent{} + b.GetAction() + b = nil + b.GetAction() +} + +func TestBranchProtectionConfigurationEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionConfigurationEvent{} + b.GetEnterprise() + b = nil + b.GetEnterprise() +} + +func TestBranchProtectionConfigurationEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionConfigurationEvent{} + b.GetInstallation() + b = nil + b.GetInstallation() +} + +func TestBranchProtectionConfigurationEvent_GetOrg(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionConfigurationEvent{} + b.GetOrg() + b = nil + b.GetOrg() +} + +func TestBranchProtectionConfigurationEvent_GetRepo(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionConfigurationEvent{} + b.GetRepo() + b = nil + b.GetRepo() +} + +func TestBranchProtectionConfigurationEvent_GetSender(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionConfigurationEvent{} + b.GetSender() + b = nil + b.GetSender() +} + +func TestBranchProtectionRule_GetAdminEnforced(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{AdminEnforced: &zeroValue} + b.GetAdminEnforced() + b = &BranchProtectionRule{} + b.GetAdminEnforced() + b = nil + b.GetAdminEnforced() +} + +func TestBranchProtectionRule_GetAllowDeletionsEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{AllowDeletionsEnforcementLevel: &zeroValue} + b.GetAllowDeletionsEnforcementLevel() + b = &BranchProtectionRule{} + b.GetAllowDeletionsEnforcementLevel() + b = nil + b.GetAllowDeletionsEnforcementLevel() +} + +func TestBranchProtectionRule_GetAllowForcePushesEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{AllowForcePushesEnforcementLevel: &zeroValue} + b.GetAllowForcePushesEnforcementLevel() + b = &BranchProtectionRule{} + b.GetAllowForcePushesEnforcementLevel() + b = nil + b.GetAllowForcePushesEnforcementLevel() +} + +func TestBranchProtectionRule_GetAuthorizedActorNames(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BranchProtectionRule{AuthorizedActorNames: zeroValue} + b.GetAuthorizedActorNames() + b = &BranchProtectionRule{} + b.GetAuthorizedActorNames() + b = nil + b.GetAuthorizedActorNames() +} + +func TestBranchProtectionRule_GetAuthorizedActorsOnly(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{AuthorizedActorsOnly: &zeroValue} + b.GetAuthorizedActorsOnly() + b = &BranchProtectionRule{} + b.GetAuthorizedActorsOnly() + b = nil + b.GetAuthorizedActorsOnly() +} + +func TestBranchProtectionRule_GetAuthorizedDismissalActorsOnly(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{AuthorizedDismissalActorsOnly: &zeroValue} + b.GetAuthorizedDismissalActorsOnly() + b = &BranchProtectionRule{} + b.GetAuthorizedDismissalActorsOnly() + b = nil + b.GetAuthorizedDismissalActorsOnly() +} + +func TestBranchProtectionRule_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + b := &BranchProtectionRule{CreatedAt: &zeroValue} + b.GetCreatedAt() + b = &BranchProtectionRule{} + b.GetCreatedAt() + b = nil + b.GetCreatedAt() +} + +func TestBranchProtectionRule_GetDismissStaleReviewsOnPush(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{DismissStaleReviewsOnPush: &zeroValue} + b.GetDismissStaleReviewsOnPush() + b = &BranchProtectionRule{} + b.GetDismissStaleReviewsOnPush() + b = nil + b.GetDismissStaleReviewsOnPush() +} + +func TestBranchProtectionRule_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + b := &BranchProtectionRule{ID: &zeroValue} + b.GetID() + b = &BranchProtectionRule{} + b.GetID() + b = nil + b.GetID() +} + +func TestBranchProtectionRule_GetIgnoreApprovalsFromContributors(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{IgnoreApprovalsFromContributors: &zeroValue} + b.GetIgnoreApprovalsFromContributors() + b = &BranchProtectionRule{} + b.GetIgnoreApprovalsFromContributors() + b = nil + b.GetIgnoreApprovalsFromContributors() +} + +func TestBranchProtectionRule_GetLinearHistoryRequirementEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{LinearHistoryRequirementEnforcementLevel: &zeroValue} + b.GetLinearHistoryRequirementEnforcementLevel() + b = &BranchProtectionRule{} + b.GetLinearHistoryRequirementEnforcementLevel() + b = nil + b.GetLinearHistoryRequirementEnforcementLevel() +} + +func TestBranchProtectionRule_GetMergeQueueEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{MergeQueueEnforcementLevel: &zeroValue} + b.GetMergeQueueEnforcementLevel() + b = &BranchProtectionRule{} + b.GetMergeQueueEnforcementLevel() + b = nil + b.GetMergeQueueEnforcementLevel() +} + +func TestBranchProtectionRule_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{Name: &zeroValue} + b.GetName() + b = &BranchProtectionRule{} + b.GetName() + b = nil + b.GetName() +} + +func TestBranchProtectionRule_GetPullRequestReviewsEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{PullRequestReviewsEnforcementLevel: &zeroValue} + b.GetPullRequestReviewsEnforcementLevel() + b = &BranchProtectionRule{} + b.GetPullRequestReviewsEnforcementLevel() + b = nil + b.GetPullRequestReviewsEnforcementLevel() +} + +func TestBranchProtectionRule_GetRepositoryID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + b := &BranchProtectionRule{RepositoryID: &zeroValue} + b.GetRepositoryID() + b = &BranchProtectionRule{} + b.GetRepositoryID() + b = nil + b.GetRepositoryID() +} + +func TestBranchProtectionRule_GetRequireCodeOwnerReview(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{RequireCodeOwnerReview: &zeroValue} + b.GetRequireCodeOwnerReview() + b = &BranchProtectionRule{} + b.GetRequireCodeOwnerReview() + b = nil + b.GetRequireCodeOwnerReview() +} + +func TestBranchProtectionRule_GetRequiredApprovingReviewCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + b := &BranchProtectionRule{RequiredApprovingReviewCount: &zeroValue} + b.GetRequiredApprovingReviewCount() + b = &BranchProtectionRule{} + b.GetRequiredApprovingReviewCount() + b = nil + b.GetRequiredApprovingReviewCount() +} + +func TestBranchProtectionRule_GetRequiredConversationResolutionLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{RequiredConversationResolutionLevel: &zeroValue} + b.GetRequiredConversationResolutionLevel() + b = &BranchProtectionRule{} + b.GetRequiredConversationResolutionLevel() + b = nil + b.GetRequiredConversationResolutionLevel() +} + +func TestBranchProtectionRule_GetRequiredDeploymentsEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{RequiredDeploymentsEnforcementLevel: &zeroValue} + b.GetRequiredDeploymentsEnforcementLevel() + b = &BranchProtectionRule{} + b.GetRequiredDeploymentsEnforcementLevel() + b = nil + b.GetRequiredDeploymentsEnforcementLevel() +} + +func TestBranchProtectionRule_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BranchProtectionRule{RequiredStatusChecks: zeroValue} + b.GetRequiredStatusChecks() + b = &BranchProtectionRule{} + b.GetRequiredStatusChecks() + b = nil + b.GetRequiredStatusChecks() +} + +func TestBranchProtectionRule_GetRequiredStatusChecksEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{RequiredStatusChecksEnforcementLevel: &zeroValue} + b.GetRequiredStatusChecksEnforcementLevel() + b = &BranchProtectionRule{} + b.GetRequiredStatusChecksEnforcementLevel() + b = nil + b.GetRequiredStatusChecksEnforcementLevel() +} + +func TestBranchProtectionRule_GetRequireLastPushApproval(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{RequireLastPushApproval: &zeroValue} + b.GetRequireLastPushApproval() + b = &BranchProtectionRule{} + b.GetRequireLastPushApproval() + b = nil + b.GetRequireLastPushApproval() +} + +func TestBranchProtectionRule_GetSignatureRequirementEnforcementLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRule{SignatureRequirementEnforcementLevel: &zeroValue} + b.GetSignatureRequirementEnforcementLevel() + b = &BranchProtectionRule{} + b.GetSignatureRequirementEnforcementLevel() + b = nil + b.GetSignatureRequirementEnforcementLevel() +} + +func TestBranchProtectionRule_GetStrictRequiredStatusChecksPolicy(tt *testing.T) { + tt.Parallel() + var zeroValue bool + b := &BranchProtectionRule{StrictRequiredStatusChecksPolicy: &zeroValue} + b.GetStrictRequiredStatusChecksPolicy() + b = &BranchProtectionRule{} + b.GetStrictRequiredStatusChecksPolicy() + b = nil + b.GetStrictRequiredStatusChecksPolicy() +} + +func TestBranchProtectionRule_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + b := &BranchProtectionRule{UpdatedAt: &zeroValue} + b.GetUpdatedAt() + b = &BranchProtectionRule{} + b.GetUpdatedAt() + b = nil + b.GetUpdatedAt() +} + +func TestBranchProtectionRuleEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + b := &BranchProtectionRuleEvent{Action: &zeroValue} + b.GetAction() + b = &BranchProtectionRuleEvent{} + b.GetAction() + b = nil + b.GetAction() +} + +func TestBranchProtectionRuleEvent_GetChanges(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionRuleEvent{} + b.GetChanges() + b = nil + b.GetChanges() +} + +func TestBranchProtectionRuleEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionRuleEvent{} + b.GetInstallation() + b = nil + b.GetInstallation() +} + +func TestBranchProtectionRuleEvent_GetOrg(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionRuleEvent{} + b.GetOrg() + b = nil + b.GetOrg() +} + +func TestBranchProtectionRuleEvent_GetRepo(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionRuleEvent{} + b.GetRepo() + b = nil + b.GetRepo() +} + +func TestBranchProtectionRuleEvent_GetRule(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionRuleEvent{} + b.GetRule() + b = nil + b.GetRule() +} + +func TestBranchProtectionRuleEvent_GetSender(tt *testing.T) { + tt.Parallel() + b := &BranchProtectionRuleEvent{} + b.GetSender() + b = nil + b.GetSender() +} + +func TestBranchRestrictions_GetApps(tt *testing.T) { + tt.Parallel() + zeroValue := []*App{} + b := &BranchRestrictions{Apps: zeroValue} + b.GetApps() + b = &BranchRestrictions{} + b.GetApps() + b = nil + b.GetApps() +} + +func TestBranchRestrictions_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + b := &BranchRestrictions{Teams: zeroValue} + b.GetTeams() + b = &BranchRestrictions{} + b.GetTeams() + b = nil + b.GetTeams() +} + +func TestBranchRestrictions_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + b := &BranchRestrictions{Users: zeroValue} + b.GetUsers() + b = &BranchRestrictions{} + b.GetUsers() + b = nil + b.GetUsers() +} + +func TestBranchRestrictionsRequest_GetApps(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BranchRestrictionsRequest{Apps: zeroValue} + b.GetApps() + b = &BranchRestrictionsRequest{} + b.GetApps() + b = nil + b.GetApps() +} + +func TestBranchRestrictionsRequest_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BranchRestrictionsRequest{Teams: zeroValue} + b.GetTeams() + b = &BranchRestrictionsRequest{} + b.GetTeams() + b = nil + b.GetTeams() +} + +func TestBranchRestrictionsRequest_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BranchRestrictionsRequest{Users: zeroValue} + b.GetUsers() + b = &BranchRestrictionsRequest{} + b.GetUsers() + b = nil + b.GetUsers() +} + +func TestBranchRuleMetadata_GetRulesetID(tt *testing.T) { + tt.Parallel() + b := &BranchRuleMetadata{} + b.GetRulesetID() + b = nil + b.GetRulesetID() +} + +func TestBranchRuleMetadata_GetRulesetSource(tt *testing.T) { + tt.Parallel() + b := &BranchRuleMetadata{} + b.GetRulesetSource() + b = nil + b.GetRulesetSource() +} + +func TestBranchRuleMetadata_GetRulesetSourceType(tt *testing.T) { + tt.Parallel() + b := &BranchRuleMetadata{} + b.GetRulesetSourceType() + b = nil + b.GetRulesetSourceType() +} + +func TestBranchRules_GetBranchNamePattern(tt *testing.T) { + tt.Parallel() + zeroValue := []*PatternBranchRule{} + b := &BranchRules{BranchNamePattern: zeroValue} + b.GetBranchNamePattern() + b = &BranchRules{} + b.GetBranchNamePattern() + b = nil + b.GetBranchNamePattern() +} + +func TestBranchRules_GetCodeScanning(tt *testing.T) { + tt.Parallel() + zeroValue := []*CodeScanningBranchRule{} + b := &BranchRules{CodeScanning: zeroValue} + b.GetCodeScanning() + b = &BranchRules{} + b.GetCodeScanning() + b = nil + b.GetCodeScanning() +} + +func TestBranchRules_GetCommitAuthorEmailPattern(tt *testing.T) { + tt.Parallel() + zeroValue := []*PatternBranchRule{} + b := &BranchRules{CommitAuthorEmailPattern: zeroValue} + b.GetCommitAuthorEmailPattern() + b = &BranchRules{} + b.GetCommitAuthorEmailPattern() + b = nil + b.GetCommitAuthorEmailPattern() +} + +func TestBranchRules_GetCommitMessagePattern(tt *testing.T) { + tt.Parallel() + zeroValue := []*PatternBranchRule{} + b := &BranchRules{CommitMessagePattern: zeroValue} + b.GetCommitMessagePattern() + b = &BranchRules{} + b.GetCommitMessagePattern() + b = nil + b.GetCommitMessagePattern() +} + +func TestBranchRules_GetCommitterEmailPattern(tt *testing.T) { + tt.Parallel() + zeroValue := []*PatternBranchRule{} + b := &BranchRules{CommitterEmailPattern: zeroValue} + b.GetCommitterEmailPattern() + b = &BranchRules{} + b.GetCommitterEmailPattern() + b = nil + b.GetCommitterEmailPattern() +} + +func TestBranchRules_GetCopilotCodeReview(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotCodeReviewBranchRule{} + b := &BranchRules{CopilotCodeReview: zeroValue} + b.GetCopilotCodeReview() + b = &BranchRules{} + b.GetCopilotCodeReview() + b = nil + b.GetCopilotCodeReview() +} + +func TestBranchRules_GetCreation(tt *testing.T) { + tt.Parallel() + zeroValue := []*BranchRuleMetadata{} + b := &BranchRules{Creation: zeroValue} + b.GetCreation() + b = &BranchRules{} + b.GetCreation() + b = nil + b.GetCreation() +} + +func TestBranchRules_GetDeletion(tt *testing.T) { + tt.Parallel() + zeroValue := []*BranchRuleMetadata{} + b := &BranchRules{Deletion: zeroValue} + b.GetDeletion() + b = &BranchRules{} + b.GetDeletion() + b = nil + b.GetDeletion() +} + +func TestBranchRules_GetFileExtensionRestriction(tt *testing.T) { + tt.Parallel() + zeroValue := []*FileExtensionRestrictionBranchRule{} + b := &BranchRules{FileExtensionRestriction: zeroValue} + b.GetFileExtensionRestriction() + b = &BranchRules{} + b.GetFileExtensionRestriction() + b = nil + b.GetFileExtensionRestriction() +} + +func TestBranchRules_GetFilePathRestriction(tt *testing.T) { + tt.Parallel() + zeroValue := []*FilePathRestrictionBranchRule{} + b := &BranchRules{FilePathRestriction: zeroValue} + b.GetFilePathRestriction() + b = &BranchRules{} + b.GetFilePathRestriction() + b = nil + b.GetFilePathRestriction() +} + +func TestBranchRules_GetMaxFilePathLength(tt *testing.T) { + tt.Parallel() + zeroValue := []*MaxFilePathLengthBranchRule{} + b := &BranchRules{MaxFilePathLength: zeroValue} + b.GetMaxFilePathLength() + b = &BranchRules{} + b.GetMaxFilePathLength() + b = nil + b.GetMaxFilePathLength() +} + +func TestBranchRules_GetMaxFileSize(tt *testing.T) { + tt.Parallel() + zeroValue := []*MaxFileSizeBranchRule{} + b := &BranchRules{MaxFileSize: zeroValue} + b.GetMaxFileSize() + b = &BranchRules{} + b.GetMaxFileSize() + b = nil + b.GetMaxFileSize() +} + +func TestBranchRules_GetMergeQueue(tt *testing.T) { + tt.Parallel() + zeroValue := []*MergeQueueBranchRule{} + b := &BranchRules{MergeQueue: zeroValue} + b.GetMergeQueue() + b = &BranchRules{} + b.GetMergeQueue() + b = nil + b.GetMergeQueue() +} + +func TestBranchRules_GetNonFastForward(tt *testing.T) { + tt.Parallel() + zeroValue := []*BranchRuleMetadata{} + b := &BranchRules{NonFastForward: zeroValue} + b.GetNonFastForward() + b = &BranchRules{} + b.GetNonFastForward() + b = nil + b.GetNonFastForward() +} + +func TestBranchRules_GetPullRequest(tt *testing.T) { + tt.Parallel() + zeroValue := []*PullRequestBranchRule{} + b := &BranchRules{PullRequest: zeroValue} + b.GetPullRequest() + b = &BranchRules{} + b.GetPullRequest() + b = nil + b.GetPullRequest() +} + +func TestBranchRules_GetRequiredDeployments(tt *testing.T) { + tt.Parallel() + zeroValue := []*RequiredDeploymentsBranchRule{} + b := &BranchRules{RequiredDeployments: zeroValue} + b.GetRequiredDeployments() + b = &BranchRules{} + b.GetRequiredDeployments() + b = nil + b.GetRequiredDeployments() +} + +func TestBranchRules_GetRequiredLinearHistory(tt *testing.T) { + tt.Parallel() + zeroValue := []*BranchRuleMetadata{} + b := &BranchRules{RequiredLinearHistory: zeroValue} + b.GetRequiredLinearHistory() + b = &BranchRules{} + b.GetRequiredLinearHistory() + b = nil + b.GetRequiredLinearHistory() +} + +func TestBranchRules_GetRequiredSignatures(tt *testing.T) { + tt.Parallel() + zeroValue := []*BranchRuleMetadata{} + b := &BranchRules{RequiredSignatures: zeroValue} + b.GetRequiredSignatures() + b = &BranchRules{} + b.GetRequiredSignatures() + b = nil + b.GetRequiredSignatures() +} + +func TestBranchRules_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + zeroValue := []*RequiredStatusChecksBranchRule{} + b := &BranchRules{RequiredStatusChecks: zeroValue} + b.GetRequiredStatusChecks() + b = &BranchRules{} + b.GetRequiredStatusChecks() + b = nil + b.GetRequiredStatusChecks() +} + +func TestBranchRules_GetTagNamePattern(tt *testing.T) { + tt.Parallel() + zeroValue := []*PatternBranchRule{} + b := &BranchRules{TagNamePattern: zeroValue} + b.GetTagNamePattern() + b = &BranchRules{} + b.GetTagNamePattern() + b = nil + b.GetTagNamePattern() +} + +func TestBranchRules_GetUpdate(tt *testing.T) { + tt.Parallel() + zeroValue := []*UpdateBranchRule{} + b := &BranchRules{Update: zeroValue} + b.GetUpdate() + b = &BranchRules{} + b.GetUpdate() + b = nil + b.GetUpdate() +} + +func TestBranchRules_GetWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []*WorkflowsBranchRule{} + b := &BranchRules{Workflows: zeroValue} + b.GetWorkflows() + b = &BranchRules{} + b.GetWorkflows() + b = nil + b.GetWorkflows() +} + +func TestBypassActor_GetActorID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + b := &BypassActor{ActorID: &zeroValue} + b.GetActorID() + b = &BypassActor{} + b.GetActorID() + b = nil + b.GetActorID() +} + +func TestBypassActor_GetActorType(tt *testing.T) { + tt.Parallel() + b := &BypassActor{} + b.GetActorType() + b = nil + b.GetActorType() +} + +func TestBypassActor_GetBypassMode(tt *testing.T) { + tt.Parallel() + b := &BypassActor{} + b.GetBypassMode() + b = nil + b.GetBypassMode() +} + +func TestBypassPullRequestAllowances_GetApps(tt *testing.T) { + tt.Parallel() + zeroValue := []*App{} + b := &BypassPullRequestAllowances{Apps: zeroValue} + b.GetApps() + b = &BypassPullRequestAllowances{} + b.GetApps() + b = nil + b.GetApps() +} + +func TestBypassPullRequestAllowances_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + b := &BypassPullRequestAllowances{Teams: zeroValue} + b.GetTeams() + b = &BypassPullRequestAllowances{} + b.GetTeams() + b = nil + b.GetTeams() +} + +func TestBypassPullRequestAllowances_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + b := &BypassPullRequestAllowances{Users: zeroValue} + b.GetUsers() + b = &BypassPullRequestAllowances{} + b.GetUsers() + b = nil + b.GetUsers() +} + +func TestBypassPullRequestAllowancesRequest_GetApps(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BypassPullRequestAllowancesRequest{Apps: zeroValue} + b.GetApps() + b = &BypassPullRequestAllowancesRequest{} + b.GetApps() + b = nil + b.GetApps() +} + +func TestBypassPullRequestAllowancesRequest_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BypassPullRequestAllowancesRequest{Teams: zeroValue} + b.GetTeams() + b = &BypassPullRequestAllowancesRequest{} + b.GetTeams() + b = nil + b.GetTeams() +} + +func TestBypassPullRequestAllowancesRequest_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + b := &BypassPullRequestAllowancesRequest{Users: zeroValue} + b.GetUsers() + b = &BypassPullRequestAllowancesRequest{} + b.GetUsers() + b = nil + b.GetUsers() +} + +func TestBypassReviewer_GetReviewerID(tt *testing.T) { + tt.Parallel() + b := &BypassReviewer{} + b.GetReviewerID() + b = nil + b.GetReviewerID() +} + +func TestBypassReviewer_GetReviewerType(tt *testing.T) { + tt.Parallel() + b := &BypassReviewer{} + b.GetReviewerType() + b = nil + b.GetReviewerType() +} + +func TestBypassReviewer_GetSecurityConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + b := &BypassReviewer{SecurityConfigurationID: &zeroValue} + b.GetSecurityConfigurationID() + b = &BypassReviewer{} + b.GetSecurityConfigurationID() + b = nil + b.GetSecurityConfigurationID() +} + +func TestCheckRun_GetApp(tt *testing.T) { + tt.Parallel() + c := &CheckRun{} + c.GetApp() + c = nil + c.GetApp() +} + +func TestCheckRun_GetCheckSuite(tt *testing.T) { + tt.Parallel() + c := &CheckRun{} + c.GetCheckSuite() + c = nil + c.GetCheckSuite() +} + +func TestCheckRun_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CheckRun{CompletedAt: &zeroValue} + c.GetCompletedAt() + c = &CheckRun{} + c.GetCompletedAt() + c = nil + c.GetCompletedAt() +} + +func TestCheckRun_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{Conclusion: &zeroValue} + c.GetConclusion() + c = &CheckRun{} + c.GetConclusion() + c = nil + c.GetConclusion() +} + +func TestCheckRun_GetDetailsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{DetailsURL: &zeroValue} + c.GetDetailsURL() + c = &CheckRun{} + c.GetDetailsURL() + c = nil + c.GetDetailsURL() +} + +func TestCheckRun_GetExternalID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{ExternalID: &zeroValue} + c.GetExternalID() + c = &CheckRun{} + c.GetExternalID() + c = nil + c.GetExternalID() +} + +func TestCheckRun_GetHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{HeadSHA: &zeroValue} + c.GetHeadSHA() + c = &CheckRun{} + c.GetHeadSHA() + c = nil + c.GetHeadSHA() +} + +func TestCheckRun_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CheckRun{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCheckRun_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CheckRun{ID: &zeroValue} + c.GetID() + c = &CheckRun{} + c.GetID() + c = nil + c.GetID() +} + +func TestCheckRun_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{Name: &zeroValue} + c.GetName() + c = &CheckRun{} + c.GetName() + c = nil + c.GetName() +} + +func TestCheckRun_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{NodeID: &zeroValue} + c.GetNodeID() + c = &CheckRun{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestCheckRun_GetOutput(tt *testing.T) { + tt.Parallel() + c := &CheckRun{} + c.GetOutput() + c = nil + c.GetOutput() +} + +func TestCheckRun_GetPullRequests(tt *testing.T) { + tt.Parallel() + zeroValue := []*PullRequest{} + c := &CheckRun{PullRequests: zeroValue} + c.GetPullRequests() + c = &CheckRun{} + c.GetPullRequests() + c = nil + c.GetPullRequests() +} + +func TestCheckRun_GetStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CheckRun{StartedAt: &zeroValue} + c.GetStartedAt() + c = &CheckRun{} + c.GetStartedAt() + c = nil + c.GetStartedAt() +} + +func TestCheckRun_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{Status: &zeroValue} + c.GetStatus() + c = &CheckRun{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCheckRun_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRun{URL: &zeroValue} + c.GetURL() + c = &CheckRun{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCheckRunAction_GetDescription(tt *testing.T) { + tt.Parallel() + c := &CheckRunAction{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCheckRunAction_GetIdentifier(tt *testing.T) { + tt.Parallel() + c := &CheckRunAction{} + c.GetIdentifier() + c = nil + c.GetIdentifier() +} + +func TestCheckRunAction_GetLabel(tt *testing.T) { + tt.Parallel() + c := &CheckRunAction{} + c.GetLabel() + c = nil + c.GetLabel() +} + +func TestCheckRunAnnotation_GetAnnotationLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunAnnotation{AnnotationLevel: &zeroValue} + c.GetAnnotationLevel() + c = &CheckRunAnnotation{} + c.GetAnnotationLevel() + c = nil + c.GetAnnotationLevel() +} + +func TestCheckRunAnnotation_GetEndColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CheckRunAnnotation{EndColumn: &zeroValue} + c.GetEndColumn() + c = &CheckRunAnnotation{} + c.GetEndColumn() + c = nil + c.GetEndColumn() +} + +func TestCheckRunAnnotation_GetEndLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CheckRunAnnotation{EndLine: &zeroValue} + c.GetEndLine() + c = &CheckRunAnnotation{} + c.GetEndLine() + c = nil + c.GetEndLine() +} + +func TestCheckRunAnnotation_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunAnnotation{Message: &zeroValue} + c.GetMessage() + c = &CheckRunAnnotation{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCheckRunAnnotation_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunAnnotation{Path: &zeroValue} + c.GetPath() + c = &CheckRunAnnotation{} + c.GetPath() + c = nil + c.GetPath() +} + +func TestCheckRunAnnotation_GetRawDetails(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunAnnotation{RawDetails: &zeroValue} + c.GetRawDetails() + c = &CheckRunAnnotation{} + c.GetRawDetails() + c = nil + c.GetRawDetails() +} + +func TestCheckRunAnnotation_GetStartColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CheckRunAnnotation{StartColumn: &zeroValue} + c.GetStartColumn() + c = &CheckRunAnnotation{} + c.GetStartColumn() + c = nil + c.GetStartColumn() +} + +func TestCheckRunAnnotation_GetStartLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CheckRunAnnotation{StartLine: &zeroValue} + c.GetStartLine() + c = &CheckRunAnnotation{} + c.GetStartLine() + c = nil + c.GetStartLine() +} + +func TestCheckRunAnnotation_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunAnnotation{Title: &zeroValue} + c.GetTitle() + c = &CheckRunAnnotation{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCheckRunEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunEvent{Action: &zeroValue} + c.GetAction() + c = &CheckRunEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestCheckRunEvent_GetCheckRun(tt *testing.T) { + tt.Parallel() + c := &CheckRunEvent{} + c.GetCheckRun() + c = nil + c.GetCheckRun() +} + +func TestCheckRunEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CheckRunEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCheckRunEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CheckRunEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCheckRunEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CheckRunEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCheckRunEvent_GetRequestedAction(tt *testing.T) { + tt.Parallel() + c := &CheckRunEvent{} + c.GetRequestedAction() + c = nil + c.GetRequestedAction() +} + +func TestCheckRunEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CheckRunEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCheckRunImage_GetAlt(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunImage{Alt: &zeroValue} + c.GetAlt() + c = &CheckRunImage{} + c.GetAlt() + c = nil + c.GetAlt() +} + +func TestCheckRunImage_GetCaption(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunImage{Caption: &zeroValue} + c.GetCaption() + c = &CheckRunImage{} + c.GetCaption() + c = nil + c.GetCaption() +} + +func TestCheckRunImage_GetImageURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunImage{ImageURL: &zeroValue} + c.GetImageURL() + c = &CheckRunImage{} + c.GetImageURL() + c = nil + c.GetImageURL() +} + +func TestCheckRunOutput_GetAnnotations(tt *testing.T) { + tt.Parallel() + zeroValue := []*CheckRunAnnotation{} + c := &CheckRunOutput{Annotations: zeroValue} + c.GetAnnotations() + c = &CheckRunOutput{} + c.GetAnnotations() + c = nil + c.GetAnnotations() +} + +func TestCheckRunOutput_GetAnnotationsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CheckRunOutput{AnnotationsCount: &zeroValue} + c.GetAnnotationsCount() + c = &CheckRunOutput{} + c.GetAnnotationsCount() + c = nil + c.GetAnnotationsCount() +} + +func TestCheckRunOutput_GetAnnotationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunOutput{AnnotationsURL: &zeroValue} + c.GetAnnotationsURL() + c = &CheckRunOutput{} + c.GetAnnotationsURL() + c = nil + c.GetAnnotationsURL() +} + +func TestCheckRunOutput_GetImages(tt *testing.T) { + tt.Parallel() + zeroValue := []*CheckRunImage{} + c := &CheckRunOutput{Images: zeroValue} + c.GetImages() + c = &CheckRunOutput{} + c.GetImages() + c = nil + c.GetImages() +} + +func TestCheckRunOutput_GetSummary(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunOutput{Summary: &zeroValue} + c.GetSummary() + c = &CheckRunOutput{} + c.GetSummary() + c = nil + c.GetSummary() +} + +func TestCheckRunOutput_GetText(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunOutput{Text: &zeroValue} + c.GetText() + c = &CheckRunOutput{} + c.GetText() + c = nil + c.GetText() +} + +func TestCheckRunOutput_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckRunOutput{Title: &zeroValue} + c.GetTitle() + c = &CheckRunOutput{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCheckSuite_GetAfterSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{AfterSHA: &zeroValue} + c.GetAfterSHA() + c = &CheckSuite{} + c.GetAfterSHA() + c = nil + c.GetAfterSHA() +} + +func TestCheckSuite_GetApp(tt *testing.T) { + tt.Parallel() + c := &CheckSuite{} + c.GetApp() + c = nil + c.GetApp() +} + +func TestCheckSuite_GetBeforeSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{BeforeSHA: &zeroValue} + c.GetBeforeSHA() + c = &CheckSuite{} + c.GetBeforeSHA() + c = nil + c.GetBeforeSHA() +} + +func TestCheckSuite_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{Conclusion: &zeroValue} + c.GetConclusion() + c = &CheckSuite{} + c.GetConclusion() + c = nil + c.GetConclusion() +} + +func TestCheckSuite_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CheckSuite{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CheckSuite{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCheckSuite_GetHeadBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{HeadBranch: &zeroValue} + c.GetHeadBranch() + c = &CheckSuite{} + c.GetHeadBranch() + c = nil + c.GetHeadBranch() +} + +func TestCheckSuite_GetHeadCommit(tt *testing.T) { + tt.Parallel() + c := &CheckSuite{} + c.GetHeadCommit() + c = nil + c.GetHeadCommit() +} + +func TestCheckSuite_GetHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{HeadSHA: &zeroValue} + c.GetHeadSHA() + c = &CheckSuite{} + c.GetHeadSHA() + c = nil + c.GetHeadSHA() +} + +func TestCheckSuite_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CheckSuite{ID: &zeroValue} + c.GetID() + c = &CheckSuite{} + c.GetID() + c = nil + c.GetID() +} + +func TestCheckSuite_GetLatestCheckRunsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CheckSuite{LatestCheckRunsCount: &zeroValue} + c.GetLatestCheckRunsCount() + c = &CheckSuite{} + c.GetLatestCheckRunsCount() + c = nil + c.GetLatestCheckRunsCount() +} + +func TestCheckSuite_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{NodeID: &zeroValue} + c.GetNodeID() + c = &CheckSuite{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestCheckSuite_GetPullRequests(tt *testing.T) { + tt.Parallel() + zeroValue := []*PullRequest{} + c := &CheckSuite{PullRequests: zeroValue} + c.GetPullRequests() + c = &CheckSuite{} + c.GetPullRequests() + c = nil + c.GetPullRequests() +} + +func TestCheckSuite_GetRepository(tt *testing.T) { + tt.Parallel() + c := &CheckSuite{} + c.GetRepository() + c = nil + c.GetRepository() +} + +func TestCheckSuite_GetRerequestable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CheckSuite{Rerequestable: &zeroValue} + c.GetRerequestable() + c = &CheckSuite{} + c.GetRerequestable() + c = nil + c.GetRerequestable() +} + +func TestCheckSuite_GetRunsRerequestable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CheckSuite{RunsRerequestable: &zeroValue} + c.GetRunsRerequestable() + c = &CheckSuite{} + c.GetRunsRerequestable() + c = nil + c.GetRunsRerequestable() +} + +func TestCheckSuite_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{Status: &zeroValue} + c.GetStatus() + c = &CheckSuite{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCheckSuite_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CheckSuite{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CheckSuite{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCheckSuite_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuite{URL: &zeroValue} + c.GetURL() + c = &CheckSuite{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCheckSuiteEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CheckSuiteEvent{Action: &zeroValue} + c.GetAction() + c = &CheckSuiteEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestCheckSuiteEvent_GetCheckSuite(tt *testing.T) { + tt.Parallel() + c := &CheckSuiteEvent{} + c.GetCheckSuite() + c = nil + c.GetCheckSuite() +} + +func TestCheckSuiteEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CheckSuiteEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCheckSuiteEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CheckSuiteEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCheckSuiteEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CheckSuiteEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCheckSuiteEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CheckSuiteEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCheckSuitePreferenceOptions_GetAutoTriggerChecks(tt *testing.T) { + tt.Parallel() + zeroValue := []*AutoTriggerCheck{} + c := &CheckSuitePreferenceOptions{AutoTriggerChecks: zeroValue} + c.GetAutoTriggerChecks() + c = &CheckSuitePreferenceOptions{} + c.GetAutoTriggerChecks() + c = nil + c.GetAutoTriggerChecks() +} + +func TestCheckSuitePreferenceResults_GetPreferences(tt *testing.T) { + tt.Parallel() + c := &CheckSuitePreferenceResults{} + c.GetPreferences() + c = nil + c.GetPreferences() +} + +func TestCheckSuitePreferenceResults_GetRepository(tt *testing.T) { + tt.Parallel() + c := &CheckSuitePreferenceResults{} + c.GetRepository() + c = nil + c.GetRepository() +} + +func TestClassroom_GetArchived(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &Classroom{Archived: &zeroValue} + c.GetArchived() + c = &Classroom{} + c.GetArchived() + c = nil + c.GetArchived() +} + +func TestClassroom_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &Classroom{ID: &zeroValue} + c.GetID() + c = &Classroom{} + c.GetID() + c = nil + c.GetID() +} + +func TestClassroom_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Classroom{Name: &zeroValue} + c.GetName() + c = &Classroom{} + c.GetName() + c = nil + c.GetName() +} + +func TestClassroom_GetOrganization(tt *testing.T) { + tt.Parallel() + c := &Classroom{} + c.GetOrganization() + c = nil + c.GetOrganization() +} + +func TestClassroom_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Classroom{URL: &zeroValue} + c.GetURL() + c = &Classroom{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestClassroomAssignment_GetAccepted(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ClassroomAssignment{Accepted: &zeroValue} + c.GetAccepted() + c = &ClassroomAssignment{} + c.GetAccepted() + c = nil + c.GetAccepted() +} + +func TestClassroomAssignment_GetClassroom(tt *testing.T) { + tt.Parallel() + c := &ClassroomAssignment{} + c.GetClassroom() + c = nil + c.GetClassroom() +} + +func TestClassroomAssignment_GetDeadline(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &ClassroomAssignment{Deadline: &zeroValue} + c.GetDeadline() + c = &ClassroomAssignment{} + c.GetDeadline() + c = nil + c.GetDeadline() +} + +func TestClassroomAssignment_GetEditor(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomAssignment{Editor: &zeroValue} + c.GetEditor() + c = &ClassroomAssignment{} + c.GetEditor() + c = nil + c.GetEditor() +} + +func TestClassroomAssignment_GetFeedbackPullRequestsEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ClassroomAssignment{FeedbackPullRequestsEnabled: &zeroValue} + c.GetFeedbackPullRequestsEnabled() + c = &ClassroomAssignment{} + c.GetFeedbackPullRequestsEnabled() + c = nil + c.GetFeedbackPullRequestsEnabled() +} + +func TestClassroomAssignment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &ClassroomAssignment{ID: &zeroValue} + c.GetID() + c = &ClassroomAssignment{} + c.GetID() + c = nil + c.GetID() +} + +func TestClassroomAssignment_GetInvitationsEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ClassroomAssignment{InvitationsEnabled: &zeroValue} + c.GetInvitationsEnabled() + c = &ClassroomAssignment{} + c.GetInvitationsEnabled() + c = nil + c.GetInvitationsEnabled() +} + +func TestClassroomAssignment_GetInviteLink(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomAssignment{InviteLink: &zeroValue} + c.GetInviteLink() + c = &ClassroomAssignment{} + c.GetInviteLink() + c = nil + c.GetInviteLink() +} + +func TestClassroomAssignment_GetLanguage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomAssignment{Language: &zeroValue} + c.GetLanguage() + c = &ClassroomAssignment{} + c.GetLanguage() + c = nil + c.GetLanguage() +} + +func TestClassroomAssignment_GetMaxMembers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ClassroomAssignment{MaxMembers: &zeroValue} + c.GetMaxMembers() + c = &ClassroomAssignment{} + c.GetMaxMembers() + c = nil + c.GetMaxMembers() +} + +func TestClassroomAssignment_GetMaxTeams(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ClassroomAssignment{MaxTeams: &zeroValue} + c.GetMaxTeams() + c = &ClassroomAssignment{} + c.GetMaxTeams() + c = nil + c.GetMaxTeams() +} + +func TestClassroomAssignment_GetPassing(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ClassroomAssignment{Passing: &zeroValue} + c.GetPassing() + c = &ClassroomAssignment{} + c.GetPassing() + c = nil + c.GetPassing() +} + +func TestClassroomAssignment_GetPublicRepo(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ClassroomAssignment{PublicRepo: &zeroValue} + c.GetPublicRepo() + c = &ClassroomAssignment{} + c.GetPublicRepo() + c = nil + c.GetPublicRepo() +} + +func TestClassroomAssignment_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomAssignment{Slug: &zeroValue} + c.GetSlug() + c = &ClassroomAssignment{} + c.GetSlug() + c = nil + c.GetSlug() +} + +func TestClassroomAssignment_GetStarterCodeRepository(tt *testing.T) { + tt.Parallel() + c := &ClassroomAssignment{} + c.GetStarterCodeRepository() + c = nil + c.GetStarterCodeRepository() +} + +func TestClassroomAssignment_GetStudentsAreRepoAdmins(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ClassroomAssignment{StudentsAreRepoAdmins: &zeroValue} + c.GetStudentsAreRepoAdmins() + c = &ClassroomAssignment{} + c.GetStudentsAreRepoAdmins() + c = nil + c.GetStudentsAreRepoAdmins() +} + +func TestClassroomAssignment_GetSubmitted(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ClassroomAssignment{Submitted: &zeroValue} + c.GetSubmitted() + c = &ClassroomAssignment{} + c.GetSubmitted() + c = nil + c.GetSubmitted() +} + +func TestClassroomAssignment_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomAssignment{Title: &zeroValue} + c.GetTitle() + c = &ClassroomAssignment{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestClassroomAssignment_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomAssignment{Type: &zeroValue} + c.GetType() + c = &ClassroomAssignment{} + c.GetType() + c = nil + c.GetType() +} + +func TestClassroomUser_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomUser{AvatarURL: &zeroValue} + c.GetAvatarURL() + c = &ClassroomUser{} + c.GetAvatarURL() + c = nil + c.GetAvatarURL() +} + +func TestClassroomUser_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomUser{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &ClassroomUser{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestClassroomUser_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &ClassroomUser{ID: &zeroValue} + c.GetID() + c = &ClassroomUser{} + c.GetID() + c = nil + c.GetID() +} + +func TestClassroomUser_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClassroomUser{Login: &zeroValue} + c.GetLogin() + c = &ClassroomUser{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestClusterArtifactDeployment_GetDeploymentName(tt *testing.T) { + tt.Parallel() + c := &ClusterArtifactDeployment{} + c.GetDeploymentName() + c = nil + c.GetDeploymentName() +} + +func TestClusterArtifactDeployment_GetDigest(tt *testing.T) { + tt.Parallel() + c := &ClusterArtifactDeployment{} + c.GetDigest() + c = nil + c.GetDigest() +} + +func TestClusterArtifactDeployment_GetGithubRepository(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterArtifactDeployment{GithubRepository: &zeroValue} + c.GetGithubRepository() + c = &ClusterArtifactDeployment{} + c.GetGithubRepository() + c = nil + c.GetGithubRepository() +} + +func TestClusterArtifactDeployment_GetName(tt *testing.T) { + tt.Parallel() + c := &ClusterArtifactDeployment{} + c.GetName() + c = nil + c.GetName() +} + +func TestClusterArtifactDeployment_GetRuntimeRisks(tt *testing.T) { + tt.Parallel() + zeroValue := []DeploymentRuntimeRisk{} + c := &ClusterArtifactDeployment{RuntimeRisks: zeroValue} + c.GetRuntimeRisks() + c = &ClusterArtifactDeployment{} + c.GetRuntimeRisks() + c = nil + c.GetRuntimeRisks() +} + +func TestClusterArtifactDeployment_GetStatus(tt *testing.T) { + tt.Parallel() + c := &ClusterArtifactDeployment{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestClusterArtifactDeployment_GetTags(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + c := &ClusterArtifactDeployment{Tags: zeroValue} + c.GetTags() + c = &ClusterArtifactDeployment{} + c.GetTags() + c = nil + c.GetTags() +} + +func TestClusterArtifactDeployment_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterArtifactDeployment{Version: &zeroValue} + c.GetVersion() + c = &ClusterArtifactDeployment{} + c.GetVersion() + c = nil + c.GetVersion() +} + +func TestClusterDeploymentRecordsRequest_GetDeployments(tt *testing.T) { + tt.Parallel() + zeroValue := []*ClusterArtifactDeployment{} + c := &ClusterDeploymentRecordsRequest{Deployments: zeroValue} + c.GetDeployments() + c = &ClusterDeploymentRecordsRequest{} + c.GetDeployments() + c = nil + c.GetDeployments() +} + +func TestClusterDeploymentRecordsRequest_GetLogicalEnvironment(tt *testing.T) { + tt.Parallel() + c := &ClusterDeploymentRecordsRequest{} + c.GetLogicalEnvironment() + c = nil + c.GetLogicalEnvironment() +} + +func TestClusterDeploymentRecordsRequest_GetPhysicalEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterDeploymentRecordsRequest{PhysicalEnvironment: &zeroValue} + c.GetPhysicalEnvironment() + c = &ClusterDeploymentRecordsRequest{} + c.GetPhysicalEnvironment() + c = nil + c.GetPhysicalEnvironment() +} + +func TestClusterSSHKey_GetFingerprint(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterSSHKey{Fingerprint: &zeroValue} + c.GetFingerprint() + c = &ClusterSSHKey{} + c.GetFingerprint() + c = nil + c.GetFingerprint() +} + +func TestClusterSSHKey_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterSSHKey{Key: &zeroValue} + c.GetKey() + c = &ClusterSSHKey{} + c.GetKey() + c = nil + c.GetKey() +} + +func TestClusterStatus_GetNodes(tt *testing.T) { + tt.Parallel() + zeroValue := []*ClusterStatusNode{} + c := &ClusterStatus{Nodes: zeroValue} + c.GetNodes() + c = &ClusterStatus{} + c.GetNodes() + c = nil + c.GetNodes() +} + +func TestClusterStatus_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterStatus{Status: &zeroValue} + c.GetStatus() + c = &ClusterStatus{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestClusterStatusNode_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterStatusNode{Hostname: &zeroValue} + c.GetHostname() + c = &ClusterStatusNode{} + c.GetHostname() + c = nil + c.GetHostname() +} + +func TestClusterStatusNode_GetServices(tt *testing.T) { + tt.Parallel() + zeroValue := []*ClusterStatusNodeServiceItem{} + c := &ClusterStatusNode{Services: zeroValue} + c.GetServices() + c = &ClusterStatusNode{} + c.GetServices() + c = nil + c.GetServices() +} + +func TestClusterStatusNode_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterStatusNode{Status: &zeroValue} + c.GetStatus() + c = &ClusterStatusNode{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestClusterStatusNodeServiceItem_GetDetails(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterStatusNodeServiceItem{Details: &zeroValue} + c.GetDetails() + c = &ClusterStatusNodeServiceItem{} + c.GetDetails() + c = nil + c.GetDetails() +} + +func TestClusterStatusNodeServiceItem_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterStatusNodeServiceItem{Name: &zeroValue} + c.GetName() + c = &ClusterStatusNodeServiceItem{} + c.GetName() + c = nil + c.GetName() +} + +func TestClusterStatusNodeServiceItem_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ClusterStatusNodeServiceItem{Status: &zeroValue} + c.GetStatus() + c = &ClusterStatusNodeServiceItem{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCodeOfConduct_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeOfConduct{Body: &zeroValue} + c.GetBody() + c = &CodeOfConduct{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCodeOfConduct_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeOfConduct{Key: &zeroValue} + c.GetKey() + c = &CodeOfConduct{} + c.GetKey() + c = nil + c.GetKey() +} + +func TestCodeOfConduct_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeOfConduct{Name: &zeroValue} + c.GetName() + c = &CodeOfConduct{} + c.GetName() + c = nil + c.GetName() +} + +func TestCodeOfConduct_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeOfConduct{URL: &zeroValue} + c.GetURL() + c = &CodeOfConduct{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCodeownersError_GetColumn(tt *testing.T) { + tt.Parallel() + c := &CodeownersError{} + c.GetColumn() + c = nil + c.GetColumn() +} + +func TestCodeownersError_GetKind(tt *testing.T) { + tt.Parallel() + c := &CodeownersError{} + c.GetKind() + c = nil + c.GetKind() +} + +func TestCodeownersError_GetLine(tt *testing.T) { + tt.Parallel() + c := &CodeownersError{} + c.GetLine() + c = nil + c.GetLine() +} + +func TestCodeownersError_GetMessage(tt *testing.T) { + tt.Parallel() + c := &CodeownersError{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCodeownersError_GetPath(tt *testing.T) { + tt.Parallel() + c := &CodeownersError{} + c.GetPath() + c = nil + c.GetPath() +} + +func TestCodeownersError_GetSource(tt *testing.T) { + tt.Parallel() + c := &CodeownersError{} + c.GetSource() + c = nil + c.GetSource() +} + +func TestCodeownersError_GetSuggestion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeownersError{Suggestion: &zeroValue} + c.GetSuggestion() + c = &CodeownersError{} + c.GetSuggestion() + c = nil + c.GetSuggestion() +} + +func TestCodeownersErrors_GetErrors(tt *testing.T) { + tt.Parallel() + zeroValue := []*CodeownersError{} + c := &CodeownersErrors{Errors: zeroValue} + c.GetErrors() + c = &CodeownersErrors{} + c.GetErrors() + c = nil + c.GetErrors() +} + +func TestCodeQLDatabase_GetContentType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQLDatabase{ContentType: &zeroValue} + c.GetContentType() + c = &CodeQLDatabase{} + c.GetContentType() + c = nil + c.GetContentType() +} + +func TestCodeQLDatabase_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeQLDatabase{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CodeQLDatabase{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCodeQLDatabase_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CodeQLDatabase{ID: &zeroValue} + c.GetID() + c = &CodeQLDatabase{} + c.GetID() + c = nil + c.GetID() +} + +func TestCodeQLDatabase_GetLanguage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQLDatabase{Language: &zeroValue} + c.GetLanguage() + c = &CodeQLDatabase{} + c.GetLanguage() + c = nil + c.GetLanguage() +} + +func TestCodeQLDatabase_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQLDatabase{Name: &zeroValue} + c.GetName() + c = &CodeQLDatabase{} + c.GetName() + c = nil + c.GetName() +} + +func TestCodeQLDatabase_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CodeQLDatabase{Size: &zeroValue} + c.GetSize() + c = &CodeQLDatabase{} + c.GetSize() + c = nil + c.GetSize() +} + +func TestCodeQLDatabase_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeQLDatabase{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CodeQLDatabase{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCodeQLDatabase_GetUploader(tt *testing.T) { + tt.Parallel() + c := &CodeQLDatabase{} + c.GetUploader() + c = nil + c.GetUploader() +} + +func TestCodeQLDatabase_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQLDatabase{URL: &zeroValue} + c.GetURL() + c = &CodeQLDatabase{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCodeQualityFinding_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeQualityFinding{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CodeQualityFinding{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCodeQualityFinding_GetLocation(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFinding{} + c.GetLocation() + c = nil + c.GetLocation() +} + +func TestCodeQualityFinding_GetMessage(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFinding{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCodeQualityFinding_GetNumber(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFinding{} + c.GetNumber() + c = nil + c.GetNumber() +} + +func TestCodeQualityFinding_GetRule(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFinding{} + c.GetRule() + c = nil + c.GetRule() +} + +func TestCodeQualityFinding_GetState(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFinding{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodeQualityFinding_GetURL(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFinding{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCodeQualityFindingLocation_GetEndColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodeQualityFindingLocation{EndColumn: &zeroValue} + c.GetEndColumn() + c = &CodeQualityFindingLocation{} + c.GetEndColumn() + c = nil + c.GetEndColumn() +} + +func TestCodeQualityFindingLocation_GetEndLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodeQualityFindingLocation{EndLine: &zeroValue} + c.GetEndLine() + c = &CodeQualityFindingLocation{} + c.GetEndLine() + c = nil + c.GetEndLine() +} + +func TestCodeQualityFindingLocation_GetPath(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingLocation{} + c.GetPath() + c = nil + c.GetPath() +} + +func TestCodeQualityFindingLocation_GetStartColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodeQualityFindingLocation{StartColumn: &zeroValue} + c.GetStartColumn() + c = &CodeQualityFindingLocation{} + c.GetStartColumn() + c = nil + c.GetStartColumn() +} + +func TestCodeQualityFindingLocation_GetStartLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodeQualityFindingLocation{StartLine: &zeroValue} + c.GetStartLine() + c = &CodeQualityFindingLocation{} + c.GetStartLine() + c = nil + c.GetStartLine() +} + +func TestCodeQualityFindingMessage_GetMarkdown(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingMessage{} + c.GetMarkdown() + c = nil + c.GetMarkdown() +} + +func TestCodeQualityFindingMessage_GetText(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingMessage{} + c.GetText() + c = nil + c.GetText() +} + +func TestCodeQualityFindingRule_GetCategory(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingRule{} + c.GetCategory() + c = nil + c.GetCategory() +} + +func TestCodeQualityFindingRule_GetDescription(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingRule{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCodeQualityFindingRule_GetHelp(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualityFindingRule{Help: &zeroValue} + c.GetHelp() + c = &CodeQualityFindingRule{} + c.GetHelp() + c = nil + c.GetHelp() +} + +func TestCodeQualityFindingRule_GetID(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingRule{} + c.GetID() + c = nil + c.GetID() +} + +func TestCodeQualityFindingRule_GetSeverity(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingRule{} + c.GetSeverity() + c = nil + c.GetSeverity() +} + +func TestCodeQualityFindingRule_GetTitle(tt *testing.T) { + tt.Parallel() + c := &CodeQualityFindingRule{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCodeQualitySetupConfiguration_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CodeQualitySetupConfiguration{Languages: zeroValue} + c.GetLanguages() + c = &CodeQualitySetupConfiguration{} + c.GetLanguages() + c = nil + c.GetLanguages() +} + +func TestCodeQualitySetupConfiguration_GetRunnerLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{RunnerLabel: &zeroValue} + c.GetRunnerLabel() + c = &CodeQualitySetupConfiguration{} + c.GetRunnerLabel() + c = nil + c.GetRunnerLabel() +} + +func TestCodeQualitySetupConfiguration_GetRunnerType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{RunnerType: &zeroValue} + c.GetRunnerType() + c = &CodeQualitySetupConfiguration{} + c.GetRunnerType() + c = nil + c.GetRunnerType() +} + +func TestCodeQualitySetupConfiguration_GetSchedule(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{Schedule: &zeroValue} + c.GetSchedule() + c = &CodeQualitySetupConfiguration{} + c.GetSchedule() + c = nil + c.GetSchedule() +} + +func TestCodeQualitySetupConfiguration_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualitySetupConfiguration{State: &zeroValue} + c.GetState() + c = &CodeQualitySetupConfiguration{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodeQualitySetupConfiguration_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeQualitySetupConfiguration{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CodeQualitySetupConfiguration{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCodeQualityUpdateSetupRequest_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CodeQualityUpdateSetupRequest{Languages: zeroValue} + c.GetLanguages() + c = &CodeQualityUpdateSetupRequest{} + c.GetLanguages() + c = nil + c.GetLanguages() +} + +func TestCodeQualityUpdateSetupRequest_GetRunnerLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualityUpdateSetupRequest{RunnerLabel: &zeroValue} + c.GetRunnerLabel() + c = &CodeQualityUpdateSetupRequest{} + c.GetRunnerLabel() + c = nil + c.GetRunnerLabel() +} + +func TestCodeQualityUpdateSetupRequest_GetRunnerType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualityUpdateSetupRequest{RunnerType: &zeroValue} + c.GetRunnerType() + c = &CodeQualityUpdateSetupRequest{} + c.GetRunnerType() + c = nil + c.GetRunnerType() +} + +func TestCodeQualityUpdateSetupRequest_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualityUpdateSetupRequest{State: &zeroValue} + c.GetState() + c = &CodeQualityUpdateSetupRequest{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodeQualityUpdateSetupResponse_GetRunID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CodeQualityUpdateSetupResponse{RunID: &zeroValue} + c.GetRunID() + c = &CodeQualityUpdateSetupResponse{} + c.GetRunID() + c = nil + c.GetRunID() +} + +func TestCodeQualityUpdateSetupResponse_GetRunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeQualityUpdateSetupResponse{RunURL: &zeroValue} + c.GetRunURL() + c = &CodeQualityUpdateSetupResponse{} + c.GetRunURL() + c = nil + c.GetRunURL() +} + +func TestCodeResult_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeResult{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CodeResult{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCodeResult_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeResult{Name: &zeroValue} + c.GetName() + c = &CodeResult{} + c.GetName() + c = nil + c.GetName() +} + +func TestCodeResult_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeResult{Path: &zeroValue} + c.GetPath() + c = &CodeResult{} + c.GetPath() + c = nil + c.GetPath() +} + +func TestCodeResult_GetRepository(tt *testing.T) { + tt.Parallel() + c := &CodeResult{} + c.GetRepository() + c = nil + c.GetRepository() +} + +func TestCodeResult_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeResult{SHA: &zeroValue} + c.GetSHA() + c = &CodeResult{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCodeResult_GetTextMatches(tt *testing.T) { + tt.Parallel() + zeroValue := []*TextMatch{} + c := &CodeResult{TextMatches: zeroValue} + c.GetTextMatches() + c = &CodeResult{} + c.GetTextMatches() + c = nil + c.GetTextMatches() +} + +func TestCodeScanningAlertEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeScanningAlertEvent{Action: &zeroValue} + c.GetAction() + c = &CodeScanningAlertEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestCodeScanningAlertEvent_GetAlert(tt *testing.T) { + tt.Parallel() + c := &CodeScanningAlertEvent{} + c.GetAlert() + c = nil + c.GetAlert() +} + +func TestCodeScanningAlertEvent_GetCommitOID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeScanningAlertEvent{CommitOID: &zeroValue} + c.GetCommitOID() + c = &CodeScanningAlertEvent{} + c.GetCommitOID() + c = nil + c.GetCommitOID() +} + +func TestCodeScanningAlertEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CodeScanningAlertEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCodeScanningAlertEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CodeScanningAlertEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCodeScanningAlertEvent_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeScanningAlertEvent{Ref: &zeroValue} + c.GetRef() + c = &CodeScanningAlertEvent{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCodeScanningAlertEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CodeScanningAlertEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCodeScanningAlertEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CodeScanningAlertEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCodeScanningAlertState_GetDismissedComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeScanningAlertState{DismissedComment: &zeroValue} + c.GetDismissedComment() + c = &CodeScanningAlertState{} + c.GetDismissedComment() + c = nil + c.GetDismissedComment() +} + +func TestCodeScanningAlertState_GetDismissedReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeScanningAlertState{DismissedReason: &zeroValue} + c.GetDismissedReason() + c = &CodeScanningAlertState{} + c.GetDismissedReason() + c = nil + c.GetDismissedReason() +} + +func TestCodeScanningAlertState_GetState(tt *testing.T) { + tt.Parallel() + c := &CodeScanningAlertState{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodeScanningBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + c := &CodeScanningBranchRule{} + c.GetParameters() + c = nil + c.GetParameters() +} + +func TestCodeScanningDefaultSetupOptions_GetRunnerLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeScanningDefaultSetupOptions{RunnerLabel: &zeroValue} + c.GetRunnerLabel() + c = &CodeScanningDefaultSetupOptions{} + c.GetRunnerLabel() + c = nil + c.GetRunnerLabel() +} + +func TestCodeScanningDefaultSetupOptions_GetRunnerType(tt *testing.T) { + tt.Parallel() + c := &CodeScanningDefaultSetupOptions{} + c.GetRunnerType() + c = nil + c.GetRunnerType() +} + +func TestCodeScanningOptions_GetAllowAdvanced(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CodeScanningOptions{AllowAdvanced: &zeroValue} + c.GetAllowAdvanced() + c = &CodeScanningOptions{} + c.GetAllowAdvanced() + c = nil + c.GetAllowAdvanced() +} + +func TestCodeScanningRuleParameters_GetCodeScanningTools(tt *testing.T) { + tt.Parallel() + zeroValue := []*RuleCodeScanningTool{} + c := &CodeScanningRuleParameters{CodeScanningTools: zeroValue} + c.GetCodeScanningTools() + c = &CodeScanningRuleParameters{} + c.GetCodeScanningTools() + c = nil + c.GetCodeScanningTools() +} + +func TestCodeSearchResult_GetCodeResults(tt *testing.T) { + tt.Parallel() + zeroValue := []*CodeResult{} + c := &CodeSearchResult{CodeResults: zeroValue} + c.GetCodeResults() + c = &CodeSearchResult{} + c.GetCodeResults() + c = nil + c.GetCodeResults() +} + +func TestCodeSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CodeSearchResult{IncompleteResults: &zeroValue} + c.GetIncompleteResults() + c = &CodeSearchResult{} + c.GetIncompleteResults() + c = nil + c.GetIncompleteResults() +} + +func TestCodeSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodeSearchResult{Total: &zeroValue} + c.GetTotal() + c = &CodeSearchResult{} + c.GetTotal() + c = nil + c.GetTotal() +} + +func TestCodeSecurity_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurity{Status: &zeroValue} + c.GetStatus() + c = &CodeSecurity{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCodeSecurityConfiguration_GetAdvancedSecurity(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{AdvancedSecurity: &zeroValue} + c.GetAdvancedSecurity() + c = &CodeSecurityConfiguration{} + c.GetAdvancedSecurity() + c = nil + c.GetAdvancedSecurity() +} + +func TestCodeSecurityConfiguration_GetCodeScanningDefaultSetup(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{CodeScanningDefaultSetup: &zeroValue} + c.GetCodeScanningDefaultSetup() + c = &CodeSecurityConfiguration{} + c.GetCodeScanningDefaultSetup() + c = nil + c.GetCodeScanningDefaultSetup() +} + +func TestCodeSecurityConfiguration_GetCodeScanningDefaultSetupOptions(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetCodeScanningDefaultSetupOptions() + c = nil + c.GetCodeScanningDefaultSetupOptions() +} + +func TestCodeSecurityConfiguration_GetCodeScanningDelegatedAlertDismissal(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{CodeScanningDelegatedAlertDismissal: &zeroValue} + c.GetCodeScanningDelegatedAlertDismissal() + c = &CodeSecurityConfiguration{} + c.GetCodeScanningDelegatedAlertDismissal() + c = nil + c.GetCodeScanningDelegatedAlertDismissal() +} + +func TestCodeSecurityConfiguration_GetCodeScanningOptions(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetCodeScanningOptions() + c = nil + c.GetCodeScanningOptions() +} + +func TestCodeSecurityConfiguration_GetCodeSecurity(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{CodeSecurity: &zeroValue} + c.GetCodeSecurity() + c = &CodeSecurityConfiguration{} + c.GetCodeSecurity() + c = nil + c.GetCodeSecurity() +} + +func TestCodeSecurityConfiguration_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeSecurityConfiguration{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CodeSecurityConfiguration{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCodeSecurityConfiguration_GetDependabotAlerts(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{DependabotAlerts: &zeroValue} + c.GetDependabotAlerts() + c = &CodeSecurityConfiguration{} + c.GetDependabotAlerts() + c = nil + c.GetDependabotAlerts() +} + +func TestCodeSecurityConfiguration_GetDependabotDelegatedAlertDismissal(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{DependabotDelegatedAlertDismissal: &zeroValue} + c.GetDependabotDelegatedAlertDismissal() + c = &CodeSecurityConfiguration{} + c.GetDependabotDelegatedAlertDismissal() + c = nil + c.GetDependabotDelegatedAlertDismissal() +} + +func TestCodeSecurityConfiguration_GetDependabotSecurityUpdates(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{DependabotSecurityUpdates: &zeroValue} + c.GetDependabotSecurityUpdates() + c = &CodeSecurityConfiguration{} + c.GetDependabotSecurityUpdates() + c = nil + c.GetDependabotSecurityUpdates() +} + +func TestCodeSecurityConfiguration_GetDependencyGraph(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{DependencyGraph: &zeroValue} + c.GetDependencyGraph() + c = &CodeSecurityConfiguration{} + c.GetDependencyGraph() + c = nil + c.GetDependencyGraph() +} + +func TestCodeSecurityConfiguration_GetDependencyGraphAutosubmitAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{DependencyGraphAutosubmitAction: &zeroValue} + c.GetDependencyGraphAutosubmitAction() + c = &CodeSecurityConfiguration{} + c.GetDependencyGraphAutosubmitAction() + c = nil + c.GetDependencyGraphAutosubmitAction() +} + +func TestCodeSecurityConfiguration_GetDependencyGraphAutosubmitActionOptions(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetDependencyGraphAutosubmitActionOptions() + c = nil + c.GetDependencyGraphAutosubmitActionOptions() +} + +func TestCodeSecurityConfiguration_GetDescription(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCodeSecurityConfiguration_GetEnforcement(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{Enforcement: &zeroValue} + c.GetEnforcement() + c = &CodeSecurityConfiguration{} + c.GetEnforcement() + c = nil + c.GetEnforcement() +} + +func TestCodeSecurityConfiguration_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CodeSecurityConfiguration{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCodeSecurityConfiguration_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CodeSecurityConfiguration{ID: &zeroValue} + c.GetID() + c = &CodeSecurityConfiguration{} + c.GetID() + c = nil + c.GetID() +} + +func TestCodeSecurityConfiguration_GetName(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetName() + c = nil + c.GetName() +} + +func TestCodeSecurityConfiguration_GetPrivateVulnerabilityReporting(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{PrivateVulnerabilityReporting: &zeroValue} + c.GetPrivateVulnerabilityReporting() + c = &CodeSecurityConfiguration{} + c.GetPrivateVulnerabilityReporting() + c = nil + c.GetPrivateVulnerabilityReporting() +} + +func TestCodeSecurityConfiguration_GetSecretProtection(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretProtection: &zeroValue} + c.GetSecretProtection() + c = &CodeSecurityConfiguration{} + c.GetSecretProtection() + c = nil + c.GetSecretProtection() +} + +func TestCodeSecurityConfiguration_GetSecretScanning(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanning: &zeroValue} + c.GetSecretScanning() + c = &CodeSecurityConfiguration{} + c.GetSecretScanning() + c = nil + c.GetSecretScanning() +} + +func TestCodeSecurityConfiguration_GetSecretScanningDelegatedAlertDismissal(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningDelegatedAlertDismissal: &zeroValue} + c.GetSecretScanningDelegatedAlertDismissal() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningDelegatedAlertDismissal() + c = nil + c.GetSecretScanningDelegatedAlertDismissal() +} + +func TestCodeSecurityConfiguration_GetSecretScanningDelegatedBypass(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningDelegatedBypass: &zeroValue} + c.GetSecretScanningDelegatedBypass() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningDelegatedBypass() + c = nil + c.GetSecretScanningDelegatedBypass() +} + +func TestCodeSecurityConfiguration_GetSecretScanningDelegatedBypassOptions(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfiguration{} + c.GetSecretScanningDelegatedBypassOptions() + c = nil + c.GetSecretScanningDelegatedBypassOptions() +} + +func TestCodeSecurityConfiguration_GetSecretScanningExtendedMetadata(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningExtendedMetadata: &zeroValue} + c.GetSecretScanningExtendedMetadata() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningExtendedMetadata() + c = nil + c.GetSecretScanningExtendedMetadata() +} + +func TestCodeSecurityConfiguration_GetSecretScanningGenericSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningGenericSecrets: &zeroValue} + c.GetSecretScanningGenericSecrets() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningGenericSecrets() + c = nil + c.GetSecretScanningGenericSecrets() +} + +func TestCodeSecurityConfiguration_GetSecretScanningNonProviderPatterns(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningNonProviderPatterns: &zeroValue} + c.GetSecretScanningNonProviderPatterns() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningNonProviderPatterns() + c = nil + c.GetSecretScanningNonProviderPatterns() +} + +func TestCodeSecurityConfiguration_GetSecretScanningPushProtection(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningPushProtection: &zeroValue} + c.GetSecretScanningPushProtection() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningPushProtection() + c = nil + c.GetSecretScanningPushProtection() +} + +func TestCodeSecurityConfiguration_GetSecretScanningValidityChecks(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{SecretScanningValidityChecks: &zeroValue} + c.GetSecretScanningValidityChecks() + c = &CodeSecurityConfiguration{} + c.GetSecretScanningValidityChecks() + c = nil + c.GetSecretScanningValidityChecks() +} + +func TestCodeSecurityConfiguration_GetTargetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{TargetType: &zeroValue} + c.GetTargetType() + c = &CodeSecurityConfiguration{} + c.GetTargetType() + c = nil + c.GetTargetType() +} + +func TestCodeSecurityConfiguration_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodeSecurityConfiguration{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CodeSecurityConfiguration{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCodeSecurityConfiguration_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfiguration{URL: &zeroValue} + c.GetURL() + c = &CodeSecurityConfiguration{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCodeSecurityConfigurationWithDefaultForNewRepos_GetConfiguration(tt *testing.T) { + tt.Parallel() + c := &CodeSecurityConfigurationWithDefaultForNewRepos{} + c.GetConfiguration() + c = nil + c.GetConfiguration() +} + +func TestCodeSecurityConfigurationWithDefaultForNewRepos_GetDefaultForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodeSecurityConfigurationWithDefaultForNewRepos{DefaultForNewRepos: &zeroValue} + c.GetDefaultForNewRepos() + c = &CodeSecurityConfigurationWithDefaultForNewRepos{} + c.GetDefaultForNewRepos() + c = nil + c.GetDefaultForNewRepos() +} + +func TestCodespace_GetBillableOwner(tt *testing.T) { + tt.Parallel() + c := &Codespace{} + c.GetBillableOwner() + c = nil + c.GetBillableOwner() +} + +func TestCodespace_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &Codespace{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &Codespace{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCodespace_GetDevcontainerPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{DevcontainerPath: &zeroValue} + c.GetDevcontainerPath() + c = &Codespace{} + c.GetDevcontainerPath() + c = nil + c.GetDevcontainerPath() +} + +func TestCodespace_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{DisplayName: &zeroValue} + c.GetDisplayName() + c = &Codespace{} + c.GetDisplayName() + c = nil + c.GetDisplayName() +} + +func TestCodespace_GetEnvironmentID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{EnvironmentID: &zeroValue} + c.GetEnvironmentID() + c = &Codespace{} + c.GetEnvironmentID() + c = nil + c.GetEnvironmentID() +} + +func TestCodespace_GetGitStatus(tt *testing.T) { + tt.Parallel() + c := &Codespace{} + c.GetGitStatus() + c = nil + c.GetGitStatus() +} + +func TestCodespace_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &Codespace{ID: &zeroValue} + c.GetID() + c = &Codespace{} + c.GetID() + c = nil + c.GetID() +} + +func TestCodespace_GetIdleTimeoutMinutes(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &Codespace{IdleTimeoutMinutes: &zeroValue} + c.GetIdleTimeoutMinutes() + c = &Codespace{} + c.GetIdleTimeoutMinutes() + c = nil + c.GetIdleTimeoutMinutes() +} + +func TestCodespace_GetIdleTimeoutNotice(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{IdleTimeoutNotice: &zeroValue} + c.GetIdleTimeoutNotice() + c = &Codespace{} + c.GetIdleTimeoutNotice() + c = nil + c.GetIdleTimeoutNotice() +} + +func TestCodespace_GetLastKnownStopNotice(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{LastKnownStopNotice: &zeroValue} + c.GetLastKnownStopNotice() + c = &Codespace{} + c.GetLastKnownStopNotice() + c = nil + c.GetLastKnownStopNotice() +} + +func TestCodespace_GetLastUsedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &Codespace{LastUsedAt: &zeroValue} + c.GetLastUsedAt() + c = &Codespace{} + c.GetLastUsedAt() + c = nil + c.GetLastUsedAt() +} + +func TestCodespace_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{Location: &zeroValue} + c.GetLocation() + c = &Codespace{} + c.GetLocation() + c = nil + c.GetLocation() +} + +func TestCodespace_GetMachine(tt *testing.T) { + tt.Parallel() + c := &Codespace{} + c.GetMachine() + c = nil + c.GetMachine() +} + +func TestCodespace_GetMachinesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{MachinesURL: &zeroValue} + c.GetMachinesURL() + c = &Codespace{} + c.GetMachinesURL() + c = nil + c.GetMachinesURL() +} + +func TestCodespace_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{Name: &zeroValue} + c.GetName() + c = &Codespace{} + c.GetName() + c = nil + c.GetName() +} + +func TestCodespace_GetOwner(tt *testing.T) { + tt.Parallel() + c := &Codespace{} + c.GetOwner() + c = nil + c.GetOwner() +} + +func TestCodespace_GetPendingOperation(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &Codespace{PendingOperation: &zeroValue} + c.GetPendingOperation() + c = &Codespace{} + c.GetPendingOperation() + c = nil + c.GetPendingOperation() +} + +func TestCodespace_GetPendingOperationDisabledReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{PendingOperationDisabledReason: &zeroValue} + c.GetPendingOperationDisabledReason() + c = &Codespace{} + c.GetPendingOperationDisabledReason() + c = nil + c.GetPendingOperationDisabledReason() +} + +func TestCodespace_GetPrebuild(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &Codespace{Prebuild: &zeroValue} + c.GetPrebuild() + c = &Codespace{} + c.GetPrebuild() + c = nil + c.GetPrebuild() +} + +func TestCodespace_GetPullsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{PullsURL: &zeroValue} + c.GetPullsURL() + c = &Codespace{} + c.GetPullsURL() + c = nil + c.GetPullsURL() +} + +func TestCodespace_GetRecentFolders(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &Codespace{RecentFolders: zeroValue} + c.GetRecentFolders() + c = &Codespace{} + c.GetRecentFolders() + c = nil + c.GetRecentFolders() +} + +func TestCodespace_GetRepository(tt *testing.T) { + tt.Parallel() + c := &Codespace{} + c.GetRepository() + c = nil + c.GetRepository() +} + +func TestCodespace_GetRetentionExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &Codespace{RetentionExpiresAt: &zeroValue} + c.GetRetentionExpiresAt() + c = &Codespace{} + c.GetRetentionExpiresAt() + c = nil + c.GetRetentionExpiresAt() +} + +func TestCodespace_GetRetentionPeriodMinutes(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &Codespace{RetentionPeriodMinutes: &zeroValue} + c.GetRetentionPeriodMinutes() + c = &Codespace{} + c.GetRetentionPeriodMinutes() + c = nil + c.GetRetentionPeriodMinutes() +} + +func TestCodespace_GetRuntimeConstraints(tt *testing.T) { + tt.Parallel() + c := &Codespace{} + c.GetRuntimeConstraints() + c = nil + c.GetRuntimeConstraints() +} + +func TestCodespace_GetStartURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{StartURL: &zeroValue} + c.GetStartURL() + c = &Codespace{} + c.GetStartURL() + c = nil + c.GetStartURL() +} + +func TestCodespace_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{State: &zeroValue} + c.GetState() + c = &Codespace{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodespace_GetStopURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{StopURL: &zeroValue} + c.GetStopURL() + c = &Codespace{} + c.GetStopURL() + c = nil + c.GetStopURL() +} + +func TestCodespace_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &Codespace{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &Codespace{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCodespace_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{URL: &zeroValue} + c.GetURL() + c = &Codespace{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCodespace_GetWebURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Codespace{WebURL: &zeroValue} + c.GetWebURL() + c = &Codespace{} + c.GetWebURL() + c = nil + c.GetWebURL() +} + +func TestCodespaceCreateForUserOptions_GetClientIP(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{ClientIP: &zeroValue} + c.GetClientIP() + c = &CodespaceCreateForUserOptions{} + c.GetClientIP() + c = nil + c.GetClientIP() +} + +func TestCodespaceCreateForUserOptions_GetDevcontainerPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{DevcontainerPath: &zeroValue} + c.GetDevcontainerPath() + c = &CodespaceCreateForUserOptions{} + c.GetDevcontainerPath() + c = nil + c.GetDevcontainerPath() +} + +func TestCodespaceCreateForUserOptions_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{DisplayName: &zeroValue} + c.GetDisplayName() + c = &CodespaceCreateForUserOptions{} + c.GetDisplayName() + c = nil + c.GetDisplayName() +} + +func TestCodespaceCreateForUserOptions_GetGeo(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{Geo: &zeroValue} + c.GetGeo() + c = &CodespaceCreateForUserOptions{} + c.GetGeo() + c = nil + c.GetGeo() +} + +func TestCodespaceCreateForUserOptions_GetIdleTimeoutMinutes(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodespaceCreateForUserOptions{IdleTimeoutMinutes: &zeroValue} + c.GetIdleTimeoutMinutes() + c = &CodespaceCreateForUserOptions{} + c.GetIdleTimeoutMinutes() + c = nil + c.GetIdleTimeoutMinutes() +} + +func TestCodespaceCreateForUserOptions_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{Location: &zeroValue} + c.GetLocation() + c = &CodespaceCreateForUserOptions{} + c.GetLocation() + c = nil + c.GetLocation() +} + +func TestCodespaceCreateForUserOptions_GetMachine(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{Machine: &zeroValue} + c.GetMachine() + c = &CodespaceCreateForUserOptions{} + c.GetMachine() + c = nil + c.GetMachine() +} + +func TestCodespaceCreateForUserOptions_GetMultiRepoPermissionsOptOut(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CodespaceCreateForUserOptions{MultiRepoPermissionsOptOut: &zeroValue} + c.GetMultiRepoPermissionsOptOut() + c = &CodespaceCreateForUserOptions{} + c.GetMultiRepoPermissionsOptOut() + c = nil + c.GetMultiRepoPermissionsOptOut() +} + +func TestCodespaceCreateForUserOptions_GetPullRequest(tt *testing.T) { + tt.Parallel() + c := &CodespaceCreateForUserOptions{} + c.GetPullRequest() + c = nil + c.GetPullRequest() +} + +func TestCodespaceCreateForUserOptions_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{Ref: &zeroValue} + c.GetRef() + c = &CodespaceCreateForUserOptions{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCodespaceCreateForUserOptions_GetRepositoryID(tt *testing.T) { + tt.Parallel() + c := &CodespaceCreateForUserOptions{} + c.GetRepositoryID() + c = nil + c.GetRepositoryID() +} + +func TestCodespaceCreateForUserOptions_GetRetentionPeriodMinutes(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodespaceCreateForUserOptions{RetentionPeriodMinutes: &zeroValue} + c.GetRetentionPeriodMinutes() + c = &CodespaceCreateForUserOptions{} + c.GetRetentionPeriodMinutes() + c = nil + c.GetRetentionPeriodMinutes() +} + +func TestCodespaceCreateForUserOptions_GetWorkingDirectory(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceCreateForUserOptions{WorkingDirectory: &zeroValue} + c.GetWorkingDirectory() + c = &CodespaceCreateForUserOptions{} + c.GetWorkingDirectory() + c = nil + c.GetWorkingDirectory() +} + +func TestCodespaceDefaultAttributes_GetBillableOwner(tt *testing.T) { + tt.Parallel() + c := &CodespaceDefaultAttributes{} + c.GetBillableOwner() + c = nil + c.GetBillableOwner() +} + +func TestCodespaceDefaultAttributes_GetDefaults(tt *testing.T) { + tt.Parallel() + c := &CodespaceDefaultAttributes{} + c.GetDefaults() + c = nil + c.GetDefaults() +} + +func TestCodespaceDefaults_GetDevcontainerPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceDefaults{DevcontainerPath: &zeroValue} + c.GetDevcontainerPath() + c = &CodespaceDefaults{} + c.GetDevcontainerPath() + c = nil + c.GetDevcontainerPath() +} + +func TestCodespaceDefaults_GetLocation(tt *testing.T) { + tt.Parallel() + c := &CodespaceDefaults{} + c.GetLocation() + c = nil + c.GetLocation() +} + +func TestCodespaceExport_GetBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceExport{Branch: &zeroValue} + c.GetBranch() + c = &CodespaceExport{} + c.GetBranch() + c = nil + c.GetBranch() +} + +func TestCodespaceExport_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CodespaceExport{CompletedAt: &zeroValue} + c.GetCompletedAt() + c = &CodespaceExport{} + c.GetCompletedAt() + c = nil + c.GetCompletedAt() +} + +func TestCodespaceExport_GetExportURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceExport{ExportURL: &zeroValue} + c.GetExportURL() + c = &CodespaceExport{} + c.GetExportURL() + c = nil + c.GetExportURL() +} + +func TestCodespaceExport_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceExport{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CodespaceExport{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCodespaceExport_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceExport{ID: &zeroValue} + c.GetID() + c = &CodespaceExport{} + c.GetID() + c = nil + c.GetID() +} + +func TestCodespaceExport_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceExport{SHA: &zeroValue} + c.GetSHA() + c = &CodespaceExport{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCodespaceExport_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceExport{State: &zeroValue} + c.GetState() + c = &CodespaceExport{} + c.GetState() + c = nil + c.GetState() +} + +func TestCodespaceGetDefaultAttributesOptions_GetClientIP(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceGetDefaultAttributesOptions{ClientIP: &zeroValue} + c.GetClientIP() + c = &CodespaceGetDefaultAttributesOptions{} + c.GetClientIP() + c = nil + c.GetClientIP() +} + +func TestCodespaceGetDefaultAttributesOptions_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespaceGetDefaultAttributesOptions{Ref: &zeroValue} + c.GetRef() + c = &CodespaceGetDefaultAttributesOptions{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCodespacePermissions_GetAccepted(tt *testing.T) { + tt.Parallel() + c := &CodespacePermissions{} + c.GetAccepted() + c = nil + c.GetAccepted() +} + +func TestCodespacePullRequestOptions_GetPullRequestNumber(tt *testing.T) { + tt.Parallel() + c := &CodespacePullRequestOptions{} + c.GetPullRequestNumber() + c = nil + c.GetPullRequestNumber() +} + +func TestCodespacePullRequestOptions_GetRepositoryID(tt *testing.T) { + tt.Parallel() + c := &CodespacePullRequestOptions{} + c.GetRepositoryID() + c = nil + c.GetRepositoryID() +} + +func TestCodespacesGitStatus_GetAhead(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodespacesGitStatus{Ahead: &zeroValue} + c.GetAhead() + c = &CodespacesGitStatus{} + c.GetAhead() + c = nil + c.GetAhead() +} + +func TestCodespacesGitStatus_GetBehind(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodespacesGitStatus{Behind: &zeroValue} + c.GetBehind() + c = &CodespacesGitStatus{} + c.GetBehind() + c = nil + c.GetBehind() +} + +func TestCodespacesGitStatus_GetHasUncommittedChanges(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CodespacesGitStatus{HasUncommittedChanges: &zeroValue} + c.GetHasUncommittedChanges() + c = &CodespacesGitStatus{} + c.GetHasUncommittedChanges() + c = nil + c.GetHasUncommittedChanges() +} + +func TestCodespacesGitStatus_GetHasUnpushedChanges(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CodespacesGitStatus{HasUnpushedChanges: &zeroValue} + c.GetHasUnpushedChanges() + c = &CodespacesGitStatus{} + c.GetHasUnpushedChanges() + c = nil + c.GetHasUnpushedChanges() +} + +func TestCodespacesGitStatus_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespacesGitStatus{Ref: &zeroValue} + c.GetRef() + c = &CodespacesGitStatus{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCodespacesMachine_GetCPUs(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CodespacesMachine{CPUs: &zeroValue} + c.GetCPUs() + c = &CodespacesMachine{} + c.GetCPUs() + c = nil + c.GetCPUs() +} + +func TestCodespacesMachine_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespacesMachine{DisplayName: &zeroValue} + c.GetDisplayName() + c = &CodespacesMachine{} + c.GetDisplayName() + c = nil + c.GetDisplayName() +} + +func TestCodespacesMachine_GetMemoryInBytes(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CodespacesMachine{MemoryInBytes: &zeroValue} + c.GetMemoryInBytes() + c = &CodespacesMachine{} + c.GetMemoryInBytes() + c = nil + c.GetMemoryInBytes() +} + +func TestCodespacesMachine_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespacesMachine{Name: &zeroValue} + c.GetName() + c = &CodespacesMachine{} + c.GetName() + c = nil + c.GetName() +} + +func TestCodespacesMachine_GetOperatingSystem(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespacesMachine{OperatingSystem: &zeroValue} + c.GetOperatingSystem() + c = &CodespacesMachine{} + c.GetOperatingSystem() + c = nil + c.GetOperatingSystem() +} + +func TestCodespacesMachine_GetPrebuildAvailability(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CodespacesMachine{PrebuildAvailability: &zeroValue} + c.GetPrebuildAvailability() + c = &CodespacesMachine{} + c.GetPrebuildAvailability() + c = nil + c.GetPrebuildAvailability() +} + +func TestCodespacesMachine_GetStorageInBytes(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CodespacesMachine{StorageInBytes: &zeroValue} + c.GetStorageInBytes() + c = &CodespacesMachine{} + c.GetStorageInBytes() + c = nil + c.GetStorageInBytes() +} + +func TestCodespacesMachines_GetMachines(tt *testing.T) { + tt.Parallel() + zeroValue := []*CodespacesMachine{} + c := &CodespacesMachines{Machines: zeroValue} + c.GetMachines() + c = &CodespacesMachines{} + c.GetMachines() + c = nil + c.GetMachines() +} + +func TestCodespacesMachines_GetTotalCount(tt *testing.T) { + tt.Parallel() + c := &CodespacesMachines{} + c.GetTotalCount() + c = nil + c.GetTotalCount() +} + +func TestCodespacesOrgAccessControlRequest_GetSelectedUsernames(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CodespacesOrgAccessControlRequest{SelectedUsernames: zeroValue} + c.GetSelectedUsernames() + c = &CodespacesOrgAccessControlRequest{} + c.GetSelectedUsernames() + c = nil + c.GetSelectedUsernames() +} + +func TestCodespacesOrgAccessControlRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + c := &CodespacesOrgAccessControlRequest{} + c.GetVisibility() + c = nil + c.GetVisibility() +} + +func TestCodespacesRuntimeConstraints_GetAllowedPortPrivacySettings(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CodespacesRuntimeConstraints{AllowedPortPrivacySettings: zeroValue} + c.GetAllowedPortPrivacySettings() + c = &CodespacesRuntimeConstraints{} + c.GetAllowedPortPrivacySettings() + c = nil + c.GetAllowedPortPrivacySettings() +} + +func TestCollaboratorInvitation_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CollaboratorInvitation{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CollaboratorInvitation{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCollaboratorInvitation_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CollaboratorInvitation{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CollaboratorInvitation{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCollaboratorInvitation_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CollaboratorInvitation{ID: &zeroValue} + c.GetID() + c = &CollaboratorInvitation{} + c.GetID() + c = nil + c.GetID() +} + +func TestCollaboratorInvitation_GetInvitee(tt *testing.T) { + tt.Parallel() + c := &CollaboratorInvitation{} + c.GetInvitee() + c = nil + c.GetInvitee() +} + +func TestCollaboratorInvitation_GetInviter(tt *testing.T) { + tt.Parallel() + c := &CollaboratorInvitation{} + c.GetInviter() + c = nil + c.GetInviter() +} + +func TestCollaboratorInvitation_GetPermissions(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CollaboratorInvitation{Permissions: &zeroValue} + c.GetPermissions() + c = &CollaboratorInvitation{} + c.GetPermissions() + c = nil + c.GetPermissions() +} + +func TestCollaboratorInvitation_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CollaboratorInvitation{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCollaboratorInvitation_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CollaboratorInvitation{URL: &zeroValue} + c.GetURL() + c = &CollaboratorInvitation{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCombinedStatus_GetCommitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CombinedStatus{CommitURL: &zeroValue} + c.GetCommitURL() + c = &CombinedStatus{} + c.GetCommitURL() + c = nil + c.GetCommitURL() +} + +func TestCombinedStatus_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CombinedStatus{Name: &zeroValue} + c.GetName() + c = &CombinedStatus{} + c.GetName() + c = nil + c.GetName() +} + +func TestCombinedStatus_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CombinedStatus{RepositoryURL: &zeroValue} + c.GetRepositoryURL() + c = &CombinedStatus{} + c.GetRepositoryURL() + c = nil + c.GetRepositoryURL() +} + +func TestCombinedStatus_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CombinedStatus{SHA: &zeroValue} + c.GetSHA() + c = &CombinedStatus{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCombinedStatus_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CombinedStatus{State: &zeroValue} + c.GetState() + c = &CombinedStatus{} + c.GetState() + c = nil + c.GetState() +} + +func TestCombinedStatus_GetStatuses(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepoStatus{} + c := &CombinedStatus{Statuses: zeroValue} + c.GetStatuses() + c = &CombinedStatus{} + c.GetStatuses() + c = nil + c.GetStatuses() +} + +func TestCombinedStatus_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CombinedStatus{TotalCount: &zeroValue} + c.GetTotalCount() + c = &CombinedStatus{} + c.GetTotalCount() + c = nil + c.GetTotalCount() +} + +func TestComment_GetBody(tt *testing.T) { + tt.Parallel() + c := &Comment{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestComment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &Comment{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &Comment{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCommentDiscussion_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommentDiscussion{AuthorAssociation: &zeroValue} + c.GetAuthorAssociation() + c = &CommentDiscussion{} + c.GetAuthorAssociation() + c = nil + c.GetAuthorAssociation() +} + +func TestCommentDiscussion_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommentDiscussion{Body: &zeroValue} + c.GetBody() + c = &CommentDiscussion{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCommentDiscussion_GetChildCommentCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommentDiscussion{ChildCommentCount: &zeroValue} + c.GetChildCommentCount() + c = &CommentDiscussion{} + c.GetChildCommentCount() + c = nil + c.GetChildCommentCount() +} + +func TestCommentDiscussion_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CommentDiscussion{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CommentDiscussion{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCommentDiscussion_GetDiscussionID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CommentDiscussion{DiscussionID: &zeroValue} + c.GetDiscussionID() + c = &CommentDiscussion{} + c.GetDiscussionID() + c = nil + c.GetDiscussionID() +} + +func TestCommentDiscussion_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommentDiscussion{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CommentDiscussion{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCommentDiscussion_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CommentDiscussion{ID: &zeroValue} + c.GetID() + c = &CommentDiscussion{} + c.GetID() + c = nil + c.GetID() +} + +func TestCommentDiscussion_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommentDiscussion{NodeID: &zeroValue} + c.GetNodeID() + c = &CommentDiscussion{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestCommentDiscussion_GetParentID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CommentDiscussion{ParentID: &zeroValue} + c.GetParentID() + c = &CommentDiscussion{} + c.GetParentID() + c = nil + c.GetParentID() +} + +func TestCommentDiscussion_GetReactions(tt *testing.T) { + tt.Parallel() + c := &CommentDiscussion{} + c.GetReactions() + c = nil + c.GetReactions() +} + +func TestCommentDiscussion_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommentDiscussion{RepositoryURL: &zeroValue} + c.GetRepositoryURL() + c = &CommentDiscussion{} + c.GetRepositoryURL() + c = nil + c.GetRepositoryURL() +} + +func TestCommentDiscussion_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CommentDiscussion{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CommentDiscussion{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCommentDiscussion_GetUser(tt *testing.T) { + tt.Parallel() + c := &CommentDiscussion{} + c.GetUser() + c = nil + c.GetUser() +} + +func TestCommentStats_GetTotalCommitComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommentStats{TotalCommitComments: &zeroValue} + c.GetTotalCommitComments() + c = &CommentStats{} + c.GetTotalCommitComments() + c = nil + c.GetTotalCommitComments() +} + +func TestCommentStats_GetTotalGistComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommentStats{TotalGistComments: &zeroValue} + c.GetTotalGistComments() + c = &CommentStats{} + c.GetTotalGistComments() + c = nil + c.GetTotalGistComments() +} + +func TestCommentStats_GetTotalIssueComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommentStats{TotalIssueComments: &zeroValue} + c.GetTotalIssueComments() + c = &CommentStats{} + c.GetTotalIssueComments() + c = nil + c.GetTotalIssueComments() +} + +func TestCommentStats_GetTotalPullRequestComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommentStats{TotalPullRequestComments: &zeroValue} + c.GetTotalPullRequestComments() + c = &CommentStats{} + c.GetTotalPullRequestComments() + c = nil + c.GetTotalPullRequestComments() +} + +func TestCommit_GetAuthor(tt *testing.T) { + tt.Parallel() + c := &Commit{} + c.GetAuthor() + c = nil + c.GetAuthor() +} + +func TestCommit_GetCommentCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &Commit{CommentCount: &zeroValue} + c.GetCommentCount() + c = &Commit{} + c.GetCommentCount() + c = nil + c.GetCommentCount() +} + +func TestCommit_GetCommitter(tt *testing.T) { + tt.Parallel() + c := &Commit{} + c.GetCommitter() + c = nil + c.GetCommitter() +} + +func TestCommit_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Commit{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &Commit{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCommit_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Commit{Message: &zeroValue} + c.GetMessage() + c = &Commit{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCommit_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Commit{NodeID: &zeroValue} + c.GetNodeID() + c = &Commit{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestCommit_GetParents(tt *testing.T) { + tt.Parallel() + zeroValue := []*Commit{} + c := &Commit{Parents: zeroValue} + c.GetParents() + c = &Commit{} + c.GetParents() + c = nil + c.GetParents() +} + +func TestCommit_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Commit{SHA: &zeroValue} + c.GetSHA() + c = &Commit{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCommit_GetTree(tt *testing.T) { + tt.Parallel() + c := &Commit{} + c.GetTree() + c = nil + c.GetTree() +} + +func TestCommit_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Commit{URL: &zeroValue} + c.GetURL() + c = &Commit{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCommit_GetVerification(tt *testing.T) { + tt.Parallel() + c := &Commit{} + c.GetVerification() + c = nil + c.GetVerification() +} + +func TestCommitAuthor_GetDate(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CommitAuthor{Date: &zeroValue} + c.GetDate() + c = &CommitAuthor{} + c.GetDate() + c = nil + c.GetDate() +} + +func TestCommitAuthor_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitAuthor{Email: &zeroValue} + c.GetEmail() + c = &CommitAuthor{} + c.GetEmail() + c = nil + c.GetEmail() +} + +func TestCommitAuthor_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitAuthor{Login: &zeroValue} + c.GetLogin() + c = &CommitAuthor{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestCommitAuthor_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitAuthor{Name: &zeroValue} + c.GetName() + c = &CommitAuthor{} + c.GetName() + c = nil + c.GetName() +} + +func TestCommitCommentEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitCommentEvent{Action: &zeroValue} + c.GetAction() + c = &CommitCommentEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestCommitCommentEvent_GetComment(tt *testing.T) { + tt.Parallel() + c := &CommitCommentEvent{} + c.GetComment() + c = nil + c.GetComment() +} + +func TestCommitCommentEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CommitCommentEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCommitCommentEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CommitCommentEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCommitCommentEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CommitCommentEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCommitCommentEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CommitCommentEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCommitFile_GetAdditions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitFile{Additions: &zeroValue} + c.GetAdditions() + c = &CommitFile{} + c.GetAdditions() + c = nil + c.GetAdditions() +} + +func TestCommitFile_GetBlobURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{BlobURL: &zeroValue} + c.GetBlobURL() + c = &CommitFile{} + c.GetBlobURL() + c = nil + c.GetBlobURL() +} + +func TestCommitFile_GetChanges(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitFile{Changes: &zeroValue} + c.GetChanges() + c = &CommitFile{} + c.GetChanges() + c = nil + c.GetChanges() +} + +func TestCommitFile_GetContentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{ContentsURL: &zeroValue} + c.GetContentsURL() + c = &CommitFile{} + c.GetContentsURL() + c = nil + c.GetContentsURL() +} + +func TestCommitFile_GetDeletions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitFile{Deletions: &zeroValue} + c.GetDeletions() + c = &CommitFile{} + c.GetDeletions() + c = nil + c.GetDeletions() +} + +func TestCommitFile_GetFilename(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{Filename: &zeroValue} + c.GetFilename() + c = &CommitFile{} + c.GetFilename() + c = nil + c.GetFilename() +} + +func TestCommitFile_GetPatch(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{Patch: &zeroValue} + c.GetPatch() + c = &CommitFile{} + c.GetPatch() + c = nil + c.GetPatch() +} + +func TestCommitFile_GetPreviousFilename(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{PreviousFilename: &zeroValue} + c.GetPreviousFilename() + c = &CommitFile{} + c.GetPreviousFilename() + c = nil + c.GetPreviousFilename() +} + +func TestCommitFile_GetRawURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{RawURL: &zeroValue} + c.GetRawURL() + c = &CommitFile{} + c.GetRawURL() + c = nil + c.GetRawURL() +} + +func TestCommitFile_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{SHA: &zeroValue} + c.GetSHA() + c = &CommitFile{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCommitFile_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitFile{Status: &zeroValue} + c.GetStatus() + c = &CommitFile{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCommitResult_GetAuthor(tt *testing.T) { + tt.Parallel() + c := &CommitResult{} + c.GetAuthor() + c = nil + c.GetAuthor() +} + +func TestCommitResult_GetCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitResult{CommentsURL: &zeroValue} + c.GetCommentsURL() + c = &CommitResult{} + c.GetCommentsURL() + c = nil + c.GetCommentsURL() +} + +func TestCommitResult_GetCommit(tt *testing.T) { + tt.Parallel() + c := &CommitResult{} + c.GetCommit() + c = nil + c.GetCommit() +} + +func TestCommitResult_GetCommitter(tt *testing.T) { + tt.Parallel() + c := &CommitResult{} + c.GetCommitter() + c = nil + c.GetCommitter() +} + +func TestCommitResult_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitResult{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CommitResult{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCommitResult_GetParents(tt *testing.T) { + tt.Parallel() + zeroValue := []*Commit{} + c := &CommitResult{Parents: zeroValue} + c.GetParents() + c = &CommitResult{} + c.GetParents() + c = nil + c.GetParents() +} + +func TestCommitResult_GetRepository(tt *testing.T) { + tt.Parallel() + c := &CommitResult{} + c.GetRepository() + c = nil + c.GetRepository() +} + +func TestCommitResult_GetScore(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CommitResult{Score: &zeroValue} + c.GetScore() + c = &CommitResult{} + c.GetScore() + c = nil + c.GetScore() +} + +func TestCommitResult_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitResult{SHA: &zeroValue} + c.GetSHA() + c = &CommitResult{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCommitResult_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitResult{URL: &zeroValue} + c.GetURL() + c = &CommitResult{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCommitsComparison_GetAheadBy(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitsComparison{AheadBy: &zeroValue} + c.GetAheadBy() + c = &CommitsComparison{} + c.GetAheadBy() + c = nil + c.GetAheadBy() +} + +func TestCommitsComparison_GetBaseCommit(tt *testing.T) { + tt.Parallel() + c := &CommitsComparison{} + c.GetBaseCommit() + c = nil + c.GetBaseCommit() +} + +func TestCommitsComparison_GetBehindBy(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitsComparison{BehindBy: &zeroValue} + c.GetBehindBy() + c = &CommitsComparison{} + c.GetBehindBy() + c = nil + c.GetBehindBy() +} + +func TestCommitsComparison_GetCommits(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryCommit{} + c := &CommitsComparison{Commits: zeroValue} + c.GetCommits() + c = &CommitsComparison{} + c.GetCommits() + c = nil + c.GetCommits() +} + +func TestCommitsComparison_GetDiffURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitsComparison{DiffURL: &zeroValue} + c.GetDiffURL() + c = &CommitsComparison{} + c.GetDiffURL() + c = nil + c.GetDiffURL() +} + +func TestCommitsComparison_GetFiles(tt *testing.T) { + tt.Parallel() + zeroValue := []*CommitFile{} + c := &CommitsComparison{Files: zeroValue} + c.GetFiles() + c = &CommitsComparison{} + c.GetFiles() + c = nil + c.GetFiles() +} + +func TestCommitsComparison_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitsComparison{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &CommitsComparison{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestCommitsComparison_GetMergeBaseCommit(tt *testing.T) { + tt.Parallel() + c := &CommitsComparison{} + c.GetMergeBaseCommit() + c = nil + c.GetMergeBaseCommit() +} + +func TestCommitsComparison_GetPatchURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitsComparison{PatchURL: &zeroValue} + c.GetPatchURL() + c = &CommitsComparison{} + c.GetPatchURL() + c = nil + c.GetPatchURL() +} + +func TestCommitsComparison_GetPermalinkURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitsComparison{PermalinkURL: &zeroValue} + c.GetPermalinkURL() + c = &CommitsComparison{} + c.GetPermalinkURL() + c = nil + c.GetPermalinkURL() +} + +func TestCommitsComparison_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitsComparison{Status: &zeroValue} + c.GetStatus() + c = &CommitsComparison{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCommitsComparison_GetTotalCommits(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitsComparison{TotalCommits: &zeroValue} + c.GetTotalCommits() + c = &CommitsComparison{} + c.GetTotalCommits() + c = nil + c.GetTotalCommits() +} + +func TestCommitsComparison_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommitsComparison{URL: &zeroValue} + c.GetURL() + c = &CommitsComparison{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCommitsListOptions_GetAuthor(tt *testing.T) { + tt.Parallel() + c := &CommitsListOptions{} + c.GetAuthor() + c = nil + c.GetAuthor() +} + +func TestCommitsListOptions_GetPath(tt *testing.T) { + tt.Parallel() + c := &CommitsListOptions{} + c.GetPath() + c = nil + c.GetPath() +} + +func TestCommitsListOptions_GetSHA(tt *testing.T) { + tt.Parallel() + c := &CommitsListOptions{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCommitsListOptions_GetSince(tt *testing.T) { + tt.Parallel() + c := &CommitsListOptions{} + c.GetSince() + c = nil + c.GetSince() +} + +func TestCommitsListOptions_GetUntil(tt *testing.T) { + tt.Parallel() + c := &CommitsListOptions{} + c.GetUntil() + c = nil + c.GetUntil() +} + +func TestCommitsSearchResult_GetCommits(tt *testing.T) { + tt.Parallel() + zeroValue := []*CommitResult{} + c := &CommitsSearchResult{Commits: zeroValue} + c.GetCommits() + c = &CommitsSearchResult{} + c.GetCommits() + c = nil + c.GetCommits() +} + +func TestCommitsSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CommitsSearchResult{IncompleteResults: &zeroValue} + c.GetIncompleteResults() + c = &CommitsSearchResult{} + c.GetIncompleteResults() + c = nil + c.GetIncompleteResults() +} + +func TestCommitsSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitsSearchResult{Total: &zeroValue} + c.GetTotal() + c = &CommitsSearchResult{} + c.GetTotal() + c = nil + c.GetTotal() +} + +func TestCommitStats_GetAdditions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitStats{Additions: &zeroValue} + c.GetAdditions() + c = &CommitStats{} + c.GetAdditions() + c = nil + c.GetAdditions() +} + +func TestCommitStats_GetDeletions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitStats{Deletions: &zeroValue} + c.GetDeletions() + c = &CommitStats{} + c.GetDeletions() + c = nil + c.GetDeletions() +} + +func TestCommitStats_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommitStats{Total: &zeroValue} + c.GetTotal() + c = &CommitStats{} + c.GetTotal() + c = nil + c.GetTotal() +} + +func TestCommunityHealthFiles_GetCodeOfConduct(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetCodeOfConduct() + c = nil + c.GetCodeOfConduct() +} + +func TestCommunityHealthFiles_GetCodeOfConductFile(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetCodeOfConductFile() + c = nil + c.GetCodeOfConductFile() +} + +func TestCommunityHealthFiles_GetContributing(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetContributing() + c = nil + c.GetContributing() +} + +func TestCommunityHealthFiles_GetIssueTemplate(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetIssueTemplate() + c = nil + c.GetIssueTemplate() +} + +func TestCommunityHealthFiles_GetLicense(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetLicense() + c = nil + c.GetLicense() +} + +func TestCommunityHealthFiles_GetPullRequestTemplate(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetPullRequestTemplate() + c = nil + c.GetPullRequestTemplate() +} + +func TestCommunityHealthFiles_GetReadme(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthFiles{} + c.GetReadme() + c = nil + c.GetReadme() +} + +func TestCommunityHealthMetrics_GetContentReportsEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CommunityHealthMetrics{ContentReportsEnabled: &zeroValue} + c.GetContentReportsEnabled() + c = &CommunityHealthMetrics{} + c.GetContentReportsEnabled() + c = nil + c.GetContentReportsEnabled() +} + +func TestCommunityHealthMetrics_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommunityHealthMetrics{Description: &zeroValue} + c.GetDescription() + c = &CommunityHealthMetrics{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCommunityHealthMetrics_GetDocumentation(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CommunityHealthMetrics{Documentation: &zeroValue} + c.GetDocumentation() + c = &CommunityHealthMetrics{} + c.GetDocumentation() + c = nil + c.GetDocumentation() +} + +func TestCommunityHealthMetrics_GetFiles(tt *testing.T) { + tt.Parallel() + c := &CommunityHealthMetrics{} + c.GetFiles() + c = nil + c.GetFiles() +} + +func TestCommunityHealthMetrics_GetHealthPercentage(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CommunityHealthMetrics{HealthPercentage: &zeroValue} + c.GetHealthPercentage() + c = &CommunityHealthMetrics{} + c.GetHealthPercentage() + c = nil + c.GetHealthPercentage() +} + +func TestCommunityHealthMetrics_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CommunityHealthMetrics{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CommunityHealthMetrics{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestConfigApplyEvents_GetNodes(tt *testing.T) { + tt.Parallel() + zeroValue := []*ConfigApplyEventsNode{} + c := &ConfigApplyEvents{Nodes: zeroValue} + c.GetNodes() + c = &ConfigApplyEvents{} + c.GetNodes() + c = nil + c.GetNodes() +} + +func TestConfigApplyEventsNode_GetEvents(tt *testing.T) { + tt.Parallel() + zeroValue := []*ConfigApplyEventsNodeEvent{} + c := &ConfigApplyEventsNode{Events: zeroValue} + c.GetEvents() + c = &ConfigApplyEventsNode{} + c.GetEvents() + c = nil + c.GetEvents() +} + +func TestConfigApplyEventsNode_GetLastRequestID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNode{LastRequestID: &zeroValue} + c.GetLastRequestID() + c = &ConfigApplyEventsNode{} + c.GetLastRequestID() + c = nil + c.GetLastRequestID() +} + +func TestConfigApplyEventsNode_GetNode(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNode{Node: &zeroValue} + c.GetNode() + c = &ConfigApplyEventsNode{} + c.GetNode() + c = nil + c.GetNode() +} + +func TestConfigApplyEventsNodeEvent_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{Body: &zeroValue} + c.GetBody() + c = &ConfigApplyEventsNodeEvent{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestConfigApplyEventsNodeEvent_GetConfigRunID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{ConfigRunID: &zeroValue} + c.GetConfigRunID() + c = &ConfigApplyEventsNodeEvent{} + c.GetConfigRunID() + c = nil + c.GetConfigRunID() +} + +func TestConfigApplyEventsNodeEvent_GetEventName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{EventName: &zeroValue} + c.GetEventName() + c = &ConfigApplyEventsNodeEvent{} + c.GetEventName() + c = nil + c.GetEventName() +} + +func TestConfigApplyEventsNodeEvent_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{Hostname: &zeroValue} + c.GetHostname() + c = &ConfigApplyEventsNodeEvent{} + c.GetHostname() + c = nil + c.GetHostname() +} + +func TestConfigApplyEventsNodeEvent_GetSeverityText(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{SeverityText: &zeroValue} + c.GetSeverityText() + c = &ConfigApplyEventsNodeEvent{} + c.GetSeverityText() + c = nil + c.GetSeverityText() +} + +func TestConfigApplyEventsNodeEvent_GetSpanDepth(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigApplyEventsNodeEvent{SpanDepth: &zeroValue} + c.GetSpanDepth() + c = &ConfigApplyEventsNodeEvent{} + c.GetSpanDepth() + c = nil + c.GetSpanDepth() +} + +func TestConfigApplyEventsNodeEvent_GetSpanID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{SpanID: &zeroValue} + c.GetSpanID() + c = &ConfigApplyEventsNodeEvent{} + c.GetSpanID() + c = nil + c.GetSpanID() +} + +func TestConfigApplyEventsNodeEvent_GetSpanParentID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &ConfigApplyEventsNodeEvent{SpanParentID: &zeroValue} + c.GetSpanParentID() + c = &ConfigApplyEventsNodeEvent{} + c.GetSpanParentID() + c = nil + c.GetSpanParentID() +} + +func TestConfigApplyEventsNodeEvent_GetTimestamp(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &ConfigApplyEventsNodeEvent{Timestamp: &zeroValue} + c.GetTimestamp() + c = &ConfigApplyEventsNodeEvent{} + c.GetTimestamp() + c = nil + c.GetTimestamp() +} + +func TestConfigApplyEventsNodeEvent_GetTopology(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{Topology: &zeroValue} + c.GetTopology() + c = &ConfigApplyEventsNodeEvent{} + c.GetTopology() + c = nil + c.GetTopology() +} + +func TestConfigApplyEventsNodeEvent_GetTraceID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsNodeEvent{TraceID: &zeroValue} + c.GetTraceID() + c = &ConfigApplyEventsNodeEvent{} + c.GetTraceID() + c = nil + c.GetTraceID() +} + +func TestConfigApplyEventsOptions_GetLastRequestID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyEventsOptions{LastRequestID: &zeroValue} + c.GetLastRequestID() + c = &ConfigApplyEventsOptions{} + c.GetLastRequestID() + c = nil + c.GetLastRequestID() +} + +func TestConfigApplyOptions_GetRunID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyOptions{RunID: &zeroValue} + c.GetRunID() + c = &ConfigApplyOptions{} + c.GetRunID() + c = nil + c.GetRunID() +} + +func TestConfigApplyStatus_GetNodes(tt *testing.T) { + tt.Parallel() + zeroValue := []*ConfigApplyStatusNode{} + c := &ConfigApplyStatus{Nodes: zeroValue} + c.GetNodes() + c = &ConfigApplyStatus{} + c.GetNodes() + c = nil + c.GetNodes() +} + +func TestConfigApplyStatus_GetRunning(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigApplyStatus{Running: &zeroValue} + c.GetRunning() + c = &ConfigApplyStatus{} + c.GetRunning() + c = nil + c.GetRunning() +} + +func TestConfigApplyStatus_GetSuccessful(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigApplyStatus{Successful: &zeroValue} + c.GetSuccessful() + c = &ConfigApplyStatus{} + c.GetSuccessful() + c = nil + c.GetSuccessful() +} + +func TestConfigApplyStatusNode_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyStatusNode{Hostname: &zeroValue} + c.GetHostname() + c = &ConfigApplyStatusNode{} + c.GetHostname() + c = nil + c.GetHostname() +} + +func TestConfigApplyStatusNode_GetRunID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigApplyStatusNode{RunID: &zeroValue} + c.GetRunID() + c = &ConfigApplyStatusNode{} + c.GetRunID() + c = nil + c.GetRunID() +} + +func TestConfigApplyStatusNode_GetRunning(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigApplyStatusNode{Running: &zeroValue} + c.GetRunning() + c = &ConfigApplyStatusNode{} + c.GetRunning() + c = nil + c.GetRunning() +} + +func TestConfigApplyStatusNode_GetSuccessful(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigApplyStatusNode{Successful: &zeroValue} + c.GetSuccessful() + c = &ConfigApplyStatusNode{} + c.GetSuccessful() + c = nil + c.GetSuccessful() +} + +func TestConfigSettings_GetAdminPassword(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{AdminPassword: &zeroValue} + c.GetAdminPassword() + c = &ConfigSettings{} + c.GetAdminPassword() + c = nil + c.GetAdminPassword() +} + +func TestConfigSettings_GetAssets(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{Assets: &zeroValue} + c.GetAssets() + c = &ConfigSettings{} + c.GetAssets() + c = nil + c.GetAssets() +} + +func TestConfigSettings_GetAuthMode(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{AuthMode: &zeroValue} + c.GetAuthMode() + c = &ConfigSettings{} + c.GetAuthMode() + c = nil + c.GetAuthMode() +} + +func TestConfigSettings_GetAvatar(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetAvatar() + c = nil + c.GetAvatar() +} + +func TestConfigSettings_GetCAS(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetCAS() + c = nil + c.GetCAS() +} + +func TestConfigSettings_GetCollectd(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetCollectd() + c = nil + c.GetCollectd() +} + +func TestConfigSettings_GetConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &ConfigSettings{ConfigurationID: &zeroValue} + c.GetConfigurationID() + c = &ConfigSettings{} + c.GetConfigurationID() + c = nil + c.GetConfigurationID() +} + +func TestConfigSettings_GetConfigurationRunCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigSettings{ConfigurationRunCount: &zeroValue} + c.GetConfigurationRunCount() + c = &ConfigSettings{} + c.GetConfigurationRunCount() + c = nil + c.GetConfigurationRunCount() +} + +func TestConfigSettings_GetCustomer(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetCustomer() + c = nil + c.GetCustomer() +} + +func TestConfigSettings_GetExpireSessions(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettings{ExpireSessions: &zeroValue} + c.GetExpireSessions() + c = &ConfigSettings{} + c.GetExpireSessions() + c = nil + c.GetExpireSessions() +} + +func TestConfigSettings_GetGithubHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{GithubHostname: &zeroValue} + c.GetGithubHostname() + c = &ConfigSettings{} + c.GetGithubHostname() + c = nil + c.GetGithubHostname() +} + +func TestConfigSettings_GetGithubOAuth(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetGithubOAuth() + c = nil + c.GetGithubOAuth() +} + +func TestConfigSettings_GetGithubSSL(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetGithubSSL() + c = nil + c.GetGithubSSL() +} + +func TestConfigSettings_GetHTTPProxy(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{HTTPProxy: &zeroValue} + c.GetHTTPProxy() + c = &ConfigSettings{} + c.GetHTTPProxy() + c = nil + c.GetHTTPProxy() +} + +func TestConfigSettings_GetIdenticonsHost(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{IdenticonsHost: &zeroValue} + c.GetIdenticonsHost() + c = &ConfigSettings{} + c.GetIdenticonsHost() + c = nil + c.GetIdenticonsHost() +} + +func TestConfigSettings_GetLDAP(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetLDAP() + c = nil + c.GetLDAP() +} + +func TestConfigSettings_GetLicense(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetLicense() + c = nil + c.GetLicense() +} + +func TestConfigSettings_GetLoadBalancer(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{LoadBalancer: &zeroValue} + c.GetLoadBalancer() + c = &ConfigSettings{} + c.GetLoadBalancer() + c = nil + c.GetLoadBalancer() +} + +func TestConfigSettings_GetMapping(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetMapping() + c = nil + c.GetMapping() +} + +func TestConfigSettings_GetNTP(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetNTP() + c = nil + c.GetNTP() +} + +func TestConfigSettings_GetPages(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetPages() + c = nil + c.GetPages() +} + +func TestConfigSettings_GetPrivateMode(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettings{PrivateMode: &zeroValue} + c.GetPrivateMode() + c = &ConfigSettings{} + c.GetPrivateMode() + c = nil + c.GetPrivateMode() +} + +func TestConfigSettings_GetPublicPages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettings{PublicPages: &zeroValue} + c.GetPublicPages() + c = &ConfigSettings{} + c.GetPublicPages() + c = nil + c.GetPublicPages() +} + +func TestConfigSettings_GetSAML(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetSAML() + c = nil + c.GetSAML() +} + +func TestConfigSettings_GetSignupEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettings{SignupEnabled: &zeroValue} + c.GetSignupEnabled() + c = &ConfigSettings{} + c.GetSignupEnabled() + c = nil + c.GetSignupEnabled() +} + +func TestConfigSettings_GetSMTP(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetSMTP() + c = nil + c.GetSMTP() +} + +func TestConfigSettings_GetSNMP(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetSNMP() + c = nil + c.GetSNMP() +} + +func TestConfigSettings_GetSubdomainIsolation(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettings{SubdomainIsolation: &zeroValue} + c.GetSubdomainIsolation() + c = &ConfigSettings{} + c.GetSubdomainIsolation() + c = nil + c.GetSubdomainIsolation() +} + +func TestConfigSettings_GetSyslog(tt *testing.T) { + tt.Parallel() + c := &ConfigSettings{} + c.GetSyslog() + c = nil + c.GetSyslog() +} + +func TestConfigSettings_GetTimezone(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettings{Timezone: &zeroValue} + c.GetTimezone() + c = &ConfigSettings{} + c.GetTimezone() + c = nil + c.GetTimezone() +} + +func TestConfigSettingsAvatar_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsAvatar{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsAvatar{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsAvatar_GetURI(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsAvatar{URI: &zeroValue} + c.GetURI() + c = &ConfigSettingsAvatar{} + c.GetURI() + c = nil + c.GetURI() +} + +func TestConfigSettingsCAS_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCAS{URL: &zeroValue} + c.GetURL() + c = &ConfigSettingsCAS{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestConfigSettingsCollectd_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsCollectd{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsCollectd{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsCollectd_GetEncryption(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCollectd{Encryption: &zeroValue} + c.GetEncryption() + c = &ConfigSettingsCollectd{} + c.GetEncryption() + c = nil + c.GetEncryption() +} + +func TestConfigSettingsCollectd_GetPassword(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCollectd{Password: &zeroValue} + c.GetPassword() + c = &ConfigSettingsCollectd{} + c.GetPassword() + c = nil + c.GetPassword() +} + +func TestConfigSettingsCollectd_GetPort(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigSettingsCollectd{Port: &zeroValue} + c.GetPort() + c = &ConfigSettingsCollectd{} + c.GetPort() + c = nil + c.GetPort() +} + +func TestConfigSettingsCollectd_GetServer(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCollectd{Server: &zeroValue} + c.GetServer() + c = &ConfigSettingsCollectd{} + c.GetServer() + c = nil + c.GetServer() +} + +func TestConfigSettingsCollectd_GetUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCollectd{Username: &zeroValue} + c.GetUsername() + c = &ConfigSettingsCollectd{} + c.GetUsername() + c = nil + c.GetUsername() +} + +func TestConfigSettingsCustomer_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCustomer{Email: &zeroValue} + c.GetEmail() + c = &ConfigSettingsCustomer{} + c.GetEmail() + c = nil + c.GetEmail() +} + +func TestConfigSettingsCustomer_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCustomer{Name: &zeroValue} + c.GetName() + c = &ConfigSettingsCustomer{} + c.GetName() + c = nil + c.GetName() +} + +func TestConfigSettingsCustomer_GetPublicKeyData(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCustomer{PublicKeyData: &zeroValue} + c.GetPublicKeyData() + c = &ConfigSettingsCustomer{} + c.GetPublicKeyData() + c = nil + c.GetPublicKeyData() +} + +func TestConfigSettingsCustomer_GetSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCustomer{Secret: &zeroValue} + c.GetSecret() + c = &ConfigSettingsCustomer{} + c.GetSecret() + c = nil + c.GetSecret() +} + +func TestConfigSettingsCustomer_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsCustomer{UUID: &zeroValue} + c.GetUUID() + c = &ConfigSettingsCustomer{} + c.GetUUID() + c = nil + c.GetUUID() +} + +func TestConfigSettingsGithubOAuth_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsGithubOAuth{ClientID: &zeroValue} + c.GetClientID() + c = &ConfigSettingsGithubOAuth{} + c.GetClientID() + c = nil + c.GetClientID() +} + +func TestConfigSettingsGithubOAuth_GetClientSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsGithubOAuth{ClientSecret: &zeroValue} + c.GetClientSecret() + c = &ConfigSettingsGithubOAuth{} + c.GetClientSecret() + c = nil + c.GetClientSecret() +} + +func TestConfigSettingsGithubOAuth_GetOrganizationName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsGithubOAuth{OrganizationName: &zeroValue} + c.GetOrganizationName() + c = &ConfigSettingsGithubOAuth{} + c.GetOrganizationName() + c = nil + c.GetOrganizationName() +} + +func TestConfigSettingsGithubOAuth_GetOrganizationTeam(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsGithubOAuth{OrganizationTeam: &zeroValue} + c.GetOrganizationTeam() + c = &ConfigSettingsGithubOAuth{} + c.GetOrganizationTeam() + c = nil + c.GetOrganizationTeam() +} + +func TestConfigSettingsGithubSSL_GetCert(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsGithubSSL{Cert: &zeroValue} + c.GetCert() + c = &ConfigSettingsGithubSSL{} + c.GetCert() + c = nil + c.GetCert() +} + +func TestConfigSettingsGithubSSL_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsGithubSSL{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsGithubSSL{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsGithubSSL_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsGithubSSL{Key: &zeroValue} + c.GetKey() + c = &ConfigSettingsGithubSSL{} + c.GetKey() + c = nil + c.GetKey() +} + +func TestConfigSettingsLDAP_GetAdminGroup(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{AdminGroup: &zeroValue} + c.GetAdminGroup() + c = &ConfigSettingsLDAP{} + c.GetAdminGroup() + c = nil + c.GetAdminGroup() +} + +func TestConfigSettingsLDAP_GetBase(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &ConfigSettingsLDAP{Base: zeroValue} + c.GetBase() + c = &ConfigSettingsLDAP{} + c.GetBase() + c = nil + c.GetBase() +} + +func TestConfigSettingsLDAP_GetBindDN(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{BindDN: &zeroValue} + c.GetBindDN() + c = &ConfigSettingsLDAP{} + c.GetBindDN() + c = nil + c.GetBindDN() +} + +func TestConfigSettingsLDAP_GetHost(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{Host: &zeroValue} + c.GetHost() + c = &ConfigSettingsLDAP{} + c.GetHost() + c = nil + c.GetHost() +} + +func TestConfigSettingsLDAP_GetMethod(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{Method: &zeroValue} + c.GetMethod() + c = &ConfigSettingsLDAP{} + c.GetMethod() + c = nil + c.GetMethod() +} + +func TestConfigSettingsLDAP_GetPassword(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{Password: &zeroValue} + c.GetPassword() + c = &ConfigSettingsLDAP{} + c.GetPassword() + c = nil + c.GetPassword() +} + +func TestConfigSettingsLDAP_GetPort(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigSettingsLDAP{Port: &zeroValue} + c.GetPort() + c = &ConfigSettingsLDAP{} + c.GetPort() + c = nil + c.GetPort() +} + +func TestConfigSettingsLDAP_GetPosixSupport(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLDAP{PosixSupport: &zeroValue} + c.GetPosixSupport() + c = &ConfigSettingsLDAP{} + c.GetPosixSupport() + c = nil + c.GetPosixSupport() +} + +func TestConfigSettingsLDAP_GetProfile(tt *testing.T) { + tt.Parallel() + c := &ConfigSettingsLDAP{} + c.GetProfile() + c = nil + c.GetProfile() +} + +func TestConfigSettingsLDAP_GetReconciliation(tt *testing.T) { + tt.Parallel() + c := &ConfigSettingsLDAP{} + c.GetReconciliation() + c = nil + c.GetReconciliation() +} + +func TestConfigSettingsLDAP_GetRecursiveGroupSearch(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLDAP{RecursiveGroupSearch: &zeroValue} + c.GetRecursiveGroupSearch() + c = &ConfigSettingsLDAP{} + c.GetRecursiveGroupSearch() + c = nil + c.GetRecursiveGroupSearch() +} + +func TestConfigSettingsLDAP_GetSearchStrategy(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{SearchStrategy: &zeroValue} + c.GetSearchStrategy() + c = &ConfigSettingsLDAP{} + c.GetSearchStrategy() + c = nil + c.GetSearchStrategy() +} + +func TestConfigSettingsLDAP_GetSyncEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLDAP{SyncEnabled: &zeroValue} + c.GetSyncEnabled() + c = &ConfigSettingsLDAP{} + c.GetSyncEnabled() + c = nil + c.GetSyncEnabled() +} + +func TestConfigSettingsLDAP_GetTeamSyncInterval(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigSettingsLDAP{TeamSyncInterval: &zeroValue} + c.GetTeamSyncInterval() + c = &ConfigSettingsLDAP{} + c.GetTeamSyncInterval() + c = nil + c.GetTeamSyncInterval() +} + +func TestConfigSettingsLDAP_GetUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAP{UID: &zeroValue} + c.GetUID() + c = &ConfigSettingsLDAP{} + c.GetUID() + c = nil + c.GetUID() +} + +func TestConfigSettingsLDAP_GetUserGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &ConfigSettingsLDAP{UserGroups: zeroValue} + c.GetUserGroups() + c = &ConfigSettingsLDAP{} + c.GetUserGroups() + c = nil + c.GetUserGroups() +} + +func TestConfigSettingsLDAP_GetUserSyncEmails(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLDAP{UserSyncEmails: &zeroValue} + c.GetUserSyncEmails() + c = &ConfigSettingsLDAP{} + c.GetUserSyncEmails() + c = nil + c.GetUserSyncEmails() +} + +func TestConfigSettingsLDAP_GetUserSyncInterval(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigSettingsLDAP{UserSyncInterval: &zeroValue} + c.GetUserSyncInterval() + c = &ConfigSettingsLDAP{} + c.GetUserSyncInterval() + c = nil + c.GetUserSyncInterval() +} + +func TestConfigSettingsLDAP_GetUserSyncKeys(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLDAP{UserSyncKeys: &zeroValue} + c.GetUserSyncKeys() + c = &ConfigSettingsLDAP{} + c.GetUserSyncKeys() + c = nil + c.GetUserSyncKeys() +} + +func TestConfigSettingsLDAP_GetVirtualAttributeEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLDAP{VirtualAttributeEnabled: &zeroValue} + c.GetVirtualAttributeEnabled() + c = &ConfigSettingsLDAP{} + c.GetVirtualAttributeEnabled() + c = nil + c.GetVirtualAttributeEnabled() +} + +func TestConfigSettingsLDAPProfile_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAPProfile{Key: &zeroValue} + c.GetKey() + c = &ConfigSettingsLDAPProfile{} + c.GetKey() + c = nil + c.GetKey() +} + +func TestConfigSettingsLDAPProfile_GetMail(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAPProfile{Mail: &zeroValue} + c.GetMail() + c = &ConfigSettingsLDAPProfile{} + c.GetMail() + c = nil + c.GetMail() +} + +func TestConfigSettingsLDAPProfile_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAPProfile{Name: &zeroValue} + c.GetName() + c = &ConfigSettingsLDAPProfile{} + c.GetName() + c = nil + c.GetName() +} + +func TestConfigSettingsLDAPProfile_GetUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAPProfile{UID: &zeroValue} + c.GetUID() + c = &ConfigSettingsLDAPProfile{} + c.GetUID() + c = nil + c.GetUID() +} + +func TestConfigSettingsLDAPReconciliation_GetOrg(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAPReconciliation{Org: &zeroValue} + c.GetOrg() + c = &ConfigSettingsLDAPReconciliation{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestConfigSettingsLDAPReconciliation_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLDAPReconciliation{User: &zeroValue} + c.GetUser() + c = &ConfigSettingsLDAPReconciliation{} + c.GetUser() + c = nil + c.GetUser() +} + +func TestConfigSettingsLicenseSettings_GetClusterSupport(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLicenseSettings{ClusterSupport: &zeroValue} + c.GetClusterSupport() + c = &ConfigSettingsLicenseSettings{} + c.GetClusterSupport() + c = nil + c.GetClusterSupport() +} + +func TestConfigSettingsLicenseSettings_GetEvaluation(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLicenseSettings{Evaluation: &zeroValue} + c.GetEvaluation() + c = &ConfigSettingsLicenseSettings{} + c.GetEvaluation() + c = nil + c.GetEvaluation() +} + +func TestConfigSettingsLicenseSettings_GetExpireAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &ConfigSettingsLicenseSettings{ExpireAt: &zeroValue} + c.GetExpireAt() + c = &ConfigSettingsLicenseSettings{} + c.GetExpireAt() + c = nil + c.GetExpireAt() +} + +func TestConfigSettingsLicenseSettings_GetPerpetual(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLicenseSettings{Perpetual: &zeroValue} + c.GetPerpetual() + c = &ConfigSettingsLicenseSettings{} + c.GetPerpetual() + c = nil + c.GetPerpetual() +} + +func TestConfigSettingsLicenseSettings_GetSeats(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConfigSettingsLicenseSettings{Seats: &zeroValue} + c.GetSeats() + c = &ConfigSettingsLicenseSettings{} + c.GetSeats() + c = nil + c.GetSeats() +} + +func TestConfigSettingsLicenseSettings_GetSSHAllowed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLicenseSettings{SSHAllowed: &zeroValue} + c.GetSSHAllowed() + c = &ConfigSettingsLicenseSettings{} + c.GetSSHAllowed() + c = nil + c.GetSSHAllowed() +} + +func TestConfigSettingsLicenseSettings_GetSupportKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsLicenseSettings{SupportKey: &zeroValue} + c.GetSupportKey() + c = &ConfigSettingsLicenseSettings{} + c.GetSupportKey() + c = nil + c.GetSupportKey() +} + +func TestConfigSettingsLicenseSettings_GetUnlimitedSeating(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsLicenseSettings{UnlimitedSeating: &zeroValue} + c.GetUnlimitedSeating() + c = &ConfigSettingsLicenseSettings{} + c.GetUnlimitedSeating() + c = nil + c.GetUnlimitedSeating() +} + +func TestConfigSettingsMapping_GetBasemap(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsMapping{Basemap: &zeroValue} + c.GetBasemap() + c = &ConfigSettingsMapping{} + c.GetBasemap() + c = nil + c.GetBasemap() +} + +func TestConfigSettingsMapping_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsMapping{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsMapping{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsMapping_GetTileserver(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsMapping{Tileserver: &zeroValue} + c.GetTileserver() + c = &ConfigSettingsMapping{} + c.GetTileserver() + c = nil + c.GetTileserver() +} + +func TestConfigSettingsMapping_GetToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsMapping{Token: &zeroValue} + c.GetToken() + c = &ConfigSettingsMapping{} + c.GetToken() + c = nil + c.GetToken() +} + +func TestConfigSettingsNTP_GetPrimaryServer(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsNTP{PrimaryServer: &zeroValue} + c.GetPrimaryServer() + c = &ConfigSettingsNTP{} + c.GetPrimaryServer() + c = nil + c.GetPrimaryServer() +} + +func TestConfigSettingsNTP_GetSecondaryServer(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsNTP{SecondaryServer: &zeroValue} + c.GetSecondaryServer() + c = &ConfigSettingsNTP{} + c.GetSecondaryServer() + c = nil + c.GetSecondaryServer() +} + +func TestConfigSettingsPagesSettings_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsPagesSettings{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsPagesSettings{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsSAML_GetCertificate(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSAML{Certificate: &zeroValue} + c.GetCertificate() + c = &ConfigSettingsSAML{} + c.GetCertificate() + c = nil + c.GetCertificate() +} + +func TestConfigSettingsSAML_GetCertificatePath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSAML{CertificatePath: &zeroValue} + c.GetCertificatePath() + c = &ConfigSettingsSAML{} + c.GetCertificatePath() + c = nil + c.GetCertificatePath() +} + +func TestConfigSettingsSAML_GetDisableAdminDemote(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSAML{DisableAdminDemote: &zeroValue} + c.GetDisableAdminDemote() + c = &ConfigSettingsSAML{} + c.GetDisableAdminDemote() + c = nil + c.GetDisableAdminDemote() +} + +func TestConfigSettingsSAML_GetIDPInitiatedSSO(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSAML{IDPInitiatedSSO: &zeroValue} + c.GetIDPInitiatedSSO() + c = &ConfigSettingsSAML{} + c.GetIDPInitiatedSSO() + c = nil + c.GetIDPInitiatedSSO() +} + +func TestConfigSettingsSAML_GetIssuer(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSAML{Issuer: &zeroValue} + c.GetIssuer() + c = &ConfigSettingsSAML{} + c.GetIssuer() + c = nil + c.GetIssuer() +} + +func TestConfigSettingsSAML_GetSSOURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSAML{SSOURL: &zeroValue} + c.GetSSOURL() + c = &ConfigSettingsSAML{} + c.GetSSOURL() + c = nil + c.GetSSOURL() +} + +func TestConfigSettingsSMTP_GetAddress(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{Address: &zeroValue} + c.GetAddress() + c = &ConfigSettingsSMTP{} + c.GetAddress() + c = nil + c.GetAddress() +} + +func TestConfigSettingsSMTP_GetAuthentication(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{Authentication: &zeroValue} + c.GetAuthentication() + c = &ConfigSettingsSMTP{} + c.GetAuthentication() + c = nil + c.GetAuthentication() +} + +func TestConfigSettingsSMTP_GetDiscardToNoreplyAddress(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSMTP{DiscardToNoreplyAddress: &zeroValue} + c.GetDiscardToNoreplyAddress() + c = &ConfigSettingsSMTP{} + c.GetDiscardToNoreplyAddress() + c = nil + c.GetDiscardToNoreplyAddress() +} + +func TestConfigSettingsSMTP_GetDomain(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{Domain: &zeroValue} + c.GetDomain() + c = &ConfigSettingsSMTP{} + c.GetDomain() + c = nil + c.GetDomain() +} + +func TestConfigSettingsSMTP_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSMTP{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsSMTP{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsSMTP_GetEnableStarttlsAuto(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSMTP{EnableStarttlsAuto: &zeroValue} + c.GetEnableStarttlsAuto() + c = &ConfigSettingsSMTP{} + c.GetEnableStarttlsAuto() + c = nil + c.GetEnableStarttlsAuto() +} + +func TestConfigSettingsSMTP_GetNoreplyAddress(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{NoreplyAddress: &zeroValue} + c.GetNoreplyAddress() + c = &ConfigSettingsSMTP{} + c.GetNoreplyAddress() + c = nil + c.GetNoreplyAddress() +} + +func TestConfigSettingsSMTP_GetPassword(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{Password: &zeroValue} + c.GetPassword() + c = &ConfigSettingsSMTP{} + c.GetPassword() + c = nil + c.GetPassword() +} + +func TestConfigSettingsSMTP_GetPort(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{Port: &zeroValue} + c.GetPort() + c = &ConfigSettingsSMTP{} + c.GetPort() + c = nil + c.GetPort() +} + +func TestConfigSettingsSMTP_GetSupportAddress(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{SupportAddress: &zeroValue} + c.GetSupportAddress() + c = &ConfigSettingsSMTP{} + c.GetSupportAddress() + c = nil + c.GetSupportAddress() +} + +func TestConfigSettingsSMTP_GetSupportAddressType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{SupportAddressType: &zeroValue} + c.GetSupportAddressType() + c = &ConfigSettingsSMTP{} + c.GetSupportAddressType() + c = nil + c.GetSupportAddressType() +} + +func TestConfigSettingsSMTP_GetUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{Username: &zeroValue} + c.GetUsername() + c = &ConfigSettingsSMTP{} + c.GetUsername() + c = nil + c.GetUsername() +} + +func TestConfigSettingsSMTP_GetUserName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSMTP{UserName: &zeroValue} + c.GetUserName() + c = &ConfigSettingsSMTP{} + c.GetUserName() + c = nil + c.GetUserName() +} + +func TestConfigSettingsSNMP_GetCommunity(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSNMP{Community: &zeroValue} + c.GetCommunity() + c = &ConfigSettingsSNMP{} + c.GetCommunity() + c = nil + c.GetCommunity() +} + +func TestConfigSettingsSNMP_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSNMP{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsSNMP{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsSyslog_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &ConfigSettingsSyslog{Enabled: &zeroValue} + c.GetEnabled() + c = &ConfigSettingsSyslog{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestConfigSettingsSyslog_GetProtocolName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSyslog{ProtocolName: &zeroValue} + c.GetProtocolName() + c = &ConfigSettingsSyslog{} + c.GetProtocolName() + c = nil + c.GetProtocolName() +} + +func TestConfigSettingsSyslog_GetServer(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConfigSettingsSyslog{Server: &zeroValue} + c.GetServer() + c = &ConfigSettingsSyslog{} + c.GetServer() + c = nil + c.GetServer() +} + +func TestConnectionServiceItem_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ConnectionServiceItem{Name: &zeroValue} + c.GetName() + c = &ConnectionServiceItem{} + c.GetName() + c = nil + c.GetName() +} + +func TestConnectionServiceItem_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ConnectionServiceItem{Number: &zeroValue} + c.GetNumber() + c = &ConnectionServiceItem{} + c.GetNumber() + c = nil + c.GetNumber() +} + +func TestContentReference_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &ContentReference{ID: &zeroValue} + c.GetID() + c = &ContentReference{} + c.GetID() + c = nil + c.GetID() +} + +func TestContentReference_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ContentReference{NodeID: &zeroValue} + c.GetNodeID() + c = &ContentReference{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestContentReference_GetReference(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ContentReference{Reference: &zeroValue} + c.GetReference() + c = &ContentReference{} + c.GetReference() + c = nil + c.GetReference() +} + +func TestContentReferenceEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &ContentReferenceEvent{Action: &zeroValue} + c.GetAction() + c = &ContentReferenceEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestContentReferenceEvent_GetContentReference(tt *testing.T) { + tt.Parallel() + c := &ContentReferenceEvent{} + c.GetContentReference() + c = nil + c.GetContentReference() +} + +func TestContentReferenceEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &ContentReferenceEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestContentReferenceEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &ContentReferenceEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestContentReferenceEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &ContentReferenceEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestContributor_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{AvatarURL: &zeroValue} + c.GetAvatarURL() + c = &Contributor{} + c.GetAvatarURL() + c = nil + c.GetAvatarURL() +} + +func TestContributor_GetContributions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &Contributor{Contributions: &zeroValue} + c.GetContributions() + c = &Contributor{} + c.GetContributions() + c = nil + c.GetContributions() +} + +func TestContributor_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{Email: &zeroValue} + c.GetEmail() + c = &Contributor{} + c.GetEmail() + c = nil + c.GetEmail() +} + +func TestContributor_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{EventsURL: &zeroValue} + c.GetEventsURL() + c = &Contributor{} + c.GetEventsURL() + c = nil + c.GetEventsURL() +} + +func TestContributor_GetFollowersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{FollowersURL: &zeroValue} + c.GetFollowersURL() + c = &Contributor{} + c.GetFollowersURL() + c = nil + c.GetFollowersURL() +} + +func TestContributor_GetFollowingURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{FollowingURL: &zeroValue} + c.GetFollowingURL() + c = &Contributor{} + c.GetFollowingURL() + c = nil + c.GetFollowingURL() +} + +func TestContributor_GetGistsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{GistsURL: &zeroValue} + c.GetGistsURL() + c = &Contributor{} + c.GetGistsURL() + c = nil + c.GetGistsURL() +} + +func TestContributor_GetGravatarID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{GravatarID: &zeroValue} + c.GetGravatarID() + c = &Contributor{} + c.GetGravatarID() + c = nil + c.GetGravatarID() +} + +func TestContributor_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{HTMLURL: &zeroValue} + c.GetHTMLURL() + c = &Contributor{} + c.GetHTMLURL() + c = nil + c.GetHTMLURL() +} + +func TestContributor_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &Contributor{ID: &zeroValue} + c.GetID() + c = &Contributor{} + c.GetID() + c = nil + c.GetID() +} + +func TestContributor_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{Login: &zeroValue} + c.GetLogin() + c = &Contributor{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestContributor_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{Name: &zeroValue} + c.GetName() + c = &Contributor{} + c.GetName() + c = nil + c.GetName() +} + +func TestContributor_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{NodeID: &zeroValue} + c.GetNodeID() + c = &Contributor{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestContributor_GetOrganizationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{OrganizationsURL: &zeroValue} + c.GetOrganizationsURL() + c = &Contributor{} + c.GetOrganizationsURL() + c = nil + c.GetOrganizationsURL() +} + +func TestContributor_GetReceivedEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{ReceivedEventsURL: &zeroValue} + c.GetReceivedEventsURL() + c = &Contributor{} + c.GetReceivedEventsURL() + c = nil + c.GetReceivedEventsURL() +} + +func TestContributor_GetReposURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{ReposURL: &zeroValue} + c.GetReposURL() + c = &Contributor{} + c.GetReposURL() + c = nil + c.GetReposURL() +} + +func TestContributor_GetSiteAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &Contributor{SiteAdmin: &zeroValue} + c.GetSiteAdmin() + c = &Contributor{} + c.GetSiteAdmin() + c = nil + c.GetSiteAdmin() +} + +func TestContributor_GetStarredURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{StarredURL: &zeroValue} + c.GetStarredURL() + c = &Contributor{} + c.GetStarredURL() + c = nil + c.GetStarredURL() +} + +func TestContributor_GetSubscriptionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{SubscriptionsURL: &zeroValue} + c.GetSubscriptionsURL() + c = &Contributor{} + c.GetSubscriptionsURL() + c = nil + c.GetSubscriptionsURL() +} + +func TestContributor_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{Type: &zeroValue} + c.GetType() + c = &Contributor{} + c.GetType() + c = nil + c.GetType() +} + +func TestContributor_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Contributor{URL: &zeroValue} + c.GetURL() + c = &Contributor{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestContributorApprovalPermissions_GetApprovalPolicy(tt *testing.T) { + tt.Parallel() + c := &ContributorApprovalPermissions{} + c.GetApprovalPolicy() + c = nil + c.GetApprovalPolicy() +} + +func TestContributorStats_GetAuthor(tt *testing.T) { + tt.Parallel() + c := &ContributorStats{} + c.GetAuthor() + c = nil + c.GetAuthor() +} + +func TestContributorStats_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &ContributorStats{Total: &zeroValue} + c.GetTotal() + c = &ContributorStats{} + c.GetTotal() + c = nil + c.GetTotal() +} + +func TestContributorStats_GetWeeks(tt *testing.T) { + tt.Parallel() + zeroValue := []*WeeklyStats{} + c := &ContributorStats{Weeks: zeroValue} + c.GetWeeks() + c = &ContributorStats{} + c.GetWeeks() + c = nil + c.GetWeeks() +} + +func TestCopilotCloudAgentConfiguration_GetCustomAllowlist(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CopilotCloudAgentConfiguration{CustomAllowlist: zeroValue} + c.GetCustomAllowlist() + c = &CopilotCloudAgentConfiguration{} + c.GetCustomAllowlist() + c = nil + c.GetCustomAllowlist() +} + +func TestCopilotCloudAgentConfiguration_GetEnabledTools(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentConfiguration{} + c.GetEnabledTools() + c = nil + c.GetEnabledTools() +} + +func TestCopilotCloudAgentConfiguration_GetIsFirewallEnabled(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentConfiguration{} + c.GetIsFirewallEnabled() + c = nil + c.GetIsFirewallEnabled() +} + +func TestCopilotCloudAgentConfiguration_GetIsFirewallRecommendedAllowlistEnabled(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentConfiguration{} + c.GetIsFirewallRecommendedAllowlistEnabled() + c = nil + c.GetIsFirewallRecommendedAllowlistEnabled() +} + +func TestCopilotCloudAgentConfiguration_GetMCPConfiguration(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentConfiguration{} + c.GetMCPConfiguration() + c = nil + c.GetMCPConfiguration() +} + +func TestCopilotCloudAgentConfiguration_GetRequireActionsWorkflowApproval(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentConfiguration{} + c.GetRequireActionsWorkflowApproval() + c = nil + c.GetRequireActionsWorkflowApproval() +} + +func TestCopilotCloudAgentEnabledTools_GetCodeql(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentEnabledTools{} + c.GetCodeql() + c = nil + c.GetCodeql() +} + +func TestCopilotCloudAgentEnabledTools_GetCopilotCodeReview(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentEnabledTools{} + c.GetCopilotCodeReview() + c = nil + c.GetCopilotCodeReview() +} + +func TestCopilotCloudAgentEnabledTools_GetDependencyVulnerabilityChecks(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentEnabledTools{} + c.GetDependencyVulnerabilityChecks() + c = nil + c.GetDependencyVulnerabilityChecks() +} + +func TestCopilotCloudAgentEnabledTools_GetSecretScanning(tt *testing.T) { + tt.Parallel() + c := &CopilotCloudAgentEnabledTools{} + c.GetSecretScanning() + c = nil + c.GetSecretScanning() +} + +func TestCopilotCodeReviewBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + c := &CopilotCodeReviewBranchRule{} + c.GetParameters() + c = nil + c.GetParameters() +} + +func TestCopilotCodeReviewRuleParameters_GetReviewDraftPullRequests(tt *testing.T) { + tt.Parallel() + c := &CopilotCodeReviewRuleParameters{} + c.GetReviewDraftPullRequests() + c = nil + c.GetReviewDraftPullRequests() +} + +func TestCopilotCodeReviewRuleParameters_GetReviewOnPush(tt *testing.T) { + tt.Parallel() + c := &CopilotCodeReviewRuleParameters{} + c.GetReviewOnPush() + c = nil + c.GetReviewOnPush() +} + +func TestCopilotDailyMetrics_GetCodeAcceptanceActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{CodeAcceptanceActivityCount: &zeroValue} + c.GetCodeAcceptanceActivityCount() + c = &CopilotDailyMetrics{} + c.GetCodeAcceptanceActivityCount() + c = nil + c.GetCodeAcceptanceActivityCount() +} + +func TestCopilotDailyMetrics_GetCodeGenerationActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{CodeGenerationActivityCount: &zeroValue} + c.GetCodeGenerationActivityCount() + c = &CopilotDailyMetrics{} + c.GetCodeGenerationActivityCount() + c = nil + c.GetCodeGenerationActivityCount() +} + +func TestCopilotDailyMetrics_GetDailyActiveCLIUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{DailyActiveCLIUsers: &zeroValue} + c.GetDailyActiveCLIUsers() + c = &CopilotDailyMetrics{} + c.GetDailyActiveCLIUsers() + c = nil + c.GetDailyActiveCLIUsers() +} + +func TestCopilotDailyMetrics_GetDailyActiveCopilotCloudAgentUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{DailyActiveCopilotCloudAgentUsers: &zeroValue} + c.GetDailyActiveCopilotCloudAgentUsers() + c = &CopilotDailyMetrics{} + c.GetDailyActiveCopilotCloudAgentUsers() + c = nil + c.GetDailyActiveCopilotCloudAgentUsers() +} + +func TestCopilotDailyMetrics_GetDailyActiveUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{DailyActiveUsers: &zeroValue} + c.GetDailyActiveUsers() + c = &CopilotDailyMetrics{} + c.GetDailyActiveUsers() + c = nil + c.GetDailyActiveUsers() +} + +func TestCopilotDailyMetrics_GetDay(tt *testing.T) { + tt.Parallel() + c := &CopilotDailyMetrics{} + c.GetDay() + c = nil + c.GetDay() +} + +func TestCopilotDailyMetrics_GetEnterpriseID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotDailyMetrics{EnterpriseID: &zeroValue} + c.GetEnterpriseID() + c = &CopilotDailyMetrics{} + c.GetEnterpriseID() + c = nil + c.GetEnterpriseID() +} + +func TestCopilotDailyMetrics_GetLOCAddedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{LOCAddedSum: &zeroValue} + c.GetLOCAddedSum() + c = &CopilotDailyMetrics{} + c.GetLOCAddedSum() + c = nil + c.GetLOCAddedSum() +} + +func TestCopilotDailyMetrics_GetLOCDeletedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{LOCDeletedSum: &zeroValue} + c.GetLOCDeletedSum() + c = &CopilotDailyMetrics{} + c.GetLOCDeletedSum() + c = nil + c.GetLOCDeletedSum() +} + +func TestCopilotDailyMetrics_GetLOCSuggestedToAddSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{LOCSuggestedToAddSum: &zeroValue} + c.GetLOCSuggestedToAddSum() + c = &CopilotDailyMetrics{} + c.GetLOCSuggestedToAddSum() + c = nil + c.GetLOCSuggestedToAddSum() +} + +func TestCopilotDailyMetrics_GetLOCSuggestedToDeleteSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{LOCSuggestedToDeleteSum: &zeroValue} + c.GetLOCSuggestedToDeleteSum() + c = &CopilotDailyMetrics{} + c.GetLOCSuggestedToDeleteSum() + c = nil + c.GetLOCSuggestedToDeleteSum() +} + +func TestCopilotDailyMetrics_GetMonthlyActiveAgentUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{MonthlyActiveAgentUsers: &zeroValue} + c.GetMonthlyActiveAgentUsers() + c = &CopilotDailyMetrics{} + c.GetMonthlyActiveAgentUsers() + c = nil + c.GetMonthlyActiveAgentUsers() +} + +func TestCopilotDailyMetrics_GetMonthlyActiveChatUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{MonthlyActiveChatUsers: &zeroValue} + c.GetMonthlyActiveChatUsers() + c = &CopilotDailyMetrics{} + c.GetMonthlyActiveChatUsers() + c = nil + c.GetMonthlyActiveChatUsers() +} + +func TestCopilotDailyMetrics_GetMonthlyActiveCopilotCloudAgentUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{MonthlyActiveCopilotCloudAgentUsers: &zeroValue} + c.GetMonthlyActiveCopilotCloudAgentUsers() + c = &CopilotDailyMetrics{} + c.GetMonthlyActiveCopilotCloudAgentUsers() + c = nil + c.GetMonthlyActiveCopilotCloudAgentUsers() +} + +func TestCopilotDailyMetrics_GetMonthlyActiveUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{MonthlyActiveUsers: &zeroValue} + c.GetMonthlyActiveUsers() + c = &CopilotDailyMetrics{} + c.GetMonthlyActiveUsers() + c = nil + c.GetMonthlyActiveUsers() +} + +func TestCopilotDailyMetrics_GetOrganizationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotDailyMetrics{OrganizationID: &zeroValue} + c.GetOrganizationID() + c = &CopilotDailyMetrics{} + c.GetOrganizationID() + c = nil + c.GetOrganizationID() +} + +func TestCopilotDailyMetrics_GetPullRequests(tt *testing.T) { + tt.Parallel() + c := &CopilotDailyMetrics{} + c.GetPullRequests() + c = nil + c.GetPullRequests() +} + +func TestCopilotDailyMetrics_GetTotalsByCLI(tt *testing.T) { + tt.Parallel() + c := &CopilotDailyMetrics{} + c.GetTotalsByCLI() + c = nil + c.GetTotalsByCLI() +} + +func TestCopilotDailyMetrics_GetTotalsByFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsFeature{} + c := &CopilotDailyMetrics{TotalsByFeature: zeroValue} + c.GetTotalsByFeature() + c = &CopilotDailyMetrics{} + c.GetTotalsByFeature() + c = nil + c.GetTotalsByFeature() +} + +func TestCopilotDailyMetrics_GetTotalsByIDE(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsIDE{} + c := &CopilotDailyMetrics{TotalsByIDE: zeroValue} + c.GetTotalsByIDE() + c = &CopilotDailyMetrics{} + c.GetTotalsByIDE() + c = nil + c.GetTotalsByIDE() +} + +func TestCopilotDailyMetrics_GetTotalsByLanguageFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsLanguageFeature{} + c := &CopilotDailyMetrics{TotalsByLanguageFeature: zeroValue} + c.GetTotalsByLanguageFeature() + c = &CopilotDailyMetrics{} + c.GetTotalsByLanguageFeature() + c = nil + c.GetTotalsByLanguageFeature() +} + +func TestCopilotDailyMetrics_GetTotalsByLanguageModel(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsLanguageModel{} + c := &CopilotDailyMetrics{TotalsByLanguageModel: zeroValue} + c.GetTotalsByLanguageModel() + c = &CopilotDailyMetrics{} + c.GetTotalsByLanguageModel() + c = nil + c.GetTotalsByLanguageModel() +} + +func TestCopilotDailyMetrics_GetTotalsByModelFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsModelFeature{} + c := &CopilotDailyMetrics{TotalsByModelFeature: zeroValue} + c.GetTotalsByModelFeature() + c = &CopilotDailyMetrics{} + c.GetTotalsByModelFeature() + c = nil + c.GetTotalsByModelFeature() +} + +func TestCopilotDailyMetrics_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotDailyMetrics{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotDailyMetrics_GetWeeklyActiveCopilotCloudAgentUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{WeeklyActiveCopilotCloudAgentUsers: &zeroValue} + c.GetWeeklyActiveCopilotCloudAgentUsers() + c = &CopilotDailyMetrics{} + c.GetWeeklyActiveCopilotCloudAgentUsers() + c = nil + c.GetWeeklyActiveCopilotCloudAgentUsers() +} + +func TestCopilotDailyMetrics_GetWeeklyActiveUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotDailyMetrics{WeeklyActiveUsers: &zeroValue} + c.GetWeeklyActiveUsers() + c = &CopilotDailyMetrics{} + c.GetWeeklyActiveUsers() + c = nil + c.GetWeeklyActiveUsers() +} + +func TestCopilotDailyMetricsReport_GetDownloadLinks(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CopilotDailyMetricsReport{DownloadLinks: zeroValue} + c.GetDownloadLinks() + c = &CopilotDailyMetricsReport{} + c.GetDownloadLinks() + c = nil + c.GetDownloadLinks() +} + +func TestCopilotDailyMetricsReport_GetReportDay(tt *testing.T) { + tt.Parallel() + c := &CopilotDailyMetricsReport{} + c.GetReportDay() + c = nil + c.GetReportDay() +} + +func TestCopilotDotcomChat_GetModels(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotDotcomChatModel{} + c := &CopilotDotcomChat{Models: zeroValue} + c.GetModels() + c = &CopilotDotcomChat{} + c.GetModels() + c = nil + c.GetModels() +} + +func TestCopilotDotcomChat_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomChat{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotDotcomChatModel_GetCustomModelTrainingDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotDotcomChatModel{CustomModelTrainingDate: &zeroValue} + c.GetCustomModelTrainingDate() + c = &CopilotDotcomChatModel{} + c.GetCustomModelTrainingDate() + c = nil + c.GetCustomModelTrainingDate() +} + +func TestCopilotDotcomChatModel_GetIsCustomModel(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomChatModel{} + c.GetIsCustomModel() + c = nil + c.GetIsCustomModel() +} + +func TestCopilotDotcomChatModel_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomChatModel{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotDotcomChatModel_GetTotalChats(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomChatModel{} + c.GetTotalChats() + c = nil + c.GetTotalChats() +} + +func TestCopilotDotcomChatModel_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomChatModel{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotDotcomPullRequests_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotDotcomPullRequestsRepository{} + c := &CopilotDotcomPullRequests{Repositories: zeroValue} + c.GetRepositories() + c = &CopilotDotcomPullRequests{} + c.GetRepositories() + c = nil + c.GetRepositories() +} + +func TestCopilotDotcomPullRequests_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequests{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotDotcomPullRequestsModel_GetCustomModelTrainingDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotDotcomPullRequestsModel{CustomModelTrainingDate: &zeroValue} + c.GetCustomModelTrainingDate() + c = &CopilotDotcomPullRequestsModel{} + c.GetCustomModelTrainingDate() + c = nil + c.GetCustomModelTrainingDate() +} + +func TestCopilotDotcomPullRequestsModel_GetIsCustomModel(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequestsModel{} + c.GetIsCustomModel() + c = nil + c.GetIsCustomModel() +} + +func TestCopilotDotcomPullRequestsModel_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequestsModel{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotDotcomPullRequestsModel_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequestsModel{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotDotcomPullRequestsModel_GetTotalPRSummariesCreated(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequestsModel{} + c.GetTotalPRSummariesCreated() + c = nil + c.GetTotalPRSummariesCreated() +} + +func TestCopilotDotcomPullRequestsRepository_GetModels(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotDotcomPullRequestsModel{} + c := &CopilotDotcomPullRequestsRepository{Models: zeroValue} + c.GetModels() + c = &CopilotDotcomPullRequestsRepository{} + c.GetModels() + c = nil + c.GetModels() +} + +func TestCopilotDotcomPullRequestsRepository_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequestsRepository{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotDotcomPullRequestsRepository_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotDotcomPullRequestsRepository{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDEChat_GetEditors(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotIDEChatEditor{} + c := &CopilotIDEChat{Editors: zeroValue} + c.GetEditors() + c = &CopilotIDEChat{} + c.GetEditors() + c = nil + c.GetEditors() +} + +func TestCopilotIDEChat_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChat{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDEChatEditor_GetModels(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotIDEChatModel{} + c := &CopilotIDEChatEditor{Models: zeroValue} + c.GetModels() + c = &CopilotIDEChatEditor{} + c.GetModels() + c = nil + c.GetModels() +} + +func TestCopilotIDEChatEditor_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatEditor{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotIDEChatEditor_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatEditor{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDEChatModel_GetCustomModelTrainingDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotIDEChatModel{CustomModelTrainingDate: &zeroValue} + c.GetCustomModelTrainingDate() + c = &CopilotIDEChatModel{} + c.GetCustomModelTrainingDate() + c = nil + c.GetCustomModelTrainingDate() +} + +func TestCopilotIDEChatModel_GetIsCustomModel(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatModel{} + c.GetIsCustomModel() + c = nil + c.GetIsCustomModel() +} + +func TestCopilotIDEChatModel_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatModel{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotIDEChatModel_GetTotalChatCopyEvents(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatModel{} + c.GetTotalChatCopyEvents() + c = nil + c.GetTotalChatCopyEvents() +} + +func TestCopilotIDEChatModel_GetTotalChatInsertionEvents(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatModel{} + c.GetTotalChatInsertionEvents() + c = nil + c.GetTotalChatInsertionEvents() +} + +func TestCopilotIDEChatModel_GetTotalChats(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatModel{} + c.GetTotalChats() + c = nil + c.GetTotalChats() +} + +func TestCopilotIDEChatModel_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDEChatModel{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDECodeCompletions_GetEditors(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotIDECodeCompletionsEditor{} + c := &CopilotIDECodeCompletions{Editors: zeroValue} + c.GetEditors() + c = &CopilotIDECodeCompletions{} + c.GetEditors() + c = nil + c.GetEditors() +} + +func TestCopilotIDECodeCompletions_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotIDECodeCompletionsLanguage{} + c := &CopilotIDECodeCompletions{Languages: zeroValue} + c.GetLanguages() + c = &CopilotIDECodeCompletions{} + c.GetLanguages() + c = nil + c.GetLanguages() +} + +func TestCopilotIDECodeCompletions_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletions{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDECodeCompletionsEditor_GetModels(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotIDECodeCompletionsModel{} + c := &CopilotIDECodeCompletionsEditor{Models: zeroValue} + c.GetModels() + c = &CopilotIDECodeCompletionsEditor{} + c.GetModels() + c = nil + c.GetModels() +} + +func TestCopilotIDECodeCompletionsEditor_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsEditor{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotIDECodeCompletionsEditor_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsEditor{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDECodeCompletionsLanguage_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsLanguage{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotIDECodeCompletionsLanguage_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsLanguage{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDECodeCompletionsModel_GetCustomModelTrainingDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotIDECodeCompletionsModel{CustomModelTrainingDate: &zeroValue} + c.GetCustomModelTrainingDate() + c = &CopilotIDECodeCompletionsModel{} + c.GetCustomModelTrainingDate() + c = nil + c.GetCustomModelTrainingDate() +} + +func TestCopilotIDECodeCompletionsModel_GetIsCustomModel(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModel{} + c.GetIsCustomModel() + c = nil + c.GetIsCustomModel() +} + +func TestCopilotIDECodeCompletionsModel_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotIDECodeCompletionsModelLanguage{} + c := &CopilotIDECodeCompletionsModel{Languages: zeroValue} + c.GetLanguages() + c = &CopilotIDECodeCompletionsModel{} + c.GetLanguages() + c = nil + c.GetLanguages() +} + +func TestCopilotIDECodeCompletionsModel_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModel{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotIDECodeCompletionsModel_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModel{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotIDECodeCompletionsModelLanguage_GetName(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModelLanguage{} + c.GetName() + c = nil + c.GetName() +} + +func TestCopilotIDECodeCompletionsModelLanguage_GetTotalCodeAcceptances(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModelLanguage{} + c.GetTotalCodeAcceptances() + c = nil + c.GetTotalCodeAcceptances() +} + +func TestCopilotIDECodeCompletionsModelLanguage_GetTotalCodeLinesAccepted(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModelLanguage{} + c.GetTotalCodeLinesAccepted() + c = nil + c.GetTotalCodeLinesAccepted() +} + +func TestCopilotIDECodeCompletionsModelLanguage_GetTotalCodeLinesSuggested(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModelLanguage{} + c.GetTotalCodeLinesSuggested() + c = nil + c.GetTotalCodeLinesSuggested() +} + +func TestCopilotIDECodeCompletionsModelLanguage_GetTotalCodeSuggestions(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModelLanguage{} + c.GetTotalCodeSuggestions() + c = nil + c.GetTotalCodeSuggestions() +} + +func TestCopilotIDECodeCompletionsModelLanguage_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + c := &CopilotIDECodeCompletionsModelLanguage{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotMetrics_GetCopilotDotcomChat(tt *testing.T) { + tt.Parallel() + c := &CopilotMetrics{} + c.GetCopilotDotcomChat() + c = nil + c.GetCopilotDotcomChat() +} + +func TestCopilotMetrics_GetCopilotDotcomPullRequests(tt *testing.T) { + tt.Parallel() + c := &CopilotMetrics{} + c.GetCopilotDotcomPullRequests() + c = nil + c.GetCopilotDotcomPullRequests() +} + +func TestCopilotMetrics_GetCopilotIDEChat(tt *testing.T) { + tt.Parallel() + c := &CopilotMetrics{} + c.GetCopilotIDEChat() + c = nil + c.GetCopilotIDEChat() +} + +func TestCopilotMetrics_GetCopilotIDECodeCompletions(tt *testing.T) { + tt.Parallel() + c := &CopilotMetrics{} + c.GetCopilotIDECodeCompletions() + c = nil + c.GetCopilotIDECodeCompletions() +} + +func TestCopilotMetrics_GetDate(tt *testing.T) { + tt.Parallel() + c := &CopilotMetrics{} + c.GetDate() + c = nil + c.GetDate() +} + +func TestCopilotMetrics_GetTotalActiveUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetrics{TotalActiveUsers: &zeroValue} + c.GetTotalActiveUsers() + c = &CopilotMetrics{} + c.GetTotalActiveUsers() + c = nil + c.GetTotalActiveUsers() +} + +func TestCopilotMetrics_GetTotalEngagedUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetrics{TotalEngagedUsers: &zeroValue} + c.GetTotalEngagedUsers() + c = &CopilotMetrics{} + c.GetTotalEngagedUsers() + c = nil + c.GetTotalEngagedUsers() +} + +func TestCopilotMetricsChatPanel_GetChatPanelAgentMode(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsChatPanel{ChatPanelAgentMode: &zeroValue} + c.GetChatPanelAgentMode() + c = &CopilotMetricsChatPanel{} + c.GetChatPanelAgentMode() + c = nil + c.GetChatPanelAgentMode() +} + +func TestCopilotMetricsChatPanel_GetChatPanelAskMode(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsChatPanel{ChatPanelAskMode: &zeroValue} + c.GetChatPanelAskMode() + c = &CopilotMetricsChatPanel{} + c.GetChatPanelAskMode() + c = nil + c.GetChatPanelAskMode() +} + +func TestCopilotMetricsChatPanel_GetChatPanelCustomMode(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsChatPanel{ChatPanelCustomMode: &zeroValue} + c.GetChatPanelCustomMode() + c = &CopilotMetricsChatPanel{} + c.GetChatPanelCustomMode() + c = nil + c.GetChatPanelCustomMode() +} + +func TestCopilotMetricsChatPanel_GetChatPanelEditMode(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsChatPanel{ChatPanelEditMode: &zeroValue} + c.GetChatPanelEditMode() + c = &CopilotMetricsChatPanel{} + c.GetChatPanelEditMode() + c = nil + c.GetChatPanelEditMode() +} + +func TestCopilotMetricsChatPanel_GetChatPanelUnknownMode(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsChatPanel{ChatPanelUnknownMode: &zeroValue} + c.GetChatPanelUnknownMode() + c = &CopilotMetricsChatPanel{} + c.GetChatPanelUnknownMode() + c = nil + c.GetChatPanelUnknownMode() +} + +func TestCopilotMetricsCLI_GetLastKnownCLIVersion(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsCLI{} + c.GetLastKnownCLIVersion() + c = nil + c.GetLastKnownCLIVersion() +} + +func TestCopilotMetricsCLI_GetPromptCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCLI{PromptCount: &zeroValue} + c.GetPromptCount() + c = &CopilotMetricsCLI{} + c.GetPromptCount() + c = nil + c.GetPromptCount() +} + +func TestCopilotMetricsCLI_GetRequestCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCLI{RequestCount: &zeroValue} + c.GetRequestCount() + c = &CopilotMetricsCLI{} + c.GetRequestCount() + c = nil + c.GetRequestCount() +} + +func TestCopilotMetricsCLI_GetSessionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCLI{SessionCount: &zeroValue} + c.GetSessionCount() + c = &CopilotMetricsCLI{} + c.GetSessionCount() + c = nil + c.GetSessionCount() +} + +func TestCopilotMetricsCLI_GetTokenUsage(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsCLI{} + c.GetTokenUsage() + c = nil + c.GetTokenUsage() +} + +func TestCopilotMetricsCLITokenUsage_GetAvgTokensPerRequest(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CopilotMetricsCLITokenUsage{AvgTokensPerRequest: &zeroValue} + c.GetAvgTokensPerRequest() + c = &CopilotMetricsCLITokenUsage{} + c.GetAvgTokensPerRequest() + c = nil + c.GetAvgTokensPerRequest() +} + +func TestCopilotMetricsCLITokenUsage_GetOutputTokensSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCLITokenUsage{OutputTokensSum: &zeroValue} + c.GetOutputTokensSum() + c = &CopilotMetricsCLITokenUsage{} + c.GetOutputTokensSum() + c = nil + c.GetOutputTokensSum() +} + +func TestCopilotMetricsCLITokenUsage_GetPromptTokensSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCLITokenUsage{PromptTokensSum: &zeroValue} + c.GetPromptTokensSum() + c = &CopilotMetricsCLITokenUsage{} + c.GetPromptTokensSum() + c = nil + c.GetPromptTokensSum() +} + +func TestCopilotMetricsCLIVersion_GetCLIVersion(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsCLIVersion{} + c.GetCLIVersion() + c = nil + c.GetCLIVersion() +} + +func TestCopilotMetricsCLIVersion_GetSampledAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotMetricsCLIVersion{SampledAt: &zeroValue} + c.GetSampledAt() + c = &CopilotMetricsCLIVersion{} + c.GetSampledAt() + c = nil + c.GetSampledAt() +} + +func TestCopilotMetricsCodeActivity_GetCodeAcceptanceActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCodeActivity{CodeAcceptanceActivityCount: &zeroValue} + c.GetCodeAcceptanceActivityCount() + c = &CopilotMetricsCodeActivity{} + c.GetCodeAcceptanceActivityCount() + c = nil + c.GetCodeAcceptanceActivityCount() +} + +func TestCopilotMetricsCodeActivity_GetCodeGenerationActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCodeActivity{CodeGenerationActivityCount: &zeroValue} + c.GetCodeGenerationActivityCount() + c = &CopilotMetricsCodeActivity{} + c.GetCodeGenerationActivityCount() + c = nil + c.GetCodeGenerationActivityCount() +} + +func TestCopilotMetricsCodeActivity_GetLOCAddedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCodeActivity{LOCAddedSum: &zeroValue} + c.GetLOCAddedSum() + c = &CopilotMetricsCodeActivity{} + c.GetLOCAddedSum() + c = nil + c.GetLOCAddedSum() +} + +func TestCopilotMetricsCodeActivity_GetLOCDeletedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCodeActivity{LOCDeletedSum: &zeroValue} + c.GetLOCDeletedSum() + c = &CopilotMetricsCodeActivity{} + c.GetLOCDeletedSum() + c = nil + c.GetLOCDeletedSum() +} + +func TestCopilotMetricsCodeActivity_GetLOCSuggestedToAddSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCodeActivity{LOCSuggestedToAddSum: &zeroValue} + c.GetLOCSuggestedToAddSum() + c = &CopilotMetricsCodeActivity{} + c.GetLOCSuggestedToAddSum() + c = nil + c.GetLOCSuggestedToAddSum() +} + +func TestCopilotMetricsCodeActivity_GetLOCSuggestedToDeleteSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsCodeActivity{LOCSuggestedToDeleteSum: &zeroValue} + c.GetLOCSuggestedToDeleteSum() + c = &CopilotMetricsCodeActivity{} + c.GetLOCSuggestedToDeleteSum() + c = nil + c.GetLOCSuggestedToDeleteSum() +} + +func TestCopilotMetricsFeature_GetFeature(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsFeature{} + c.GetFeature() + c = nil + c.GetFeature() +} + +func TestCopilotMetricsFeature_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsFeature{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotMetricsFeature{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotMetricsIDE_GetIDE(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsIDE{} + c.GetIDE() + c = nil + c.GetIDE() +} + +func TestCopilotMetricsIDE_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsIDE{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotMetricsIDE{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotMetricsLanguageFeature_GetFeature(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsLanguageFeature{} + c.GetFeature() + c = nil + c.GetFeature() +} + +func TestCopilotMetricsLanguageFeature_GetLanguage(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsLanguageFeature{} + c.GetLanguage() + c = nil + c.GetLanguage() +} + +func TestCopilotMetricsLanguageModel_GetLanguage(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsLanguageModel{} + c.GetLanguage() + c = nil + c.GetLanguage() +} + +func TestCopilotMetricsLanguageModel_GetModel(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsLanguageModel{} + c.GetModel() + c = nil + c.GetModel() +} + +func TestCopilotMetricsListOptions_GetSince(tt *testing.T) { + tt.Parallel() + var zeroValue time.Time + c := &CopilotMetricsListOptions{Since: &zeroValue} + c.GetSince() + c = &CopilotMetricsListOptions{} + c.GetSince() + c = nil + c.GetSince() +} + +func TestCopilotMetricsListOptions_GetUntil(tt *testing.T) { + tt.Parallel() + var zeroValue time.Time + c := &CopilotMetricsListOptions{Until: &zeroValue} + c.GetUntil() + c = &CopilotMetricsListOptions{} + c.GetUntil() + c = nil + c.GetUntil() +} + +func TestCopilotMetricsModelFeature_GetFeature(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsModelFeature{} + c.GetFeature() + c = nil + c.GetFeature() +} + +func TestCopilotMetricsModelFeature_GetModel(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsModelFeature{} + c.GetModel() + c = nil + c.GetModel() +} + +func TestCopilotMetricsModelFeature_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsModelFeature{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotMetricsModelFeature{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotMetricsPullRequests_GetMedianMinutesToMerge(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CopilotMetricsPullRequests{MedianMinutesToMerge: &zeroValue} + c.GetMedianMinutesToMerge() + c = &CopilotMetricsPullRequests{} + c.GetMedianMinutesToMerge() + c = nil + c.GetMedianMinutesToMerge() +} + +func TestCopilotMetricsPullRequests_GetMedianMinutesToMergeCopilotAuthored(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CopilotMetricsPullRequests{MedianMinutesToMergeCopilotAuthored: &zeroValue} + c.GetMedianMinutesToMergeCopilotAuthored() + c = &CopilotMetricsPullRequests{} + c.GetMedianMinutesToMergeCopilotAuthored() + c = nil + c.GetMedianMinutesToMergeCopilotAuthored() +} + +func TestCopilotMetricsPullRequests_GetMedianMinutesToMergeCopilotReviewed(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CopilotMetricsPullRequests{MedianMinutesToMergeCopilotReviewed: &zeroValue} + c.GetMedianMinutesToMergeCopilotReviewed() + c = &CopilotMetricsPullRequests{} + c.GetMedianMinutesToMergeCopilotReviewed() + c = nil + c.GetMedianMinutesToMergeCopilotReviewed() +} + +func TestCopilotMetricsPullRequests_GetTotalAppliedSuggestions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalAppliedSuggestions: &zeroValue} + c.GetTotalAppliedSuggestions() + c = &CopilotMetricsPullRequests{} + c.GetTotalAppliedSuggestions() + c = nil + c.GetTotalAppliedSuggestions() +} + +func TestCopilotMetricsPullRequests_GetTotalCopilotAppliedSuggestions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalCopilotAppliedSuggestions: &zeroValue} + c.GetTotalCopilotAppliedSuggestions() + c = &CopilotMetricsPullRequests{} + c.GetTotalCopilotAppliedSuggestions() + c = nil + c.GetTotalCopilotAppliedSuggestions() +} + +func TestCopilotMetricsPullRequests_GetTotalCopilotSuggestions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalCopilotSuggestions: &zeroValue} + c.GetTotalCopilotSuggestions() + c = &CopilotMetricsPullRequests{} + c.GetTotalCopilotSuggestions() + c = nil + c.GetTotalCopilotSuggestions() +} + +func TestCopilotMetricsPullRequests_GetTotalCreated(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalCreated: &zeroValue} + c.GetTotalCreated() + c = &CopilotMetricsPullRequests{} + c.GetTotalCreated() + c = nil + c.GetTotalCreated() +} + +func TestCopilotMetricsPullRequests_GetTotalCreatedByCopilot(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalCreatedByCopilot: &zeroValue} + c.GetTotalCreatedByCopilot() + c = &CopilotMetricsPullRequests{} + c.GetTotalCreatedByCopilot() + c = nil + c.GetTotalCreatedByCopilot() +} + +func TestCopilotMetricsPullRequests_GetTotalMerged(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalMerged: &zeroValue} + c.GetTotalMerged() + c = &CopilotMetricsPullRequests{} + c.GetTotalMerged() + c = nil + c.GetTotalMerged() +} + +func TestCopilotMetricsPullRequests_GetTotalMergedCreatedByCopilot(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalMergedCreatedByCopilot: &zeroValue} + c.GetTotalMergedCreatedByCopilot() + c = &CopilotMetricsPullRequests{} + c.GetTotalMergedCreatedByCopilot() + c = nil + c.GetTotalMergedCreatedByCopilot() +} + +func TestCopilotMetricsPullRequests_GetTotalMergedReviewedByCopilot(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalMergedReviewedByCopilot: &zeroValue} + c.GetTotalMergedReviewedByCopilot() + c = &CopilotMetricsPullRequests{} + c.GetTotalMergedReviewedByCopilot() + c = nil + c.GetTotalMergedReviewedByCopilot() +} + +func TestCopilotMetricsPullRequests_GetTotalReviewed(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalReviewed: &zeroValue} + c.GetTotalReviewed() + c = &CopilotMetricsPullRequests{} + c.GetTotalReviewed() + c = nil + c.GetTotalReviewed() +} + +func TestCopilotMetricsPullRequests_GetTotalReviewedByCopilot(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalReviewedByCopilot: &zeroValue} + c.GetTotalReviewedByCopilot() + c = &CopilotMetricsPullRequests{} + c.GetTotalReviewedByCopilot() + c = nil + c.GetTotalReviewedByCopilot() +} + +func TestCopilotMetricsPullRequests_GetTotalSuggestions(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotMetricsPullRequests{TotalSuggestions: &zeroValue} + c.GetTotalSuggestions() + c = &CopilotMetricsPullRequests{} + c.GetTotalSuggestions() + c = nil + c.GetTotalSuggestions() +} + +func TestCopilotMetricsReport_GetDownloadLinks(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CopilotMetricsReport{DownloadLinks: zeroValue} + c.GetDownloadLinks() + c = &CopilotMetricsReport{} + c.GetDownloadLinks() + c = nil + c.GetDownloadLinks() +} + +func TestCopilotMetricsReport_GetReportEndDay(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsReport{} + c.GetReportEndDay() + c = nil + c.GetReportEndDay() +} + +func TestCopilotMetricsReport_GetReportStartDay(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsReport{} + c.GetReportStartDay() + c = nil + c.GetReportStartDay() +} + +func TestCopilotMetricsReportOptions_GetDay(tt *testing.T) { + tt.Parallel() + c := &CopilotMetricsReportOptions{} + c.GetDay() + c = nil + c.GetDay() +} + +func TestCopilotOrganizationDetails_GetCopilotChat(tt *testing.T) { + tt.Parallel() + c := &CopilotOrganizationDetails{} + c.GetCopilotChat() + c = nil + c.GetCopilotChat() +} + +func TestCopilotOrganizationDetails_GetPublicCodeSuggestions(tt *testing.T) { + tt.Parallel() + c := &CopilotOrganizationDetails{} + c.GetPublicCodeSuggestions() + c = nil + c.GetPublicCodeSuggestions() +} + +func TestCopilotOrganizationDetails_GetSeatBreakdown(tt *testing.T) { + tt.Parallel() + c := &CopilotOrganizationDetails{} + c.GetSeatBreakdown() + c = nil + c.GetSeatBreakdown() +} + +func TestCopilotOrganizationDetails_GetSeatManagementSetting(tt *testing.T) { + tt.Parallel() + c := &CopilotOrganizationDetails{} + c.GetSeatManagementSetting() + c = nil + c.GetSeatManagementSetting() +} + +func TestCopilotPeriodicMetrics_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotPeriodicMetrics{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CopilotPeriodicMetrics{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCopilotPeriodicMetrics_GetDayTotals(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotDailyMetrics{} + c := &CopilotPeriodicMetrics{DayTotals: zeroValue} + c.GetDayTotals() + c = &CopilotPeriodicMetrics{} + c.GetDayTotals() + c = nil + c.GetDayTotals() +} + +func TestCopilotPeriodicMetrics_GetEnterpriseID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotPeriodicMetrics{EnterpriseID: &zeroValue} + c.GetEnterpriseID() + c = &CopilotPeriodicMetrics{} + c.GetEnterpriseID() + c = nil + c.GetEnterpriseID() +} + +func TestCopilotPeriodicMetrics_GetOrganizationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotPeriodicMetrics{OrganizationID: &zeroValue} + c.GetOrganizationID() + c = &CopilotPeriodicMetrics{} + c.GetOrganizationID() + c = nil + c.GetOrganizationID() +} + +func TestCopilotPeriodicMetrics_GetReportEndDay(tt *testing.T) { + tt.Parallel() + c := &CopilotPeriodicMetrics{} + c.GetReportEndDay() + c = nil + c.GetReportEndDay() +} + +func TestCopilotPeriodicMetrics_GetReportStartDay(tt *testing.T) { + tt.Parallel() + c := &CopilotPeriodicMetrics{} + c.GetReportStartDay() + c = nil + c.GetReportStartDay() +} + +func TestCopilotSeatBreakdown_GetActiveThisCycle(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatBreakdown{} + c.GetActiveThisCycle() + c = nil + c.GetActiveThisCycle() +} + +func TestCopilotSeatBreakdown_GetAddedThisCycle(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatBreakdown{} + c.GetAddedThisCycle() + c = nil + c.GetAddedThisCycle() +} + +func TestCopilotSeatBreakdown_GetInactiveThisCycle(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatBreakdown{} + c.GetInactiveThisCycle() + c = nil + c.GetInactiveThisCycle() +} + +func TestCopilotSeatBreakdown_GetPendingCancellation(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatBreakdown{} + c.GetPendingCancellation() + c = nil + c.GetPendingCancellation() +} + +func TestCopilotSeatBreakdown_GetPendingInvitation(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatBreakdown{} + c.GetPendingInvitation() + c = nil + c.GetPendingInvitation() +} + +func TestCopilotSeatBreakdown_GetTotal(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatBreakdown{} + c.GetTotal() + c = nil + c.GetTotal() +} + +func TestCopilotSeatDetails_GetAssignee(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatDetails{} + c.GetAssignee() + c = nil + c.GetAssignee() +} + +func TestCopilotSeatDetails_GetAssigningTeam(tt *testing.T) { + tt.Parallel() + c := &CopilotSeatDetails{} + c.GetAssigningTeam() + c = nil + c.GetAssigningTeam() +} + +func TestCopilotSeatDetails_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotSeatDetails{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CopilotSeatDetails{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCopilotSeatDetails_GetLastActivityAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotSeatDetails{LastActivityAt: &zeroValue} + c.GetLastActivityAt() + c = &CopilotSeatDetails{} + c.GetLastActivityAt() + c = nil + c.GetLastActivityAt() +} + +func TestCopilotSeatDetails_GetLastActivityEditor(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotSeatDetails{LastActivityEditor: &zeroValue} + c.GetLastActivityEditor() + c = &CopilotSeatDetails{} + c.GetLastActivityEditor() + c = nil + c.GetLastActivityEditor() +} + +func TestCopilotSeatDetails_GetPendingCancellationDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotSeatDetails{PendingCancellationDate: &zeroValue} + c.GetPendingCancellationDate() + c = &CopilotSeatDetails{} + c.GetPendingCancellationDate() + c = nil + c.GetPendingCancellationDate() +} + +func TestCopilotSeatDetails_GetPlanType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotSeatDetails{PlanType: &zeroValue} + c.GetPlanType() + c = &CopilotSeatDetails{} + c.GetPlanType() + c = nil + c.GetPlanType() +} + +func TestCopilotSeatDetails_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotSeatDetails{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CopilotSeatDetails{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCopilotUserDailyMetrics_GetCodeAcceptanceActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{CodeAcceptanceActivityCount: &zeroValue} + c.GetCodeAcceptanceActivityCount() + c = &CopilotUserDailyMetrics{} + c.GetCodeAcceptanceActivityCount() + c = nil + c.GetCodeAcceptanceActivityCount() +} + +func TestCopilotUserDailyMetrics_GetCodeGenerationActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{CodeGenerationActivityCount: &zeroValue} + c.GetCodeGenerationActivityCount() + c = &CopilotUserDailyMetrics{} + c.GetCodeGenerationActivityCount() + c = nil + c.GetCodeGenerationActivityCount() +} + +func TestCopilotUserDailyMetrics_GetDay(tt *testing.T) { + tt.Parallel() + c := &CopilotUserDailyMetrics{} + c.GetDay() + c = nil + c.GetDay() +} + +func TestCopilotUserDailyMetrics_GetEnterpriseID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotUserDailyMetrics{EnterpriseID: &zeroValue} + c.GetEnterpriseID() + c = &CopilotUserDailyMetrics{} + c.GetEnterpriseID() + c = nil + c.GetEnterpriseID() +} + +func TestCopilotUserDailyMetrics_GetLOCAddedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{LOCAddedSum: &zeroValue} + c.GetLOCAddedSum() + c = &CopilotUserDailyMetrics{} + c.GetLOCAddedSum() + c = nil + c.GetLOCAddedSum() +} + +func TestCopilotUserDailyMetrics_GetLOCDeletedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{LOCDeletedSum: &zeroValue} + c.GetLOCDeletedSum() + c = &CopilotUserDailyMetrics{} + c.GetLOCDeletedSum() + c = nil + c.GetLOCDeletedSum() +} + +func TestCopilotUserDailyMetrics_GetLOCSuggestedToAddSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{LOCSuggestedToAddSum: &zeroValue} + c.GetLOCSuggestedToAddSum() + c = &CopilotUserDailyMetrics{} + c.GetLOCSuggestedToAddSum() + c = nil + c.GetLOCSuggestedToAddSum() +} + +func TestCopilotUserDailyMetrics_GetLOCSuggestedToDeleteSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{LOCSuggestedToDeleteSum: &zeroValue} + c.GetLOCSuggestedToDeleteSum() + c = &CopilotUserDailyMetrics{} + c.GetLOCSuggestedToDeleteSum() + c = nil + c.GetLOCSuggestedToDeleteSum() +} + +func TestCopilotUserDailyMetrics_GetOrganizationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotUserDailyMetrics{OrganizationID: &zeroValue} + c.GetOrganizationID() + c = &CopilotUserDailyMetrics{} + c.GetOrganizationID() + c = nil + c.GetOrganizationID() +} + +func TestCopilotUserDailyMetrics_GetTotalsByCLI(tt *testing.T) { + tt.Parallel() + c := &CopilotUserDailyMetrics{} + c.GetTotalsByCLI() + c = nil + c.GetTotalsByCLI() +} + +func TestCopilotUserDailyMetrics_GetTotalsByFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsFeature{} + c := &CopilotUserDailyMetrics{TotalsByFeature: zeroValue} + c.GetTotalsByFeature() + c = &CopilotUserDailyMetrics{} + c.GetTotalsByFeature() + c = nil + c.GetTotalsByFeature() +} + +func TestCopilotUserDailyMetrics_GetTotalsByIDE(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotUserMetricsIDE{} + c := &CopilotUserDailyMetrics{TotalsByIDE: zeroValue} + c.GetTotalsByIDE() + c = &CopilotUserDailyMetrics{} + c.GetTotalsByIDE() + c = nil + c.GetTotalsByIDE() +} + +func TestCopilotUserDailyMetrics_GetTotalsByLanguageFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsLanguageFeature{} + c := &CopilotUserDailyMetrics{TotalsByLanguageFeature: zeroValue} + c.GetTotalsByLanguageFeature() + c = &CopilotUserDailyMetrics{} + c.GetTotalsByLanguageFeature() + c = nil + c.GetTotalsByLanguageFeature() +} + +func TestCopilotUserDailyMetrics_GetTotalsByLanguageModel(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsLanguageModel{} + c := &CopilotUserDailyMetrics{TotalsByLanguageModel: zeroValue} + c.GetTotalsByLanguageModel() + c = &CopilotUserDailyMetrics{} + c.GetTotalsByLanguageModel() + c = nil + c.GetTotalsByLanguageModel() +} + +func TestCopilotUserDailyMetrics_GetTotalsByModelFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsModelFeature{} + c := &CopilotUserDailyMetrics{TotalsByModelFeature: zeroValue} + c.GetTotalsByModelFeature() + c = &CopilotUserDailyMetrics{} + c.GetTotalsByModelFeature() + c = nil + c.GetTotalsByModelFeature() +} + +func TestCopilotUserDailyMetrics_GetUsedAgent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserDailyMetrics{UsedAgent: &zeroValue} + c.GetUsedAgent() + c = &CopilotUserDailyMetrics{} + c.GetUsedAgent() + c = nil + c.GetUsedAgent() +} + +func TestCopilotUserDailyMetrics_GetUsedChat(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserDailyMetrics{UsedChat: &zeroValue} + c.GetUsedChat() + c = &CopilotUserDailyMetrics{} + c.GetUsedChat() + c = nil + c.GetUsedChat() +} + +func TestCopilotUserDailyMetrics_GetUsedCLI(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserDailyMetrics{UsedCLI: &zeroValue} + c.GetUsedCLI() + c = &CopilotUserDailyMetrics{} + c.GetUsedCLI() + c = nil + c.GetUsedCLI() +} + +func TestCopilotUserDailyMetrics_GetUsedCopilotCodeReviewActive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserDailyMetrics{UsedCopilotCodeReviewActive: &zeroValue} + c.GetUsedCopilotCodeReviewActive() + c = &CopilotUserDailyMetrics{} + c.GetUsedCopilotCodeReviewActive() + c = nil + c.GetUsedCopilotCodeReviewActive() +} + +func TestCopilotUserDailyMetrics_GetUsedCopilotCodeReviewPassive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserDailyMetrics{UsedCopilotCodeReviewPassive: &zeroValue} + c.GetUsedCopilotCodeReviewPassive() + c = &CopilotUserDailyMetrics{} + c.GetUsedCopilotCodeReviewPassive() + c = nil + c.GetUsedCopilotCodeReviewPassive() +} + +func TestCopilotUserDailyMetrics_GetUsedCopilotCodingAgent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserDailyMetrics{UsedCopilotCodingAgent: &zeroValue} + c.GetUsedCopilotCodingAgent() + c = &CopilotUserDailyMetrics{} + c.GetUsedCopilotCodingAgent() + c = nil + c.GetUsedCopilotCodingAgent() +} + +func TestCopilotUserDailyMetrics_GetUserID(tt *testing.T) { + tt.Parallel() + c := &CopilotUserDailyMetrics{} + c.GetUserID() + c = nil + c.GetUserID() +} + +func TestCopilotUserDailyMetrics_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserDailyMetrics{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotUserDailyMetrics{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotUserDailyMetrics_GetUserLogin(tt *testing.T) { + tt.Parallel() + c := &CopilotUserDailyMetrics{} + c.GetUserLogin() + c = nil + c.GetUserLogin() +} + +func TestCopilotUserMetricsIDE_GetIDE(tt *testing.T) { + tt.Parallel() + c := &CopilotUserMetricsIDE{} + c.GetIDE() + c = nil + c.GetIDE() +} + +func TestCopilotUserMetricsIDE_GetLastKnownIDEVersion(tt *testing.T) { + tt.Parallel() + c := &CopilotUserMetricsIDE{} + c.GetLastKnownIDEVersion() + c = nil + c.GetLastKnownIDEVersion() +} + +func TestCopilotUserMetricsIDE_GetLastKnownPluginVersion(tt *testing.T) { + tt.Parallel() + c := &CopilotUserMetricsIDE{} + c.GetLastKnownPluginVersion() + c = nil + c.GetLastKnownPluginVersion() +} + +func TestCopilotUserMetricsIDE_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserMetricsIDE{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotUserMetricsIDE{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotUserMetricsIDEVersion_GetIDEVersion(tt *testing.T) { + tt.Parallel() + c := &CopilotUserMetricsIDEVersion{} + c.GetIDEVersion() + c = nil + c.GetIDEVersion() +} + +func TestCopilotUserMetricsIDEVersion_GetSampledAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotUserMetricsIDEVersion{SampledAt: &zeroValue} + c.GetSampledAt() + c = &CopilotUserMetricsIDEVersion{} + c.GetSampledAt() + c = nil + c.GetSampledAt() +} + +func TestCopilotUserMetricsPluginVersion_GetPlugin(tt *testing.T) { + tt.Parallel() + c := &CopilotUserMetricsPluginVersion{} + c.GetPlugin() + c = nil + c.GetPlugin() +} + +func TestCopilotUserMetricsPluginVersion_GetPluginVersion(tt *testing.T) { + tt.Parallel() + c := &CopilotUserMetricsPluginVersion{} + c.GetPluginVersion() + c = nil + c.GetPluginVersion() +} + +func TestCopilotUserMetricsPluginVersion_GetSampledAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CopilotUserMetricsPluginVersion{SampledAt: &zeroValue} + c.GetSampledAt() + c = &CopilotUserMetricsPluginVersion{} + c.GetSampledAt() + c = nil + c.GetSampledAt() +} + +func TestCopilotUserPeriodicMetrics_GetCodeAcceptanceActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{CodeAcceptanceActivityCount: &zeroValue} + c.GetCodeAcceptanceActivityCount() + c = &CopilotUserPeriodicMetrics{} + c.GetCodeAcceptanceActivityCount() + c = nil + c.GetCodeAcceptanceActivityCount() +} + +func TestCopilotUserPeriodicMetrics_GetCodeGenerationActivityCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{CodeGenerationActivityCount: &zeroValue} + c.GetCodeGenerationActivityCount() + c = &CopilotUserPeriodicMetrics{} + c.GetCodeGenerationActivityCount() + c = nil + c.GetCodeGenerationActivityCount() +} + +func TestCopilotUserPeriodicMetrics_GetDay(tt *testing.T) { + tt.Parallel() + c := &CopilotUserPeriodicMetrics{} + c.GetDay() + c = nil + c.GetDay() +} + +func TestCopilotUserPeriodicMetrics_GetEnterpriseID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotUserPeriodicMetrics{EnterpriseID: &zeroValue} + c.GetEnterpriseID() + c = &CopilotUserPeriodicMetrics{} + c.GetEnterpriseID() + c = nil + c.GetEnterpriseID() +} + +func TestCopilotUserPeriodicMetrics_GetLOCAddedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{LOCAddedSum: &zeroValue} + c.GetLOCAddedSum() + c = &CopilotUserPeriodicMetrics{} + c.GetLOCAddedSum() + c = nil + c.GetLOCAddedSum() +} + +func TestCopilotUserPeriodicMetrics_GetLOCDeletedSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{LOCDeletedSum: &zeroValue} + c.GetLOCDeletedSum() + c = &CopilotUserPeriodicMetrics{} + c.GetLOCDeletedSum() + c = nil + c.GetLOCDeletedSum() +} + +func TestCopilotUserPeriodicMetrics_GetLOCSuggestedToAddSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{LOCSuggestedToAddSum: &zeroValue} + c.GetLOCSuggestedToAddSum() + c = &CopilotUserPeriodicMetrics{} + c.GetLOCSuggestedToAddSum() + c = nil + c.GetLOCSuggestedToAddSum() +} + +func TestCopilotUserPeriodicMetrics_GetLOCSuggestedToDeleteSum(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{LOCSuggestedToDeleteSum: &zeroValue} + c.GetLOCSuggestedToDeleteSum() + c = &CopilotUserPeriodicMetrics{} + c.GetLOCSuggestedToDeleteSum() + c = nil + c.GetLOCSuggestedToDeleteSum() +} + +func TestCopilotUserPeriodicMetrics_GetOrganizationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CopilotUserPeriodicMetrics{OrganizationID: &zeroValue} + c.GetOrganizationID() + c = &CopilotUserPeriodicMetrics{} + c.GetOrganizationID() + c = nil + c.GetOrganizationID() +} + +func TestCopilotUserPeriodicMetrics_GetReportEndDay(tt *testing.T) { + tt.Parallel() + c := &CopilotUserPeriodicMetrics{} + c.GetReportEndDay() + c = nil + c.GetReportEndDay() +} + +func TestCopilotUserPeriodicMetrics_GetReportStartDay(tt *testing.T) { + tt.Parallel() + c := &CopilotUserPeriodicMetrics{} + c.GetReportStartDay() + c = nil + c.GetReportStartDay() +} + +func TestCopilotUserPeriodicMetrics_GetTotalsByCLI(tt *testing.T) { + tt.Parallel() + c := &CopilotUserPeriodicMetrics{} + c.GetTotalsByCLI() + c = nil + c.GetTotalsByCLI() +} + +func TestCopilotUserPeriodicMetrics_GetTotalsByFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsFeature{} + c := &CopilotUserPeriodicMetrics{TotalsByFeature: zeroValue} + c.GetTotalsByFeature() + c = &CopilotUserPeriodicMetrics{} + c.GetTotalsByFeature() + c = nil + c.GetTotalsByFeature() +} + +func TestCopilotUserPeriodicMetrics_GetTotalsByIDE(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotUserMetricsIDE{} + c := &CopilotUserPeriodicMetrics{TotalsByIDE: zeroValue} + c.GetTotalsByIDE() + c = &CopilotUserPeriodicMetrics{} + c.GetTotalsByIDE() + c = nil + c.GetTotalsByIDE() +} + +func TestCopilotUserPeriodicMetrics_GetTotalsByLanguageFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsLanguageFeature{} + c := &CopilotUserPeriodicMetrics{TotalsByLanguageFeature: zeroValue} + c.GetTotalsByLanguageFeature() + c = &CopilotUserPeriodicMetrics{} + c.GetTotalsByLanguageFeature() + c = nil + c.GetTotalsByLanguageFeature() +} + +func TestCopilotUserPeriodicMetrics_GetTotalsByLanguageModel(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsLanguageModel{} + c := &CopilotUserPeriodicMetrics{TotalsByLanguageModel: zeroValue} + c.GetTotalsByLanguageModel() + c = &CopilotUserPeriodicMetrics{} + c.GetTotalsByLanguageModel() + c = nil + c.GetTotalsByLanguageModel() +} + +func TestCopilotUserPeriodicMetrics_GetTotalsByModelFeature(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotMetricsModelFeature{} + c := &CopilotUserPeriodicMetrics{TotalsByModelFeature: zeroValue} + c.GetTotalsByModelFeature() + c = &CopilotUserPeriodicMetrics{} + c.GetTotalsByModelFeature() + c = nil + c.GetTotalsByModelFeature() +} + +func TestCopilotUserPeriodicMetrics_GetUsedAgent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserPeriodicMetrics{UsedAgent: &zeroValue} + c.GetUsedAgent() + c = &CopilotUserPeriodicMetrics{} + c.GetUsedAgent() + c = nil + c.GetUsedAgent() +} + +func TestCopilotUserPeriodicMetrics_GetUsedChat(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserPeriodicMetrics{UsedChat: &zeroValue} + c.GetUsedChat() + c = &CopilotUserPeriodicMetrics{} + c.GetUsedChat() + c = nil + c.GetUsedChat() +} + +func TestCopilotUserPeriodicMetrics_GetUsedCLI(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserPeriodicMetrics{UsedCLI: &zeroValue} + c.GetUsedCLI() + c = &CopilotUserPeriodicMetrics{} + c.GetUsedCLI() + c = nil + c.GetUsedCLI() +} + +func TestCopilotUserPeriodicMetrics_GetUsedCopilotCodeReviewActive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserPeriodicMetrics{UsedCopilotCodeReviewActive: &zeroValue} + c.GetUsedCopilotCodeReviewActive() + c = &CopilotUserPeriodicMetrics{} + c.GetUsedCopilotCodeReviewActive() + c = nil + c.GetUsedCopilotCodeReviewActive() +} + +func TestCopilotUserPeriodicMetrics_GetUsedCopilotCodeReviewPassive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserPeriodicMetrics{UsedCopilotCodeReviewPassive: &zeroValue} + c.GetUsedCopilotCodeReviewPassive() + c = &CopilotUserPeriodicMetrics{} + c.GetUsedCopilotCodeReviewPassive() + c = nil + c.GetUsedCopilotCodeReviewPassive() +} + +func TestCopilotUserPeriodicMetrics_GetUsedCopilotCodingAgent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CopilotUserPeriodicMetrics{UsedCopilotCodingAgent: &zeroValue} + c.GetUsedCopilotCodingAgent() + c = &CopilotUserPeriodicMetrics{} + c.GetUsedCopilotCodingAgent() + c = nil + c.GetUsedCopilotCodingAgent() +} + +func TestCopilotUserPeriodicMetrics_GetUserID(tt *testing.T) { + tt.Parallel() + c := &CopilotUserPeriodicMetrics{} + c.GetUserID() + c = nil + c.GetUserID() +} + +func TestCopilotUserPeriodicMetrics_GetUserInitiatedInteractionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CopilotUserPeriodicMetrics{UserInitiatedInteractionCount: &zeroValue} + c.GetUserInitiatedInteractionCount() + c = &CopilotUserPeriodicMetrics{} + c.GetUserInitiatedInteractionCount() + c = nil + c.GetUserInitiatedInteractionCount() +} + +func TestCopilotUserPeriodicMetrics_GetUserLogin(tt *testing.T) { + tt.Parallel() + c := &CopilotUserPeriodicMetrics{} + c.GetUserLogin() + c = nil + c.GetUserLogin() +} + +func TestCostCenter_GetAzureSubscription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenter{AzureSubscription: &zeroValue} + c.GetAzureSubscription() + c = &CostCenter{} + c.GetAzureSubscription() + c = nil + c.GetAzureSubscription() +} + +func TestCostCenter_GetID(tt *testing.T) { + tt.Parallel() + c := &CostCenter{} + c.GetID() + c = nil + c.GetID() +} + +func TestCostCenter_GetName(tt *testing.T) { + tt.Parallel() + c := &CostCenter{} + c.GetName() + c = nil + c.GetName() +} + +func TestCostCenter_GetResources(tt *testing.T) { + tt.Parallel() + zeroValue := []*CostCenterResource{} + c := &CostCenter{Resources: zeroValue} + c.GetResources() + c = &CostCenter{} + c.GetResources() + c = nil + c.GetResources() +} + +func TestCostCenter_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CostCenter{State: &zeroValue} + c.GetState() + c = &CostCenter{} + c.GetState() + c = nil + c.GetState() +} + +func TestCostCenterRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CostCenterRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCostCenterResource_GetName(tt *testing.T) { + tt.Parallel() + c := &CostCenterResource{} + c.GetName() + c = nil + c.GetName() +} + +func TestCostCenterResource_GetType(tt *testing.T) { + tt.Parallel() + c := &CostCenterResource{} + c.GetType() + c = nil + c.GetType() +} + +func TestCostCenterResourceRequest_GetOrganizations(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CostCenterResourceRequest{Organizations: zeroValue} + c.GetOrganizations() + c = &CostCenterResourceRequest{} + c.GetOrganizations() + c = nil + c.GetOrganizations() +} + +func TestCostCenterResourceRequest_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CostCenterResourceRequest{Repositories: zeroValue} + c.GetRepositories() + c = &CostCenterResourceRequest{} + c.GetRepositories() + c = nil + c.GetRepositories() +} + +func TestCostCenterResourceRequest_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CostCenterResourceRequest{Users: zeroValue} + c.GetUsers() + c = &CostCenterResourceRequest{} + c.GetUsers() + c = nil + c.GetUsers() +} + +func TestCostCenters_GetCostCenters(tt *testing.T) { + tt.Parallel() + zeroValue := []*CostCenter{} + c := &CostCenters{CostCenters: zeroValue} + c.GetCostCenters() + c = &CostCenters{} + c.GetCostCenters() + c = nil + c.GetCostCenters() +} + +func TestCreateArtifactDeploymentRequest_GetCluster(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactDeploymentRequest{Cluster: &zeroValue} + c.GetCluster() + c = &CreateArtifactDeploymentRequest{} + c.GetCluster() + c = nil + c.GetCluster() +} + +func TestCreateArtifactDeploymentRequest_GetDeploymentName(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactDeploymentRequest{} + c.GetDeploymentName() + c = nil + c.GetDeploymentName() +} + +func TestCreateArtifactDeploymentRequest_GetDigest(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactDeploymentRequest{} + c.GetDigest() + c = nil + c.GetDigest() +} + +func TestCreateArtifactDeploymentRequest_GetGithubRepository(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactDeploymentRequest{GithubRepository: &zeroValue} + c.GetGithubRepository() + c = &CreateArtifactDeploymentRequest{} + c.GetGithubRepository() + c = nil + c.GetGithubRepository() +} + +func TestCreateArtifactDeploymentRequest_GetLogicalEnvironment(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactDeploymentRequest{} + c.GetLogicalEnvironment() + c = nil + c.GetLogicalEnvironment() +} + +func TestCreateArtifactDeploymentRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactDeploymentRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateArtifactDeploymentRequest_GetPhysicalEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactDeploymentRequest{PhysicalEnvironment: &zeroValue} + c.GetPhysicalEnvironment() + c = &CreateArtifactDeploymentRequest{} + c.GetPhysicalEnvironment() + c = nil + c.GetPhysicalEnvironment() +} + +func TestCreateArtifactDeploymentRequest_GetRuntimeRisks(tt *testing.T) { + tt.Parallel() + zeroValue := []DeploymentRuntimeRisk{} + c := &CreateArtifactDeploymentRequest{RuntimeRisks: zeroValue} + c.GetRuntimeRisks() + c = &CreateArtifactDeploymentRequest{} + c.GetRuntimeRisks() + c = nil + c.GetRuntimeRisks() +} + +func TestCreateArtifactDeploymentRequest_GetStatus(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactDeploymentRequest{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCreateArtifactDeploymentRequest_GetTags(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + c := &CreateArtifactDeploymentRequest{Tags: zeroValue} + c.GetTags() + c = &CreateArtifactDeploymentRequest{} + c.GetTags() + c = nil + c.GetTags() +} + +func TestCreateArtifactDeploymentRequest_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactDeploymentRequest{Version: &zeroValue} + c.GetVersion() + c = &CreateArtifactDeploymentRequest{} + c.GetVersion() + c = nil + c.GetVersion() +} + +func TestCreateArtifactStorageRequest_GetArtifactURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactStorageRequest{ArtifactURL: &zeroValue} + c.GetArtifactURL() + c = &CreateArtifactStorageRequest{} + c.GetArtifactURL() + c = nil + c.GetArtifactURL() +} + +func TestCreateArtifactStorageRequest_GetDigest(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactStorageRequest{} + c.GetDigest() + c = nil + c.GetDigest() +} + +func TestCreateArtifactStorageRequest_GetGithubRepository(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactStorageRequest{GithubRepository: &zeroValue} + c.GetGithubRepository() + c = &CreateArtifactStorageRequest{} + c.GetGithubRepository() + c = nil + c.GetGithubRepository() +} + +func TestCreateArtifactStorageRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactStorageRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateArtifactStorageRequest_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactStorageRequest{Path: &zeroValue} + c.GetPath() + c = &CreateArtifactStorageRequest{} + c.GetPath() + c = nil + c.GetPath() +} + +func TestCreateArtifactStorageRequest_GetRegistryURL(tt *testing.T) { + tt.Parallel() + c := &CreateArtifactStorageRequest{} + c.GetRegistryURL() + c = nil + c.GetRegistryURL() +} + +func TestCreateArtifactStorageRequest_GetRepository(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactStorageRequest{Repository: &zeroValue} + c.GetRepository() + c = &CreateArtifactStorageRequest{} + c.GetRepository() + c = nil + c.GetRepository() +} + +func TestCreateArtifactStorageRequest_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactStorageRequest{Status: &zeroValue} + c.GetStatus() + c = &CreateArtifactStorageRequest{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCreateArtifactStorageRequest_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateArtifactStorageRequest{Version: &zeroValue} + c.GetVersion() + c = &CreateArtifactStorageRequest{} + c.GetVersion() + c = nil + c.GetVersion() +} + +func TestCreateAutolinkRequest_GetIsAlphanumeric(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateAutolinkRequest{IsAlphanumeric: &zeroValue} + c.GetIsAlphanumeric() + c = &CreateAutolinkRequest{} + c.GetIsAlphanumeric() + c = nil + c.GetIsAlphanumeric() +} + +func TestCreateAutolinkRequest_GetKeyPrefix(tt *testing.T) { + tt.Parallel() + c := &CreateAutolinkRequest{} + c.GetKeyPrefix() + c = nil + c.GetKeyPrefix() +} + +func TestCreateAutolinkRequest_GetURLTemplate(tt *testing.T) { + tt.Parallel() + c := &CreateAutolinkRequest{} + c.GetURLTemplate() + c = nil + c.GetURLTemplate() +} + +func TestCreateCheckRunOptions_GetActions(tt *testing.T) { + tt.Parallel() + zeroValue := []*CheckRunAction{} + c := &CreateCheckRunOptions{Actions: zeroValue} + c.GetActions() + c = &CreateCheckRunOptions{} + c.GetActions() + c = nil + c.GetActions() +} + +func TestCreateCheckRunOptions_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CreateCheckRunOptions{CompletedAt: &zeroValue} + c.GetCompletedAt() + c = &CreateCheckRunOptions{} + c.GetCompletedAt() + c = nil + c.GetCompletedAt() +} + +func TestCreateCheckRunOptions_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCheckRunOptions{Conclusion: &zeroValue} + c.GetConclusion() + c = &CreateCheckRunOptions{} + c.GetConclusion() + c = nil + c.GetConclusion() +} + +func TestCreateCheckRunOptions_GetDetailsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCheckRunOptions{DetailsURL: &zeroValue} + c.GetDetailsURL() + c = &CreateCheckRunOptions{} + c.GetDetailsURL() + c = nil + c.GetDetailsURL() +} + +func TestCreateCheckRunOptions_GetExternalID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCheckRunOptions{ExternalID: &zeroValue} + c.GetExternalID() + c = &CreateCheckRunOptions{} + c.GetExternalID() + c = nil + c.GetExternalID() +} + +func TestCreateCheckRunOptions_GetHeadSHA(tt *testing.T) { + tt.Parallel() + c := &CreateCheckRunOptions{} + c.GetHeadSHA() + c = nil + c.GetHeadSHA() +} + +func TestCreateCheckRunOptions_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateCheckRunOptions{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateCheckRunOptions_GetOutput(tt *testing.T) { + tt.Parallel() + c := &CreateCheckRunOptions{} + c.GetOutput() + c = nil + c.GetOutput() +} + +func TestCreateCheckRunOptions_GetStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CreateCheckRunOptions{StartedAt: &zeroValue} + c.GetStartedAt() + c = &CreateCheckRunOptions{} + c.GetStartedAt() + c = nil + c.GetStartedAt() +} + +func TestCreateCheckRunOptions_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCheckRunOptions{Status: &zeroValue} + c.GetStatus() + c = &CreateCheckRunOptions{} + c.GetStatus() + c = nil + c.GetStatus() +} + +func TestCreateCheckSuiteOptions_GetHeadBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCheckSuiteOptions{HeadBranch: &zeroValue} + c.GetHeadBranch() + c = &CreateCheckSuiteOptions{} + c.GetHeadBranch() + c = nil + c.GetHeadBranch() +} + +func TestCreateCheckSuiteOptions_GetHeadSHA(tt *testing.T) { + tt.Parallel() + c := &CreateCheckSuiteOptions{} + c.GetHeadSHA() + c = nil + c.GetHeadSHA() +} + +func TestCreateCodespaceOptions_GetClientIP(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{ClientIP: &zeroValue} + c.GetClientIP() + c = &CreateCodespaceOptions{} + c.GetClientIP() + c = nil + c.GetClientIP() +} + +func TestCreateCodespaceOptions_GetDevcontainerPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{DevcontainerPath: &zeroValue} + c.GetDevcontainerPath() + c = &CreateCodespaceOptions{} + c.GetDevcontainerPath() + c = nil + c.GetDevcontainerPath() +} + +func TestCreateCodespaceOptions_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{DisplayName: &zeroValue} + c.GetDisplayName() + c = &CreateCodespaceOptions{} + c.GetDisplayName() + c = nil + c.GetDisplayName() +} + +func TestCreateCodespaceOptions_GetGeo(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{Geo: &zeroValue} + c.GetGeo() + c = &CreateCodespaceOptions{} + c.GetGeo() + c = nil + c.GetGeo() +} + +func TestCreateCodespaceOptions_GetIdleTimeoutMinutes(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CreateCodespaceOptions{IdleTimeoutMinutes: &zeroValue} + c.GetIdleTimeoutMinutes() + c = &CreateCodespaceOptions{} + c.GetIdleTimeoutMinutes() + c = nil + c.GetIdleTimeoutMinutes() +} + +func TestCreateCodespaceOptions_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{Location: &zeroValue} + c.GetLocation() + c = &CreateCodespaceOptions{} + c.GetLocation() + c = nil + c.GetLocation() +} + +func TestCreateCodespaceOptions_GetMachine(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{Machine: &zeroValue} + c.GetMachine() + c = &CreateCodespaceOptions{} + c.GetMachine() + c = nil + c.GetMachine() +} + +func TestCreateCodespaceOptions_GetMultiRepoPermissionsOptOut(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateCodespaceOptions{MultiRepoPermissionsOptOut: &zeroValue} + c.GetMultiRepoPermissionsOptOut() + c = &CreateCodespaceOptions{} + c.GetMultiRepoPermissionsOptOut() + c = nil + c.GetMultiRepoPermissionsOptOut() +} + +func TestCreateCodespaceOptions_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{Ref: &zeroValue} + c.GetRef() + c = &CreateCodespaceOptions{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCreateCodespaceOptions_GetRetentionPeriodMinutes(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CreateCodespaceOptions{RetentionPeriodMinutes: &zeroValue} + c.GetRetentionPeriodMinutes() + c = &CreateCodespaceOptions{} + c.GetRetentionPeriodMinutes() + c = nil + c.GetRetentionPeriodMinutes() +} + +func TestCreateCodespaceOptions_GetWorkingDirectory(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCodespaceOptions{WorkingDirectory: &zeroValue} + c.GetWorkingDirectory() + c = &CreateCodespaceOptions{} + c.GetWorkingDirectory() + c = nil + c.GetWorkingDirectory() +} + +func TestCreateCommitOptions_GetSigner(tt *testing.T) { + tt.Parallel() + c := &CreateCommitOptions{} + c.GetSigner() + c = nil + c.GetSigner() +} + +func TestCreateCustomOrgRoleRequest_GetBaseRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCustomOrgRoleRequest{BaseRole: &zeroValue} + c.GetBaseRole() + c = &CreateCustomOrgRoleRequest{} + c.GetBaseRole() + c = nil + c.GetBaseRole() +} + +func TestCreateCustomOrgRoleRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCustomOrgRoleRequest{Description: &zeroValue} + c.GetDescription() + c = &CreateCustomOrgRoleRequest{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateCustomOrgRoleRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateCustomOrgRoleRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateCustomOrgRoleRequest_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateCustomOrgRoleRequest{Permissions: zeroValue} + c.GetPermissions() + c = &CreateCustomOrgRoleRequest{} + c.GetPermissions() + c = nil + c.GetPermissions() +} + +func TestCreateCustomRepoRoleRequest_GetBaseRole(tt *testing.T) { + tt.Parallel() + c := &CreateCustomRepoRoleRequest{} + c.GetBaseRole() + c = nil + c.GetBaseRole() +} + +func TestCreateCustomRepoRoleRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateCustomRepoRoleRequest{Description: &zeroValue} + c.GetDescription() + c = &CreateCustomRepoRoleRequest{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateCustomRepoRoleRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateCustomRepoRoleRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateCustomRepoRoleRequest_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateCustomRepoRoleRequest{Permissions: zeroValue} + c.GetPermissions() + c = &CreateCustomRepoRoleRequest{} + c.GetPermissions() + c = nil + c.GetPermissions() +} + +func TestCreateDeploymentBranchPolicyRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateDeploymentBranchPolicyRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateDeploymentBranchPolicyRequest_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateDeploymentBranchPolicyRequest{Type: &zeroValue} + c.GetType() + c = &CreateDeploymentBranchPolicyRequest{} + c.GetType() + c = nil + c.GetType() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetAllowsPublicRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateEnterpriseRunnerGroupRequest{AllowsPublicRepositories: &zeroValue} + c.GetAllowsPublicRepositories() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetAllowsPublicRepositories() + c = nil + c.GetAllowsPublicRepositories() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEnterpriseRunnerGroupRequest{Name: &zeroValue} + c.GetName() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEnterpriseRunnerGroupRequest{NetworkConfigurationID: &zeroValue} + c.GetNetworkConfigurationID() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetNetworkConfigurationID() + c = nil + c.GetNetworkConfigurationID() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetRestrictedToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateEnterpriseRunnerGroupRequest{RestrictedToWorkflows: &zeroValue} + c.GetRestrictedToWorkflows() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetRestrictedToWorkflows() + c = nil + c.GetRestrictedToWorkflows() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetRunners(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateEnterpriseRunnerGroupRequest{Runners: zeroValue} + c.GetRunners() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetRunners() + c = nil + c.GetRunners() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetSelectedOrganizationIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateEnterpriseRunnerGroupRequest{SelectedOrganizationIDs: zeroValue} + c.GetSelectedOrganizationIDs() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetSelectedOrganizationIDs() + c = nil + c.GetSelectedOrganizationIDs() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetSelectedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateEnterpriseRunnerGroupRequest{SelectedWorkflows: zeroValue} + c.GetSelectedWorkflows() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetSelectedWorkflows() + c = nil + c.GetSelectedWorkflows() +} + +func TestCreateEnterpriseRunnerGroupRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEnterpriseRunnerGroupRequest{Visibility: &zeroValue} + c.GetVisibility() + c = &CreateEnterpriseRunnerGroupRequest{} + c.GetVisibility() + c = nil + c.GetVisibility() +} + +func TestCreateEvent_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEvent{Description: &zeroValue} + c.GetDescription() + c = &CreateEvent{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CreateEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCreateEvent_GetMasterBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEvent{MasterBranch: &zeroValue} + c.GetMasterBranch() + c = &CreateEvent{} + c.GetMasterBranch() + c = nil + c.GetMasterBranch() +} + +func TestCreateEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CreateEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCreateEvent_GetPusherType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEvent{PusherType: &zeroValue} + c.GetPusherType() + c = &CreateEvent{} + c.GetPusherType() + c = nil + c.GetPusherType() +} + +func TestCreateEvent_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEvent{Ref: &zeroValue} + c.GetRef() + c = &CreateEvent{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCreateEvent_GetRefType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateEvent{RefType: &zeroValue} + c.GetRefType() + c = &CreateEvent{} + c.GetRefType() + c = nil + c.GetRefType() +} + +func TestCreateEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CreateEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCreateEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CreateEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCreateGistCommentRequest_GetBody(tt *testing.T) { + tt.Parallel() + c := &CreateGistCommentRequest{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCreateGistFile_GetContent(tt *testing.T) { + tt.Parallel() + c := &CreateGistFile{} + c.GetContent() + c = nil + c.GetContent() +} + +func TestCreateGistRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateGistRequest{Description: &zeroValue} + c.GetDescription() + c = &CreateGistRequest{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateGistRequest_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateGistRequest{Public: &zeroValue} + c.GetPublic() + c = &CreateGistRequest{} + c.GetPublic() + c = nil + c.GetPublic() +} + +func TestCreateHostedRunnerRequest_GetEnableStaticIP(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateHostedRunnerRequest{EnableStaticIP: &zeroValue} + c.GetEnableStaticIP() + c = &CreateHostedRunnerRequest{} + c.GetEnableStaticIP() + c = nil + c.GetEnableStaticIP() +} + +func TestCreateHostedRunnerRequest_GetImage(tt *testing.T) { + tt.Parallel() + c := &CreateHostedRunnerRequest{} + c.GetImage() + c = nil + c.GetImage() +} + +func TestCreateHostedRunnerRequest_GetImageGen(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateHostedRunnerRequest{ImageGen: &zeroValue} + c.GetImageGen() + c = &CreateHostedRunnerRequest{} + c.GetImageGen() + c = nil + c.GetImageGen() +} + +func TestCreateHostedRunnerRequest_GetMaximumRunners(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CreateHostedRunnerRequest{MaximumRunners: &zeroValue} + c.GetMaximumRunners() + c = &CreateHostedRunnerRequest{} + c.GetMaximumRunners() + c = nil + c.GetMaximumRunners() +} + +func TestCreateHostedRunnerRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateHostedRunnerRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateHostedRunnerRequest_GetRunnerGroupID(tt *testing.T) { + tt.Parallel() + c := &CreateHostedRunnerRequest{} + c.GetRunnerGroupID() + c = nil + c.GetRunnerGroupID() +} + +func TestCreateHostedRunnerRequest_GetSize(tt *testing.T) { + tt.Parallel() + c := &CreateHostedRunnerRequest{} + c.GetSize() + c = nil + c.GetSize() +} + +func TestCreateIssueLabelRequest_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateIssueLabelRequest{Color: &zeroValue} + c.GetColor() + c = &CreateIssueLabelRequest{} + c.GetColor() + c = nil + c.GetColor() +} + +func TestCreateIssueLabelRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateIssueLabelRequest{Description: &zeroValue} + c.GetDescription() + c = &CreateIssueLabelRequest{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateIssueLabelRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateIssueLabelRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateIssueRequest_GetAssignee(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateIssueRequest{Assignee: &zeroValue} + c.GetAssignee() + c = &CreateIssueRequest{} + c.GetAssignee() + c = nil + c.GetAssignee() +} + +func TestCreateIssueRequest_GetAssignees(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateIssueRequest{Assignees: zeroValue} + c.GetAssignees() + c = &CreateIssueRequest{} + c.GetAssignees() + c = nil + c.GetAssignees() +} + +func TestCreateIssueRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateIssueRequest{Body: &zeroValue} + c.GetBody() + c = &CreateIssueRequest{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCreateIssueRequest_GetIssueFieldValues(tt *testing.T) { + tt.Parallel() + zeroValue := []*IssueRequestFieldValue{} + c := &CreateIssueRequest{IssueFieldValues: zeroValue} + c.GetIssueFieldValues() + c = &CreateIssueRequest{} + c.GetIssueFieldValues() + c = nil + c.GetIssueFieldValues() +} + +func TestCreateIssueRequest_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateIssueRequest{Labels: zeroValue} + c.GetLabels() + c = &CreateIssueRequest{} + c.GetLabels() + c = nil + c.GetLabels() +} + +func TestCreateIssueRequest_GetMilestone(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CreateIssueRequest{Milestone: &zeroValue} + c.GetMilestone() + c = &CreateIssueRequest{} + c.GetMilestone() + c = nil + c.GetMilestone() +} + +func TestCreateIssueRequest_GetTitle(tt *testing.T) { + tt.Parallel() + c := &CreateIssueRequest{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCreateIssueRequest_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateIssueRequest{Type: &zeroValue} + c.GetType() + c = &CreateIssueRequest{} + c.GetType() + c = nil + c.GetType() +} + +func TestCreateJITConfigRequest_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateJITConfigRequest{Labels: zeroValue} + c.GetLabels() + c = &CreateJITConfigRequest{} + c.GetLabels() + c = nil + c.GetLabels() +} + +func TestCreateJITConfigRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateJITConfigRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateJITConfigRequest_GetRunnerGroupID(tt *testing.T) { + tt.Parallel() + c := &CreateJITConfigRequest{} + c.GetRunnerGroupID() + c = nil + c.GetRunnerGroupID() +} + +func TestCreateJITConfigRequest_GetWorkFolder(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateJITConfigRequest{WorkFolder: &zeroValue} + c.GetWorkFolder() + c = &CreateJITConfigRequest{} + c.GetWorkFolder() + c = nil + c.GetWorkFolder() +} + +func TestCreateMilestoneRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateMilestoneRequest{Description: &zeroValue} + c.GetDescription() + c = &CreateMilestoneRequest{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateMilestoneRequest_GetDueOn(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CreateMilestoneRequest{DueOn: &zeroValue} + c.GetDueOn() + c = &CreateMilestoneRequest{} + c.GetDueOn() + c = nil + c.GetDueOn() +} + +func TestCreateMilestoneRequest_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateMilestoneRequest{State: &zeroValue} + c.GetState() + c = &CreateMilestoneRequest{} + c.GetState() + c = nil + c.GetState() +} + +func TestCreateMilestoneRequest_GetTitle(tt *testing.T) { + tt.Parallel() + c := &CreateMilestoneRequest{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCreateOrganizationPrivateRegistry_GetAccountID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{AccountID: &zeroValue} + c.GetAccountID() + c = &CreateOrganizationPrivateRegistry{} + c.GetAccountID() + c = nil + c.GetAccountID() +} + +func TestCreateOrganizationPrivateRegistry_GetAudience(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{Audience: &zeroValue} + c.GetAudience() + c = &CreateOrganizationPrivateRegistry{} + c.GetAudience() + c = nil + c.GetAudience() +} + +func TestCreateOrganizationPrivateRegistry_GetAuthType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{AuthType: &zeroValue} + c.GetAuthType() + c = &CreateOrganizationPrivateRegistry{} + c.GetAuthType() + c = nil + c.GetAuthType() +} + +func TestCreateOrganizationPrivateRegistry_GetAWSRegion(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{AWSRegion: &zeroValue} + c.GetAWSRegion() + c = &CreateOrganizationPrivateRegistry{} + c.GetAWSRegion() + c = nil + c.GetAWSRegion() +} + +func TestCreateOrganizationPrivateRegistry_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{ClientID: &zeroValue} + c.GetClientID() + c = &CreateOrganizationPrivateRegistry{} + c.GetClientID() + c = nil + c.GetClientID() +} + +func TestCreateOrganizationPrivateRegistry_GetDomain(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{Domain: &zeroValue} + c.GetDomain() + c = &CreateOrganizationPrivateRegistry{} + c.GetDomain() + c = nil + c.GetDomain() +} + +func TestCreateOrganizationPrivateRegistry_GetDomainOwner(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{DomainOwner: &zeroValue} + c.GetDomainOwner() + c = &CreateOrganizationPrivateRegistry{} + c.GetDomainOwner() + c = nil + c.GetDomainOwner() +} + +func TestCreateOrganizationPrivateRegistry_GetEncryptedValue(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{EncryptedValue: &zeroValue} + c.GetEncryptedValue() + c = &CreateOrganizationPrivateRegistry{} + c.GetEncryptedValue() + c = nil + c.GetEncryptedValue() +} + +func TestCreateOrganizationPrivateRegistry_GetIdentityMappingName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{IdentityMappingName: &zeroValue} + c.GetIdentityMappingName() + c = &CreateOrganizationPrivateRegistry{} + c.GetIdentityMappingName() + c = nil + c.GetIdentityMappingName() +} + +func TestCreateOrganizationPrivateRegistry_GetJFrogOIDCProviderName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{JFrogOIDCProviderName: &zeroValue} + c.GetJFrogOIDCProviderName() + c = &CreateOrganizationPrivateRegistry{} + c.GetJFrogOIDCProviderName() + c = nil + c.GetJFrogOIDCProviderName() +} + +func TestCreateOrganizationPrivateRegistry_GetKeyID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{KeyID: &zeroValue} + c.GetKeyID() + c = &CreateOrganizationPrivateRegistry{} + c.GetKeyID() + c = nil + c.GetKeyID() +} + +func TestCreateOrganizationPrivateRegistry_GetRegistryType(tt *testing.T) { + tt.Parallel() + c := &CreateOrganizationPrivateRegistry{} + c.GetRegistryType() + c = nil + c.GetRegistryType() +} + +func TestCreateOrganizationPrivateRegistry_GetReplacesBase(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateOrganizationPrivateRegistry{ReplacesBase: &zeroValue} + c.GetReplacesBase() + c = &CreateOrganizationPrivateRegistry{} + c.GetReplacesBase() + c = nil + c.GetReplacesBase() +} + +func TestCreateOrganizationPrivateRegistry_GetRoleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{RoleName: &zeroValue} + c.GetRoleName() + c = &CreateOrganizationPrivateRegistry{} + c.GetRoleName() + c = nil + c.GetRoleName() +} + +func TestCreateOrganizationPrivateRegistry_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateOrganizationPrivateRegistry{SelectedRepositoryIDs: zeroValue} + c.GetSelectedRepositoryIDs() + c = &CreateOrganizationPrivateRegistry{} + c.GetSelectedRepositoryIDs() + c = nil + c.GetSelectedRepositoryIDs() +} + +func TestCreateOrganizationPrivateRegistry_GetTenantID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{TenantID: &zeroValue} + c.GetTenantID() + c = &CreateOrganizationPrivateRegistry{} + c.GetTenantID() + c = nil + c.GetTenantID() +} + +func TestCreateOrganizationPrivateRegistry_GetURL(tt *testing.T) { + tt.Parallel() + c := &CreateOrganizationPrivateRegistry{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCreateOrganizationPrivateRegistry_GetUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrganizationPrivateRegistry{Username: &zeroValue} + c.GetUsername() + c = &CreateOrganizationPrivateRegistry{} + c.GetUsername() + c = nil + c.GetUsername() +} + +func TestCreateOrganizationPrivateRegistry_GetVisibility(tt *testing.T) { + tt.Parallel() + c := &CreateOrganizationPrivateRegistry{} + c.GetVisibility() + c = nil + c.GetVisibility() +} + +func TestCreateOrgInvitationOptions_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrgInvitationOptions{Email: &zeroValue} + c.GetEmail() + c = &CreateOrgInvitationOptions{} + c.GetEmail() + c = nil + c.GetEmail() +} + +func TestCreateOrgInvitationOptions_GetInviteeID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CreateOrgInvitationOptions{InviteeID: &zeroValue} + c.GetInviteeID() + c = &CreateOrgInvitationOptions{} + c.GetInviteeID() + c = nil + c.GetInviteeID() +} + +func TestCreateOrgInvitationOptions_GetRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrgInvitationOptions{Role: &zeroValue} + c.GetRole() + c = &CreateOrgInvitationOptions{} + c.GetRole() + c = nil + c.GetRole() +} + +func TestCreateOrgInvitationOptions_GetTeamID(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateOrgInvitationOptions{TeamID: zeroValue} + c.GetTeamID() + c = &CreateOrgInvitationOptions{} + c.GetTeamID() + c = nil + c.GetTeamID() +} + +func TestCreateOrUpdateIssueTypesOptions_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrUpdateIssueTypesOptions{Color: &zeroValue} + c.GetColor() + c = &CreateOrUpdateIssueTypesOptions{} + c.GetColor() + c = nil + c.GetColor() +} + +func TestCreateOrUpdateIssueTypesOptions_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateOrUpdateIssueTypesOptions{Description: &zeroValue} + c.GetDescription() + c = &CreateOrUpdateIssueTypesOptions{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateOrUpdateIssueTypesOptions_GetIsEnabled(tt *testing.T) { + tt.Parallel() + c := &CreateOrUpdateIssueTypesOptions{} + c.GetIsEnabled() + c = nil + c.GetIsEnabled() +} + +func TestCreateOrUpdateIssueTypesOptions_GetIsPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateOrUpdateIssueTypesOptions{IsPrivate: &zeroValue} + c.GetIsPrivate() + c = &CreateOrUpdateIssueTypesOptions{} + c.GetIsPrivate() + c = nil + c.GetIsPrivate() +} + +func TestCreateOrUpdateIssueTypesOptions_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateOrUpdateIssueTypesOptions{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateProjectV2DraftItemRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateProjectV2DraftItemRequest{Body: &zeroValue} + c.GetBody() + c = &CreateProjectV2DraftItemRequest{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCreateProjectV2DraftItemRequest_GetTitle(tt *testing.T) { + tt.Parallel() + c := &CreateProjectV2DraftItemRequest{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCreateProjectV2ViewRequest_GetFilter(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateProjectV2ViewRequest{Filter: &zeroValue} + c.GetFilter() + c = &CreateProjectV2ViewRequest{} + c.GetFilter() + c = nil + c.GetFilter() +} + +func TestCreateProjectV2ViewRequest_GetLayout(tt *testing.T) { + tt.Parallel() + c := &CreateProjectV2ViewRequest{} + c.GetLayout() + c = nil + c.GetLayout() +} + +func TestCreateProjectV2ViewRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateProjectV2ViewRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateProjectV2ViewRequest_GetVisibleFields(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateProjectV2ViewRequest{VisibleFields: zeroValue} + c.GetVisibleFields() + c = &CreateProjectV2ViewRequest{} + c.GetVisibleFields() + c = nil + c.GetVisibleFields() +} + +func TestCreateProtectedChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateProtectedChanges{From: &zeroValue} + c.GetFrom() + c = &CreateProtectedChanges{} + c.GetFrom() + c = nil + c.GetFrom() +} + +func TestCreatePullRequest_GetBase(tt *testing.T) { + tt.Parallel() + c := &CreatePullRequest{} + c.GetBase() + c = nil + c.GetBase() +} + +func TestCreatePullRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreatePullRequest{Body: &zeroValue} + c.GetBody() + c = &CreatePullRequest{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCreatePullRequest_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreatePullRequest{Draft: &zeroValue} + c.GetDraft() + c = &CreatePullRequest{} + c.GetDraft() + c = nil + c.GetDraft() +} + +func TestCreatePullRequest_GetHead(tt *testing.T) { + tt.Parallel() + c := &CreatePullRequest{} + c.GetHead() + c = nil + c.GetHead() +} + +func TestCreatePullRequest_GetHeadRepo(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreatePullRequest{HeadRepo: &zeroValue} + c.GetHeadRepo() + c = &CreatePullRequest{} + c.GetHeadRepo() + c = nil + c.GetHeadRepo() +} + +func TestCreatePullRequest_GetIssue(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CreatePullRequest{Issue: &zeroValue} + c.GetIssue() + c = &CreatePullRequest{} + c.GetIssue() + c = nil + c.GetIssue() +} + +func TestCreatePullRequest_GetMaintainerCanModify(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreatePullRequest{MaintainerCanModify: &zeroValue} + c.GetMaintainerCanModify() + c = &CreatePullRequest{} + c.GetMaintainerCanModify() + c = nil + c.GetMaintainerCanModify() +} + +func TestCreatePullRequest_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreatePullRequest{Title: &zeroValue} + c.GetTitle() + c = &CreatePullRequest{} + c.GetTitle() + c = nil + c.GetTitle() +} + +func TestCreateRef_GetRef(tt *testing.T) { + tt.Parallel() + c := &CreateRef{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCreateRef_GetSHA(tt *testing.T) { + tt.Parallel() + c := &CreateRef{} + c.GetSHA() + c = nil + c.GetSHA() +} + +func TestCreateReleaseRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateReleaseRequest{Body: &zeroValue} + c.GetBody() + c = &CreateReleaseRequest{} + c.GetBody() + c = nil + c.GetBody() +} + +func TestCreateReleaseRequest_GetDiscussionCategoryName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateReleaseRequest{DiscussionCategoryName: &zeroValue} + c.GetDiscussionCategoryName() + c = &CreateReleaseRequest{} + c.GetDiscussionCategoryName() + c = nil + c.GetDiscussionCategoryName() +} + +func TestCreateReleaseRequest_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateReleaseRequest{Draft: &zeroValue} + c.GetDraft() + c = &CreateReleaseRequest{} + c.GetDraft() + c = nil + c.GetDraft() +} + +func TestCreateReleaseRequest_GetGenerateReleaseNotes(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateReleaseRequest{GenerateReleaseNotes: &zeroValue} + c.GetGenerateReleaseNotes() + c = &CreateReleaseRequest{} + c.GetGenerateReleaseNotes() + c = nil + c.GetGenerateReleaseNotes() +} + +func TestCreateReleaseRequest_GetMakeLatest(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateReleaseRequest{MakeLatest: &zeroValue} + c.GetMakeLatest() + c = &CreateReleaseRequest{} + c.GetMakeLatest() + c = nil + c.GetMakeLatest() +} + +func TestCreateReleaseRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateReleaseRequest{Name: &zeroValue} + c.GetName() + c = &CreateReleaseRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateReleaseRequest_GetPrerelease(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateReleaseRequest{Prerelease: &zeroValue} + c.GetPrerelease() + c = &CreateReleaseRequest{} + c.GetPrerelease() + c = nil + c.GetPrerelease() +} + +func TestCreateReleaseRequest_GetTagName(tt *testing.T) { + tt.Parallel() + c := &CreateReleaseRequest{} + c.GetTagName() + c = nil + c.GetTagName() +} + +func TestCreateReleaseRequest_GetTargetCommitish(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateReleaseRequest{TargetCommitish: &zeroValue} + c.GetTargetCommitish() + c = &CreateReleaseRequest{} + c.GetTargetCommitish() + c = nil + c.GetTargetCommitish() +} + +func TestCreateRunnerGroupRequest_GetAllowsPublicRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateRunnerGroupRequest{AllowsPublicRepositories: &zeroValue} + c.GetAllowsPublicRepositories() + c = &CreateRunnerGroupRequest{} + c.GetAllowsPublicRepositories() + c = nil + c.GetAllowsPublicRepositories() +} + +func TestCreateRunnerGroupRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateRunnerGroupRequest{Name: &zeroValue} + c.GetName() + c = &CreateRunnerGroupRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateRunnerGroupRequest_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateRunnerGroupRequest{NetworkConfigurationID: &zeroValue} + c.GetNetworkConfigurationID() + c = &CreateRunnerGroupRequest{} + c.GetNetworkConfigurationID() + c = nil + c.GetNetworkConfigurationID() +} + +func TestCreateRunnerGroupRequest_GetRestrictedToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateRunnerGroupRequest{RestrictedToWorkflows: &zeroValue} + c.GetRestrictedToWorkflows() + c = &CreateRunnerGroupRequest{} + c.GetRestrictedToWorkflows() + c = nil + c.GetRestrictedToWorkflows() +} + +func TestCreateRunnerGroupRequest_GetRunners(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateRunnerGroupRequest{Runners: zeroValue} + c.GetRunners() + c = &CreateRunnerGroupRequest{} + c.GetRunners() + c = nil + c.GetRunners() +} + +func TestCreateRunnerGroupRequest_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + c := &CreateRunnerGroupRequest{SelectedRepositoryIDs: zeroValue} + c.GetSelectedRepositoryIDs() + c = &CreateRunnerGroupRequest{} + c.GetSelectedRepositoryIDs() + c = nil + c.GetSelectedRepositoryIDs() +} + +func TestCreateRunnerGroupRequest_GetSelectedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateRunnerGroupRequest{SelectedWorkflows: zeroValue} + c.GetSelectedWorkflows() + c = &CreateRunnerGroupRequest{} + c.GetSelectedWorkflows() + c = nil + c.GetSelectedWorkflows() +} + +func TestCreateRunnerGroupRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateRunnerGroupRequest{Visibility: &zeroValue} + c.GetVisibility() + c = &CreateRunnerGroupRequest{} + c.GetVisibility() + c = nil + c.GetVisibility() +} + +func TestCreateTag_GetMessage(tt *testing.T) { + tt.Parallel() + c := &CreateTag{} + c.GetMessage() + c = nil + c.GetMessage() +} + +func TestCreateTag_GetObject(tt *testing.T) { + tt.Parallel() + c := &CreateTag{} + c.GetObject() + c = nil + c.GetObject() +} + +func TestCreateTag_GetTag(tt *testing.T) { + tt.Parallel() + c := &CreateTag{} + c.GetTag() + c = nil + c.GetTag() +} + +func TestCreateTag_GetTagger(tt *testing.T) { + tt.Parallel() + c := &CreateTag{} + c.GetTagger() + c = nil + c.GetTagger() +} + +func TestCreateTag_GetType(tt *testing.T) { + tt.Parallel() + c := &CreateTag{} + c.GetType() + c = nil + c.GetType() +} + +func TestCreateTeamRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateTeamRequest{Description: &zeroValue} + c.GetDescription() + c = &CreateTeamRequest{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCreateTeamRequest_GetMaintainers(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateTeamRequest{Maintainers: zeroValue} + c.GetMaintainers() + c = &CreateTeamRequest{} + c.GetMaintainers() + c = nil + c.GetMaintainers() +} + +func TestCreateTeamRequest_GetName(tt *testing.T) { + tt.Parallel() + c := &CreateTeamRequest{} + c.GetName() + c = nil + c.GetName() +} + +func TestCreateTeamRequest_GetNotificationSetting(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateTeamRequest{NotificationSetting: &zeroValue} + c.GetNotificationSetting() + c = &CreateTeamRequest{} + c.GetNotificationSetting() + c = nil + c.GetNotificationSetting() +} + +func TestCreateTeamRequest_GetParentTeamID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CreateTeamRequest{ParentTeamID: &zeroValue} + c.GetParentTeamID() + c = &CreateTeamRequest{} + c.GetParentTeamID() + c = nil + c.GetParentTeamID() +} + +func TestCreateTeamRequest_GetParentTeamSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateTeamRequest{ParentTeamSlug: &zeroValue} + c.GetParentTeamSlug() + c = &CreateTeamRequest{} + c.GetParentTeamSlug() + c = nil + c.GetParentTeamSlug() +} + +func TestCreateTeamRequest_GetPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateTeamRequest{Permission: &zeroValue} + c.GetPermission() + c = &CreateTeamRequest{} + c.GetPermission() + c = nil + c.GetPermission() +} + +func TestCreateTeamRequest_GetPrivacy(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateTeamRequest{Privacy: &zeroValue} + c.GetPrivacy() + c = &CreateTeamRequest{} + c.GetPrivacy() + c = nil + c.GetPrivacy() +} + +func TestCreateTeamRequest_GetRepoNames(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreateTeamRequest{RepoNames: zeroValue} + c.GetRepoNames() + c = &CreateTeamRequest{} + c.GetRepoNames() + c = nil + c.GetRepoNames() +} + +func TestCreateUpdateEnvironment_GetCanAdminsBypass(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateUpdateEnvironment{CanAdminsBypass: &zeroValue} + c.GetCanAdminsBypass() + c = &CreateUpdateEnvironment{} + c.GetCanAdminsBypass() + c = nil + c.GetCanAdminsBypass() +} + +func TestCreateUpdateEnvironment_GetDeploymentBranchPolicy(tt *testing.T) { + tt.Parallel() + c := &CreateUpdateEnvironment{} + c.GetDeploymentBranchPolicy() + c = nil + c.GetDeploymentBranchPolicy() +} + +func TestCreateUpdateEnvironment_GetPreventSelfReview(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateUpdateEnvironment{PreventSelfReview: &zeroValue} + c.GetPreventSelfReview() + c = &CreateUpdateEnvironment{} + c.GetPreventSelfReview() + c = nil + c.GetPreventSelfReview() +} + +func TestCreateUpdateEnvironment_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnvReviewers{} + c := &CreateUpdateEnvironment{Reviewers: zeroValue} + c.GetReviewers() + c = &CreateUpdateEnvironment{} + c.GetReviewers() + c = nil + c.GetReviewers() +} + +func TestCreateUpdateEnvironment_GetWaitTimer(tt *testing.T) { + tt.Parallel() + var zeroValue int + c := &CreateUpdateEnvironment{WaitTimer: &zeroValue} + c.GetWaitTimer() + c = &CreateUpdateEnvironment{} + c.GetWaitTimer() + c = nil + c.GetWaitTimer() +} + +func TestCreateUserRequest_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CreateUserRequest{Email: &zeroValue} + c.GetEmail() + c = &CreateUserRequest{} + c.GetEmail() + c = nil + c.GetEmail() +} + +func TestCreateUserRequest_GetLogin(tt *testing.T) { + tt.Parallel() + c := &CreateUserRequest{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestCreateUserRequest_GetSuspended(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateUserRequest{Suspended: &zeroValue} + c.GetSuspended() + c = &CreateUserRequest{} + c.GetSuspended() + c = nil + c.GetSuspended() +} + +func TestCreateWorkflowDispatchEventRequest_GetInputs(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + c := &CreateWorkflowDispatchEventRequest{Inputs: zeroValue} + c.GetInputs() + c = &CreateWorkflowDispatchEventRequest{} + c.GetInputs() + c = nil + c.GetInputs() +} + +func TestCreateWorkflowDispatchEventRequest_GetRef(tt *testing.T) { + tt.Parallel() + c := &CreateWorkflowDispatchEventRequest{} + c.GetRef() + c = nil + c.GetRef() +} + +func TestCreateWorkflowDispatchEventRequest_GetReturnRunDetails(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CreateWorkflowDispatchEventRequest{ReturnRunDetails: &zeroValue} + c.GetReturnRunDetails() + c = &CreateWorkflowDispatchEventRequest{} + c.GetReturnRunDetails() + c = nil + c.GetReturnRunDetails() +} + +func TestCreationInfo_GetCreated(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CreationInfo{Created: &zeroValue} + c.GetCreated() + c = &CreationInfo{} + c.GetCreated() + c = nil + c.GetCreated() +} + +func TestCreationInfo_GetCreators(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CreationInfo{Creators: zeroValue} + c.GetCreators() + c = &CreationInfo{} + c.GetCreators() + c = nil + c.GetCreators() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CredentialAuthorization{AuthorizedCredentialExpiresAt: &zeroValue} + c.GetAuthorizedCredentialExpiresAt() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialExpiresAt() + c = nil + c.GetAuthorizedCredentialExpiresAt() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CredentialAuthorization{AuthorizedCredentialID: &zeroValue} + c.GetAuthorizedCredentialID() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialID() + c = nil + c.GetAuthorizedCredentialID() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialNote(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CredentialAuthorization{AuthorizedCredentialNote: &zeroValue} + c.GetAuthorizedCredentialNote() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialNote() + c = nil + c.GetAuthorizedCredentialNote() +} + +func TestCredentialAuthorization_GetAuthorizedCredentialTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CredentialAuthorization{AuthorizedCredentialTitle: &zeroValue} + c.GetAuthorizedCredentialTitle() + c = &CredentialAuthorization{} + c.GetAuthorizedCredentialTitle() + c = nil + c.GetAuthorizedCredentialTitle() +} + +func TestCredentialAuthorization_GetCredentialAccessedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CredentialAuthorization{CredentialAccessedAt: &zeroValue} + c.GetCredentialAccessedAt() + c = &CredentialAuthorization{} + c.GetCredentialAccessedAt() + c = nil + c.GetCredentialAccessedAt() +} + +func TestCredentialAuthorization_GetCredentialAuthorizedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CredentialAuthorization{CredentialAuthorizedAt: &zeroValue} + c.GetCredentialAuthorizedAt() + c = &CredentialAuthorization{} + c.GetCredentialAuthorizedAt() + c = nil + c.GetCredentialAuthorizedAt() +} + +func TestCredentialAuthorization_GetCredentialID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CredentialAuthorization{CredentialID: &zeroValue} + c.GetCredentialID() + c = &CredentialAuthorization{} + c.GetCredentialID() + c = nil + c.GetCredentialID() +} + +func TestCredentialAuthorization_GetCredentialType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CredentialAuthorization{CredentialType: &zeroValue} + c.GetCredentialType() + c = &CredentialAuthorization{} + c.GetCredentialType() + c = nil + c.GetCredentialType() +} + +func TestCredentialAuthorization_GetFingerprint(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CredentialAuthorization{Fingerprint: &zeroValue} + c.GetFingerprint() + c = &CredentialAuthorization{} + c.GetFingerprint() + c = nil + c.GetFingerprint() +} + +func TestCredentialAuthorization_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CredentialAuthorization{Login: &zeroValue} + c.GetLogin() + c = &CredentialAuthorization{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestCredentialAuthorization_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CredentialAuthorization{Scopes: zeroValue} + c.GetScopes() + c = &CredentialAuthorization{} + c.GetScopes() + c = nil + c.GetScopes() +} + +func TestCredentialAuthorization_GetTokenLastEight(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CredentialAuthorization{TokenLastEight: &zeroValue} + c.GetTokenLastEight() + c = &CredentialAuthorization{} + c.GetTokenLastEight() + c = nil + c.GetTokenLastEight() +} + +func TestCredentialAuthorizationsListOptions_GetLogin(tt *testing.T) { + tt.Parallel() + c := &CredentialAuthorizationsListOptions{} + c.GetLogin() + c = nil + c.GetLogin() +} + +func TestCredit_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &Credit{Type: &zeroValue} + c.GetType() + c = &Credit{} + c.GetType() + c = nil + c.GetType() +} + +func TestCredit_GetUser(tt *testing.T) { + tt.Parallel() + c := &Credit{} + c.GetUser() + c = nil + c.GetUser() +} + +func TestCustomDeploymentProtectionRule_GetApp(tt *testing.T) { + tt.Parallel() + c := &CustomDeploymentProtectionRule{} + c.GetApp() + c = nil + c.GetApp() +} + +func TestCustomDeploymentProtectionRule_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CustomDeploymentProtectionRule{Enabled: &zeroValue} + c.GetEnabled() + c = &CustomDeploymentProtectionRule{} + c.GetEnabled() + c = nil + c.GetEnabled() +} + +func TestCustomDeploymentProtectionRule_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CustomDeploymentProtectionRule{ID: &zeroValue} + c.GetID() + c = &CustomDeploymentProtectionRule{} + c.GetID() + c = nil + c.GetID() +} + +func TestCustomDeploymentProtectionRule_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomDeploymentProtectionRule{NodeID: &zeroValue} + c.GetNodeID() + c = &CustomDeploymentProtectionRule{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestCustomDeploymentProtectionRuleApp_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CustomDeploymentProtectionRuleApp{ID: &zeroValue} + c.GetID() + c = &CustomDeploymentProtectionRuleApp{} + c.GetID() + c = nil + c.GetID() +} + +func TestCustomDeploymentProtectionRuleApp_GetIntegrationURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomDeploymentProtectionRuleApp{IntegrationURL: &zeroValue} + c.GetIntegrationURL() + c = &CustomDeploymentProtectionRuleApp{} + c.GetIntegrationURL() + c = nil + c.GetIntegrationURL() +} + +func TestCustomDeploymentProtectionRuleApp_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomDeploymentProtectionRuleApp{NodeID: &zeroValue} + c.GetNodeID() + c = &CustomDeploymentProtectionRuleApp{} + c.GetNodeID() + c = nil + c.GetNodeID() +} + +func TestCustomDeploymentProtectionRuleApp_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomDeploymentProtectionRuleApp{Slug: &zeroValue} + c.GetSlug() + c = &CustomDeploymentProtectionRuleApp{} + c.GetSlug() + c = nil + c.GetSlug() +} + +func TestCustomDeploymentProtectionRuleRequest_GetIntegrationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CustomDeploymentProtectionRuleRequest{IntegrationID: &zeroValue} + c.GetIntegrationID() + c = &CustomDeploymentProtectionRuleRequest{} + c.GetIntegrationID() + c = nil + c.GetIntegrationID() +} + +func TestCustomOrgRole_GetBaseRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomOrgRole{BaseRole: &zeroValue} + c.GetBaseRole() + c = &CustomOrgRole{} + c.GetBaseRole() + c = nil + c.GetBaseRole() +} + +func TestCustomOrgRole_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CustomOrgRole{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CustomOrgRole{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCustomOrgRole_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomOrgRole{Description: &zeroValue} + c.GetDescription() + c = &CustomOrgRole{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCustomOrgRole_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CustomOrgRole{ID: &zeroValue} + c.GetID() + c = &CustomOrgRole{} + c.GetID() + c = nil + c.GetID() +} + +func TestCustomOrgRole_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomOrgRole{Name: &zeroValue} + c.GetName() + c = &CustomOrgRole{} + c.GetName() + c = nil + c.GetName() +} + +func TestCustomOrgRole_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CustomOrgRole{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCustomOrgRole_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CustomOrgRole{Permissions: zeroValue} + c.GetPermissions() + c = &CustomOrgRole{} + c.GetPermissions() + c = nil + c.GetPermissions() +} + +func TestCustomOrgRole_GetSource(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomOrgRole{Source: &zeroValue} + c.GetSource() + c = &CustomOrgRole{} + c.GetSource() + c = nil + c.GetSource() +} + +func TestCustomOrgRole_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CustomOrgRole{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CustomOrgRole{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestCustomPatternBackfillScan_GetPatternScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomPatternBackfillScan{PatternScope: &zeroValue} + c.GetPatternScope() + c = &CustomPatternBackfillScan{} + c.GetPatternScope() + c = nil + c.GetPatternScope() +} + +func TestCustomPatternBackfillScan_GetPatternSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomPatternBackfillScan{PatternSlug: &zeroValue} + c.GetPatternSlug() + c = &CustomPatternBackfillScan{} + c.GetPatternSlug() + c = nil + c.GetPatternSlug() +} + +func TestCustomProperty_GetAllowedValues(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CustomProperty{AllowedValues: zeroValue} + c.GetAllowedValues() + c = &CustomProperty{} + c.GetAllowedValues() + c = nil + c.GetAllowedValues() +} + +func TestCustomProperty_GetDefaultValue(tt *testing.T) { + tt.Parallel() + c := &CustomProperty{} + c.GetDefaultValue() + c = nil + c.GetDefaultValue() +} + +func TestCustomProperty_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomProperty{Description: &zeroValue} + c.GetDescription() + c = &CustomProperty{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCustomProperty_GetPropertyName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomProperty{PropertyName: &zeroValue} + c.GetPropertyName() + c = &CustomProperty{} + c.GetPropertyName() + c = nil + c.GetPropertyName() +} + +func TestCustomProperty_GetRequired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + c := &CustomProperty{Required: &zeroValue} + c.GetRequired() + c = &CustomProperty{} + c.GetRequired() + c = nil + c.GetRequired() +} + +func TestCustomProperty_GetSourceType(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomProperty{SourceType: &zeroValue} + c.GetSourceType() + c = &CustomProperty{} + c.GetSourceType() + c = nil + c.GetSourceType() +} + +func TestCustomProperty_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomProperty{URL: &zeroValue} + c.GetURL() + c = &CustomProperty{} + c.GetURL() + c = nil + c.GetURL() +} + +func TestCustomProperty_GetValuesEditableBy(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomProperty{ValuesEditableBy: &zeroValue} + c.GetValuesEditableBy() + c = &CustomProperty{} + c.GetValuesEditableBy() + c = nil + c.GetValuesEditableBy() +} + +func TestCustomProperty_GetValueType(tt *testing.T) { + tt.Parallel() + c := &CustomProperty{} + c.GetValueType() + c = nil + c.GetValueType() +} + +func TestCustomPropertyEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomPropertyEvent{Action: &zeroValue} + c.GetAction() + c = &CustomPropertyEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestCustomPropertyEvent_GetDefinition(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyEvent{} + c.GetDefinition() + c = nil + c.GetDefinition() +} + +func TestCustomPropertyEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyEvent{} + c.GetEnterprise() + c = nil + c.GetEnterprise() +} + +func TestCustomPropertyEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCustomPropertyEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCustomPropertyEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCustomPropertyValue_GetPropertyName(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValue{} + c.GetPropertyName() + c = nil + c.GetPropertyName() +} + +func TestCustomPropertyValue_GetValue(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValue{} + c.GetValue() + c = nil + c.GetValue() +} + +func TestCustomPropertyValuesEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomPropertyValuesEvent{Action: &zeroValue} + c.GetAction() + c = &CustomPropertyValuesEvent{} + c.GetAction() + c = nil + c.GetAction() +} + +func TestCustomPropertyValuesEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValuesEvent{} + c.GetEnterprise() + c = nil + c.GetEnterprise() +} + +func TestCustomPropertyValuesEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValuesEvent{} + c.GetInstallation() + c = nil + c.GetInstallation() +} + +func TestCustomPropertyValuesEvent_GetNewPropertyValues(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPropertyValue{} + c := &CustomPropertyValuesEvent{NewPropertyValues: zeroValue} + c.GetNewPropertyValues() + c = &CustomPropertyValuesEvent{} + c.GetNewPropertyValues() + c = nil + c.GetNewPropertyValues() +} + +func TestCustomPropertyValuesEvent_GetOldPropertyValues(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPropertyValue{} + c := &CustomPropertyValuesEvent{OldPropertyValues: zeroValue} + c.GetOldPropertyValues() + c = &CustomPropertyValuesEvent{} + c.GetOldPropertyValues() + c = nil + c.GetOldPropertyValues() +} + +func TestCustomPropertyValuesEvent_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValuesEvent{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCustomPropertyValuesEvent_GetRepo(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValuesEvent{} + c.GetRepo() + c = nil + c.GetRepo() +} + +func TestCustomPropertyValuesEvent_GetSender(tt *testing.T) { + tt.Parallel() + c := &CustomPropertyValuesEvent{} + c.GetSender() + c = nil + c.GetSender() +} + +func TestCustomRepoRoles_GetBaseRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomRepoRoles{BaseRole: &zeroValue} + c.GetBaseRole() + c = &CustomRepoRoles{} + c.GetBaseRole() + c = nil + c.GetBaseRole() +} + +func TestCustomRepoRoles_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CustomRepoRoles{CreatedAt: &zeroValue} + c.GetCreatedAt() + c = &CustomRepoRoles{} + c.GetCreatedAt() + c = nil + c.GetCreatedAt() +} + +func TestCustomRepoRoles_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomRepoRoles{Description: &zeroValue} + c.GetDescription() + c = &CustomRepoRoles{} + c.GetDescription() + c = nil + c.GetDescription() +} + +func TestCustomRepoRoles_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + c := &CustomRepoRoles{ID: &zeroValue} + c.GetID() + c = &CustomRepoRoles{} + c.GetID() + c = nil + c.GetID() +} + +func TestCustomRepoRoles_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + c := &CustomRepoRoles{Name: &zeroValue} + c.GetName() + c = &CustomRepoRoles{} + c.GetName() + c = nil + c.GetName() +} + +func TestCustomRepoRoles_GetOrg(tt *testing.T) { + tt.Parallel() + c := &CustomRepoRoles{} + c.GetOrg() + c = nil + c.GetOrg() +} + +func TestCustomRepoRoles_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + c := &CustomRepoRoles{Permissions: zeroValue} + c.GetPermissions() + c = &CustomRepoRoles{} + c.GetPermissions() + c = nil + c.GetPermissions() +} + +func TestCustomRepoRoles_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + c := &CustomRepoRoles{UpdatedAt: &zeroValue} + c.GetUpdatedAt() + c = &CustomRepoRoles{} + c.GetUpdatedAt() + c = nil + c.GetUpdatedAt() +} + +func TestDatadogConfig_GetEncryptedToken(tt *testing.T) { + tt.Parallel() + d := &DatadogConfig{} + d.GetEncryptedToken() + d = nil + d.GetEncryptedToken() +} + +func TestDatadogConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + d := &DatadogConfig{} + d.GetKeyID() + d = nil + d.GetKeyID() +} + +func TestDatadogConfig_GetSite(tt *testing.T) { + tt.Parallel() + d := &DatadogConfig{} + d.GetSite() + d = nil + d.GetSite() +} + +func TestDefaultSetupConfiguration_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + d := &DefaultSetupConfiguration{Languages: zeroValue} + d.GetLanguages() + d = &DefaultSetupConfiguration{} + d.GetLanguages() + d = nil + d.GetLanguages() +} + +func TestDefaultSetupConfiguration_GetQuerySuite(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DefaultSetupConfiguration{QuerySuite: &zeroValue} + d.GetQuerySuite() + d = &DefaultSetupConfiguration{} + d.GetQuerySuite() + d = nil + d.GetQuerySuite() +} + +func TestDefaultSetupConfiguration_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DefaultSetupConfiguration{State: &zeroValue} + d.GetState() + d = &DefaultSetupConfiguration{} + d.GetState() + d = nil + d.GetState() +} + +func TestDefaultSetupConfiguration_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DefaultSetupConfiguration{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &DefaultSetupConfiguration{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDefaultWorkflowPermissionEnterprise_GetCanApprovePullRequestReviews(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DefaultWorkflowPermissionEnterprise{CanApprovePullRequestReviews: &zeroValue} + d.GetCanApprovePullRequestReviews() + d = &DefaultWorkflowPermissionEnterprise{} + d.GetCanApprovePullRequestReviews() + d = nil + d.GetCanApprovePullRequestReviews() +} + +func TestDefaultWorkflowPermissionEnterprise_GetDefaultWorkflowPermissions(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DefaultWorkflowPermissionEnterprise{DefaultWorkflowPermissions: &zeroValue} + d.GetDefaultWorkflowPermissions() + d = &DefaultWorkflowPermissionEnterprise{} + d.GetDefaultWorkflowPermissions() + d = nil + d.GetDefaultWorkflowPermissions() +} + +func TestDefaultWorkflowPermissionOrganization_GetCanApprovePullRequestReviews(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DefaultWorkflowPermissionOrganization{CanApprovePullRequestReviews: &zeroValue} + d.GetCanApprovePullRequestReviews() + d = &DefaultWorkflowPermissionOrganization{} + d.GetCanApprovePullRequestReviews() + d = nil + d.GetCanApprovePullRequestReviews() +} + +func TestDefaultWorkflowPermissionOrganization_GetDefaultWorkflowPermissions(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DefaultWorkflowPermissionOrganization{DefaultWorkflowPermissions: &zeroValue} + d.GetDefaultWorkflowPermissions() + d = &DefaultWorkflowPermissionOrganization{} + d.GetDefaultWorkflowPermissions() + d = nil + d.GetDefaultWorkflowPermissions() +} + +func TestDefaultWorkflowPermissionRepository_GetCanApprovePullRequestReviews(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DefaultWorkflowPermissionRepository{CanApprovePullRequestReviews: &zeroValue} + d.GetCanApprovePullRequestReviews() + d = &DefaultWorkflowPermissionRepository{} + d.GetCanApprovePullRequestReviews() + d = nil + d.GetCanApprovePullRequestReviews() +} + +func TestDefaultWorkflowPermissionRepository_GetDefaultWorkflowPermissions(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: &zeroValue} + d.GetDefaultWorkflowPermissions() + d = &DefaultWorkflowPermissionRepository{} + d.GetDefaultWorkflowPermissions() + d = nil + d.GetDefaultWorkflowPermissions() +} + +func TestDeleteAnalysis_GetConfirmDeleteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeleteAnalysis{ConfirmDeleteURL: &zeroValue} + d.GetConfirmDeleteURL() + d = &DeleteAnalysis{} + d.GetConfirmDeleteURL() + d = nil + d.GetConfirmDeleteURL() +} + +func TestDeleteAnalysis_GetNextAnalysisURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeleteAnalysis{NextAnalysisURL: &zeroValue} + d.GetNextAnalysisURL() + d = &DeleteAnalysis{} + d.GetNextAnalysisURL() + d = nil + d.GetNextAnalysisURL() +} + +func TestDeleteCostCenterResponse_GetCostCenterState(tt *testing.T) { + tt.Parallel() + d := &DeleteCostCenterResponse{} + d.GetCostCenterState() + d = nil + d.GetCostCenterState() +} + +func TestDeleteCostCenterResponse_GetID(tt *testing.T) { + tt.Parallel() + d := &DeleteCostCenterResponse{} + d.GetID() + d = nil + d.GetID() +} + +func TestDeleteCostCenterResponse_GetMessage(tt *testing.T) { + tt.Parallel() + d := &DeleteCostCenterResponse{} + d.GetMessage() + d = nil + d.GetMessage() +} + +func TestDeleteCostCenterResponse_GetName(tt *testing.T) { + tt.Parallel() + d := &DeleteCostCenterResponse{} + d.GetName() + d = nil + d.GetName() +} + +func TestDeleteEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DeleteEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDeleteEvent_GetOrg(tt *testing.T) { + tt.Parallel() + d := &DeleteEvent{} + d.GetOrg() + d = nil + d.GetOrg() +} + +func TestDeleteEvent_GetPusherType(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeleteEvent{PusherType: &zeroValue} + d.GetPusherType() + d = &DeleteEvent{} + d.GetPusherType() + d = nil + d.GetPusherType() +} + +func TestDeleteEvent_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeleteEvent{Ref: &zeroValue} + d.GetRef() + d = &DeleteEvent{} + d.GetRef() + d = nil + d.GetRef() +} + +func TestDeleteEvent_GetRefType(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeleteEvent{RefType: &zeroValue} + d.GetRefType() + d = &DeleteEvent{} + d.GetRefType() + d = nil + d.GetRefType() +} + +func TestDeleteEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DeleteEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDeleteEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DeleteEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDependabotAlert_GetAutoDismissedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotAlert{AutoDismissedAt: &zeroValue} + d.GetAutoDismissedAt() + d = &DependabotAlert{} + d.GetAutoDismissedAt() + d = nil + d.GetAutoDismissedAt() +} + +func TestDependabotAlert_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotAlert{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &DependabotAlert{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDependabotAlert_GetDependency(tt *testing.T) { + tt.Parallel() + d := &DependabotAlert{} + d.GetDependency() + d = nil + d.GetDependency() +} + +func TestDependabotAlert_GetDismissedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotAlert{DismissedAt: &zeroValue} + d.GetDismissedAt() + d = &DependabotAlert{} + d.GetDismissedAt() + d = nil + d.GetDismissedAt() +} + +func TestDependabotAlert_GetDismissedBy(tt *testing.T) { + tt.Parallel() + d := &DependabotAlert{} + d.GetDismissedBy() + d = nil + d.GetDismissedBy() +} + +func TestDependabotAlert_GetDismissedComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlert{DismissedComment: &zeroValue} + d.GetDismissedComment() + d = &DependabotAlert{} + d.GetDismissedComment() + d = nil + d.GetDismissedComment() +} + +func TestDependabotAlert_GetDismissedReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlert{DismissedReason: &zeroValue} + d.GetDismissedReason() + d = &DependabotAlert{} + d.GetDismissedReason() + d = nil + d.GetDismissedReason() +} + +func TestDependabotAlert_GetFixedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotAlert{FixedAt: &zeroValue} + d.GetFixedAt() + d = &DependabotAlert{} + d.GetFixedAt() + d = nil + d.GetFixedAt() +} + +func TestDependabotAlert_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlert{HTMLURL: &zeroValue} + d.GetHTMLURL() + d = &DependabotAlert{} + d.GetHTMLURL() + d = nil + d.GetHTMLURL() +} + +func TestDependabotAlert_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &DependabotAlert{Number: &zeroValue} + d.GetNumber() + d = &DependabotAlert{} + d.GetNumber() + d = nil + d.GetNumber() +} + +func TestDependabotAlert_GetRepository(tt *testing.T) { + tt.Parallel() + d := &DependabotAlert{} + d.GetRepository() + d = nil + d.GetRepository() +} + +func TestDependabotAlert_GetSecurityAdvisory(tt *testing.T) { + tt.Parallel() + d := &DependabotAlert{} + d.GetSecurityAdvisory() + d = nil + d.GetSecurityAdvisory() +} + +func TestDependabotAlert_GetSecurityVulnerability(tt *testing.T) { + tt.Parallel() + d := &DependabotAlert{} + d.GetSecurityVulnerability() + d = nil + d.GetSecurityVulnerability() +} + +func TestDependabotAlert_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlert{State: &zeroValue} + d.GetState() + d = &DependabotAlert{} + d.GetState() + d = nil + d.GetState() +} + +func TestDependabotAlert_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotAlert{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &DependabotAlert{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDependabotAlert_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlert{URL: &zeroValue} + d.GetURL() + d = &DependabotAlert{} + d.GetURL() + d = nil + d.GetURL() +} + +func TestDependabotAlertEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlertEvent{Action: &zeroValue} + d.GetAction() + d = &DependabotAlertEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDependabotAlertEvent_GetAlert(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertEvent{} + d.GetAlert() + d = nil + d.GetAlert() +} + +func TestDependabotAlertEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertEvent{} + d.GetEnterprise() + d = nil + d.GetEnterprise() +} + +func TestDependabotAlertEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDependabotAlertEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertEvent{} + d.GetOrganization() + d = nil + d.GetOrganization() +} + +func TestDependabotAlertEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDependabotAlertEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDependabotAlertState_GetDismissedComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlertState{DismissedComment: &zeroValue} + d.GetDismissedComment() + d = &DependabotAlertState{} + d.GetDismissedComment() + d = nil + d.GetDismissedComment() +} + +func TestDependabotAlertState_GetDismissedReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotAlertState{DismissedReason: &zeroValue} + d.GetDismissedReason() + d = &DependabotAlertState{} + d.GetDismissedReason() + d = nil + d.GetDismissedReason() +} + +func TestDependabotAlertState_GetState(tt *testing.T) { + tt.Parallel() + d := &DependabotAlertState{} + d.GetState() + d = nil + d.GetState() +} + +func TestDependabotSecurityAdvisory_GetClassification(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityAdvisory{Classification: &zeroValue} + d.GetClassification() + d = &DependabotSecurityAdvisory{} + d.GetClassification() + d = nil + d.GetClassification() +} + +func TestDependabotSecurityAdvisory_GetCVEID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityAdvisory{CVEID: &zeroValue} + d.GetCVEID() + d = &DependabotSecurityAdvisory{} + d.GetCVEID() + d = nil + d.GetCVEID() +} + +func TestDependabotSecurityAdvisory_GetCVSS(tt *testing.T) { + tt.Parallel() + d := &DependabotSecurityAdvisory{} + d.GetCVSS() + d = nil + d.GetCVSS() +} + +func TestDependabotSecurityAdvisory_GetCVSSSeverities(tt *testing.T) { + tt.Parallel() + d := &DependabotSecurityAdvisory{} + d.GetCVSSSeverities() + d = nil + d.GetCVSSSeverities() +} + +func TestDependabotSecurityAdvisory_GetCWEs(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryCWEs{} + d := &DependabotSecurityAdvisory{CWEs: zeroValue} + d.GetCWEs() + d = &DependabotSecurityAdvisory{} + d.GetCWEs() + d = nil + d.GetCWEs() +} + +func TestDependabotSecurityAdvisory_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityAdvisory{Description: &zeroValue} + d.GetDescription() + d = &DependabotSecurityAdvisory{} + d.GetDescription() + d = nil + d.GetDescription() +} + +func TestDependabotSecurityAdvisory_GetEPSS(tt *testing.T) { + tt.Parallel() + d := &DependabotSecurityAdvisory{} + d.GetEPSS() + d = nil + d.GetEPSS() +} + +func TestDependabotSecurityAdvisory_GetGHSAID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityAdvisory{GHSAID: &zeroValue} + d.GetGHSAID() + d = &DependabotSecurityAdvisory{} + d.GetGHSAID() + d = nil + d.GetGHSAID() +} + +func TestDependabotSecurityAdvisory_GetIdentifiers(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryIdentifier{} + d := &DependabotSecurityAdvisory{Identifiers: zeroValue} + d.GetIdentifiers() + d = &DependabotSecurityAdvisory{} + d.GetIdentifiers() + d = nil + d.GetIdentifiers() +} + +func TestDependabotSecurityAdvisory_GetPublishedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotSecurityAdvisory{PublishedAt: &zeroValue} + d.GetPublishedAt() + d = &DependabotSecurityAdvisory{} + d.GetPublishedAt() + d = nil + d.GetPublishedAt() +} + +func TestDependabotSecurityAdvisory_GetReferences(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryReference{} + d := &DependabotSecurityAdvisory{References: zeroValue} + d.GetReferences() + d = &DependabotSecurityAdvisory{} + d.GetReferences() + d = nil + d.GetReferences() +} + +func TestDependabotSecurityAdvisory_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityAdvisory{Severity: &zeroValue} + d.GetSeverity() + d = &DependabotSecurityAdvisory{} + d.GetSeverity() + d = nil + d.GetSeverity() +} + +func TestDependabotSecurityAdvisory_GetSummary(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityAdvisory{Summary: &zeroValue} + d.GetSummary() + d = &DependabotSecurityAdvisory{} + d.GetSummary() + d = nil + d.GetSummary() +} + +func TestDependabotSecurityAdvisory_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotSecurityAdvisory{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &DependabotSecurityAdvisory{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDependabotSecurityAdvisory_GetVulnerabilities(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryVulnerability{} + d := &DependabotSecurityAdvisory{Vulnerabilities: zeroValue} + d.GetVulnerabilities() + d = &DependabotSecurityAdvisory{} + d.GetVulnerabilities() + d = nil + d.GetVulnerabilities() +} + +func TestDependabotSecurityAdvisory_GetWithdrawnAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependabotSecurityAdvisory{WithdrawnAt: &zeroValue} + d.GetWithdrawnAt() + d = &DependabotSecurityAdvisory{} + d.GetWithdrawnAt() + d = nil + d.GetWithdrawnAt() +} + +func TestDependabotSecurityUpdates_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependabotSecurityUpdates{Status: &zeroValue} + d.GetStatus() + d = &DependabotSecurityUpdates{} + d.GetStatus() + d = nil + d.GetStatus() +} + +func TestDependency_GetManifestPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Dependency{ManifestPath: &zeroValue} + d.GetManifestPath() + d = &Dependency{} + d.GetManifestPath() + d = nil + d.GetManifestPath() +} + +func TestDependency_GetPackage(tt *testing.T) { + tt.Parallel() + d := &Dependency{} + d.GetPackage() + d = nil + d.GetPackage() +} + +func TestDependency_GetScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Dependency{Scope: &zeroValue} + d.GetScope() + d = &Dependency{} + d.GetScope() + d = nil + d.GetScope() +} + +func TestDependencyGraphAutosubmitActionOptions_GetLabeledRunners(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DependencyGraphAutosubmitActionOptions{LabeledRunners: &zeroValue} + d.GetLabeledRunners() + d = &DependencyGraphAutosubmitActionOptions{} + d.GetLabeledRunners() + d = nil + d.GetLabeledRunners() +} + +func TestDependencyGraphSnapshot_GetDetector(tt *testing.T) { + tt.Parallel() + d := &DependencyGraphSnapshot{} + d.GetDetector() + d = nil + d.GetDetector() +} + +func TestDependencyGraphSnapshot_GetJob(tt *testing.T) { + tt.Parallel() + d := &DependencyGraphSnapshot{} + d.GetJob() + d = nil + d.GetJob() +} + +func TestDependencyGraphSnapshot_GetMetadata(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + d := &DependencyGraphSnapshot{Metadata: zeroValue} + d.GetMetadata() + d = &DependencyGraphSnapshot{} + d.GetMetadata() + d = nil + d.GetMetadata() +} + +func TestDependencyGraphSnapshot_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshot{Ref: &zeroValue} + d.GetRef() + d = &DependencyGraphSnapshot{} + d.GetRef() + d = nil + d.GetRef() +} + +func TestDependencyGraphSnapshot_GetScanned(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependencyGraphSnapshot{Scanned: &zeroValue} + d.GetScanned() + d = &DependencyGraphSnapshot{} + d.GetScanned() + d = nil + d.GetScanned() +} + +func TestDependencyGraphSnapshot_GetSha(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshot{Sha: &zeroValue} + d.GetSha() + d = &DependencyGraphSnapshot{} + d.GetSha() + d = nil + d.GetSha() +} + +func TestDependencyGraphSnapshot_GetVersion(tt *testing.T) { + tt.Parallel() + d := &DependencyGraphSnapshot{} + d.GetVersion() + d = nil + d.GetVersion() +} + +func TestDependencyGraphSnapshotCreationData_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DependencyGraphSnapshotCreationData{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &DependencyGraphSnapshotCreationData{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDependencyGraphSnapshotCreationData_GetID(tt *testing.T) { + tt.Parallel() + d := &DependencyGraphSnapshotCreationData{} + d.GetID() + d = nil + d.GetID() +} + +func TestDependencyGraphSnapshotCreationData_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotCreationData{Message: &zeroValue} + d.GetMessage() + d = &DependencyGraphSnapshotCreationData{} + d.GetMessage() + d = nil + d.GetMessage() +} + +func TestDependencyGraphSnapshotCreationData_GetResult(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotCreationData{Result: &zeroValue} + d.GetResult() + d = &DependencyGraphSnapshotCreationData{} + d.GetResult() + d = nil + d.GetResult() +} + +func TestDependencyGraphSnapshotDetector_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotDetector{Name: &zeroValue} + d.GetName() + d = &DependencyGraphSnapshotDetector{} + d.GetName() + d = nil + d.GetName() +} + +func TestDependencyGraphSnapshotDetector_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotDetector{URL: &zeroValue} + d.GetURL() + d = &DependencyGraphSnapshotDetector{} + d.GetURL() + d = nil + d.GetURL() +} + +func TestDependencyGraphSnapshotDetector_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotDetector{Version: &zeroValue} + d.GetVersion() + d = &DependencyGraphSnapshotDetector{} + d.GetVersion() + d = nil + d.GetVersion() +} + +func TestDependencyGraphSnapshotJob_GetCorrelator(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotJob{Correlator: &zeroValue} + d.GetCorrelator() + d = &DependencyGraphSnapshotJob{} + d.GetCorrelator() + d = nil + d.GetCorrelator() +} + +func TestDependencyGraphSnapshotJob_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotJob{HTMLURL: &zeroValue} + d.GetHTMLURL() + d = &DependencyGraphSnapshotJob{} + d.GetHTMLURL() + d = nil + d.GetHTMLURL() +} + +func TestDependencyGraphSnapshotJob_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotJob{ID: &zeroValue} + d.GetID() + d = &DependencyGraphSnapshotJob{} + d.GetID() + d = nil + d.GetID() +} + +func TestDependencyGraphSnapshotManifest_GetFile(tt *testing.T) { + tt.Parallel() + d := &DependencyGraphSnapshotManifest{} + d.GetFile() + d = nil + d.GetFile() +} + +func TestDependencyGraphSnapshotManifest_GetMetadata(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + d := &DependencyGraphSnapshotManifest{Metadata: zeroValue} + d.GetMetadata() + d = &DependencyGraphSnapshotManifest{} + d.GetMetadata() + d = nil + d.GetMetadata() +} + +func TestDependencyGraphSnapshotManifest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotManifest{Name: &zeroValue} + d.GetName() + d = &DependencyGraphSnapshotManifest{} + d.GetName() + d = nil + d.GetName() +} + +func TestDependencyGraphSnapshotManifestFile_GetSourceLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotManifestFile{SourceLocation: &zeroValue} + d.GetSourceLocation() + d = &DependencyGraphSnapshotManifestFile{} + d.GetSourceLocation() + d = nil + d.GetSourceLocation() +} + +func TestDependencyGraphSnapshotResolvedDependency_GetDependencies(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + d := &DependencyGraphSnapshotResolvedDependency{Dependencies: zeroValue} + d.GetDependencies() + d = &DependencyGraphSnapshotResolvedDependency{} + d.GetDependencies() + d = nil + d.GetDependencies() +} + +func TestDependencyGraphSnapshotResolvedDependency_GetMetadata(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + d := &DependencyGraphSnapshotResolvedDependency{Metadata: zeroValue} + d.GetMetadata() + d = &DependencyGraphSnapshotResolvedDependency{} + d.GetMetadata() + d = nil + d.GetMetadata() +} + +func TestDependencyGraphSnapshotResolvedDependency_GetPackageURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotResolvedDependency{PackageURL: &zeroValue} + d.GetPackageURL() + d = &DependencyGraphSnapshotResolvedDependency{} + d.GetPackageURL() + d = nil + d.GetPackageURL() +} + +func TestDependencyGraphSnapshotResolvedDependency_GetRelationship(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotResolvedDependency{Relationship: &zeroValue} + d.GetRelationship() + d = &DependencyGraphSnapshotResolvedDependency{} + d.GetRelationship() + d = nil + d.GetRelationship() +} + +func TestDependencyGraphSnapshotResolvedDependency_GetScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DependencyGraphSnapshotResolvedDependency{Scope: &zeroValue} + d.GetScope() + d = &DependencyGraphSnapshotResolvedDependency{} + d.GetScope() + d = nil + d.GetScope() +} + +func TestDeployKeyEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeployKeyEvent{Action: &zeroValue} + d.GetAction() + d = &DeployKeyEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDeployKeyEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DeployKeyEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDeployKeyEvent_GetKey(tt *testing.T) { + tt.Parallel() + d := &DeployKeyEvent{} + d.GetKey() + d = nil + d.GetKey() +} + +func TestDeployKeyEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + d := &DeployKeyEvent{} + d.GetOrganization() + d = nil + d.GetOrganization() +} + +func TestDeployKeyEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DeployKeyEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDeployKeyEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DeployKeyEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDeployment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &Deployment{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &Deployment{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDeployment_GetCreator(tt *testing.T) { + tt.Parallel() + d := &Deployment{} + d.GetCreator() + d = nil + d.GetCreator() +} + +func TestDeployment_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{Description: &zeroValue} + d.GetDescription() + d = &Deployment{} + d.GetDescription() + d = nil + d.GetDescription() +} + +func TestDeployment_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{Environment: &zeroValue} + d.GetEnvironment() + d = &Deployment{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeployment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &Deployment{ID: &zeroValue} + d.GetID() + d = &Deployment{} + d.GetID() + d = nil + d.GetID() +} + +func TestDeployment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{NodeID: &zeroValue} + d.GetNodeID() + d = &Deployment{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDeployment_GetPayload(tt *testing.T) { + tt.Parallel() + d := &Deployment{} + d.GetPayload() + d = nil + d.GetPayload() +} + +func TestDeployment_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{Ref: &zeroValue} + d.GetRef() + d = &Deployment{} + d.GetRef() + d = nil + d.GetRef() +} + +func TestDeployment_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{RepositoryURL: &zeroValue} + d.GetRepositoryURL() + d = &Deployment{} + d.GetRepositoryURL() + d = nil + d.GetRepositoryURL() +} + +func TestDeployment_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{SHA: &zeroValue} + d.GetSHA() + d = &Deployment{} + d.GetSHA() + d = nil + d.GetSHA() +} + +func TestDeployment_GetStatusesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{StatusesURL: &zeroValue} + d.GetStatusesURL() + d = &Deployment{} + d.GetStatusesURL() + d = nil + d.GetStatusesURL() +} + +func TestDeployment_GetTask(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{Task: &zeroValue} + d.GetTask() + d = &Deployment{} + d.GetTask() + d = nil + d.GetTask() +} + +func TestDeployment_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &Deployment{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &Deployment{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDeployment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Deployment{URL: &zeroValue} + d.GetURL() + d = &Deployment{} + d.GetURL() + d = nil + d.GetURL() +} + +func TestDeploymentBranchPolicy_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &DeploymentBranchPolicy{ID: &zeroValue} + d.GetID() + d = &DeploymentBranchPolicy{} + d.GetID() + d = nil + d.GetID() +} + +func TestDeploymentBranchPolicy_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentBranchPolicy{Name: &zeroValue} + d.GetName() + d = &DeploymentBranchPolicy{} + d.GetName() + d = nil + d.GetName() +} + +func TestDeploymentBranchPolicy_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentBranchPolicy{NodeID: &zeroValue} + d.GetNodeID() + d = &DeploymentBranchPolicy{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDeploymentBranchPolicy_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentBranchPolicy{Type: &zeroValue} + d.GetType() + d = &DeploymentBranchPolicy{} + d.GetType() + d = nil + d.GetType() +} + +func TestDeploymentBranchPolicyResponse_GetBranchPolicies(tt *testing.T) { + tt.Parallel() + zeroValue := []*DeploymentBranchPolicy{} + d := &DeploymentBranchPolicyResponse{BranchPolicies: zeroValue} + d.GetBranchPolicies() + d = &DeploymentBranchPolicyResponse{} + d.GetBranchPolicies() + d = nil + d.GetBranchPolicies() +} + +func TestDeploymentBranchPolicyResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &DeploymentBranchPolicyResponse{TotalCount: &zeroValue} + d.GetTotalCount() + d = &DeploymentBranchPolicyResponse{} + d.GetTotalCount() + d = nil + d.GetTotalCount() +} + +func TestDeploymentEvent_GetDeployment(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetDeployment() + d = nil + d.GetDeployment() +} + +func TestDeploymentEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDeploymentEvent_GetOrg(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetOrg() + d = nil + d.GetOrg() +} + +func TestDeploymentEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDeploymentEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDeploymentEvent_GetWorkflow(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetWorkflow() + d = nil + d.GetWorkflow() +} + +func TestDeploymentEvent_GetWorkflowRun(tt *testing.T) { + tt.Parallel() + d := &DeploymentEvent{} + d.GetWorkflowRun() + d = nil + d.GetWorkflowRun() +} + +func TestDeploymentProtectionRuleEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentProtectionRuleEvent{Action: &zeroValue} + d.GetAction() + d = &DeploymentProtectionRuleEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDeploymentProtectionRuleEvent_GetDeployment(tt *testing.T) { + tt.Parallel() + d := &DeploymentProtectionRuleEvent{} + d.GetDeployment() + d = nil + d.GetDeployment() +} + +func TestDeploymentProtectionRuleEvent_GetDeploymentCallbackURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentProtectionRuleEvent{DeploymentCallbackURL: &zeroValue} + d.GetDeploymentCallbackURL() + d = &DeploymentProtectionRuleEvent{} + d.GetDeploymentCallbackURL() + d = nil + d.GetDeploymentCallbackURL() +} + +func TestDeploymentProtectionRuleEvent_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentProtectionRuleEvent{Environment: &zeroValue} + d.GetEnvironment() + d = &DeploymentProtectionRuleEvent{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeploymentProtectionRuleEvent_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentProtectionRuleEvent{Event: &zeroValue} + d.GetEvent() + d = &DeploymentProtectionRuleEvent{} + d.GetEvent() + d = nil + d.GetEvent() +} + +func TestDeploymentProtectionRuleEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DeploymentProtectionRuleEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDeploymentProtectionRuleEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + d := &DeploymentProtectionRuleEvent{} + d.GetOrganization() + d = nil + d.GetOrganization() +} + +func TestDeploymentProtectionRuleEvent_GetPullRequests(tt *testing.T) { + tt.Parallel() + zeroValue := []*PullRequest{} + d := &DeploymentProtectionRuleEvent{PullRequests: zeroValue} + d.GetPullRequests() + d = &DeploymentProtectionRuleEvent{} + d.GetPullRequests() + d = nil + d.GetPullRequests() +} + +func TestDeploymentProtectionRuleEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DeploymentProtectionRuleEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDeploymentProtectionRuleEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DeploymentProtectionRuleEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDeploymentRequest_GetAutoMerge(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DeploymentRequest{AutoMerge: &zeroValue} + d.GetAutoMerge() + d = &DeploymentRequest{} + d.GetAutoMerge() + d = nil + d.GetAutoMerge() +} + +func TestDeploymentRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentRequest{Description: &zeroValue} + d.GetDescription() + d = &DeploymentRequest{} + d.GetDescription() + d = nil + d.GetDescription() +} + +func TestDeploymentRequest_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentRequest{Environment: &zeroValue} + d.GetEnvironment() + d = &DeploymentRequest{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeploymentRequest_GetPayload(tt *testing.T) { + tt.Parallel() + d := &DeploymentRequest{} + d.GetPayload() + d = nil + d.GetPayload() +} + +func TestDeploymentRequest_GetProductionEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DeploymentRequest{ProductionEnvironment: &zeroValue} + d.GetProductionEnvironment() + d = &DeploymentRequest{} + d.GetProductionEnvironment() + d = nil + d.GetProductionEnvironment() +} + +func TestDeploymentRequest_GetRef(tt *testing.T) { + tt.Parallel() + d := &DeploymentRequest{} + d.GetRef() + d = nil + d.GetRef() +} + +func TestDeploymentRequest_GetRequiredContexts(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + d := &DeploymentRequest{RequiredContexts: zeroValue} + d.GetRequiredContexts() + d = &DeploymentRequest{} + d.GetRequiredContexts() + d = nil + d.GetRequiredContexts() +} + +func TestDeploymentRequest_GetTask(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentRequest{Task: &zeroValue} + d.GetTask() + d = &DeploymentRequest{} + d.GetTask() + d = nil + d.GetTask() +} + +func TestDeploymentRequest_GetTransientEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DeploymentRequest{TransientEnvironment: &zeroValue} + d.GetTransientEnvironment() + d = &DeploymentRequest{} + d.GetTransientEnvironment() + d = nil + d.GetTransientEnvironment() +} + +func TestDeploymentReviewEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentReviewEvent{Action: &zeroValue} + d.GetAction() + d = &DeploymentReviewEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDeploymentReviewEvent_GetApprover(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetApprover() + d = nil + d.GetApprover() +} + +func TestDeploymentReviewEvent_GetComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentReviewEvent{Comment: &zeroValue} + d.GetComment() + d = &DeploymentReviewEvent{} + d.GetComment() + d = nil + d.GetComment() +} + +func TestDeploymentReviewEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetEnterprise() + d = nil + d.GetEnterprise() +} + +func TestDeploymentReviewEvent_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentReviewEvent{Environment: &zeroValue} + d.GetEnvironment() + d = &DeploymentReviewEvent{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeploymentReviewEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDeploymentReviewEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetOrganization() + d = nil + d.GetOrganization() +} + +func TestDeploymentReviewEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDeploymentReviewEvent_GetRequester(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetRequester() + d = nil + d.GetRequester() +} + +func TestDeploymentReviewEvent_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*RequiredReviewer{} + d := &DeploymentReviewEvent{Reviewers: zeroValue} + d.GetReviewers() + d = &DeploymentReviewEvent{} + d.GetReviewers() + d = nil + d.GetReviewers() +} + +func TestDeploymentReviewEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDeploymentReviewEvent_GetSince(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentReviewEvent{Since: &zeroValue} + d.GetSince() + d = &DeploymentReviewEvent{} + d.GetSince() + d = nil + d.GetSince() +} + +func TestDeploymentReviewEvent_GetWorkflowJobRun(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetWorkflowJobRun() + d = nil + d.GetWorkflowJobRun() +} + +func TestDeploymentReviewEvent_GetWorkflowJobRuns(tt *testing.T) { + tt.Parallel() + zeroValue := []*WorkflowJobRun{} + d := &DeploymentReviewEvent{WorkflowJobRuns: zeroValue} + d.GetWorkflowJobRuns() + d = &DeploymentReviewEvent{} + d.GetWorkflowJobRuns() + d = nil + d.GetWorkflowJobRuns() +} + +func TestDeploymentReviewEvent_GetWorkflowRun(tt *testing.T) { + tt.Parallel() + d := &DeploymentReviewEvent{} + d.GetWorkflowRun() + d = nil + d.GetWorkflowRun() +} + +func TestDeploymentsListOptions_GetEnvironment(tt *testing.T) { + tt.Parallel() + d := &DeploymentsListOptions{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeploymentsListOptions_GetRef(tt *testing.T) { + tt.Parallel() + d := &DeploymentsListOptions{} + d.GetRef() + d = nil + d.GetRef() +} + +func TestDeploymentsListOptions_GetSHA(tt *testing.T) { + tt.Parallel() + d := &DeploymentsListOptions{} + d.GetSHA() + d = nil + d.GetSHA() +} + +func TestDeploymentsListOptions_GetTask(tt *testing.T) { + tt.Parallel() + d := &DeploymentsListOptions{} + d.GetTask() + d = nil + d.GetTask() +} + +func TestDeploymentStatus_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DeploymentStatus{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &DeploymentStatus{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDeploymentStatus_GetCreator(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatus{} + d.GetCreator() + d = nil + d.GetCreator() +} + +func TestDeploymentStatus_GetDeploymentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{DeploymentURL: &zeroValue} + d.GetDeploymentURL() + d = &DeploymentStatus{} + d.GetDeploymentURL() + d = nil + d.GetDeploymentURL() +} + +func TestDeploymentStatus_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{Description: &zeroValue} + d.GetDescription() + d = &DeploymentStatus{} + d.GetDescription() + d = nil + d.GetDescription() +} + +func TestDeploymentStatus_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{Environment: &zeroValue} + d.GetEnvironment() + d = &DeploymentStatus{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeploymentStatus_GetEnvironmentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{EnvironmentURL: &zeroValue} + d.GetEnvironmentURL() + d = &DeploymentStatus{} + d.GetEnvironmentURL() + d = nil + d.GetEnvironmentURL() +} + +func TestDeploymentStatus_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &DeploymentStatus{ID: &zeroValue} + d.GetID() + d = &DeploymentStatus{} + d.GetID() + d = nil + d.GetID() +} + +func TestDeploymentStatus_GetLogURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{LogURL: &zeroValue} + d.GetLogURL() + d = &DeploymentStatus{} + d.GetLogURL() + d = nil + d.GetLogURL() +} + +func TestDeploymentStatus_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{NodeID: &zeroValue} + d.GetNodeID() + d = &DeploymentStatus{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDeploymentStatus_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{RepositoryURL: &zeroValue} + d.GetRepositoryURL() + d = &DeploymentStatus{} + d.GetRepositoryURL() + d = nil + d.GetRepositoryURL() +} + +func TestDeploymentStatus_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{State: &zeroValue} + d.GetState() + d = &DeploymentStatus{} + d.GetState() + d = nil + d.GetState() +} + +func TestDeploymentStatus_GetTargetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{TargetURL: &zeroValue} + d.GetTargetURL() + d = &DeploymentStatus{} + d.GetTargetURL() + d = nil + d.GetTargetURL() +} + +func TestDeploymentStatus_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DeploymentStatus{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &DeploymentStatus{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDeploymentStatus_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatus{URL: &zeroValue} + d.GetURL() + d = &DeploymentStatus{} + d.GetURL() + d = nil + d.GetURL() +} + +func TestDeploymentStatusEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatusEvent{Action: &zeroValue} + d.GetAction() + d = &DeploymentStatusEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDeploymentStatusEvent_GetDeployment(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusEvent{} + d.GetDeployment() + d = nil + d.GetDeployment() +} + +func TestDeploymentStatusEvent_GetDeploymentStatus(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusEvent{} + d.GetDeploymentStatus() + d = nil + d.GetDeploymentStatus() +} + +func TestDeploymentStatusEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDeploymentStatusEvent_GetOrg(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusEvent{} + d.GetOrg() + d = nil + d.GetOrg() +} + +func TestDeploymentStatusEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDeploymentStatusEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDeploymentStatusRequest_GetAutoInactive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DeploymentStatusRequest{AutoInactive: &zeroValue} + d.GetAutoInactive() + d = &DeploymentStatusRequest{} + d.GetAutoInactive() + d = nil + d.GetAutoInactive() +} + +func TestDeploymentStatusRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatusRequest{Description: &zeroValue} + d.GetDescription() + d = &DeploymentStatusRequest{} + d.GetDescription() + d = nil + d.GetDescription() +} + +func TestDeploymentStatusRequest_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatusRequest{Environment: &zeroValue} + d.GetEnvironment() + d = &DeploymentStatusRequest{} + d.GetEnvironment() + d = nil + d.GetEnvironment() +} + +func TestDeploymentStatusRequest_GetEnvironmentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatusRequest{EnvironmentURL: &zeroValue} + d.GetEnvironmentURL() + d = &DeploymentStatusRequest{} + d.GetEnvironmentURL() + d = nil + d.GetEnvironmentURL() +} + +func TestDeploymentStatusRequest_GetLogURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatusRequest{LogURL: &zeroValue} + d.GetLogURL() + d = &DeploymentStatusRequest{} + d.GetLogURL() + d = nil + d.GetLogURL() +} + +func TestDeploymentStatusRequest_GetState(tt *testing.T) { + tt.Parallel() + d := &DeploymentStatusRequest{} + d.GetState() + d = nil + d.GetState() +} + +func TestDeploymentStatusRequest_GetTargetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DeploymentStatusRequest{TargetURL: &zeroValue} + d.GetTargetURL() + d = &DeploymentStatusRequest{} + d.GetTargetURL() + d = nil + d.GetTargetURL() +} + +func TestDevContainer_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DevContainer{DisplayName: &zeroValue} + d.GetDisplayName() + d = &DevContainer{} + d.GetDisplayName() + d = nil + d.GetDisplayName() +} + +func TestDevContainer_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DevContainer{Name: &zeroValue} + d.GetName() + d = &DevContainer{} + d.GetName() + d = nil + d.GetName() +} + +func TestDevContainer_GetPath(tt *testing.T) { + tt.Parallel() + d := &DevContainer{} + d.GetPath() + d = nil + d.GetPath() +} + +func TestDevContainerConfigurations_GetDevcontainers(tt *testing.T) { + tt.Parallel() + zeroValue := []*DevContainer{} + d := &DevContainerConfigurations{Devcontainers: zeroValue} + d.GetDevcontainers() + d = &DevContainerConfigurations{} + d.GetDevcontainers() + d = nil + d.GetDevcontainers() +} + +func TestDevContainerConfigurations_GetTotalCount(tt *testing.T) { + tt.Parallel() + d := &DevContainerConfigurations{} + d.GetTotalCount() + d = nil + d.GetTotalCount() +} + +func TestDiscussion_GetActiveLockReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{ActiveLockReason: &zeroValue} + d.GetActiveLockReason() + d = &Discussion{} + d.GetActiveLockReason() + d = nil + d.GetActiveLockReason() +} + +func TestDiscussion_GetAnswerChosenAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &Discussion{AnswerChosenAt: &zeroValue} + d.GetAnswerChosenAt() + d = &Discussion{} + d.GetAnswerChosenAt() + d = nil + d.GetAnswerChosenAt() +} + +func TestDiscussion_GetAnswerChosenBy(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{AnswerChosenBy: &zeroValue} + d.GetAnswerChosenBy() + d = &Discussion{} + d.GetAnswerChosenBy() + d = nil + d.GetAnswerChosenBy() +} + +func TestDiscussion_GetAnswerHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{AnswerHTMLURL: &zeroValue} + d.GetAnswerHTMLURL() + d = &Discussion{} + d.GetAnswerHTMLURL() + d = nil + d.GetAnswerHTMLURL() +} + +func TestDiscussion_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{AuthorAssociation: &zeroValue} + d.GetAuthorAssociation() + d = &Discussion{} + d.GetAuthorAssociation() + d = nil + d.GetAuthorAssociation() +} + +func TestDiscussion_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{Body: &zeroValue} + d.GetBody() + d = &Discussion{} + d.GetBody() + d = nil + d.GetBody() +} + +func TestDiscussion_GetComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &Discussion{Comments: &zeroValue} + d.GetComments() + d = &Discussion{} + d.GetComments() + d = nil + d.GetComments() +} + +func TestDiscussion_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &Discussion{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &Discussion{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDiscussion_GetDiscussionCategory(tt *testing.T) { + tt.Parallel() + d := &Discussion{} + d.GetDiscussionCategory() + d = nil + d.GetDiscussionCategory() +} + +func TestDiscussion_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{HTMLURL: &zeroValue} + d.GetHTMLURL() + d = &Discussion{} + d.GetHTMLURL() + d = nil + d.GetHTMLURL() +} + +func TestDiscussion_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &Discussion{ID: &zeroValue} + d.GetID() + d = &Discussion{} + d.GetID() + d = nil + d.GetID() +} + +func TestDiscussion_GetLocked(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &Discussion{Locked: &zeroValue} + d.GetLocked() + d = &Discussion{} + d.GetLocked() + d = nil + d.GetLocked() +} + +func TestDiscussion_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{NodeID: &zeroValue} + d.GetNodeID() + d = &Discussion{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDiscussion_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &Discussion{Number: &zeroValue} + d.GetNumber() + d = &Discussion{} + d.GetNumber() + d = nil + d.GetNumber() +} + +func TestDiscussion_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{RepositoryURL: &zeroValue} + d.GetRepositoryURL() + d = &Discussion{} + d.GetRepositoryURL() + d = nil + d.GetRepositoryURL() +} + +func TestDiscussion_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{State: &zeroValue} + d.GetState() + d = &Discussion{} + d.GetState() + d = nil + d.GetState() +} + +func TestDiscussion_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &Discussion{Title: &zeroValue} + d.GetTitle() + d = &Discussion{} + d.GetTitle() + d = nil + d.GetTitle() +} + +func TestDiscussion_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &Discussion{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &Discussion{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDiscussion_GetUser(tt *testing.T) { + tt.Parallel() + d := &Discussion{} + d.GetUser() + d = nil + d.GetUser() +} + +func TestDiscussionCategory_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DiscussionCategory{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &DiscussionCategory{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDiscussionCategory_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionCategory{Description: &zeroValue} + d.GetDescription() + d = &DiscussionCategory{} + d.GetDescription() + d = nil + d.GetDescription() +} + +func TestDiscussionCategory_GetEmoji(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionCategory{Emoji: &zeroValue} + d.GetEmoji() + d = &DiscussionCategory{} + d.GetEmoji() + d = nil + d.GetEmoji() +} + +func TestDiscussionCategory_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &DiscussionCategory{ID: &zeroValue} + d.GetID() + d = &DiscussionCategory{} + d.GetID() + d = nil + d.GetID() +} + +func TestDiscussionCategory_GetIsAnswerable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DiscussionCategory{IsAnswerable: &zeroValue} + d.GetIsAnswerable() + d = &DiscussionCategory{} + d.GetIsAnswerable() + d = nil + d.GetIsAnswerable() +} + +func TestDiscussionCategory_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionCategory{Name: &zeroValue} + d.GetName() + d = &DiscussionCategory{} + d.GetName() + d = nil + d.GetName() +} + +func TestDiscussionCategory_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionCategory{NodeID: &zeroValue} + d.GetNodeID() + d = &DiscussionCategory{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDiscussionCategory_GetRepositoryID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &DiscussionCategory{RepositoryID: &zeroValue} + d.GetRepositoryID() + d = &DiscussionCategory{} + d.GetRepositoryID() + d = nil + d.GetRepositoryID() +} + +func TestDiscussionCategory_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionCategory{Slug: &zeroValue} + d.GetSlug() + d = &DiscussionCategory{} + d.GetSlug() + d = nil + d.GetSlug() +} + +func TestDiscussionCategory_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DiscussionCategory{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &DiscussionCategory{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDiscussionComment_GetAuthor(tt *testing.T) { + tt.Parallel() + d := &DiscussionComment{} + d.GetAuthor() + d = nil + d.GetAuthor() +} + +func TestDiscussionComment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{Body: &zeroValue} + d.GetBody() + d = &DiscussionComment{} + d.GetBody() + d = nil + d.GetBody() +} + +func TestDiscussionComment_GetBodyHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{BodyHTML: &zeroValue} + d.GetBodyHTML() + d = &DiscussionComment{} + d.GetBodyHTML() + d = nil + d.GetBodyHTML() +} + +func TestDiscussionComment_GetBodyVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{BodyVersion: &zeroValue} + d.GetBodyVersion() + d = &DiscussionComment{} + d.GetBodyVersion() + d = nil + d.GetBodyVersion() +} + +func TestDiscussionComment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DiscussionComment{CreatedAt: &zeroValue} + d.GetCreatedAt() + d = &DiscussionComment{} + d.GetCreatedAt() + d = nil + d.GetCreatedAt() +} + +func TestDiscussionComment_GetDiscussionURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{DiscussionURL: &zeroValue} + d.GetDiscussionURL() + d = &DiscussionComment{} + d.GetDiscussionURL() + d = nil + d.GetDiscussionURL() +} + +func TestDiscussionComment_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{HTMLURL: &zeroValue} + d.GetHTMLURL() + d = &DiscussionComment{} + d.GetHTMLURL() + d = nil + d.GetHTMLURL() +} + +func TestDiscussionComment_GetLastEditedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DiscussionComment{LastEditedAt: &zeroValue} + d.GetLastEditedAt() + d = &DiscussionComment{} + d.GetLastEditedAt() + d = nil + d.GetLastEditedAt() +} + +func TestDiscussionComment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{NodeID: &zeroValue} + d.GetNodeID() + d = &DiscussionComment{} + d.GetNodeID() + d = nil + d.GetNodeID() +} + +func TestDiscussionComment_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &DiscussionComment{Number: &zeroValue} + d.GetNumber() + d = &DiscussionComment{} + d.GetNumber() + d = nil + d.GetNumber() +} + +func TestDiscussionComment_GetReactions(tt *testing.T) { + tt.Parallel() + d := &DiscussionComment{} + d.GetReactions() + d = nil + d.GetReactions() +} + +func TestDiscussionComment_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + d := &DiscussionComment{UpdatedAt: &zeroValue} + d.GetUpdatedAt() + d = &DiscussionComment{} + d.GetUpdatedAt() + d = nil + d.GetUpdatedAt() +} + +func TestDiscussionComment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionComment{URL: &zeroValue} + d.GetURL() + d = &DiscussionComment{} + d.GetURL() + d = nil + d.GetURL() +} + +func TestDiscussionCommentEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionCommentEvent{Action: &zeroValue} + d.GetAction() + d = &DiscussionCommentEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDiscussionCommentEvent_GetComment(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentEvent{} + d.GetComment() + d = nil + d.GetComment() +} + +func TestDiscussionCommentEvent_GetDiscussion(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentEvent{} + d.GetDiscussion() + d = nil + d.GetDiscussion() +} + +func TestDiscussionCommentEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDiscussionCommentEvent_GetOrg(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentEvent{} + d.GetOrg() + d = nil + d.GetOrg() +} + +func TestDiscussionCommentEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDiscussionCommentEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDiscussionCommentListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + d := &DiscussionCommentListOptions{} + d.GetDirection() + d = nil + d.GetDirection() +} + +func TestDiscussionEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DiscussionEvent{Action: &zeroValue} + d.GetAction() + d = &DiscussionEvent{} + d.GetAction() + d = nil + d.GetAction() +} + +func TestDiscussionEvent_GetDiscussion(tt *testing.T) { + tt.Parallel() + d := &DiscussionEvent{} + d.GetDiscussion() + d = nil + d.GetDiscussion() +} + +func TestDiscussionEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + d := &DiscussionEvent{} + d.GetInstallation() + d = nil + d.GetInstallation() +} + +func TestDiscussionEvent_GetOrg(tt *testing.T) { + tt.Parallel() + d := &DiscussionEvent{} + d.GetOrg() + d = nil + d.GetOrg() +} + +func TestDiscussionEvent_GetRepo(tt *testing.T) { + tt.Parallel() + d := &DiscussionEvent{} + d.GetRepo() + d = nil + d.GetRepo() +} + +func TestDiscussionEvent_GetSender(tt *testing.T) { + tt.Parallel() + d := &DiscussionEvent{} + d.GetSender() + d = nil + d.GetSender() +} + +func TestDiscussionListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + d := &DiscussionListOptions{} + d.GetDirection() + d = nil + d.GetDirection() +} + +func TestDismissalRestrictions_GetApps(tt *testing.T) { + tt.Parallel() + zeroValue := []*App{} + d := &DismissalRestrictions{Apps: zeroValue} + d.GetApps() + d = &DismissalRestrictions{} + d.GetApps() + d = nil + d.GetApps() +} + +func TestDismissalRestrictions_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + d := &DismissalRestrictions{Teams: zeroValue} + d.GetTeams() + d = &DismissalRestrictions{} + d.GetTeams() + d = nil + d.GetTeams() +} + +func TestDismissalRestrictions_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + d := &DismissalRestrictions{Users: zeroValue} + d.GetUsers() + d = &DismissalRestrictions{} + d.GetUsers() + d = nil + d.GetUsers() +} + +func TestDismissalRestrictionsRequest_GetApps(tt *testing.T) { + tt.Parallel() + var zeroValue []string + d := &DismissalRestrictionsRequest{Apps: &zeroValue} + d.GetApps() + d = &DismissalRestrictionsRequest{} + d.GetApps() + d = nil + d.GetApps() +} + +func TestDismissalRestrictionsRequest_GetTeams(tt *testing.T) { + tt.Parallel() + var zeroValue []string + d := &DismissalRestrictionsRequest{Teams: &zeroValue} + d.GetTeams() + d = &DismissalRestrictionsRequest{} + d.GetTeams() + d = nil + d.GetTeams() +} + +func TestDismissalRestrictionsRequest_GetUsers(tt *testing.T) { + tt.Parallel() + var zeroValue []string + d := &DismissalRestrictionsRequest{Users: &zeroValue} + d.GetUsers() + d = &DismissalRestrictionsRequest{} + d.GetUsers() + d = nil + d.GetUsers() +} + +func TestDismissedReview_GetDismissalCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DismissedReview{DismissalCommitID: &zeroValue} + d.GetDismissalCommitID() + d = &DismissedReview{} + d.GetDismissalCommitID() + d = nil + d.GetDismissalCommitID() +} + +func TestDismissedReview_GetDismissalMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DismissedReview{DismissalMessage: &zeroValue} + d.GetDismissalMessage() + d = &DismissedReview{} + d.GetDismissalMessage() + d = nil + d.GetDismissalMessage() +} + +func TestDismissedReview_GetReviewID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + d := &DismissedReview{ReviewID: &zeroValue} + d.GetReviewID() + d = &DismissedReview{} + d.GetReviewID() + d = nil + d.GetReviewID() +} + +func TestDismissedReview_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DismissedReview{State: &zeroValue} + d.GetState() + d = &DismissedReview{} + d.GetState() + d = nil + d.GetState() +} + +func TestDismissStaleReviewsOnPushChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + d := &DismissStaleReviewsOnPushChanges{From: &zeroValue} + d.GetFrom() + d = &DismissStaleReviewsOnPushChanges{} + d.GetFrom() + d = nil + d.GetFrom() +} + +func TestDispatchRequestOptions_GetClientPayload(tt *testing.T) { + tt.Parallel() + var zeroValue json.RawMessage + d := &DispatchRequestOptions{ClientPayload: &zeroValue} + d.GetClientPayload() + d = &DispatchRequestOptions{} + d.GetClientPayload() + d = nil + d.GetClientPayload() +} + +func TestDispatchRequestOptions_GetEventType(tt *testing.T) { + tt.Parallel() + d := &DispatchRequestOptions{} + d.GetEventType() + d = nil + d.GetEventType() +} + +func TestDraftReviewComment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DraftReviewComment{Body: &zeroValue} + d.GetBody() + d = &DraftReviewComment{} + d.GetBody() + d = nil + d.GetBody() +} + +func TestDraftReviewComment_GetLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &DraftReviewComment{Line: &zeroValue} + d.GetLine() + d = &DraftReviewComment{} + d.GetLine() + d = nil + d.GetLine() +} + +func TestDraftReviewComment_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DraftReviewComment{Path: &zeroValue} + d.GetPath() + d = &DraftReviewComment{} + d.GetPath() + d = nil + d.GetPath() +} + +func TestDraftReviewComment_GetPosition(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &DraftReviewComment{Position: &zeroValue} + d.GetPosition() + d = &DraftReviewComment{} + d.GetPosition() + d = nil + d.GetPosition() +} + +func TestDraftReviewComment_GetSide(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DraftReviewComment{Side: &zeroValue} + d.GetSide() + d = &DraftReviewComment{} + d.GetSide() + d = nil + d.GetSide() +} + +func TestDraftReviewComment_GetStartLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + d := &DraftReviewComment{StartLine: &zeroValue} + d.GetStartLine() + d = &DraftReviewComment{} + d.GetStartLine() + d = nil + d.GetStartLine() +} + +func TestDraftReviewComment_GetStartSide(tt *testing.T) { + tt.Parallel() + var zeroValue string + d := &DraftReviewComment{StartSide: &zeroValue} + d.GetStartSide() + d = &DraftReviewComment{} + d.GetStartSide() + d = nil + d.GetStartSide() +} + +func TestEditBase_GetRef(tt *testing.T) { + tt.Parallel() + e := &EditBase{} + e.GetRef() + e = nil + e.GetRef() +} + +func TestEditBase_GetSHA(tt *testing.T) { + tt.Parallel() + e := &EditBase{} + e.GetSHA() + e = nil + e.GetSHA() +} + +func TestEditBody_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EditBody{From: &zeroValue} + e.GetFrom() + e = &EditBody{} + e.GetFrom() + e = nil + e.GetFrom() +} + +func TestEditChange_GetBase(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetBase() + e = nil + e.GetBase() +} + +func TestEditChange_GetBody(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetBody() + e = nil + e.GetBody() +} + +func TestEditChange_GetDefaultBranch(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetDefaultBranch() + e = nil + e.GetDefaultBranch() +} + +func TestEditChange_GetOwner(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetOwner() + e = nil + e.GetOwner() +} + +func TestEditChange_GetRepo(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetRepo() + e = nil + e.GetRepo() +} + +func TestEditChange_GetTitle(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetTitle() + e = nil + e.GetTitle() +} + +func TestEditChange_GetTopics(tt *testing.T) { + tt.Parallel() + e := &EditChange{} + e.GetTopics() + e = nil + e.GetTopics() +} + +func TestEditDefaultBranch_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EditDefaultBranch{From: &zeroValue} + e.GetFrom() + e = &EditDefaultBranch{} + e.GetFrom() + e = nil + e.GetFrom() +} + +func TestEditOwner_GetOwnerInfo(tt *testing.T) { + tt.Parallel() + e := &EditOwner{} + e.GetOwnerInfo() + e = nil + e.GetOwnerInfo() +} + +func TestEditRef_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EditRef{From: &zeroValue} + e.GetFrom() + e = &EditRef{} + e.GetFrom() + e = nil + e.GetFrom() +} + +func TestEditRepo_GetName(tt *testing.T) { + tt.Parallel() + e := &EditRepo{} + e.GetName() + e = nil + e.GetName() +} + +func TestEditSHA_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EditSHA{From: &zeroValue} + e.GetFrom() + e = &EditSHA{} + e.GetFrom() + e = nil + e.GetFrom() +} + +func TestEditTitle_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EditTitle{From: &zeroValue} + e.GetFrom() + e = &EditTitle{} + e.GetFrom() + e = nil + e.GetFrom() +} + +func TestEditTopics_GetFrom(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EditTopics{From: zeroValue} + e.GetFrom() + e = &EditTopics{} + e.GetFrom() + e = nil + e.GetFrom() +} + +func TestEncryptedSecret_GetEncryptedValue(tt *testing.T) { + tt.Parallel() + e := &EncryptedSecret{} + e.GetEncryptedValue() + e = nil + e.GetEncryptedValue() +} + +func TestEncryptedSecret_GetKeyID(tt *testing.T) { + tt.Parallel() + e := &EncryptedSecret{} + e.GetKeyID() + e = nil + e.GetKeyID() +} + +func TestEncryptedSecret_GetName(tt *testing.T) { + tt.Parallel() + e := &EncryptedSecret{} + e.GetName() + e = nil + e.GetName() +} + +func TestEncryptedSecret_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + e := &EncryptedSecret{} + e.GetSelectedRepositoryIDs() + e = nil + e.GetSelectedRepositoryIDs() +} + +func TestEncryptedSecret_GetVisibility(tt *testing.T) { + tt.Parallel() + e := &EncryptedSecret{} + e.GetVisibility() + e = nil + e.GetVisibility() +} + +func TestEnterprise_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{AvatarURL: &zeroValue} + e.GetAvatarURL() + e = &Enterprise{} + e.GetAvatarURL() + e = nil + e.GetAvatarURL() +} + +func TestEnterprise_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &Enterprise{CreatedAt: &zeroValue} + e.GetCreatedAt() + e = &Enterprise{} + e.GetCreatedAt() + e = nil + e.GetCreatedAt() +} + +func TestEnterprise_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{Description: &zeroValue} + e.GetDescription() + e = &Enterprise{} + e.GetDescription() + e = nil + e.GetDescription() +} + +func TestEnterprise_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{HTMLURL: &zeroValue} + e.GetHTMLURL() + e = &Enterprise{} + e.GetHTMLURL() + e = nil + e.GetHTMLURL() +} + +func TestEnterprise_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &Enterprise{ID: &zeroValue} + e.GetID() + e = &Enterprise{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnterprise_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{Name: &zeroValue} + e.GetName() + e = &Enterprise{} + e.GetName() + e = nil + e.GetName() +} + +func TestEnterprise_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{NodeID: &zeroValue} + e.GetNodeID() + e = &Enterprise{} + e.GetNodeID() + e = nil + e.GetNodeID() +} + +func TestEnterprise_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{Slug: &zeroValue} + e.GetSlug() + e = &Enterprise{} + e.GetSlug() + e = nil + e.GetSlug() +} + +func TestEnterprise_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &Enterprise{UpdatedAt: &zeroValue} + e.GetUpdatedAt() + e = &Enterprise{} + e.GetUpdatedAt() + e = nil + e.GetUpdatedAt() +} + +func TestEnterprise_GetWebsiteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Enterprise{WebsiteURL: &zeroValue} + e.GetWebsiteURL() + e = &Enterprise{} + e.GetWebsiteURL() + e = nil + e.GetWebsiteURL() +} + +func TestEnterpriseAggregatedUsageItem_GetDiscountAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetDiscountAmount() + e = nil + e.GetDiscountAmount() +} + +func TestEnterpriseAggregatedUsageItem_GetDiscountQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetDiscountQuantity() + e = nil + e.GetDiscountQuantity() +} + +func TestEnterpriseAggregatedUsageItem_GetGrossAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetGrossAmount() + e = nil + e.GetGrossAmount() +} + +func TestEnterpriseAggregatedUsageItem_GetGrossQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetGrossQuantity() + e = nil + e.GetGrossQuantity() +} + +func TestEnterpriseAggregatedUsageItem_GetModel(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetModel() + e = nil + e.GetModel() +} + +func TestEnterpriseAggregatedUsageItem_GetNetAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetNetAmount() + e = nil + e.GetNetAmount() +} + +func TestEnterpriseAggregatedUsageItem_GetNetQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetNetQuantity() + e = nil + e.GetNetQuantity() +} + +func TestEnterpriseAggregatedUsageItem_GetPricePerUnit(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetPricePerUnit() + e = nil + e.GetPricePerUnit() +} + +func TestEnterpriseAggregatedUsageItem_GetProduct(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterpriseAggregatedUsageItem_GetSKU(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetSKU() + e = nil + e.GetSKU() +} + +func TestEnterpriseAggregatedUsageItem_GetUnitType(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageItem{} + e.GetUnitType() + e = nil + e.GetUnitType() +} + +func TestEnterpriseAggregatedUsageReport_GetCostCenter(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageReport{} + e.GetCostCenter() + e = nil + e.GetCostCenter() +} + +func TestEnterpriseAggregatedUsageReport_GetEnterprise(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageReport{} + e.GetEnterprise() + e = nil + e.GetEnterprise() +} + +func TestEnterpriseAggregatedUsageReport_GetModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseAggregatedUsageReport{Model: &zeroValue} + e.GetModel() + e = &EnterpriseAggregatedUsageReport{} + e.GetModel() + e = nil + e.GetModel() +} + +func TestEnterpriseAggregatedUsageReport_GetOrganization(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseAggregatedUsageReport{Organization: &zeroValue} + e.GetOrganization() + e = &EnterpriseAggregatedUsageReport{} + e.GetOrganization() + e = nil + e.GetOrganization() +} + +func TestEnterpriseAggregatedUsageReport_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseAggregatedUsageReport{Product: &zeroValue} + e.GetProduct() + e = &EnterpriseAggregatedUsageReport{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterpriseAggregatedUsageReport_GetTimePeriod(tt *testing.T) { + tt.Parallel() + e := &EnterpriseAggregatedUsageReport{} + e.GetTimePeriod() + e = nil + e.GetTimePeriod() +} + +func TestEnterpriseAggregatedUsageReport_GetUsageItems(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnterpriseAggregatedUsageItem{} + e := &EnterpriseAggregatedUsageReport{UsageItems: zeroValue} + e.GetUsageItems() + e = &EnterpriseAggregatedUsageReport{} + e.GetUsageItems() + e = nil + e.GetUsageItems() +} + +func TestEnterpriseAggregatedUsageReport_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseAggregatedUsageReport{User: &zeroValue} + e.GetUser() + e = &EnterpriseAggregatedUsageReport{} + e.GetUser() + e = nil + e.GetUser() +} + +func TestEnterpriseBudget_GetBudgetAlerting(tt *testing.T) { + tt.Parallel() + e := &EnterpriseBudget{} + e.GetBudgetAlerting() + e = nil + e.GetBudgetAlerting() +} + +func TestEnterpriseBudget_GetBudgetAmount(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnterpriseBudget{BudgetAmount: &zeroValue} + e.GetBudgetAmount() + e = &EnterpriseBudget{} + e.GetBudgetAmount() + e = nil + e.GetBudgetAmount() +} + +func TestEnterpriseBudget_GetBudgetEntityName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseBudget{BudgetEntityName: &zeroValue} + e.GetBudgetEntityName() + e = &EnterpriseBudget{} + e.GetBudgetEntityName() + e = nil + e.GetBudgetEntityName() +} + +func TestEnterpriseBudget_GetBudgetProductSKU(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseBudget{BudgetProductSKU: &zeroValue} + e.GetBudgetProductSKU() + e = &EnterpriseBudget{} + e.GetBudgetProductSKU() + e = nil + e.GetBudgetProductSKU() +} + +func TestEnterpriseBudget_GetBudgetScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseBudget{BudgetScope: &zeroValue} + e.GetBudgetScope() + e = &EnterpriseBudget{} + e.GetBudgetScope() + e = nil + e.GetBudgetScope() +} + +func TestEnterpriseBudget_GetBudgetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseBudget{BudgetType: &zeroValue} + e.GetBudgetType() + e = &EnterpriseBudget{} + e.GetBudgetType() + e = nil + e.GetBudgetType() +} + +func TestEnterpriseBudget_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseBudget{ID: &zeroValue} + e.GetID() + e = &EnterpriseBudget{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnterpriseBudget_GetPreventFurtherUsage(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseBudget{PreventFurtherUsage: &zeroValue} + e.GetPreventFurtherUsage() + e = &EnterpriseBudget{} + e.GetPreventFurtherUsage() + e = nil + e.GetPreventFurtherUsage() +} + +func TestEnterpriseBudgetAlerting_GetAlertRecipients(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseBudgetAlerting{AlertRecipients: zeroValue} + e.GetAlertRecipients() + e = &EnterpriseBudgetAlerting{} + e.GetAlertRecipients() + e = nil + e.GetAlertRecipients() +} + +func TestEnterpriseBudgetAlerting_GetWillAlert(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseBudgetAlerting{WillAlert: &zeroValue} + e.GetWillAlert() + e = &EnterpriseBudgetAlerting{} + e.GetWillAlert() + e = nil + e.GetWillAlert() +} + +func TestEnterpriseConsumedLicenses_GetTotalSeatsConsumed(tt *testing.T) { + tt.Parallel() + e := &EnterpriseConsumedLicenses{} + e.GetTotalSeatsConsumed() + e = nil + e.GetTotalSeatsConsumed() +} + +func TestEnterpriseConsumedLicenses_GetTotalSeatsPurchased(tt *testing.T) { + tt.Parallel() + e := &EnterpriseConsumedLicenses{} + e.GetTotalSeatsPurchased() + e = nil + e.GetTotalSeatsPurchased() +} + +func TestEnterpriseConsumedLicenses_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnterpriseLicensedUsers{} + e := &EnterpriseConsumedLicenses{Users: zeroValue} + e.GetUsers() + e = &EnterpriseConsumedLicenses{} + e.GetUsers() + e = nil + e.GetUsers() +} + +func TestEnterpriseCreateBudget_GetBudgetAlerting(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateBudget{} + e.GetBudgetAlerting() + e = nil + e.GetBudgetAlerting() +} + +func TestEnterpriseCreateBudget_GetBudgetAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateBudget{} + e.GetBudgetAmount() + e = nil + e.GetBudgetAmount() +} + +func TestEnterpriseCreateBudget_GetBudgetEntityName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseCreateBudget{BudgetEntityName: &zeroValue} + e.GetBudgetEntityName() + e = &EnterpriseCreateBudget{} + e.GetBudgetEntityName() + e = nil + e.GetBudgetEntityName() +} + +func TestEnterpriseCreateBudget_GetBudgetProductSKU(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseCreateBudget{BudgetProductSKU: &zeroValue} + e.GetBudgetProductSKU() + e = &EnterpriseCreateBudget{} + e.GetBudgetProductSKU() + e = nil + e.GetBudgetProductSKU() +} + +func TestEnterpriseCreateBudget_GetBudgetScope(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateBudget{} + e.GetBudgetScope() + e = nil + e.GetBudgetScope() +} + +func TestEnterpriseCreateBudget_GetBudgetType(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateBudget{} + e.GetBudgetType() + e = nil + e.GetBudgetType() +} + +func TestEnterpriseCreateBudget_GetPreventFurtherUsage(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateBudget{} + e.GetPreventFurtherUsage() + e = nil + e.GetPreventFurtherUsage() +} + +func TestEnterpriseCreateOrUpdateBudgetResponse_GetBudget(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateOrUpdateBudgetResponse{} + e.GetBudget() + e = nil + e.GetBudget() +} + +func TestEnterpriseCreateOrUpdateBudgetResponse_GetMessage(tt *testing.T) { + tt.Parallel() + e := &EnterpriseCreateOrUpdateBudgetResponse{} + e.GetMessage() + e = nil + e.GetMessage() +} + +func TestEnterpriseCustomPropertiesValues_GetOrganizationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &EnterpriseCustomPropertiesValues{OrganizationID: &zeroValue} + e.GetOrganizationID() + e = &EnterpriseCustomPropertiesValues{} + e.GetOrganizationID() + e = nil + e.GetOrganizationID() +} + +func TestEnterpriseCustomPropertiesValues_GetOrganizationLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseCustomPropertiesValues{OrganizationLogin: &zeroValue} + e.GetOrganizationLogin() + e = &EnterpriseCustomPropertiesValues{} + e.GetOrganizationLogin() + e = nil + e.GetOrganizationLogin() +} + +func TestEnterpriseCustomPropertiesValues_GetProperties(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPropertyValue{} + e := &EnterpriseCustomPropertiesValues{Properties: zeroValue} + e.GetProperties() + e = &EnterpriseCustomPropertiesValues{} + e.GetProperties() + e = nil + e.GetProperties() +} + +func TestEnterpriseCustomPropertySchema_GetProperties(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomProperty{} + e := &EnterpriseCustomPropertySchema{Properties: zeroValue} + e.GetProperties() + e = &EnterpriseCustomPropertySchema{} + e.GetProperties() + e = nil + e.GetProperties() +} + +func TestEnterpriseCustomPropertyValuesRequest_GetOrganizationLogin(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseCustomPropertyValuesRequest{OrganizationLogin: zeroValue} + e.GetOrganizationLogin() + e = &EnterpriseCustomPropertyValuesRequest{} + e.GetOrganizationLogin() + e = nil + e.GetOrganizationLogin() +} + +func TestEnterpriseCustomPropertyValuesRequest_GetProperties(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPropertyValue{} + e := &EnterpriseCustomPropertyValuesRequest{Properties: zeroValue} + e.GetProperties() + e = &EnterpriseCustomPropertyValuesRequest{} + e.GetProperties() + e = nil + e.GetProperties() +} + +func TestEnterpriseDeleteBudgetResponse_GetID(tt *testing.T) { + tt.Parallel() + e := &EnterpriseDeleteBudgetResponse{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnterpriseDeleteBudgetResponse_GetMessage(tt *testing.T) { + tt.Parallel() + e := &EnterpriseDeleteBudgetResponse{} + e.GetMessage() + e = nil + e.GetMessage() +} + +func TestEnterpriseLicensedUsers_GetEnterpriseServerEmails(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseLicensedUsers{EnterpriseServerEmails: zeroValue} + e.GetEnterpriseServerEmails() + e = &EnterpriseLicensedUsers{} + e.GetEnterpriseServerEmails() + e = nil + e.GetEnterpriseServerEmails() +} + +func TestEnterpriseLicensedUsers_GetEnterpriseServerUser(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseLicensedUsers{EnterpriseServerUser: &zeroValue} + e.GetEnterpriseServerUser() + e = &EnterpriseLicensedUsers{} + e.GetEnterpriseServerUser() + e = nil + e.GetEnterpriseServerUser() +} + +func TestEnterpriseLicensedUsers_GetEnterpriseServerUserIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseLicensedUsers{EnterpriseServerUserIDs: zeroValue} + e.GetEnterpriseServerUserIDs() + e = &EnterpriseLicensedUsers{} + e.GetEnterpriseServerUserIDs() + e = nil + e.GetEnterpriseServerUserIDs() +} + +func TestEnterpriseLicensedUsers_GetGithubComEnterpriseRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseLicensedUsers{GithubComEnterpriseRoles: zeroValue} + e.GetGithubComEnterpriseRoles() + e = &EnterpriseLicensedUsers{} + e.GetGithubComEnterpriseRoles() + e = nil + e.GetGithubComEnterpriseRoles() +} + +func TestEnterpriseLicensedUsers_GetGithubComLogin(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicensedUsers{} + e.GetGithubComLogin() + e = nil + e.GetGithubComLogin() +} + +func TestEnterpriseLicensedUsers_GetGithubComMemberRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseLicensedUsers{GithubComMemberRoles: zeroValue} + e.GetGithubComMemberRoles() + e = &EnterpriseLicensedUsers{} + e.GetGithubComMemberRoles() + e = nil + e.GetGithubComMemberRoles() +} + +func TestEnterpriseLicensedUsers_GetGithubComName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseLicensedUsers{GithubComName: &zeroValue} + e.GetGithubComName() + e = &EnterpriseLicensedUsers{} + e.GetGithubComName() + e = nil + e.GetGithubComName() +} + +func TestEnterpriseLicensedUsers_GetGithubComOrgsWithPendingInvites(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseLicensedUsers{GithubComOrgsWithPendingInvites: zeroValue} + e.GetGithubComOrgsWithPendingInvites() + e = &EnterpriseLicensedUsers{} + e.GetGithubComOrgsWithPendingInvites() + e = nil + e.GetGithubComOrgsWithPendingInvites() +} + +func TestEnterpriseLicensedUsers_GetGithubComProfile(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseLicensedUsers{GithubComProfile: &zeroValue} + e.GetGithubComProfile() + e = &EnterpriseLicensedUsers{} + e.GetGithubComProfile() + e = nil + e.GetGithubComProfile() +} + +func TestEnterpriseLicensedUsers_GetGithubComSamlNameID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseLicensedUsers{GithubComSamlNameID: &zeroValue} + e.GetGithubComSamlNameID() + e = &EnterpriseLicensedUsers{} + e.GetGithubComSamlNameID() + e = nil + e.GetGithubComSamlNameID() +} + +func TestEnterpriseLicensedUsers_GetGithubComTwoFactorAuth(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseLicensedUsers{GithubComTwoFactorAuth: &zeroValue} + e.GetGithubComTwoFactorAuth() + e = &EnterpriseLicensedUsers{} + e.GetGithubComTwoFactorAuth() + e = nil + e.GetGithubComTwoFactorAuth() +} + +func TestEnterpriseLicensedUsers_GetGithubComUser(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicensedUsers{} + e.GetGithubComUser() + e = nil + e.GetGithubComUser() +} + +func TestEnterpriseLicensedUsers_GetGithubComVerifiedDomainEmails(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseLicensedUsers{GithubComVerifiedDomainEmails: zeroValue} + e.GetGithubComVerifiedDomainEmails() + e = &EnterpriseLicensedUsers{} + e.GetGithubComVerifiedDomainEmails() + e = nil + e.GetGithubComVerifiedDomainEmails() +} + +func TestEnterpriseLicensedUsers_GetLicenseType(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicensedUsers{} + e.GetLicenseType() + e = nil + e.GetLicenseType() +} + +func TestEnterpriseLicensedUsers_GetTotalUserAccounts(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicensedUsers{} + e.GetTotalUserAccounts() + e = nil + e.GetTotalUserAccounts() +} + +func TestEnterpriseLicensedUsers_GetVisualStudioLicenseStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseLicensedUsers{VisualStudioLicenseStatus: &zeroValue} + e.GetVisualStudioLicenseStatus() + e = &EnterpriseLicensedUsers{} + e.GetVisualStudioLicenseStatus() + e = nil + e.GetVisualStudioLicenseStatus() +} + +func TestEnterpriseLicensedUsers_GetVisualStudioSubscriptionEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseLicensedUsers{VisualStudioSubscriptionEmail: &zeroValue} + e.GetVisualStudioSubscriptionEmail() + e = &EnterpriseLicensedUsers{} + e.GetVisualStudioSubscriptionEmail() + e = nil + e.GetVisualStudioSubscriptionEmail() +} + +func TestEnterpriseLicensedUsers_GetVisualStudioSubscriptionUser(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicensedUsers{} + e.GetVisualStudioSubscriptionUser() + e = nil + e.GetVisualStudioSubscriptionUser() +} + +func TestEnterpriseLicenseSyncStatus_GetDescription(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicenseSyncStatus{} + e.GetDescription() + e = nil + e.GetDescription() +} + +func TestEnterpriseLicenseSyncStatus_GetProperties(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicenseSyncStatus{} + e.GetProperties() + e = nil + e.GetProperties() +} + +func TestEnterpriseLicenseSyncStatus_GetTitle(tt *testing.T) { + tt.Parallel() + e := &EnterpriseLicenseSyncStatus{} + e.GetTitle() + e = nil + e.GetTitle() +} + +func TestEnterpriseListBudgets_GetBudgets(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnterpriseBudget{} + e := &EnterpriseListBudgets{Budgets: zeroValue} + e.GetBudgets() + e = &EnterpriseListBudgets{} + e.GetBudgets() + e = nil + e.GetBudgets() +} + +func TestEnterpriseListBudgets_GetHasNextPage(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseListBudgets{HasNextPage: &zeroValue} + e.GetHasNextPage() + e = &EnterpriseListBudgets{} + e.GetHasNextPage() + e = nil + e.GetHasNextPage() +} + +func TestEnterpriseListBudgets_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnterpriseListBudgets{TotalCount: &zeroValue} + e.GetTotalCount() + e = &EnterpriseListBudgets{} + e.GetTotalCount() + e = nil + e.GetTotalCount() +} + +func TestEnterprisePremiumRequestUsageReportOptions_GetModel(tt *testing.T) { + tt.Parallel() + e := &EnterprisePremiumRequestUsageReportOptions{} + e.GetModel() + e = nil + e.GetModel() +} + +func TestEnterprisePremiumRequestUsageReportOptions_GetOrganization(tt *testing.T) { + tt.Parallel() + e := &EnterprisePremiumRequestUsageReportOptions{} + e.GetOrganization() + e = nil + e.GetOrganization() +} + +func TestEnterprisePremiumRequestUsageReportOptions_GetProduct(tt *testing.T) { + tt.Parallel() + e := &EnterprisePremiumRequestUsageReportOptions{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterprisePremiumRequestUsageReportOptions_GetUser(tt *testing.T) { + tt.Parallel() + e := &EnterprisePremiumRequestUsageReportOptions{} + e.GetUser() + e = nil + e.GetUser() +} + +func TestEnterpriseRunnerGroup_GetAllowsPublicRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseRunnerGroup{AllowsPublicRepositories: &zeroValue} + e.GetAllowsPublicRepositories() + e = &EnterpriseRunnerGroup{} + e.GetAllowsPublicRepositories() + e = nil + e.GetAllowsPublicRepositories() +} + +func TestEnterpriseRunnerGroup_GetDefault(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseRunnerGroup{Default: &zeroValue} + e.GetDefault() + e = &EnterpriseRunnerGroup{} + e.GetDefault() + e = nil + e.GetDefault() +} + +func TestEnterpriseRunnerGroup_GetHostedRunnersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseRunnerGroup{HostedRunnersURL: &zeroValue} + e.GetHostedRunnersURL() + e = &EnterpriseRunnerGroup{} + e.GetHostedRunnersURL() + e = nil + e.GetHostedRunnersURL() +} + +func TestEnterpriseRunnerGroup_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &EnterpriseRunnerGroup{ID: &zeroValue} + e.GetID() + e = &EnterpriseRunnerGroup{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnterpriseRunnerGroup_GetInherited(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseRunnerGroup{Inherited: &zeroValue} + e.GetInherited() + e = &EnterpriseRunnerGroup{} + e.GetInherited() + e = nil + e.GetInherited() +} + +func TestEnterpriseRunnerGroup_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseRunnerGroup{Name: &zeroValue} + e.GetName() + e = &EnterpriseRunnerGroup{} + e.GetName() + e = nil + e.GetName() +} + +func TestEnterpriseRunnerGroup_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseRunnerGroup{NetworkConfigurationID: &zeroValue} + e.GetNetworkConfigurationID() + e = &EnterpriseRunnerGroup{} + e.GetNetworkConfigurationID() + e = nil + e.GetNetworkConfigurationID() +} + +func TestEnterpriseRunnerGroup_GetRestrictedToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseRunnerGroup{RestrictedToWorkflows: &zeroValue} + e.GetRestrictedToWorkflows() + e = &EnterpriseRunnerGroup{} + e.GetRestrictedToWorkflows() + e = nil + e.GetRestrictedToWorkflows() +} + +func TestEnterpriseRunnerGroup_GetRunnersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseRunnerGroup{RunnersURL: &zeroValue} + e.GetRunnersURL() + e = &EnterpriseRunnerGroup{} + e.GetRunnersURL() + e = nil + e.GetRunnersURL() +} + +func TestEnterpriseRunnerGroup_GetSelectedOrganizationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseRunnerGroup{SelectedOrganizationsURL: &zeroValue} + e.GetSelectedOrganizationsURL() + e = &EnterpriseRunnerGroup{} + e.GetSelectedOrganizationsURL() + e = nil + e.GetSelectedOrganizationsURL() +} + +func TestEnterpriseRunnerGroup_GetSelectedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + e := &EnterpriseRunnerGroup{SelectedWorkflows: zeroValue} + e.GetSelectedWorkflows() + e = &EnterpriseRunnerGroup{} + e.GetSelectedWorkflows() + e = nil + e.GetSelectedWorkflows() +} + +func TestEnterpriseRunnerGroup_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseRunnerGroup{Visibility: &zeroValue} + e.GetVisibility() + e = &EnterpriseRunnerGroup{} + e.GetVisibility() + e = nil + e.GetVisibility() +} + +func TestEnterpriseRunnerGroup_GetWorkflowRestrictionsReadOnly(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseRunnerGroup{WorkflowRestrictionsReadOnly: &zeroValue} + e.GetWorkflowRestrictionsReadOnly() + e = &EnterpriseRunnerGroup{} + e.GetWorkflowRestrictionsReadOnly() + e = nil + e.GetWorkflowRestrictionsReadOnly() +} + +func TestEnterpriseRunnerGroups_GetRunnerGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnterpriseRunnerGroup{} + e := &EnterpriseRunnerGroups{RunnerGroups: zeroValue} + e.GetRunnerGroups() + e = &EnterpriseRunnerGroups{} + e.GetRunnerGroups() + e = nil + e.GetRunnerGroups() +} + +func TestEnterpriseRunnerGroups_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnterpriseRunnerGroups{TotalCount: &zeroValue} + e.GetTotalCount() + e = &EnterpriseRunnerGroups{} + e.GetTotalCount() + e = nil + e.GetTotalCount() +} + +func TestEnterpriseSecurityAnalysisSettings_GetAdvancedSecurityEnabledForNewRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{AdvancedSecurityEnabledForNewRepositories: &zeroValue} + e.GetAdvancedSecurityEnabledForNewRepositories() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetAdvancedSecurityEnabledForNewRepositories() + e = nil + e.GetAdvancedSecurityEnabledForNewRepositories() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningEnabledForNewRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{SecretScanningEnabledForNewRepositories: &zeroValue} + e.GetSecretScanningEnabledForNewRepositories() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningEnabledForNewRepositories() + e = nil + e.GetSecretScanningEnabledForNewRepositories() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningPushProtectionCustomLink(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseSecurityAnalysisSettings{SecretScanningPushProtectionCustomLink: &zeroValue} + e.GetSecretScanningPushProtectionCustomLink() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningPushProtectionCustomLink() + e = nil + e.GetSecretScanningPushProtectionCustomLink() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningPushProtectionEnabledForNewRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{SecretScanningPushProtectionEnabledForNewRepositories: &zeroValue} + e.GetSecretScanningPushProtectionEnabledForNewRepositories() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningPushProtectionEnabledForNewRepositories() + e = nil + e.GetSecretScanningPushProtectionEnabledForNewRepositories() +} + +func TestEnterpriseSecurityAnalysisSettings_GetSecretScanningValidityChecksEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseSecurityAnalysisSettings{SecretScanningValidityChecksEnabled: &zeroValue} + e.GetSecretScanningValidityChecksEnabled() + e = &EnterpriseSecurityAnalysisSettings{} + e.GetSecretScanningValidityChecksEnabled() + e = nil + e.GetSecretScanningValidityChecksEnabled() +} + +func TestEnterpriseTeam_GetCreatedAt(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetCreatedAt() + e = nil + e.GetCreatedAt() +} + +func TestEnterpriseTeam_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseTeam{Description: &zeroValue} + e.GetDescription() + e = &EnterpriseTeam{} + e.GetDescription() + e = nil + e.GetDescription() +} + +func TestEnterpriseTeam_GetGroupID(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetGroupID() + e = nil + e.GetGroupID() +} + +func TestEnterpriseTeam_GetHTMLURL(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetHTMLURL() + e = nil + e.GetHTMLURL() +} + +func TestEnterpriseTeam_GetID(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnterpriseTeam_GetMemberURL(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetMemberURL() + e = nil + e.GetMemberURL() +} + +func TestEnterpriseTeam_GetName(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetName() + e = nil + e.GetName() +} + +func TestEnterpriseTeam_GetOrganizationSelectionType(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseTeam{OrganizationSelectionType: &zeroValue} + e.GetOrganizationSelectionType() + e = &EnterpriseTeam{} + e.GetOrganizationSelectionType() + e = nil + e.GetOrganizationSelectionType() +} + +func TestEnterpriseTeam_GetSlug(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetSlug() + e = nil + e.GetSlug() +} + +func TestEnterpriseTeam_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetUpdatedAt() + e = nil + e.GetUpdatedAt() +} + +func TestEnterpriseTeam_GetURL(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeam{} + e.GetURL() + e = nil + e.GetURL() +} + +func TestEnterpriseTeamCreateOrUpdateRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseTeamCreateOrUpdateRequest{Description: &zeroValue} + e.GetDescription() + e = &EnterpriseTeamCreateOrUpdateRequest{} + e.GetDescription() + e = nil + e.GetDescription() +} + +func TestEnterpriseTeamCreateOrUpdateRequest_GetGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseTeamCreateOrUpdateRequest{GroupID: &zeroValue} + e.GetGroupID() + e = &EnterpriseTeamCreateOrUpdateRequest{} + e.GetGroupID() + e = nil + e.GetGroupID() +} + +func TestEnterpriseTeamCreateOrUpdateRequest_GetName(tt *testing.T) { + tt.Parallel() + e := &EnterpriseTeamCreateOrUpdateRequest{} + e.GetName() + e = nil + e.GetName() +} + +func TestEnterpriseTeamCreateOrUpdateRequest_GetOrganizationSelectionType(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseTeamCreateOrUpdateRequest{OrganizationSelectionType: &zeroValue} + e.GetOrganizationSelectionType() + e = &EnterpriseTeamCreateOrUpdateRequest{} + e.GetOrganizationSelectionType() + e = nil + e.GetOrganizationSelectionType() +} + +func TestEnterpriseUpdateBudget_GetBudgetAlerting(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUpdateBudget{} + e.GetBudgetAlerting() + e = nil + e.GetBudgetAlerting() +} + +func TestEnterpriseUpdateBudget_GetBudgetAmount(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnterpriseUpdateBudget{BudgetAmount: &zeroValue} + e.GetBudgetAmount() + e = &EnterpriseUpdateBudget{} + e.GetBudgetAmount() + e = nil + e.GetBudgetAmount() +} + +func TestEnterpriseUpdateBudget_GetBudgetEntityName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUpdateBudget{BudgetEntityName: &zeroValue} + e.GetBudgetEntityName() + e = &EnterpriseUpdateBudget{} + e.GetBudgetEntityName() + e = nil + e.GetBudgetEntityName() +} + +func TestEnterpriseUpdateBudget_GetBudgetProductSKU(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUpdateBudget{BudgetProductSKU: &zeroValue} + e.GetBudgetProductSKU() + e = &EnterpriseUpdateBudget{} + e.GetBudgetProductSKU() + e = nil + e.GetBudgetProductSKU() +} + +func TestEnterpriseUpdateBudget_GetBudgetScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUpdateBudget{BudgetScope: &zeroValue} + e.GetBudgetScope() + e = &EnterpriseUpdateBudget{} + e.GetBudgetScope() + e = nil + e.GetBudgetScope() +} + +func TestEnterpriseUpdateBudget_GetBudgetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUpdateBudget{BudgetType: &zeroValue} + e.GetBudgetType() + e = &EnterpriseUpdateBudget{} + e.GetBudgetType() + e = nil + e.GetBudgetType() +} + +func TestEnterpriseUpdateBudget_GetPreventFurtherUsage(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &EnterpriseUpdateBudget{PreventFurtherUsage: &zeroValue} + e.GetPreventFurtherUsage() + e = &EnterpriseUpdateBudget{} + e.GetPreventFurtherUsage() + e = nil + e.GetPreventFurtherUsage() +} + +func TestEnterpriseUsageItem_GetDate(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetDate() + e = nil + e.GetDate() +} + +func TestEnterpriseUsageItem_GetDiscountAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetDiscountAmount() + e = nil + e.GetDiscountAmount() +} + +func TestEnterpriseUsageItem_GetGrossAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetGrossAmount() + e = nil + e.GetGrossAmount() +} + +func TestEnterpriseUsageItem_GetNetAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetNetAmount() + e = nil + e.GetNetAmount() +} + +func TestEnterpriseUsageItem_GetOrganizationName(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetOrganizationName() + e = nil + e.GetOrganizationName() +} + +func TestEnterpriseUsageItem_GetPricePerUnit(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetPricePerUnit() + e = nil + e.GetPricePerUnit() +} + +func TestEnterpriseUsageItem_GetProduct(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterpriseUsageItem_GetQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetQuantity() + e = nil + e.GetQuantity() +} + +func TestEnterpriseUsageItem_GetRepositoryName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUsageItem{RepositoryName: &zeroValue} + e.GetRepositoryName() + e = &EnterpriseUsageItem{} + e.GetRepositoryName() + e = nil + e.GetRepositoryName() +} + +func TestEnterpriseUsageItem_GetSKU(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetSKU() + e = nil + e.GetSKU() +} + +func TestEnterpriseUsageItem_GetUnitType(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageItem{} + e.GetUnitType() + e = nil + e.GetUnitType() +} + +func TestEnterpriseUsageReport_GetUsageItems(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnterpriseUsageItem{} + e := &EnterpriseUsageReport{UsageItems: zeroValue} + e.GetUsageItems() + e = &EnterpriseUsageReport{} + e.GetUsageItems() + e = nil + e.GetUsageItems() +} + +func TestEnterpriseUsageReportOptions_GetCostCenterID(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageReportOptions{} + e.GetCostCenterID() + e = nil + e.GetCostCenterID() +} + +func TestEnterpriseUsageReportOptions_GetDay(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageReportOptions{} + e.GetDay() + e = nil + e.GetDay() +} + +func TestEnterpriseUsageReportOptions_GetMonth(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageReportOptions{} + e.GetMonth() + e = nil + e.GetMonth() +} + +func TestEnterpriseUsageReportOptions_GetYear(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageReportOptions{} + e.GetYear() + e = nil + e.GetYear() +} + +func TestEnterpriseUsageSummaryItem_GetDiscountAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetDiscountAmount() + e = nil + e.GetDiscountAmount() +} + +func TestEnterpriseUsageSummaryItem_GetDiscountQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetDiscountQuantity() + e = nil + e.GetDiscountQuantity() +} + +func TestEnterpriseUsageSummaryItem_GetGrossAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetGrossAmount() + e = nil + e.GetGrossAmount() +} + +func TestEnterpriseUsageSummaryItem_GetGrossQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetGrossQuantity() + e = nil + e.GetGrossQuantity() +} + +func TestEnterpriseUsageSummaryItem_GetNetAmount(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetNetAmount() + e = nil + e.GetNetAmount() +} + +func TestEnterpriseUsageSummaryItem_GetNetQuantity(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetNetQuantity() + e = nil + e.GetNetQuantity() +} + +func TestEnterpriseUsageSummaryItem_GetPricePerUnit(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetPricePerUnit() + e = nil + e.GetPricePerUnit() +} + +func TestEnterpriseUsageSummaryItem_GetProduct(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterpriseUsageSummaryItem_GetSKU(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetSKU() + e = nil + e.GetSKU() +} + +func TestEnterpriseUsageSummaryItem_GetUnitType(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryItem{} + e.GetUnitType() + e = nil + e.GetUnitType() +} + +func TestEnterpriseUsageSummaryOptions_GetOrganization(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryOptions{} + e.GetOrganization() + e = nil + e.GetOrganization() +} + +func TestEnterpriseUsageSummaryOptions_GetProduct(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryOptions{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterpriseUsageSummaryOptions_GetRepository(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryOptions{} + e.GetRepository() + e = nil + e.GetRepository() +} + +func TestEnterpriseUsageSummaryOptions_GetSKU(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryOptions{} + e.GetSKU() + e = nil + e.GetSKU() +} + +func TestEnterpriseUsageSummaryReport_GetCostCenter(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryReport{} + e.GetCostCenter() + e = nil + e.GetCostCenter() +} + +func TestEnterpriseUsageSummaryReport_GetEnterprise(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryReport{} + e.GetEnterprise() + e = nil + e.GetEnterprise() +} + +func TestEnterpriseUsageSummaryReport_GetOrganization(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUsageSummaryReport{Organization: &zeroValue} + e.GetOrganization() + e = &EnterpriseUsageSummaryReport{} + e.GetOrganization() + e = nil + e.GetOrganization() +} + +func TestEnterpriseUsageSummaryReport_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUsageSummaryReport{Product: &zeroValue} + e.GetProduct() + e = &EnterpriseUsageSummaryReport{} + e.GetProduct() + e = nil + e.GetProduct() +} + +func TestEnterpriseUsageSummaryReport_GetRepository(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUsageSummaryReport{Repository: &zeroValue} + e.GetRepository() + e = &EnterpriseUsageSummaryReport{} + e.GetRepository() + e = nil + e.GetRepository() +} + +func TestEnterpriseUsageSummaryReport_GetSKU(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnterpriseUsageSummaryReport{SKU: &zeroValue} + e.GetSKU() + e = &EnterpriseUsageSummaryReport{} + e.GetSKU() + e = nil + e.GetSKU() +} + +func TestEnterpriseUsageSummaryReport_GetTimePeriod(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageSummaryReport{} + e.GetTimePeriod() + e = nil + e.GetTimePeriod() +} + +func TestEnterpriseUsageSummaryReport_GetUsageItems(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnterpriseUsageSummaryItem{} + e := &EnterpriseUsageSummaryReport{UsageItems: zeroValue} + e.GetUsageItems() + e = &EnterpriseUsageSummaryReport{} + e.GetUsageItems() + e = nil + e.GetUsageItems() +} + +func TestEnterpriseUsageTimePeriod_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnterpriseUsageTimePeriod{Day: &zeroValue} + e.GetDay() + e = &EnterpriseUsageTimePeriod{} + e.GetDay() + e = nil + e.GetDay() +} + +func TestEnterpriseUsageTimePeriod_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnterpriseUsageTimePeriod{Month: &zeroValue} + e.GetMonth() + e = &EnterpriseUsageTimePeriod{} + e.GetMonth() + e = nil + e.GetMonth() +} + +func TestEnterpriseUsageTimePeriod_GetYear(tt *testing.T) { + tt.Parallel() + e := &EnterpriseUsageTimePeriod{} + e.GetYear() + e = nil + e.GetYear() +} + +func TestEnvironment_GetCanAdminsBypass(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &Environment{CanAdminsBypass: &zeroValue} + e.GetCanAdminsBypass() + e = &Environment{} + e.GetCanAdminsBypass() + e = nil + e.GetCanAdminsBypass() +} + +func TestEnvironment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &Environment{CreatedAt: &zeroValue} + e.GetCreatedAt() + e = &Environment{} + e.GetCreatedAt() + e = nil + e.GetCreatedAt() +} + +func TestEnvironment_GetDeploymentBranchPolicy(tt *testing.T) { + tt.Parallel() + e := &Environment{} + e.GetDeploymentBranchPolicy() + e = nil + e.GetDeploymentBranchPolicy() +} + +func TestEnvironment_GetEnvironmentName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{EnvironmentName: &zeroValue} + e.GetEnvironmentName() + e = &Environment{} + e.GetEnvironmentName() + e = nil + e.GetEnvironmentName() +} + +func TestEnvironment_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{HTMLURL: &zeroValue} + e.GetHTMLURL() + e = &Environment{} + e.GetHTMLURL() + e = nil + e.GetHTMLURL() +} + +func TestEnvironment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &Environment{ID: &zeroValue} + e.GetID() + e = &Environment{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnvironment_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{Name: &zeroValue} + e.GetName() + e = &Environment{} + e.GetName() + e = nil + e.GetName() +} + +func TestEnvironment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{NodeID: &zeroValue} + e.GetNodeID() + e = &Environment{} + e.GetNodeID() + e = nil + e.GetNodeID() +} + +func TestEnvironment_GetOwner(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{Owner: &zeroValue} + e.GetOwner() + e = &Environment{} + e.GetOwner() + e = nil + e.GetOwner() +} + +func TestEnvironment_GetProtectionRules(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProtectionRule{} + e := &Environment{ProtectionRules: zeroValue} + e.GetProtectionRules() + e = &Environment{} + e.GetProtectionRules() + e = nil + e.GetProtectionRules() +} + +func TestEnvironment_GetRepo(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{Repo: &zeroValue} + e.GetRepo() + e = &Environment{} + e.GetRepo() + e = nil + e.GetRepo() +} + +func TestEnvironment_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*EnvReviewers{} + e := &Environment{Reviewers: zeroValue} + e.GetReviewers() + e = &Environment{} + e.GetReviewers() + e = nil + e.GetReviewers() +} + +func TestEnvironment_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &Environment{UpdatedAt: &zeroValue} + e.GetUpdatedAt() + e = &Environment{} + e.GetUpdatedAt() + e = nil + e.GetUpdatedAt() +} + +func TestEnvironment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Environment{URL: &zeroValue} + e.GetURL() + e = &Environment{} + e.GetURL() + e = nil + e.GetURL() +} + +func TestEnvironment_GetWaitTimer(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &Environment{WaitTimer: &zeroValue} + e.GetWaitTimer() + e = &Environment{} + e.GetWaitTimer() + e = nil + e.GetWaitTimer() +} + +func TestEnvResponse_GetEnvironments(tt *testing.T) { + tt.Parallel() + zeroValue := []*Environment{} + e := &EnvResponse{Environments: zeroValue} + e.GetEnvironments() + e = &EnvResponse{} + e.GetEnvironments() + e = nil + e.GetEnvironments() +} + +func TestEnvResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + e := &EnvResponse{TotalCount: &zeroValue} + e.GetTotalCount() + e = &EnvResponse{} + e.GetTotalCount() + e = nil + e.GetTotalCount() +} + +func TestEnvReviewers_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &EnvReviewers{ID: &zeroValue} + e.GetID() + e = &EnvReviewers{} + e.GetID() + e = nil + e.GetID() +} + +func TestEnvReviewers_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &EnvReviewers{Type: &zeroValue} + e.GetType() + e = &EnvReviewers{} + e.GetType() + e = nil + e.GetType() +} + +func TestError_GetCode(tt *testing.T) { + tt.Parallel() + e := &Error{} + e.GetCode() + e = nil + e.GetCode() +} + +func TestError_GetField(tt *testing.T) { + tt.Parallel() + e := &Error{} + e.GetField() + e = nil + e.GetField() +} + +func TestError_GetMessage(tt *testing.T) { + tt.Parallel() + e := &Error{} + e.GetMessage() + e = nil + e.GetMessage() +} + +func TestError_GetResource(tt *testing.T) { + tt.Parallel() + e := &Error{} + e.GetResource() + e = nil + e.GetResource() +} + +func TestErrorBlock_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &ErrorBlock{CreatedAt: &zeroValue} + e.GetCreatedAt() + e = &ErrorBlock{} + e.GetCreatedAt() + e = nil + e.GetCreatedAt() +} + +func TestErrorBlock_GetReason(tt *testing.T) { + tt.Parallel() + e := &ErrorBlock{} + e.GetReason() + e = nil + e.GetReason() +} + +func TestErrorResponse_GetBlock(tt *testing.T) { + tt.Parallel() + e := &ErrorResponse{} + e.GetBlock() + e = nil + e.GetBlock() +} + +func TestErrorResponse_GetDocumentationURL(tt *testing.T) { + tt.Parallel() + e := &ErrorResponse{} + e.GetDocumentationURL() + e = nil + e.GetDocumentationURL() +} + +func TestErrorResponse_GetErrors(tt *testing.T) { + tt.Parallel() + zeroValue := []Error{} + e := &ErrorResponse{Errors: zeroValue} + e.GetErrors() + e = &ErrorResponse{} + e.GetErrors() + e = nil + e.GetErrors() +} + +func TestErrorResponse_GetMessage(tt *testing.T) { + tt.Parallel() + e := &ErrorResponse{} + e.GetMessage() + e = nil + e.GetMessage() +} + +func TestEvent_GetActor(tt *testing.T) { + tt.Parallel() + e := &Event{} + e.GetActor() + e = nil + e.GetActor() +} + +func TestEvent_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &Event{CreatedAt: &zeroValue} + e.GetCreatedAt() + e = &Event{} + e.GetCreatedAt() + e = nil + e.GetCreatedAt() +} + +func TestEvent_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Event{ID: &zeroValue} + e.GetID() + e = &Event{} + e.GetID() + e = nil + e.GetID() +} + +func TestEvent_GetOrg(tt *testing.T) { + tt.Parallel() + e := &Event{} + e.GetOrg() + e = nil + e.GetOrg() +} + +func TestEvent_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + e := &Event{Public: &zeroValue} + e.GetPublic() + e = &Event{} + e.GetPublic() + e = nil + e.GetPublic() +} + +func TestEvent_GetRawPayload(tt *testing.T) { + tt.Parallel() + var zeroValue json.RawMessage + e := &Event{RawPayload: &zeroValue} + e.GetRawPayload() + e = &Event{} + e.GetRawPayload() + e = nil + e.GetRawPayload() +} + +func TestEvent_GetRepo(tt *testing.T) { + tt.Parallel() + e := &Event{} + e.GetRepo() + e = nil + e.GetRepo() +} + +func TestEvent_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &Event{Type: &zeroValue} + e.GetType() + e = &Event{} + e.GetType() + e = nil + e.GetType() +} + +func TestExternalGroup_GetGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &ExternalGroup{GroupID: &zeroValue} + e.GetGroupID() + e = &ExternalGroup{} + e.GetGroupID() + e = nil + e.GetGroupID() +} + +func TestExternalGroup_GetGroupName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &ExternalGroup{GroupName: &zeroValue} + e.GetGroupName() + e = &ExternalGroup{} + e.GetGroupName() + e = nil + e.GetGroupName() +} + +func TestExternalGroup_GetMembers(tt *testing.T) { + tt.Parallel() + zeroValue := []*ExternalGroupMember{} + e := &ExternalGroup{Members: zeroValue} + e.GetMembers() + e = &ExternalGroup{} + e.GetMembers() + e = nil + e.GetMembers() +} + +func TestExternalGroup_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*ExternalGroupTeam{} + e := &ExternalGroup{Teams: zeroValue} + e.GetTeams() + e = &ExternalGroup{} + e.GetTeams() + e = nil + e.GetTeams() +} + +func TestExternalGroup_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + e := &ExternalGroup{UpdatedAt: &zeroValue} + e.GetUpdatedAt() + e = &ExternalGroup{} + e.GetUpdatedAt() + e = nil + e.GetUpdatedAt() +} + +func TestExternalGroupList_GetGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []*ExternalGroup{} + e := &ExternalGroupList{Groups: zeroValue} + e.GetGroups() + e = &ExternalGroupList{} + e.GetGroups() + e = nil + e.GetGroups() +} + +func TestExternalGroupMember_GetMemberEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &ExternalGroupMember{MemberEmail: &zeroValue} + e.GetMemberEmail() + e = &ExternalGroupMember{} + e.GetMemberEmail() + e = nil + e.GetMemberEmail() +} + +func TestExternalGroupMember_GetMemberID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &ExternalGroupMember{MemberID: &zeroValue} + e.GetMemberID() + e = &ExternalGroupMember{} + e.GetMemberID() + e = nil + e.GetMemberID() +} + +func TestExternalGroupMember_GetMemberLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &ExternalGroupMember{MemberLogin: &zeroValue} + e.GetMemberLogin() + e = &ExternalGroupMember{} + e.GetMemberLogin() + e = nil + e.GetMemberLogin() +} + +func TestExternalGroupMember_GetMemberName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &ExternalGroupMember{MemberName: &zeroValue} + e.GetMemberName() + e = &ExternalGroupMember{} + e.GetMemberName() + e = nil + e.GetMemberName() +} + +func TestExternalGroupTeam_GetTeamID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + e := &ExternalGroupTeam{TeamID: &zeroValue} + e.GetTeamID() + e = &ExternalGroupTeam{} + e.GetTeamID() + e = nil + e.GetTeamID() +} + +func TestExternalGroupTeam_GetTeamName(tt *testing.T) { + tt.Parallel() + var zeroValue string + e := &ExternalGroupTeam{TeamName: &zeroValue} + e.GetTeamName() + e = &ExternalGroupTeam{} + e.GetTeamName() + e = nil + e.GetTeamName() +} + +func TestFeedLink_GetHRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &FeedLink{HRef: &zeroValue} + f.GetHRef() + f = &FeedLink{} + f.GetHRef() + f = nil + f.GetHRef() +} + +func TestFeedLink_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &FeedLink{Type: &zeroValue} + f.GetType() + f = &FeedLink{} + f.GetType() + f = nil + f.GetType() +} + +func TestFeedLinks_GetCurrentUser(tt *testing.T) { + tt.Parallel() + f := &FeedLinks{} + f.GetCurrentUser() + f = nil + f.GetCurrentUser() +} + +func TestFeedLinks_GetCurrentUserActor(tt *testing.T) { + tt.Parallel() + f := &FeedLinks{} + f.GetCurrentUserActor() + f = nil + f.GetCurrentUserActor() +} + +func TestFeedLinks_GetCurrentUserOrganization(tt *testing.T) { + tt.Parallel() + f := &FeedLinks{} + f.GetCurrentUserOrganization() + f = nil + f.GetCurrentUserOrganization() +} + +func TestFeedLinks_GetCurrentUserOrganizations(tt *testing.T) { + tt.Parallel() + zeroValue := []*FeedLink{} + f := &FeedLinks{CurrentUserOrganizations: zeroValue} + f.GetCurrentUserOrganizations() + f = &FeedLinks{} + f.GetCurrentUserOrganizations() + f = nil + f.GetCurrentUserOrganizations() +} + +func TestFeedLinks_GetCurrentUserPublic(tt *testing.T) { + tt.Parallel() + f := &FeedLinks{} + f.GetCurrentUserPublic() + f = nil + f.GetCurrentUserPublic() +} + +func TestFeedLinks_GetTimeline(tt *testing.T) { + tt.Parallel() + f := &FeedLinks{} + f.GetTimeline() + f = nil + f.GetTimeline() +} + +func TestFeedLinks_GetUser(tt *testing.T) { + tt.Parallel() + f := &FeedLinks{} + f.GetUser() + f = nil + f.GetUser() +} + +func TestFeeds_GetCurrentUserActorURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &Feeds{CurrentUserActorURL: &zeroValue} + f.GetCurrentUserActorURL() + f = &Feeds{} + f.GetCurrentUserActorURL() + f = nil + f.GetCurrentUserActorURL() +} + +func TestFeeds_GetCurrentUserOrganizationURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &Feeds{CurrentUserOrganizationURL: &zeroValue} + f.GetCurrentUserOrganizationURL() + f = &Feeds{} + f.GetCurrentUserOrganizationURL() + f = nil + f.GetCurrentUserOrganizationURL() +} + +func TestFeeds_GetCurrentUserOrganizationURLs(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + f := &Feeds{CurrentUserOrganizationURLs: zeroValue} + f.GetCurrentUserOrganizationURLs() + f = &Feeds{} + f.GetCurrentUserOrganizationURLs() + f = nil + f.GetCurrentUserOrganizationURLs() +} + +func TestFeeds_GetCurrentUserPublicURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &Feeds{CurrentUserPublicURL: &zeroValue} + f.GetCurrentUserPublicURL() + f = &Feeds{} + f.GetCurrentUserPublicURL() + f = nil + f.GetCurrentUserPublicURL() +} + +func TestFeeds_GetCurrentUserURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &Feeds{CurrentUserURL: &zeroValue} + f.GetCurrentUserURL() + f = &Feeds{} + f.GetCurrentUserURL() + f = nil + f.GetCurrentUserURL() +} + +func TestFeeds_GetLinks(tt *testing.T) { + tt.Parallel() + f := &Feeds{} + f.GetLinks() + f = nil + f.GetLinks() +} + +func TestFeeds_GetTimelineURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &Feeds{TimelineURL: &zeroValue} + f.GetTimelineURL() + f = &Feeds{} + f.GetTimelineURL() + f = nil + f.GetTimelineURL() +} + +func TestFeeds_GetUserURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &Feeds{UserURL: &zeroValue} + f.GetUserURL() + f = &Feeds{} + f.GetUserURL() + f = nil + f.GetUserURL() +} + +func TestFieldValue_GetFieldName(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &FieldValue{FieldName: &zeroValue} + f.GetFieldName() + f = &FieldValue{} + f.GetFieldName() + f = nil + f.GetFieldName() +} + +func TestFieldValue_GetFieldNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &FieldValue{FieldNodeID: &zeroValue} + f.GetFieldNodeID() + f = &FieldValue{} + f.GetFieldNodeID() + f = nil + f.GetFieldNodeID() +} + +func TestFieldValue_GetFieldType(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &FieldValue{FieldType: &zeroValue} + f.GetFieldType() + f = &FieldValue{} + f.GetFieldType() + f = nil + f.GetFieldType() +} + +func TestFieldValue_GetFrom(tt *testing.T) { + tt.Parallel() + f := &FieldValue{} + f.GetFrom() + f = nil + f.GetFrom() +} + +func TestFieldValue_GetProjectNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + f := &FieldValue{ProjectNumber: &zeroValue} + f.GetProjectNumber() + f = &FieldValue{} + f.GetProjectNumber() + f = nil + f.GetProjectNumber() +} + +func TestFieldValue_GetTo(tt *testing.T) { + tt.Parallel() + f := &FieldValue{} + f.GetTo() + f = nil + f.GetTo() +} + +func TestFileExtensionRestrictionBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + f := &FileExtensionRestrictionBranchRule{} + f.GetParameters() + f = nil + f.GetParameters() +} + +func TestFileExtensionRestrictionRuleParameters_GetRestrictedFileExtensions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + f := &FileExtensionRestrictionRuleParameters{RestrictedFileExtensions: zeroValue} + f.GetRestrictedFileExtensions() + f = &FileExtensionRestrictionRuleParameters{} + f.GetRestrictedFileExtensions() + f = nil + f.GetRestrictedFileExtensions() +} + +func TestFilePathRestrictionBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + f := &FilePathRestrictionBranchRule{} + f.GetParameters() + f = nil + f.GetParameters() +} + +func TestFilePathRestrictionRuleParameters_GetRestrictedFilePaths(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + f := &FilePathRestrictionRuleParameters{RestrictedFilePaths: zeroValue} + f.GetRestrictedFilePaths() + f = &FilePathRestrictionRuleParameters{} + f.GetRestrictedFilePaths() + f = nil + f.GetRestrictedFilePaths() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + f := &FineGrainedPersonalAccessTokenRequest{CreatedAt: &zeroValue} + f.GetCreatedAt() + f = &FineGrainedPersonalAccessTokenRequest{} + f.GetCreatedAt() + f = nil + f.GetCreatedAt() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetID(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetID() + f = nil + f.GetID() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetOwner(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetOwner() + f = nil + f.GetOwner() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetPermissions(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetPermissions() + f = nil + f.GetPermissions() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetReason(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetReason() + f = nil + f.GetReason() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetRepositoriesURL(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetRepositoriesURL() + f = nil + f.GetRepositoriesURL() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetRepositorySelection() + f = nil + f.GetRepositorySelection() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetTokenExpired(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetTokenExpired() + f = nil + f.GetTokenExpired() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetTokenExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + f := &FineGrainedPersonalAccessTokenRequest{TokenExpiresAt: &zeroValue} + f.GetTokenExpiresAt() + f = &FineGrainedPersonalAccessTokenRequest{} + f.GetTokenExpiresAt() + f = nil + f.GetTokenExpiresAt() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetTokenID(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetTokenID() + f = nil + f.GetTokenID() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetTokenLastUsedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + f := &FineGrainedPersonalAccessTokenRequest{TokenLastUsedAt: &zeroValue} + f.GetTokenLastUsedAt() + f = &FineGrainedPersonalAccessTokenRequest{} + f.GetTokenLastUsedAt() + f = nil + f.GetTokenLastUsedAt() +} + +func TestFineGrainedPersonalAccessTokenRequest_GetTokenName(tt *testing.T) { + tt.Parallel() + f := &FineGrainedPersonalAccessTokenRequest{} + f.GetTokenName() + f = nil + f.GetTokenName() +} + +func TestFirstPatchedVersion_GetIdentifier(tt *testing.T) { + tt.Parallel() + var zeroValue string + f := &FirstPatchedVersion{Identifier: &zeroValue} + f.GetIdentifier() + f = &FirstPatchedVersion{} + f.GetIdentifier() + f = nil + f.GetIdentifier() +} + +func TestForkEvent_GetForkee(tt *testing.T) { + tt.Parallel() + f := &ForkEvent{} + f.GetForkee() + f = nil + f.GetForkee() +} + +func TestForkEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + f := &ForkEvent{} + f.GetInstallation() + f = nil + f.GetInstallation() +} + +func TestForkEvent_GetRepo(tt *testing.T) { + tt.Parallel() + f := &ForkEvent{} + f.GetRepo() + f = nil + f.GetRepo() +} + +func TestForkEvent_GetSender(tt *testing.T) { + tt.Parallel() + f := &ForkEvent{} + f.GetSender() + f = nil + f.GetSender() +} + +func TestGenerateNotesRequest_GetConfigurationFilePath(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GenerateNotesRequest{ConfigurationFilePath: &zeroValue} + g.GetConfigurationFilePath() + g = &GenerateNotesRequest{} + g.GetConfigurationFilePath() + g = nil + g.GetConfigurationFilePath() +} + +func TestGenerateNotesRequest_GetPreviousTagName(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GenerateNotesRequest{PreviousTagName: &zeroValue} + g.GetPreviousTagName() + g = &GenerateNotesRequest{} + g.GetPreviousTagName() + g = nil + g.GetPreviousTagName() +} + +func TestGenerateNotesRequest_GetTagName(tt *testing.T) { + tt.Parallel() + g := &GenerateNotesRequest{} + g.GetTagName() + g = nil + g.GetTagName() +} + +func TestGenerateNotesRequest_GetTargetCommitish(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GenerateNotesRequest{TargetCommitish: &zeroValue} + g.GetTargetCommitish() + g = &GenerateNotesRequest{} + g.GetTargetCommitish() + g = nil + g.GetTargetCommitish() +} + +func TestGetAuditLogOptions_GetInclude(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GetAuditLogOptions{Include: &zeroValue} + g.GetInclude() + g = &GetAuditLogOptions{} + g.GetInclude() + g = nil + g.GetInclude() +} + +func TestGetAuditLogOptions_GetOrder(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GetAuditLogOptions{Order: &zeroValue} + g.GetOrder() + g = &GetAuditLogOptions{} + g.GetOrder() + g = nil + g.GetOrder() +} + +func TestGetAuditLogOptions_GetPhrase(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GetAuditLogOptions{Phrase: &zeroValue} + g.GetPhrase() + g = &GetAuditLogOptions{} + g.GetPhrase() + g = nil + g.GetPhrase() +} + +func TestGetCodeownersErrorsOptions_GetRef(tt *testing.T) { + tt.Parallel() + g := &GetCodeownersErrorsOptions{} + g.GetRef() + g = nil + g.GetRef() +} + +func TestGetProjectItemOptions_GetFields(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + g := &GetProjectItemOptions{Fields: zeroValue} + g.GetFields() + g = &GetProjectItemOptions{} + g.GetFields() + g = nil + g.GetFields() +} + +func TestGetProvisionedSCIMGroupEnterpriseOptions_GetExcludedAttributes(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GetProvisionedSCIMGroupEnterpriseOptions{ExcludedAttributes: &zeroValue} + g.GetExcludedAttributes() + g = &GetProvisionedSCIMGroupEnterpriseOptions{} + g.GetExcludedAttributes() + g = nil + g.GetExcludedAttributes() +} + +func TestGist_GetComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + g := &Gist{Comments: &zeroValue} + g.GetComments() + g = &Gist{} + g.GetComments() + g = nil + g.GetComments() +} + +func TestGist_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &Gist{CreatedAt: &zeroValue} + g.GetCreatedAt() + g = &Gist{} + g.GetCreatedAt() + g = nil + g.GetCreatedAt() +} + +func TestGist_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gist{Description: &zeroValue} + g.GetDescription() + g = &Gist{} + g.GetDescription() + g = nil + g.GetDescription() +} + +func TestGist_GetFiles(tt *testing.T) { + tt.Parallel() + zeroValue := map[GistFilename]GistFile{} + g := &Gist{Files: zeroValue} + g.GetFiles() + g = &Gist{} + g.GetFiles() + g = nil + g.GetFiles() +} + +func TestGist_GetGitPullURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gist{GitPullURL: &zeroValue} + g.GetGitPullURL() + g = &Gist{} + g.GetGitPullURL() + g = nil + g.GetGitPullURL() +} + +func TestGist_GetGitPushURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gist{GitPushURL: &zeroValue} + g.GetGitPushURL() + g = &Gist{} + g.GetGitPushURL() + g = nil + g.GetGitPushURL() +} + +func TestGist_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gist{HTMLURL: &zeroValue} + g.GetHTMLURL() + g = &Gist{} + g.GetHTMLURL() + g = nil + g.GetHTMLURL() +} + +func TestGist_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gist{ID: &zeroValue} + g.GetID() + g = &Gist{} + g.GetID() + g = nil + g.GetID() +} + +func TestGist_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gist{NodeID: &zeroValue} + g.GetNodeID() + g = &Gist{} + g.GetNodeID() + g = nil + g.GetNodeID() +} + +func TestGist_GetOwner(tt *testing.T) { + tt.Parallel() + g := &Gist{} + g.GetOwner() + g = nil + g.GetOwner() +} + +func TestGist_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + g := &Gist{Public: &zeroValue} + g.GetPublic() + g = &Gist{} + g.GetPublic() + g = nil + g.GetPublic() +} + +func TestGist_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &Gist{UpdatedAt: &zeroValue} + g.GetUpdatedAt() + g = &Gist{} + g.GetUpdatedAt() + g = nil + g.GetUpdatedAt() +} + +func TestGistComment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistComment{Body: &zeroValue} + g.GetBody() + g = &GistComment{} + g.GetBody() + g = nil + g.GetBody() +} + +func TestGistComment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GistComment{CreatedAt: &zeroValue} + g.GetCreatedAt() + g = &GistComment{} + g.GetCreatedAt() + g = nil + g.GetCreatedAt() +} + +func TestGistComment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + g := &GistComment{ID: &zeroValue} + g.GetID() + g = &GistComment{} + g.GetID() + g = nil + g.GetID() +} + +func TestGistComment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistComment{URL: &zeroValue} + g.GetURL() + g = &GistComment{} + g.GetURL() + g = nil + g.GetURL() +} + +func TestGistComment_GetUser(tt *testing.T) { + tt.Parallel() + g := &GistComment{} + g.GetUser() + g = nil + g.GetUser() +} + +func TestGistCommit_GetChangeStatus(tt *testing.T) { + tt.Parallel() + g := &GistCommit{} + g.GetChangeStatus() + g = nil + g.GetChangeStatus() +} + +func TestGistCommit_GetCommittedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GistCommit{CommittedAt: &zeroValue} + g.GetCommittedAt() + g = &GistCommit{} + g.GetCommittedAt() + g = nil + g.GetCommittedAt() +} + +func TestGistCommit_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistCommit{NodeID: &zeroValue} + g.GetNodeID() + g = &GistCommit{} + g.GetNodeID() + g = nil + g.GetNodeID() +} + +func TestGistCommit_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistCommit{URL: &zeroValue} + g.GetURL() + g = &GistCommit{} + g.GetURL() + g = nil + g.GetURL() +} + +func TestGistCommit_GetUser(tt *testing.T) { + tt.Parallel() + g := &GistCommit{} + g.GetUser() + g = nil + g.GetUser() +} + +func TestGistCommit_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistCommit{Version: &zeroValue} + g.GetVersion() + g = &GistCommit{} + g.GetVersion() + g = nil + g.GetVersion() +} + +func TestGistFile_GetContent(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFile{Content: &zeroValue} + g.GetContent() + g = &GistFile{} + g.GetContent() + g = nil + g.GetContent() +} + +func TestGistFile_GetFilename(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFile{Filename: &zeroValue} + g.GetFilename() + g = &GistFile{} + g.GetFilename() + g = nil + g.GetFilename() +} + +func TestGistFile_GetLanguage(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFile{Language: &zeroValue} + g.GetLanguage() + g = &GistFile{} + g.GetLanguage() + g = nil + g.GetLanguage() +} + +func TestGistFile_GetRawURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFile{RawURL: &zeroValue} + g.GetRawURL() + g = &GistFile{} + g.GetRawURL() + g = nil + g.GetRawURL() +} + +func TestGistFile_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + g := &GistFile{Size: &zeroValue} + g.GetSize() + g = &GistFile{} + g.GetSize() + g = nil + g.GetSize() +} + +func TestGistFile_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFile{Type: &zeroValue} + g.GetType() + g = &GistFile{} + g.GetType() + g = nil + g.GetType() +} + +func TestGistFork_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GistFork{CreatedAt: &zeroValue} + g.GetCreatedAt() + g = &GistFork{} + g.GetCreatedAt() + g = nil + g.GetCreatedAt() +} + +func TestGistFork_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFork{ID: &zeroValue} + g.GetID() + g = &GistFork{} + g.GetID() + g = nil + g.GetID() +} + +func TestGistFork_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFork{NodeID: &zeroValue} + g.GetNodeID() + g = &GistFork{} + g.GetNodeID() + g = nil + g.GetNodeID() +} + +func TestGistFork_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GistFork{UpdatedAt: &zeroValue} + g.GetUpdatedAt() + g = &GistFork{} + g.GetUpdatedAt() + g = nil + g.GetUpdatedAt() +} + +func TestGistFork_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GistFork{URL: &zeroValue} + g.GetURL() + g = &GistFork{} + g.GetURL() + g = nil + g.GetURL() +} + +func TestGistFork_GetUser(tt *testing.T) { + tt.Parallel() + g := &GistFork{} + g.GetUser() + g = nil + g.GetUser() +} + +func TestGistListOptions_GetSince(tt *testing.T) { + tt.Parallel() + g := &GistListOptions{} + g.GetSince() + g = nil + g.GetSince() +} + +func TestGistStats_GetPrivateGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + g := &GistStats{PrivateGists: &zeroValue} + g.GetPrivateGists() + g = &GistStats{} + g.GetPrivateGists() + g = nil + g.GetPrivateGists() +} + +func TestGistStats_GetPublicGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + g := &GistStats{PublicGists: &zeroValue} + g.GetPublicGists() + g = &GistStats{} + g.GetPublicGists() + g = nil + g.GetPublicGists() +} + +func TestGistStats_GetTotalGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + g := &GistStats{TotalGists: &zeroValue} + g.GetTotalGists() + g = &GistStats{} + g.GetTotalGists() + g = nil + g.GetTotalGists() +} + +func TestGitHubAppAuthorizationEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GitHubAppAuthorizationEvent{Action: &zeroValue} + g.GetAction() + g = &GitHubAppAuthorizationEvent{} + g.GetAction() + g = nil + g.GetAction() +} + +func TestGitHubAppAuthorizationEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + g := &GitHubAppAuthorizationEvent{} + g.GetInstallation() + g = nil + g.GetInstallation() +} + +func TestGitHubAppAuthorizationEvent_GetSender(tt *testing.T) { + tt.Parallel() + g := &GitHubAppAuthorizationEvent{} + g.GetSender() + g = nil + g.GetSender() +} + +func TestGitignore_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gitignore{Name: &zeroValue} + g.GetName() + g = &Gitignore{} + g.GetName() + g = nil + g.GetName() +} + +func TestGitignore_GetSource(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Gitignore{Source: &zeroValue} + g.GetSource() + g = &Gitignore{} + g.GetSource() + g = nil + g.GetSource() +} + +func TestGitObject_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GitObject{SHA: &zeroValue} + g.GetSHA() + g = &GitObject{} + g.GetSHA() + g = nil + g.GetSHA() +} + +func TestGitObject_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GitObject{Type: &zeroValue} + g.GetType() + g = &GitObject{} + g.GetType() + g = nil + g.GetType() +} + +func TestGitObject_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GitObject{URL: &zeroValue} + g.GetURL() + g = &GitObject{} + g.GetURL() + g = nil + g.GetURL() +} + +func TestGlobalSecurityAdvisory_GetCredits(tt *testing.T) { + tt.Parallel() + zeroValue := []*Credit{} + g := &GlobalSecurityAdvisory{Credits: zeroValue} + g.GetCredits() + g = &GlobalSecurityAdvisory{} + g.GetCredits() + g = nil + g.GetCredits() +} + +func TestGlobalSecurityAdvisory_GetGithubReviewedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GlobalSecurityAdvisory{GithubReviewedAt: &zeroValue} + g.GetGithubReviewedAt() + g = &GlobalSecurityAdvisory{} + g.GetGithubReviewedAt() + g = nil + g.GetGithubReviewedAt() +} + +func TestGlobalSecurityAdvisory_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + g := &GlobalSecurityAdvisory{ID: &zeroValue} + g.GetID() + g = &GlobalSecurityAdvisory{} + g.GetID() + g = nil + g.GetID() +} + +func TestGlobalSecurityAdvisory_GetNVDPublishedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GlobalSecurityAdvisory{NVDPublishedAt: &zeroValue} + g.GetNVDPublishedAt() + g = &GlobalSecurityAdvisory{} + g.GetNVDPublishedAt() + g = nil + g.GetNVDPublishedAt() +} + +func TestGlobalSecurityAdvisory_GetReferences(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + g := &GlobalSecurityAdvisory{References: zeroValue} + g.GetReferences() + g = &GlobalSecurityAdvisory{} + g.GetReferences() + g = nil + g.GetReferences() +} + +func TestGlobalSecurityAdvisory_GetRepositoryAdvisoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GlobalSecurityAdvisory{RepositoryAdvisoryURL: &zeroValue} + g.GetRepositoryAdvisoryURL() + g = &GlobalSecurityAdvisory{} + g.GetRepositoryAdvisoryURL() + g = nil + g.GetRepositoryAdvisoryURL() +} + +func TestGlobalSecurityAdvisory_GetSourceCodeLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GlobalSecurityAdvisory{SourceCodeLocation: &zeroValue} + g.GetSourceCodeLocation() + g = &GlobalSecurityAdvisory{} + g.GetSourceCodeLocation() + g = nil + g.GetSourceCodeLocation() +} + +func TestGlobalSecurityAdvisory_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GlobalSecurityAdvisory{Type: &zeroValue} + g.GetType() + g = &GlobalSecurityAdvisory{} + g.GetType() + g = nil + g.GetType() +} + +func TestGlobalSecurityAdvisory_GetVulnerabilities(tt *testing.T) { + tt.Parallel() + zeroValue := []*GlobalSecurityVulnerability{} + g := &GlobalSecurityAdvisory{Vulnerabilities: zeroValue} + g.GetVulnerabilities() + g = &GlobalSecurityAdvisory{} + g.GetVulnerabilities() + g = nil + g.GetVulnerabilities() +} + +func TestGlobalSecurityVulnerability_GetFirstPatchedVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GlobalSecurityVulnerability{FirstPatchedVersion: &zeroValue} + g.GetFirstPatchedVersion() + g = &GlobalSecurityVulnerability{} + g.GetFirstPatchedVersion() + g = nil + g.GetFirstPatchedVersion() +} + +func TestGlobalSecurityVulnerability_GetPackage(tt *testing.T) { + tt.Parallel() + g := &GlobalSecurityVulnerability{} + g.GetPackage() + g = nil + g.GetPackage() +} + +func TestGlobalSecurityVulnerability_GetVulnerableFunctions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + g := &GlobalSecurityVulnerability{VulnerableFunctions: zeroValue} + g.GetVulnerableFunctions() + g = &GlobalSecurityVulnerability{} + g.GetVulnerableFunctions() + g = nil + g.GetVulnerableFunctions() +} + +func TestGlobalSecurityVulnerability_GetVulnerableVersionRange(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GlobalSecurityVulnerability{VulnerableVersionRange: &zeroValue} + g.GetVulnerableVersionRange() + g = &GlobalSecurityVulnerability{} + g.GetVulnerableVersionRange() + g = nil + g.GetVulnerableVersionRange() +} + +func TestGollumEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + g := &GollumEvent{} + g.GetInstallation() + g = nil + g.GetInstallation() +} + +func TestGollumEvent_GetOrg(tt *testing.T) { + tt.Parallel() + g := &GollumEvent{} + g.GetOrg() + g = nil + g.GetOrg() +} + +func TestGollumEvent_GetPages(tt *testing.T) { + tt.Parallel() + zeroValue := []*Page{} + g := &GollumEvent{Pages: zeroValue} + g.GetPages() + g = &GollumEvent{} + g.GetPages() + g = nil + g.GetPages() +} + +func TestGollumEvent_GetRepo(tt *testing.T) { + tt.Parallel() + g := &GollumEvent{} + g.GetRepo() + g = nil + g.GetRepo() +} + +func TestGollumEvent_GetSender(tt *testing.T) { + tt.Parallel() + g := &GollumEvent{} + g.GetSender() + g = nil + g.GetSender() +} + +func TestGoogleCloudConfig_GetBucket(tt *testing.T) { + tt.Parallel() + g := &GoogleCloudConfig{} + g.GetBucket() + g = nil + g.GetBucket() +} + +func TestGoogleCloudConfig_GetEncryptedJSONCredentials(tt *testing.T) { + tt.Parallel() + g := &GoogleCloudConfig{} + g.GetEncryptedJSONCredentials() + g = nil + g.GetEncryptedJSONCredentials() +} + +func TestGoogleCloudConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + g := &GoogleCloudConfig{} + g.GetKeyID() + g = nil + g.GetKeyID() +} + +func TestGPGEmail_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GPGEmail{Email: &zeroValue} + g.GetEmail() + g = &GPGEmail{} + g.GetEmail() + g = nil + g.GetEmail() +} + +func TestGPGEmail_GetVerified(tt *testing.T) { + tt.Parallel() + var zeroValue bool + g := &GPGEmail{Verified: &zeroValue} + g.GetVerified() + g = &GPGEmail{} + g.GetVerified() + g = nil + g.GetVerified() +} + +func TestGPGKey_GetCanCertify(tt *testing.T) { + tt.Parallel() + var zeroValue bool + g := &GPGKey{CanCertify: &zeroValue} + g.GetCanCertify() + g = &GPGKey{} + g.GetCanCertify() + g = nil + g.GetCanCertify() +} + +func TestGPGKey_GetCanEncryptComms(tt *testing.T) { + tt.Parallel() + var zeroValue bool + g := &GPGKey{CanEncryptComms: &zeroValue} + g.GetCanEncryptComms() + g = &GPGKey{} + g.GetCanEncryptComms() + g = nil + g.GetCanEncryptComms() +} + +func TestGPGKey_GetCanEncryptStorage(tt *testing.T) { + tt.Parallel() + var zeroValue bool + g := &GPGKey{CanEncryptStorage: &zeroValue} + g.GetCanEncryptStorage() + g = &GPGKey{} + g.GetCanEncryptStorage() + g = nil + g.GetCanEncryptStorage() +} + +func TestGPGKey_GetCanSign(tt *testing.T) { + tt.Parallel() + var zeroValue bool + g := &GPGKey{CanSign: &zeroValue} + g.GetCanSign() + g = &GPGKey{} + g.GetCanSign() + g = nil + g.GetCanSign() +} + +func TestGPGKey_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GPGKey{CreatedAt: &zeroValue} + g.GetCreatedAt() + g = &GPGKey{} + g.GetCreatedAt() + g = nil + g.GetCreatedAt() +} + +func TestGPGKey_GetEmails(tt *testing.T) { + tt.Parallel() + zeroValue := []*GPGEmail{} + g := &GPGKey{Emails: zeroValue} + g.GetEmails() + g = &GPGKey{} + g.GetEmails() + g = nil + g.GetEmails() +} + +func TestGPGKey_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &GPGKey{ExpiresAt: &zeroValue} + g.GetExpiresAt() + g = &GPGKey{} + g.GetExpiresAt() + g = nil + g.GetExpiresAt() +} + +func TestGPGKey_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + g := &GPGKey{ID: &zeroValue} + g.GetID() + g = &GPGKey{} + g.GetID() + g = nil + g.GetID() +} + +func TestGPGKey_GetKeyID(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GPGKey{KeyID: &zeroValue} + g.GetKeyID() + g = &GPGKey{} + g.GetKeyID() + g = nil + g.GetKeyID() +} + +func TestGPGKey_GetPrimaryKeyID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + g := &GPGKey{PrimaryKeyID: &zeroValue} + g.GetPrimaryKeyID() + g = &GPGKey{} + g.GetPrimaryKeyID() + g = nil + g.GetPrimaryKeyID() +} + +func TestGPGKey_GetPublicKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GPGKey{PublicKey: &zeroValue} + g.GetPublicKey() + g = &GPGKey{} + g.GetPublicKey() + g = nil + g.GetPublicKey() +} + +func TestGPGKey_GetRawKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &GPGKey{RawKey: &zeroValue} + g.GetRawKey() + g = &GPGKey{} + g.GetRawKey() + g = nil + g.GetRawKey() +} + +func TestGPGKey_GetSubkeys(tt *testing.T) { + tt.Parallel() + zeroValue := []*GPGKey{} + g := &GPGKey{Subkeys: zeroValue} + g.GetSubkeys() + g = &GPGKey{} + g.GetSubkeys() + g = nil + g.GetSubkeys() +} + +func TestGrant_GetApp(tt *testing.T) { + tt.Parallel() + g := &Grant{} + g.GetApp() + g = nil + g.GetApp() +} + +func TestGrant_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &Grant{CreatedAt: &zeroValue} + g.GetCreatedAt() + g = &Grant{} + g.GetCreatedAt() + g = nil + g.GetCreatedAt() +} + +func TestGrant_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + g := &Grant{ID: &zeroValue} + g.GetID() + g = &Grant{} + g.GetID() + g = nil + g.GetID() +} + +func TestGrant_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + g := &Grant{Scopes: zeroValue} + g.GetScopes() + g = &Grant{} + g.GetScopes() + g = nil + g.GetScopes() +} + +func TestGrant_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + g := &Grant{UpdatedAt: &zeroValue} + g.GetUpdatedAt() + g = &Grant{} + g.GetUpdatedAt() + g = nil + g.GetUpdatedAt() +} + +func TestGrant_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + g := &Grant{URL: &zeroValue} + g.GetURL() + g = &Grant{} + g.GetURL() + g = nil + g.GetURL() +} + +func TestHeadCommit_GetAdded(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + h := &HeadCommit{Added: zeroValue} + h.GetAdded() + h = &HeadCommit{} + h.GetAdded() + h = nil + h.GetAdded() +} + +func TestHeadCommit_GetAuthor(tt *testing.T) { + tt.Parallel() + h := &HeadCommit{} + h.GetAuthor() + h = nil + h.GetAuthor() +} + +func TestHeadCommit_GetCommitter(tt *testing.T) { + tt.Parallel() + h := &HeadCommit{} + h.GetCommitter() + h = nil + h.GetCommitter() +} + +func TestHeadCommit_GetDistinct(tt *testing.T) { + tt.Parallel() + var zeroValue bool + h := &HeadCommit{Distinct: &zeroValue} + h.GetDistinct() + h = &HeadCommit{} + h.GetDistinct() + h = nil + h.GetDistinct() +} + +func TestHeadCommit_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HeadCommit{ID: &zeroValue} + h.GetID() + h = &HeadCommit{} + h.GetID() + h = nil + h.GetID() +} + +func TestHeadCommit_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HeadCommit{Message: &zeroValue} + h.GetMessage() + h = &HeadCommit{} + h.GetMessage() + h = nil + h.GetMessage() +} + +func TestHeadCommit_GetModified(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + h := &HeadCommit{Modified: zeroValue} + h.GetModified() + h = &HeadCommit{} + h.GetModified() + h = nil + h.GetModified() +} + +func TestHeadCommit_GetRemoved(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + h := &HeadCommit{Removed: zeroValue} + h.GetRemoved() + h = &HeadCommit{} + h.GetRemoved() + h = nil + h.GetRemoved() +} + +func TestHeadCommit_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HeadCommit{SHA: &zeroValue} + h.GetSHA() + h = &HeadCommit{} + h.GetSHA() + h = nil + h.GetSHA() +} + +func TestHeadCommit_GetTimestamp(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + h := &HeadCommit{Timestamp: &zeroValue} + h.GetTimestamp() + h = &HeadCommit{} + h.GetTimestamp() + h = nil + h.GetTimestamp() +} + +func TestHeadCommit_GetTreeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HeadCommit{TreeID: &zeroValue} + h.GetTreeID() + h = &HeadCommit{} + h.GetTreeID() + h = nil + h.GetTreeID() +} + +func TestHeadCommit_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HeadCommit{URL: &zeroValue} + h.GetURL() + h = &HeadCommit{} + h.GetURL() + h = nil + h.GetURL() +} + +func TestHecConfig_GetDomain(tt *testing.T) { + tt.Parallel() + h := &HecConfig{} + h.GetDomain() + h = nil + h.GetDomain() +} + +func TestHecConfig_GetEncryptedToken(tt *testing.T) { + tt.Parallel() + h := &HecConfig{} + h.GetEncryptedToken() + h = nil + h.GetEncryptedToken() +} + +func TestHecConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + h := &HecConfig{} + h.GetKeyID() + h = nil + h.GetKeyID() +} + +func TestHecConfig_GetPath(tt *testing.T) { + tt.Parallel() + h := &HecConfig{} + h.GetPath() + h = nil + h.GetPath() +} + +func TestHecConfig_GetPort(tt *testing.T) { + tt.Parallel() + h := &HecConfig{} + h.GetPort() + h = nil + h.GetPort() +} + +func TestHecConfig_GetSSLVerify(tt *testing.T) { + tt.Parallel() + h := &HecConfig{} + h.GetSSLVerify() + h = nil + h.GetSSLVerify() +} + +func TestHook_GetActive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + h := &Hook{Active: &zeroValue} + h.GetActive() + h = &Hook{} + h.GetActive() + h = nil + h.GetActive() +} + +func TestHook_GetConfig(tt *testing.T) { + tt.Parallel() + h := &Hook{} + h.GetConfig() + h = nil + h.GetConfig() +} + +func TestHook_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + h := &Hook{CreatedAt: &zeroValue} + h.GetCreatedAt() + h = &Hook{} + h.GetCreatedAt() + h = nil + h.GetCreatedAt() +} + +func TestHook_GetEvents(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + h := &Hook{Events: zeroValue} + h.GetEvents() + h = &Hook{} + h.GetEvents() + h = nil + h.GetEvents() +} + +func TestHook_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &Hook{ID: &zeroValue} + h.GetID() + h = &Hook{} + h.GetID() + h = nil + h.GetID() +} + +func TestHook_GetLastResponse(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + h := &Hook{LastResponse: zeroValue} + h.GetLastResponse() + h = &Hook{} + h.GetLastResponse() + h = nil + h.GetLastResponse() +} + +func TestHook_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &Hook{Name: &zeroValue} + h.GetName() + h = &Hook{} + h.GetName() + h = nil + h.GetName() +} + +func TestHook_GetPingURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &Hook{PingURL: &zeroValue} + h.GetPingURL() + h = &Hook{} + h.GetPingURL() + h = nil + h.GetPingURL() +} + +func TestHook_GetTestURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &Hook{TestURL: &zeroValue} + h.GetTestURL() + h = &Hook{} + h.GetTestURL() + h = nil + h.GetTestURL() +} + +func TestHook_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &Hook{Type: &zeroValue} + h.GetType() + h = &Hook{} + h.GetType() + h = nil + h.GetType() +} + +func TestHook_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + h := &Hook{UpdatedAt: &zeroValue} + h.GetUpdatedAt() + h = &Hook{} + h.GetUpdatedAt() + h = nil + h.GetUpdatedAt() +} + +func TestHook_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &Hook{URL: &zeroValue} + h.GetURL() + h = &Hook{} + h.GetURL() + h = nil + h.GetURL() +} + +func TestHookConfig_GetContentType(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookConfig{ContentType: &zeroValue} + h.GetContentType() + h = &HookConfig{} + h.GetContentType() + h = nil + h.GetContentType() +} + +func TestHookConfig_GetInsecureSSL(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookConfig{InsecureSSL: &zeroValue} + h.GetInsecureSSL() + h = &HookConfig{} + h.GetInsecureSSL() + h = nil + h.GetInsecureSSL() +} + +func TestHookConfig_GetSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookConfig{Secret: &zeroValue} + h.GetSecret() + h = &HookConfig{} + h.GetSecret() + h = nil + h.GetSecret() +} + +func TestHookConfig_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookConfig{URL: &zeroValue} + h.GetURL() + h = &HookConfig{} + h.GetURL() + h = nil + h.GetURL() +} + +func TestHookDelivery_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookDelivery{Action: &zeroValue} + h.GetAction() + h = &HookDelivery{} + h.GetAction() + h = nil + h.GetAction() +} + +func TestHookDelivery_GetDeliveredAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + h := &HookDelivery{DeliveredAt: &zeroValue} + h.GetDeliveredAt() + h = &HookDelivery{} + h.GetDeliveredAt() + h = nil + h.GetDeliveredAt() +} + +func TestHookDelivery_GetDuration(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + h := &HookDelivery{Duration: &zeroValue} + h.GetDuration() + h = &HookDelivery{} + h.GetDuration() + h = nil + h.GetDuration() +} + +func TestHookDelivery_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookDelivery{Event: &zeroValue} + h.GetEvent() + h = &HookDelivery{} + h.GetEvent() + h = nil + h.GetEvent() +} + +func TestHookDelivery_GetGUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookDelivery{GUID: &zeroValue} + h.GetGUID() + h = &HookDelivery{} + h.GetGUID() + h = nil + h.GetGUID() +} + +func TestHookDelivery_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HookDelivery{ID: &zeroValue} + h.GetID() + h = &HookDelivery{} + h.GetID() + h = nil + h.GetID() +} + +func TestHookDelivery_GetInstallationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HookDelivery{InstallationID: &zeroValue} + h.GetInstallationID() + h = &HookDelivery{} + h.GetInstallationID() + h = nil + h.GetInstallationID() +} + +func TestHookDelivery_GetRedelivery(tt *testing.T) { + tt.Parallel() + var zeroValue bool + h := &HookDelivery{Redelivery: &zeroValue} + h.GetRedelivery() + h = &HookDelivery{} + h.GetRedelivery() + h = nil + h.GetRedelivery() +} + +func TestHookDelivery_GetRepositoryID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HookDelivery{RepositoryID: &zeroValue} + h.GetRepositoryID() + h = &HookDelivery{} + h.GetRepositoryID() + h = nil + h.GetRepositoryID() +} + +func TestHookDelivery_GetRequest(tt *testing.T) { + tt.Parallel() + h := &HookDelivery{} + h.GetRequest() + h = nil + h.GetRequest() +} + +func TestHookDelivery_GetResponse(tt *testing.T) { + tt.Parallel() + h := &HookDelivery{} + h.GetResponse() + h = nil + h.GetResponse() +} + +func TestHookDelivery_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HookDelivery{Status: &zeroValue} + h.GetStatus() + h = &HookDelivery{} + h.GetStatus() + h = nil + h.GetStatus() +} + +func TestHookDelivery_GetStatusCode(tt *testing.T) { + tt.Parallel() + var zeroValue int + h := &HookDelivery{StatusCode: &zeroValue} + h.GetStatusCode() + h = &HookDelivery{} + h.GetStatusCode() + h = nil + h.GetStatusCode() +} + +func TestHookRequest_GetHeaders(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + h := &HookRequest{Headers: zeroValue} + h.GetHeaders() + h = &HookRequest{} + h.GetHeaders() + h = nil + h.GetHeaders() +} + +func TestHookRequest_GetRawPayload(tt *testing.T) { + tt.Parallel() + var zeroValue json.RawMessage + h := &HookRequest{RawPayload: &zeroValue} + h.GetRawPayload() + h = &HookRequest{} + h.GetRawPayload() + h = nil + h.GetRawPayload() +} + +func TestHookResponse_GetHeaders(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + h := &HookResponse{Headers: zeroValue} + h.GetHeaders() + h = &HookResponse{} + h.GetHeaders() + h = nil + h.GetHeaders() +} + +func TestHookResponse_GetRawPayload(tt *testing.T) { + tt.Parallel() + var zeroValue json.RawMessage + h := &HookResponse{RawPayload: &zeroValue} + h.GetRawPayload() + h = &HookResponse{} + h.GetRawPayload() + h = nil + h.GetRawPayload() +} + +func TestHookStats_GetActiveHooks(tt *testing.T) { + tt.Parallel() + var zeroValue int + h := &HookStats{ActiveHooks: &zeroValue} + h.GetActiveHooks() + h = &HookStats{} + h.GetActiveHooks() + h = nil + h.GetActiveHooks() +} + +func TestHookStats_GetInactiveHooks(tt *testing.T) { + tt.Parallel() + var zeroValue int + h := &HookStats{InactiveHooks: &zeroValue} + h.GetInactiveHooks() + h = &HookStats{} + h.GetInactiveHooks() + h = nil + h.GetInactiveHooks() +} + +func TestHookStats_GetTotalHooks(tt *testing.T) { + tt.Parallel() + var zeroValue int + h := &HookStats{TotalHooks: &zeroValue} + h.GetTotalHooks() + h = &HookStats{} + h.GetTotalHooks() + h = nil + h.GetTotalHooks() +} + +func TestHostedRunner_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HostedRunner{ID: &zeroValue} + h.GetID() + h = &HostedRunner{} + h.GetID() + h = nil + h.GetID() +} + +func TestHostedRunner_GetImageDetails(tt *testing.T) { + tt.Parallel() + h := &HostedRunner{} + h.GetImageDetails() + h = nil + h.GetImageDetails() +} + +func TestHostedRunner_GetLastActiveOn(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + h := &HostedRunner{LastActiveOn: &zeroValue} + h.GetLastActiveOn() + h = &HostedRunner{} + h.GetLastActiveOn() + h = nil + h.GetLastActiveOn() +} + +func TestHostedRunner_GetMachineSizeDetails(tt *testing.T) { + tt.Parallel() + h := &HostedRunner{} + h.GetMachineSizeDetails() + h = nil + h.GetMachineSizeDetails() +} + +func TestHostedRunner_GetMaximumRunners(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HostedRunner{MaximumRunners: &zeroValue} + h.GetMaximumRunners() + h = &HostedRunner{} + h.GetMaximumRunners() + h = nil + h.GetMaximumRunners() +} + +func TestHostedRunner_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunner{Name: &zeroValue} + h.GetName() + h = &HostedRunner{} + h.GetName() + h = nil + h.GetName() +} + +func TestHostedRunner_GetPlatform(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunner{Platform: &zeroValue} + h.GetPlatform() + h = &HostedRunner{} + h.GetPlatform() + h = nil + h.GetPlatform() +} + +func TestHostedRunner_GetPublicIPEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + h := &HostedRunner{PublicIPEnabled: &zeroValue} + h.GetPublicIPEnabled() + h = &HostedRunner{} + h.GetPublicIPEnabled() + h = nil + h.GetPublicIPEnabled() +} + +func TestHostedRunner_GetPublicIPs(tt *testing.T) { + tt.Parallel() + zeroValue := []*HostedRunnerPublicIP{} + h := &HostedRunner{PublicIPs: zeroValue} + h.GetPublicIPs() + h = &HostedRunner{} + h.GetPublicIPs() + h = nil + h.GetPublicIPs() +} + +func TestHostedRunner_GetRunnerGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HostedRunner{RunnerGroupID: &zeroValue} + h.GetRunnerGroupID() + h = &HostedRunner{} + h.GetRunnerGroupID() + h = nil + h.GetRunnerGroupID() +} + +func TestHostedRunner_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunner{Status: &zeroValue} + h.GetStatus() + h = &HostedRunner{} + h.GetStatus() + h = nil + h.GetStatus() +} + +func TestHostedRunnerCustomImage_GetID(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetID() + h = nil + h.GetID() +} + +func TestHostedRunnerCustomImage_GetLatestVersion(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetLatestVersion() + h = nil + h.GetLatestVersion() +} + +func TestHostedRunnerCustomImage_GetName(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetName() + h = nil + h.GetName() +} + +func TestHostedRunnerCustomImage_GetPlatform(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetPlatform() + h = nil + h.GetPlatform() +} + +func TestHostedRunnerCustomImage_GetSource(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetSource() + h = nil + h.GetSource() +} + +func TestHostedRunnerCustomImage_GetState(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetState() + h = nil + h.GetState() +} + +func TestHostedRunnerCustomImage_GetTotalVersionsSize(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetTotalVersionsSize() + h = nil + h.GetTotalVersionsSize() +} + +func TestHostedRunnerCustomImage_GetVersionsCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImage{} + h.GetVersionsCount() + h = nil + h.GetVersionsCount() +} + +func TestHostedRunnerCustomImages_GetImages(tt *testing.T) { + tt.Parallel() + zeroValue := []*HostedRunnerCustomImage{} + h := &HostedRunnerCustomImages{Images: zeroValue} + h.GetImages() + h = &HostedRunnerCustomImages{} + h.GetImages() + h = nil + h.GetImages() +} + +func TestHostedRunnerCustomImages_GetTotalCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImages{} + h.GetTotalCount() + h = nil + h.GetTotalCount() +} + +func TestHostedRunnerCustomImageVersion_GetCreatedOn(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImageVersion{} + h.GetCreatedOn() + h = nil + h.GetCreatedOn() +} + +func TestHostedRunnerCustomImageVersion_GetSizeGB(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImageVersion{} + h.GetSizeGB() + h = nil + h.GetSizeGB() +} + +func TestHostedRunnerCustomImageVersion_GetState(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImageVersion{} + h.GetState() + h = nil + h.GetState() +} + +func TestHostedRunnerCustomImageVersion_GetStateDetails(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImageVersion{} + h.GetStateDetails() + h = nil + h.GetStateDetails() +} + +func TestHostedRunnerCustomImageVersion_GetVersion(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImageVersion{} + h.GetVersion() + h = nil + h.GetVersion() +} + +func TestHostedRunnerCustomImageVersions_GetImageVersions(tt *testing.T) { + tt.Parallel() + zeroValue := []*HostedRunnerCustomImageVersion{} + h := &HostedRunnerCustomImageVersions{ImageVersions: zeroValue} + h.GetImageVersions() + h = &HostedRunnerCustomImageVersions{} + h.GetImageVersions() + h = nil + h.GetImageVersions() +} + +func TestHostedRunnerCustomImageVersions_GetTotalCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerCustomImageVersions{} + h.GetTotalCount() + h = nil + h.GetTotalCount() +} + +func TestHostedRunnerImage_GetID(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImage{} + h.GetID() + h = nil + h.GetID() +} + +func TestHostedRunnerImage_GetSource(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImage{} + h.GetSource() + h = nil + h.GetSource() +} + +func TestHostedRunnerImage_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunnerImage{Version: &zeroValue} + h.GetVersion() + h = &HostedRunnerImage{} + h.GetVersion() + h = nil + h.GetVersion() +} + +func TestHostedRunnerImageDetail_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunnerImageDetail{DisplayName: &zeroValue} + h.GetDisplayName() + h = &HostedRunnerImageDetail{} + h.GetDisplayName() + h = nil + h.GetDisplayName() +} + +func TestHostedRunnerImageDetail_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunnerImageDetail{ID: &zeroValue} + h.GetID() + h = &HostedRunnerImageDetail{} + h.GetID() + h = nil + h.GetID() +} + +func TestHostedRunnerImageDetail_GetSizeGB(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + h := &HostedRunnerImageDetail{SizeGB: &zeroValue} + h.GetSizeGB() + h = &HostedRunnerImageDetail{} + h.GetSizeGB() + h = nil + h.GetSizeGB() +} + +func TestHostedRunnerImageDetail_GetSource(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunnerImageDetail{Source: &zeroValue} + h.GetSource() + h = &HostedRunnerImageDetail{} + h.GetSource() + h = nil + h.GetSource() +} + +func TestHostedRunnerImageDetail_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + h := &HostedRunnerImageDetail{Version: &zeroValue} + h.GetVersion() + h = &HostedRunnerImageDetail{} + h.GetVersion() + h = nil + h.GetVersion() +} + +func TestHostedRunnerImages_GetImages(tt *testing.T) { + tt.Parallel() + zeroValue := []*HostedRunnerImageSpecs{} + h := &HostedRunnerImages{Images: zeroValue} + h.GetImages() + h = &HostedRunnerImages{} + h.GetImages() + h = nil + h.GetImages() +} + +func TestHostedRunnerImages_GetTotalCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImages{} + h.GetTotalCount() + h = nil + h.GetTotalCount() +} + +func TestHostedRunnerImageSpecs_GetDisplayName(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImageSpecs{} + h.GetDisplayName() + h = nil + h.GetDisplayName() +} + +func TestHostedRunnerImageSpecs_GetID(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImageSpecs{} + h.GetID() + h = nil + h.GetID() +} + +func TestHostedRunnerImageSpecs_GetPlatform(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImageSpecs{} + h.GetPlatform() + h = nil + h.GetPlatform() +} + +func TestHostedRunnerImageSpecs_GetSizeGB(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImageSpecs{} + h.GetSizeGB() + h = nil + h.GetSizeGB() +} + +func TestHostedRunnerImageSpecs_GetSource(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerImageSpecs{} + h.GetSource() + h = nil + h.GetSource() +} + +func TestHostedRunnerMachineSpec_GetCPUCores(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerMachineSpec{} + h.GetCPUCores() + h = nil + h.GetCPUCores() +} + +func TestHostedRunnerMachineSpec_GetID(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerMachineSpec{} + h.GetID() + h = nil + h.GetID() +} + +func TestHostedRunnerMachineSpec_GetMemoryGB(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerMachineSpec{} + h.GetMemoryGB() + h = nil + h.GetMemoryGB() +} + +func TestHostedRunnerMachineSpec_GetStorageGB(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerMachineSpec{} + h.GetStorageGB() + h = nil + h.GetStorageGB() +} + +func TestHostedRunnerMachineSpecs_GetMachineSpecs(tt *testing.T) { + tt.Parallel() + zeroValue := []*HostedRunnerMachineSpec{} + h := &HostedRunnerMachineSpecs{MachineSpecs: zeroValue} + h.GetMachineSpecs() + h = &HostedRunnerMachineSpecs{} + h.GetMachineSpecs() + h = nil + h.GetMachineSpecs() +} + +func TestHostedRunnerMachineSpecs_GetTotalCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerMachineSpecs{} + h.GetTotalCount() + h = nil + h.GetTotalCount() +} + +func TestHostedRunnerPlatforms_GetPlatforms(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + h := &HostedRunnerPlatforms{Platforms: zeroValue} + h.GetPlatforms() + h = &HostedRunnerPlatforms{} + h.GetPlatforms() + h = nil + h.GetPlatforms() +} + +func TestHostedRunnerPlatforms_GetTotalCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerPlatforms{} + h.GetTotalCount() + h = nil + h.GetTotalCount() +} + +func TestHostedRunnerPublicIP_GetEnabled(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerPublicIP{} + h.GetEnabled() + h = nil + h.GetEnabled() +} + +func TestHostedRunnerPublicIP_GetLength(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerPublicIP{} + h.GetLength() + h = nil + h.GetLength() +} + +func TestHostedRunnerPublicIP_GetPrefix(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerPublicIP{} + h.GetPrefix() + h = nil + h.GetPrefix() +} + +func TestHostedRunnerPublicIPLimits_GetPublicIPs(tt *testing.T) { + tt.Parallel() + h := &HostedRunnerPublicIPLimits{} + h.GetPublicIPs() + h = nil + h.GetPublicIPs() +} + +func TestHostedRunners_GetRunners(tt *testing.T) { + tt.Parallel() + zeroValue := []*HostedRunner{} + h := &HostedRunners{Runners: zeroValue} + h.GetRunners() + h = &HostedRunners{} + h.GetRunners() + h = nil + h.GetRunners() +} + +func TestHostedRunners_GetTotalCount(tt *testing.T) { + tt.Parallel() + h := &HostedRunners{} + h.GetTotalCount() + h = nil + h.GetTotalCount() +} + +func TestHovercard_GetContexts(tt *testing.T) { + tt.Parallel() + zeroValue := []*UserContext{} + h := &Hovercard{Contexts: zeroValue} + h.GetContexts() + h = &Hovercard{} + h.GetContexts() + h = nil + h.GetContexts() +} + +func TestHovercardOptions_GetSubjectID(tt *testing.T) { + tt.Parallel() + h := &HovercardOptions{} + h.GetSubjectID() + h = nil + h.GetSubjectID() +} + +func TestHovercardOptions_GetSubjectType(tt *testing.T) { + tt.Parallel() + h := &HovercardOptions{} + h.GetSubjectType() + h = nil + h.GetSubjectType() +} + +func TestIDPGroup_GetGroupDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IDPGroup{GroupDescription: &zeroValue} + i.GetGroupDescription() + i = &IDPGroup{} + i.GetGroupDescription() + i = nil + i.GetGroupDescription() +} + +func TestIDPGroup_GetGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IDPGroup{GroupID: &zeroValue} + i.GetGroupID() + i = &IDPGroup{} + i.GetGroupID() + i = nil + i.GetGroupID() +} + +func TestIDPGroup_GetGroupName(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IDPGroup{GroupName: &zeroValue} + i.GetGroupName() + i = &IDPGroup{} + i.GetGroupName() + i = nil + i.GetGroupName() +} + +func TestIDPGroupList_GetGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []*IDPGroup{} + i := &IDPGroupList{Groups: zeroValue} + i.GetGroups() + i = &IDPGroupList{} + i.GetGroups() + i = nil + i.GetGroups() +} + +func TestImmutableReleasePolicy_GetEnforcedRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &ImmutableReleasePolicy{EnforcedRepositories: &zeroValue} + i.GetEnforcedRepositories() + i = &ImmutableReleasePolicy{} + i.GetEnforcedRepositories() + i = nil + i.GetEnforcedRepositories() +} + +func TestImmutableReleasePolicy_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + i := &ImmutableReleasePolicy{SelectedRepositoryIDs: zeroValue} + i.GetSelectedRepositoryIDs() + i = &ImmutableReleasePolicy{} + i.GetSelectedRepositoryIDs() + i = nil + i.GetSelectedRepositoryIDs() +} + +func TestImmutableReleaseSettings_GetEnforcedRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &ImmutableReleaseSettings{EnforcedRepositories: &zeroValue} + i.GetEnforcedRepositories() + i = &ImmutableReleaseSettings{} + i.GetEnforcedRepositories() + i = nil + i.GetEnforcedRepositories() +} + +func TestImmutableReleaseSettings_GetSelectedRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &ImmutableReleaseSettings{SelectedRepositoriesURL: &zeroValue} + i.GetSelectedRepositoriesURL() + i = &ImmutableReleaseSettings{} + i.GetSelectedRepositoriesURL() + i = nil + i.GetSelectedRepositoriesURL() +} + +func TestImpersonateUserOptions_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &ImpersonateUserOptions{Scopes: zeroValue} + i.GetScopes() + i = &ImpersonateUserOptions{} + i.GetScopes() + i = nil + i.GetScopes() +} + +func TestImport_GetAuthorsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Import{AuthorsCount: &zeroValue} + i.GetAuthorsCount() + i = &Import{} + i.GetAuthorsCount() + i = nil + i.GetAuthorsCount() +} + +func TestImport_GetAuthorsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{AuthorsURL: &zeroValue} + i.GetAuthorsURL() + i = &Import{} + i.GetAuthorsURL() + i = nil + i.GetAuthorsURL() +} + +func TestImport_GetCommitCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Import{CommitCount: &zeroValue} + i.GetCommitCount() + i = &Import{} + i.GetCommitCount() + i = nil + i.GetCommitCount() +} + +func TestImport_GetFailedStep(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{FailedStep: &zeroValue} + i.GetFailedStep() + i = &Import{} + i.GetFailedStep() + i = nil + i.GetFailedStep() +} + +func TestImport_GetHasLargeFiles(tt *testing.T) { + tt.Parallel() + var zeroValue bool + i := &Import{HasLargeFiles: &zeroValue} + i.GetHasLargeFiles() + i = &Import{} + i.GetHasLargeFiles() + i = nil + i.GetHasLargeFiles() +} + +func TestImport_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{HTMLURL: &zeroValue} + i.GetHTMLURL() + i = &Import{} + i.GetHTMLURL() + i = nil + i.GetHTMLURL() +} + +func TestImport_GetHumanName(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{HumanName: &zeroValue} + i.GetHumanName() + i = &Import{} + i.GetHumanName() + i = nil + i.GetHumanName() +} + +func TestImport_GetLargeFilesCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Import{LargeFilesCount: &zeroValue} + i.GetLargeFilesCount() + i = &Import{} + i.GetLargeFilesCount() + i = nil + i.GetLargeFilesCount() +} + +func TestImport_GetLargeFilesSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Import{LargeFilesSize: &zeroValue} + i.GetLargeFilesSize() + i = &Import{} + i.GetLargeFilesSize() + i = nil + i.GetLargeFilesSize() +} + +func TestImport_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{Message: &zeroValue} + i.GetMessage() + i = &Import{} + i.GetMessage() + i = nil + i.GetMessage() +} + +func TestImport_GetPercent(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Import{Percent: &zeroValue} + i.GetPercent() + i = &Import{} + i.GetPercent() + i = nil + i.GetPercent() +} + +func TestImport_GetProjectChoices(tt *testing.T) { + tt.Parallel() + zeroValue := []*Import{} + i := &Import{ProjectChoices: zeroValue} + i.GetProjectChoices() + i = &Import{} + i.GetProjectChoices() + i = nil + i.GetProjectChoices() +} + +func TestImport_GetPushPercent(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Import{PushPercent: &zeroValue} + i.GetPushPercent() + i = &Import{} + i.GetPushPercent() + i = nil + i.GetPushPercent() +} + +func TestImport_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{RepositoryURL: &zeroValue} + i.GetRepositoryURL() + i = &Import{} + i.GetRepositoryURL() + i = nil + i.GetRepositoryURL() +} + +func TestImport_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{Status: &zeroValue} + i.GetStatus() + i = &Import{} + i.GetStatus() + i = nil + i.GetStatus() +} + +func TestImport_GetStatusText(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{StatusText: &zeroValue} + i.GetStatusText() + i = &Import{} + i.GetStatusText() + i = nil + i.GetStatusText() +} + +func TestImport_GetTFVCProject(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{TFVCProject: &zeroValue} + i.GetTFVCProject() + i = &Import{} + i.GetTFVCProject() + i = nil + i.GetTFVCProject() +} + +func TestImport_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{URL: &zeroValue} + i.GetURL() + i = &Import{} + i.GetURL() + i = nil + i.GetURL() +} + +func TestImport_GetUseLFS(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{UseLFS: &zeroValue} + i.GetUseLFS() + i = &Import{} + i.GetUseLFS() + i = nil + i.GetUseLFS() +} + +func TestImport_GetVCS(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{VCS: &zeroValue} + i.GetVCS() + i = &Import{} + i.GetVCS() + i = nil + i.GetVCS() +} + +func TestImport_GetVCSPassword(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{VCSPassword: &zeroValue} + i.GetVCSPassword() + i = &Import{} + i.GetVCSPassword() + i = nil + i.GetVCSPassword() +} + +func TestImport_GetVCSURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{VCSURL: &zeroValue} + i.GetVCSURL() + i = &Import{} + i.GetVCSURL() + i = nil + i.GetVCSURL() +} + +func TestImport_GetVCSUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Import{VCSUsername: &zeroValue} + i.GetVCSUsername() + i = &Import{} + i.GetVCSUsername() + i = nil + i.GetVCSUsername() +} + +func TestInitialConfigOptions_GetLicense(tt *testing.T) { + tt.Parallel() + i := &InitialConfigOptions{} + i.GetLicense() + i = nil + i.GetLicense() +} + +func TestInitialConfigOptions_GetPassword(tt *testing.T) { + tt.Parallel() + i := &InitialConfigOptions{} + i.GetPassword() + i = nil + i.GetPassword() +} + +func TestInstallableOrganization_GetAccessibleRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallableOrganization{AccessibleRepositoriesURL: &zeroValue} + i.GetAccessibleRepositoriesURL() + i = &InstallableOrganization{} + i.GetAccessibleRepositoriesURL() + i = nil + i.GetAccessibleRepositoriesURL() +} + +func TestInstallableOrganization_GetID(tt *testing.T) { + tt.Parallel() + i := &InstallableOrganization{} + i.GetID() + i = nil + i.GetID() +} + +func TestInstallableOrganization_GetLogin(tt *testing.T) { + tt.Parallel() + i := &InstallableOrganization{} + i.GetLogin() + i = nil + i.GetLogin() +} + +func TestInstallAppRequest_GetClientID(tt *testing.T) { + tt.Parallel() + i := &InstallAppRequest{} + i.GetClientID() + i = nil + i.GetClientID() +} + +func TestInstallAppRequest_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &InstallAppRequest{Repositories: zeroValue} + i.GetRepositories() + i = &InstallAppRequest{} + i.GetRepositories() + i = nil + i.GetRepositories() +} + +func TestInstallAppRequest_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + i := &InstallAppRequest{} + i.GetRepositorySelection() + i = nil + i.GetRepositorySelection() +} + +func TestInstallation_GetAccessTokensURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{AccessTokensURL: &zeroValue} + i.GetAccessTokensURL() + i = &Installation{} + i.GetAccessTokensURL() + i = nil + i.GetAccessTokensURL() +} + +func TestInstallation_GetAccount(tt *testing.T) { + tt.Parallel() + i := &Installation{} + i.GetAccount() + i = nil + i.GetAccount() +} + +func TestInstallation_GetAppID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &Installation{AppID: &zeroValue} + i.GetAppID() + i = &Installation{} + i.GetAppID() + i = nil + i.GetAppID() +} + +func TestInstallation_GetAppSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{AppSlug: &zeroValue} + i.GetAppSlug() + i = &Installation{} + i.GetAppSlug() + i = nil + i.GetAppSlug() +} + +func TestInstallation_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{ClientID: &zeroValue} + i.GetClientID() + i = &Installation{} + i.GetClientID() + i = nil + i.GetClientID() +} + +func TestInstallation_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Installation{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &Installation{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestInstallation_GetEvents(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &Installation{Events: zeroValue} + i.GetEvents() + i = &Installation{} + i.GetEvents() + i = nil + i.GetEvents() +} + +func TestInstallation_GetHasMultipleSingleFiles(tt *testing.T) { + tt.Parallel() + var zeroValue bool + i := &Installation{HasMultipleSingleFiles: &zeroValue} + i.GetHasMultipleSingleFiles() + i = &Installation{} + i.GetHasMultipleSingleFiles() + i = nil + i.GetHasMultipleSingleFiles() +} + +func TestInstallation_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{HTMLURL: &zeroValue} + i.GetHTMLURL() + i = &Installation{} + i.GetHTMLURL() + i = nil + i.GetHTMLURL() +} + +func TestInstallation_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &Installation{ID: &zeroValue} + i.GetID() + i = &Installation{} + i.GetID() + i = nil + i.GetID() +} + +func TestInstallation_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{NodeID: &zeroValue} + i.GetNodeID() + i = &Installation{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestInstallation_GetPermissions(tt *testing.T) { + tt.Parallel() + i := &Installation{} + i.GetPermissions() + i = nil + i.GetPermissions() +} + +func TestInstallation_GetRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{RepositoriesURL: &zeroValue} + i.GetRepositoriesURL() + i = &Installation{} + i.GetRepositoriesURL() + i = nil + i.GetRepositoriesURL() +} + +func TestInstallation_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{RepositorySelection: &zeroValue} + i.GetRepositorySelection() + i = &Installation{} + i.GetRepositorySelection() + i = nil + i.GetRepositorySelection() +} + +func TestInstallation_GetSingleFileName(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{SingleFileName: &zeroValue} + i.GetSingleFileName() + i = &Installation{} + i.GetSingleFileName() + i = nil + i.GetSingleFileName() +} + +func TestInstallation_GetSingleFilePaths(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &Installation{SingleFilePaths: zeroValue} + i.GetSingleFilePaths() + i = &Installation{} + i.GetSingleFilePaths() + i = nil + i.GetSingleFilePaths() +} + +func TestInstallation_GetSuspendedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Installation{SuspendedAt: &zeroValue} + i.GetSuspendedAt() + i = &Installation{} + i.GetSuspendedAt() + i = nil + i.GetSuspendedAt() +} + +func TestInstallation_GetSuspendedBy(tt *testing.T) { + tt.Parallel() + i := &Installation{} + i.GetSuspendedBy() + i = nil + i.GetSuspendedBy() +} + +func TestInstallation_GetTargetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &Installation{TargetID: &zeroValue} + i.GetTargetID() + i = &Installation{} + i.GetTargetID() + i = nil + i.GetTargetID() +} + +func TestInstallation_GetTargetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Installation{TargetType: &zeroValue} + i.GetTargetType() + i = &Installation{} + i.GetTargetType() + i = nil + i.GetTargetType() +} + +func TestInstallation_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Installation{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &Installation{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestInstallationChanges_GetLogin(tt *testing.T) { + tt.Parallel() + i := &InstallationChanges{} + i.GetLogin() + i = nil + i.GetLogin() +} + +func TestInstallationChanges_GetSlug(tt *testing.T) { + tt.Parallel() + i := &InstallationChanges{} + i.GetSlug() + i = nil + i.GetSlug() +} + +func TestInstallationEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationEvent{Action: &zeroValue} + i.GetAction() + i = &InstallationEvent{} + i.GetAction() + i = nil + i.GetAction() +} + +func TestInstallationEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + i := &InstallationEvent{} + i.GetInstallation() + i = nil + i.GetInstallation() +} + +func TestInstallationEvent_GetOrg(tt *testing.T) { + tt.Parallel() + i := &InstallationEvent{} + i.GetOrg() + i = nil + i.GetOrg() +} + +func TestInstallationEvent_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + i := &InstallationEvent{Repositories: zeroValue} + i.GetRepositories() + i = &InstallationEvent{} + i.GetRepositories() + i = nil + i.GetRepositories() +} + +func TestInstallationEvent_GetRequester(tt *testing.T) { + tt.Parallel() + i := &InstallationEvent{} + i.GetRequester() + i = nil + i.GetRequester() +} + +func TestInstallationEvent_GetSender(tt *testing.T) { + tt.Parallel() + i := &InstallationEvent{} + i.GetSender() + i = nil + i.GetSender() +} + +func TestInstallationLoginChange_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationLoginChange{From: &zeroValue} + i.GetFrom() + i = &InstallationLoginChange{} + i.GetFrom() + i = nil + i.GetFrom() +} + +func TestInstallationPermissions_GetActions(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Actions: &zeroValue} + i.GetActions() + i = &InstallationPermissions{} + i.GetActions() + i = nil + i.GetActions() +} + +func TestInstallationPermissions_GetActionsVariables(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{ActionsVariables: &zeroValue} + i.GetActionsVariables() + i = &InstallationPermissions{} + i.GetActionsVariables() + i = nil + i.GetActionsVariables() +} + +func TestInstallationPermissions_GetAdministration(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Administration: &zeroValue} + i.GetAdministration() + i = &InstallationPermissions{} + i.GetAdministration() + i = nil + i.GetAdministration() +} + +func TestInstallationPermissions_GetAttestations(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Attestations: &zeroValue} + i.GetAttestations() + i = &InstallationPermissions{} + i.GetAttestations() + i = nil + i.GetAttestations() +} + +func TestInstallationPermissions_GetBlocking(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Blocking: &zeroValue} + i.GetBlocking() + i = &InstallationPermissions{} + i.GetBlocking() + i = nil + i.GetBlocking() +} + +func TestInstallationPermissions_GetChecks(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Checks: &zeroValue} + i.GetChecks() + i = &InstallationPermissions{} + i.GetChecks() + i = nil + i.GetChecks() +} + +func TestInstallationPermissions_GetCodespaces(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Codespaces: &zeroValue} + i.GetCodespaces() + i = &InstallationPermissions{} + i.GetCodespaces() + i = nil + i.GetCodespaces() +} + +func TestInstallationPermissions_GetCodespacesLifecycleAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{CodespacesLifecycleAdmin: &zeroValue} + i.GetCodespacesLifecycleAdmin() + i = &InstallationPermissions{} + i.GetCodespacesLifecycleAdmin() + i = nil + i.GetCodespacesLifecycleAdmin() +} + +func TestInstallationPermissions_GetCodespacesMetadata(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{CodespacesMetadata: &zeroValue} + i.GetCodespacesMetadata() + i = &InstallationPermissions{} + i.GetCodespacesMetadata() + i = nil + i.GetCodespacesMetadata() +} + +func TestInstallationPermissions_GetCodespacesSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{CodespacesSecrets: &zeroValue} + i.GetCodespacesSecrets() + i = &InstallationPermissions{} + i.GetCodespacesSecrets() + i = nil + i.GetCodespacesSecrets() +} + +func TestInstallationPermissions_GetCodespacesUserSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{CodespacesUserSecrets: &zeroValue} + i.GetCodespacesUserSecrets() + i = &InstallationPermissions{} + i.GetCodespacesUserSecrets() + i = nil + i.GetCodespacesUserSecrets() +} + +func TestInstallationPermissions_GetContentReferences(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{ContentReferences: &zeroValue} + i.GetContentReferences() + i = &InstallationPermissions{} + i.GetContentReferences() + i = nil + i.GetContentReferences() +} + +func TestInstallationPermissions_GetContents(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Contents: &zeroValue} + i.GetContents() + i = &InstallationPermissions{} + i.GetContents() + i = nil + i.GetContents() +} + +func TestInstallationPermissions_GetCopilotMessages(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{CopilotMessages: &zeroValue} + i.GetCopilotMessages() + i = &InstallationPermissions{} + i.GetCopilotMessages() + i = nil + i.GetCopilotMessages() +} + +func TestInstallationPermissions_GetDependabotSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{DependabotSecrets: &zeroValue} + i.GetDependabotSecrets() + i = &InstallationPermissions{} + i.GetDependabotSecrets() + i = nil + i.GetDependabotSecrets() +} + +func TestInstallationPermissions_GetDeployments(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Deployments: &zeroValue} + i.GetDeployments() + i = &InstallationPermissions{} + i.GetDeployments() + i = nil + i.GetDeployments() +} + +func TestInstallationPermissions_GetDiscussions(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Discussions: &zeroValue} + i.GetDiscussions() + i = &InstallationPermissions{} + i.GetDiscussions() + i = nil + i.GetDiscussions() +} + +func TestInstallationPermissions_GetEmails(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Emails: &zeroValue} + i.GetEmails() + i = &InstallationPermissions{} + i.GetEmails() + i = nil + i.GetEmails() +} + +func TestInstallationPermissions_GetEnterpriseAIControls(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseAIControls: &zeroValue} + i.GetEnterpriseAIControls() + i = &InstallationPermissions{} + i.GetEnterpriseAIControls() + i = nil + i.GetEnterpriseAIControls() +} + +func TestInstallationPermissions_GetEnterpriseCopilotMetrics(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseCopilotMetrics: &zeroValue} + i.GetEnterpriseCopilotMetrics() + i = &InstallationPermissions{} + i.GetEnterpriseCopilotMetrics() + i = nil + i.GetEnterpriseCopilotMetrics() +} + +func TestInstallationPermissions_GetEnterpriseCredentials(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseCredentials: &zeroValue} + i.GetEnterpriseCredentials() + i = &InstallationPermissions{} + i.GetEnterpriseCredentials() + i = nil + i.GetEnterpriseCredentials() +} + +func TestInstallationPermissions_GetEnterpriseCustomEnterpriseRoles(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseCustomEnterpriseRoles: &zeroValue} + i.GetEnterpriseCustomEnterpriseRoles() + i = &InstallationPermissions{} + i.GetEnterpriseCustomEnterpriseRoles() + i = nil + i.GetEnterpriseCustomEnterpriseRoles() +} + +func TestInstallationPermissions_GetEnterpriseCustomOrgRoles(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseCustomOrgRoles: &zeroValue} + i.GetEnterpriseCustomOrgRoles() + i = &InstallationPermissions{} + i.GetEnterpriseCustomOrgRoles() + i = nil + i.GetEnterpriseCustomOrgRoles() +} + +func TestInstallationPermissions_GetEnterpriseCustomProperties(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseCustomProperties: &zeroValue} + i.GetEnterpriseCustomProperties() + i = &InstallationPermissions{} + i.GetEnterpriseCustomProperties() + i = nil + i.GetEnterpriseCustomProperties() +} + +func TestInstallationPermissions_GetEnterpriseCustomPropertiesForOrgs(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseCustomPropertiesForOrgs: &zeroValue} + i.GetEnterpriseCustomPropertiesForOrgs() + i = &InstallationPermissions{} + i.GetEnterpriseCustomPropertiesForOrgs() + i = nil + i.GetEnterpriseCustomPropertiesForOrgs() +} + +func TestInstallationPermissions_GetEnterpriseOrganizationInstallations(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseOrganizationInstallations: &zeroValue} + i.GetEnterpriseOrganizationInstallations() + i = &InstallationPermissions{} + i.GetEnterpriseOrganizationInstallations() + i = nil + i.GetEnterpriseOrganizationInstallations() +} + +func TestInstallationPermissions_GetEnterpriseOrganizations(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseOrganizations: &zeroValue} + i.GetEnterpriseOrganizations() + i = &InstallationPermissions{} + i.GetEnterpriseOrganizations() + i = nil + i.GetEnterpriseOrganizations() +} + +func TestInstallationPermissions_GetEnterpriseOrgInstallationRepos(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseOrgInstallationRepos: &zeroValue} + i.GetEnterpriseOrgInstallationRepos() + i = &InstallationPermissions{} + i.GetEnterpriseOrgInstallationRepos() + i = nil + i.GetEnterpriseOrgInstallationRepos() +} + +func TestInstallationPermissions_GetEnterprisePeople(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterprisePeople: &zeroValue} + i.GetEnterprisePeople() + i = &InstallationPermissions{} + i.GetEnterprisePeople() + i = nil + i.GetEnterprisePeople() +} + +func TestInstallationPermissions_GetEnterpriseSSO(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseSSO: &zeroValue} + i.GetEnterpriseSSO() + i = &InstallationPermissions{} + i.GetEnterpriseSSO() + i = nil + i.GetEnterpriseSSO() +} + +func TestInstallationPermissions_GetEnterpriseTeams(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{EnterpriseTeams: &zeroValue} + i.GetEnterpriseTeams() + i = &InstallationPermissions{} + i.GetEnterpriseTeams() + i = nil + i.GetEnterpriseTeams() +} + +func TestInstallationPermissions_GetEnvironments(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Environments: &zeroValue} + i.GetEnvironments() + i = &InstallationPermissions{} + i.GetEnvironments() + i = nil + i.GetEnvironments() +} + +func TestInstallationPermissions_GetFollowers(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Followers: &zeroValue} + i.GetFollowers() + i = &InstallationPermissions{} + i.GetFollowers() + i = nil + i.GetFollowers() +} + +func TestInstallationPermissions_GetGists(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Gists: &zeroValue} + i.GetGists() + i = &InstallationPermissions{} + i.GetGists() + i = nil + i.GetGists() +} + +func TestInstallationPermissions_GetGitSigningSSHPublicKeys(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{GitSigningSSHPublicKeys: &zeroValue} + i.GetGitSigningSSHPublicKeys() + i = &InstallationPermissions{} + i.GetGitSigningSSHPublicKeys() + i = nil + i.GetGitSigningSSHPublicKeys() +} + +func TestInstallationPermissions_GetGPGKeys(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{GPGKeys: &zeroValue} + i.GetGPGKeys() + i = &InstallationPermissions{} + i.GetGPGKeys() + i = nil + i.GetGPGKeys() +} + +func TestInstallationPermissions_GetInteractionLimits(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{InteractionLimits: &zeroValue} + i.GetInteractionLimits() + i = &InstallationPermissions{} + i.GetInteractionLimits() + i = nil + i.GetInteractionLimits() +} + +func TestInstallationPermissions_GetIssues(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Issues: &zeroValue} + i.GetIssues() + i = &InstallationPermissions{} + i.GetIssues() + i = nil + i.GetIssues() +} + +func TestInstallationPermissions_GetKeys(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Keys: &zeroValue} + i.GetKeys() + i = &InstallationPermissions{} + i.GetKeys() + i = nil + i.GetKeys() +} + +func TestInstallationPermissions_GetMembers(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Members: &zeroValue} + i.GetMembers() + i = &InstallationPermissions{} + i.GetMembers() + i = nil + i.GetMembers() +} + +func TestInstallationPermissions_GetMergeQueues(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{MergeQueues: &zeroValue} + i.GetMergeQueues() + i = &InstallationPermissions{} + i.GetMergeQueues() + i = nil + i.GetMergeQueues() +} + +func TestInstallationPermissions_GetMetadata(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Metadata: &zeroValue} + i.GetMetadata() + i = &InstallationPermissions{} + i.GetMetadata() + i = nil + i.GetMetadata() +} + +func TestInstallationPermissions_GetOrganizationActionsVariables(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationActionsVariables: &zeroValue} + i.GetOrganizationActionsVariables() + i = &InstallationPermissions{} + i.GetOrganizationActionsVariables() + i = nil + i.GetOrganizationActionsVariables() +} + +func TestInstallationPermissions_GetOrganizationAdministration(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationAdministration: &zeroValue} + i.GetOrganizationAdministration() + i = &InstallationPermissions{} + i.GetOrganizationAdministration() + i = nil + i.GetOrganizationAdministration() +} + +func TestInstallationPermissions_GetOrganizationAnnouncementBanners(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationAnnouncementBanners: &zeroValue} + i.GetOrganizationAnnouncementBanners() + i = &InstallationPermissions{} + i.GetOrganizationAnnouncementBanners() + i = nil + i.GetOrganizationAnnouncementBanners() +} + +func TestInstallationPermissions_GetOrganizationAPIInsights(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationAPIInsights: &zeroValue} + i.GetOrganizationAPIInsights() + i = &InstallationPermissions{} + i.GetOrganizationAPIInsights() + i = nil + i.GetOrganizationAPIInsights() +} + +func TestInstallationPermissions_GetOrganizationCodespaces(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCodespaces: &zeroValue} + i.GetOrganizationCodespaces() + i = &InstallationPermissions{} + i.GetOrganizationCodespaces() + i = nil + i.GetOrganizationCodespaces() +} + +func TestInstallationPermissions_GetOrganizationCodespacesSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCodespacesSecrets: &zeroValue} + i.GetOrganizationCodespacesSecrets() + i = &InstallationPermissions{} + i.GetOrganizationCodespacesSecrets() + i = nil + i.GetOrganizationCodespacesSecrets() +} + +func TestInstallationPermissions_GetOrganizationCodespacesSettings(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCodespacesSettings: &zeroValue} + i.GetOrganizationCodespacesSettings() + i = &InstallationPermissions{} + i.GetOrganizationCodespacesSettings() + i = nil + i.GetOrganizationCodespacesSettings() +} + +func TestInstallationPermissions_GetOrganizationCopilotMetrics(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCopilotMetrics: &zeroValue} + i.GetOrganizationCopilotMetrics() + i = &InstallationPermissions{} + i.GetOrganizationCopilotMetrics() + i = nil + i.GetOrganizationCopilotMetrics() +} + +func TestInstallationPermissions_GetOrganizationCopilotSeatManagement(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCopilotSeatManagement: &zeroValue} + i.GetOrganizationCopilotSeatManagement() + i = &InstallationPermissions{} + i.GetOrganizationCopilotSeatManagement() + i = nil + i.GetOrganizationCopilotSeatManagement() +} + +func TestInstallationPermissions_GetOrganizationCustomOrgRoles(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCustomOrgRoles: &zeroValue} + i.GetOrganizationCustomOrgRoles() + i = &InstallationPermissions{} + i.GetOrganizationCustomOrgRoles() + i = nil + i.GetOrganizationCustomOrgRoles() +} + +func TestInstallationPermissions_GetOrganizationCustomProperties(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCustomProperties: &zeroValue} + i.GetOrganizationCustomProperties() + i = &InstallationPermissions{} + i.GetOrganizationCustomProperties() + i = nil + i.GetOrganizationCustomProperties() +} + +func TestInstallationPermissions_GetOrganizationCustomRoles(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationCustomRoles: &zeroValue} + i.GetOrganizationCustomRoles() + i = &InstallationPermissions{} + i.GetOrganizationCustomRoles() + i = nil + i.GetOrganizationCustomRoles() +} + +func TestInstallationPermissions_GetOrganizationDependabotSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationDependabotSecrets: &zeroValue} + i.GetOrganizationDependabotSecrets() + i = &InstallationPermissions{} + i.GetOrganizationDependabotSecrets() + i = nil + i.GetOrganizationDependabotSecrets() +} + +func TestInstallationPermissions_GetOrganizationEvents(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationEvents: &zeroValue} + i.GetOrganizationEvents() + i = &InstallationPermissions{} + i.GetOrganizationEvents() + i = nil + i.GetOrganizationEvents() +} + +func TestInstallationPermissions_GetOrganizationHooks(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationHooks: &zeroValue} + i.GetOrganizationHooks() + i = &InstallationPermissions{} + i.GetOrganizationHooks() + i = nil + i.GetOrganizationHooks() +} + +func TestInstallationPermissions_GetOrganizationKnowledgeBases(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationKnowledgeBases: &zeroValue} + i.GetOrganizationKnowledgeBases() + i = &InstallationPermissions{} + i.GetOrganizationKnowledgeBases() + i = nil + i.GetOrganizationKnowledgeBases() +} + +func TestInstallationPermissions_GetOrganizationPackages(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationPackages: &zeroValue} + i.GetOrganizationPackages() + i = &InstallationPermissions{} + i.GetOrganizationPackages() + i = nil + i.GetOrganizationPackages() +} + +func TestInstallationPermissions_GetOrganizationPersonalAccessTokenRequests(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationPersonalAccessTokenRequests: &zeroValue} + i.GetOrganizationPersonalAccessTokenRequests() + i = &InstallationPermissions{} + i.GetOrganizationPersonalAccessTokenRequests() + i = nil + i.GetOrganizationPersonalAccessTokenRequests() +} + +func TestInstallationPermissions_GetOrganizationPersonalAccessTokens(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationPersonalAccessTokens: &zeroValue} + i.GetOrganizationPersonalAccessTokens() + i = &InstallationPermissions{} + i.GetOrganizationPersonalAccessTokens() + i = nil + i.GetOrganizationPersonalAccessTokens() +} + +func TestInstallationPermissions_GetOrganizationPlan(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationPlan: &zeroValue} + i.GetOrganizationPlan() + i = &InstallationPermissions{} + i.GetOrganizationPlan() + i = nil + i.GetOrganizationPlan() +} + +func TestInstallationPermissions_GetOrganizationPreReceiveHooks(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationPreReceiveHooks: &zeroValue} + i.GetOrganizationPreReceiveHooks() + i = &InstallationPermissions{} + i.GetOrganizationPreReceiveHooks() + i = nil + i.GetOrganizationPreReceiveHooks() +} + +func TestInstallationPermissions_GetOrganizationProjects(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationProjects: &zeroValue} + i.GetOrganizationProjects() + i = &InstallationPermissions{} + i.GetOrganizationProjects() + i = nil + i.GetOrganizationProjects() +} + +func TestInstallationPermissions_GetOrganizationSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationSecrets: &zeroValue} + i.GetOrganizationSecrets() + i = &InstallationPermissions{} + i.GetOrganizationSecrets() + i = nil + i.GetOrganizationSecrets() +} + +func TestInstallationPermissions_GetOrganizationSelfHostedRunners(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationSelfHostedRunners: &zeroValue} + i.GetOrganizationSelfHostedRunners() + i = &InstallationPermissions{} + i.GetOrganizationSelfHostedRunners() + i = nil + i.GetOrganizationSelfHostedRunners() +} + +func TestInstallationPermissions_GetOrganizationUserBlocking(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{OrganizationUserBlocking: &zeroValue} + i.GetOrganizationUserBlocking() + i = &InstallationPermissions{} + i.GetOrganizationUserBlocking() + i = nil + i.GetOrganizationUserBlocking() +} + +func TestInstallationPermissions_GetPackages(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Packages: &zeroValue} + i.GetPackages() + i = &InstallationPermissions{} + i.GetPackages() + i = nil + i.GetPackages() +} + +func TestInstallationPermissions_GetPages(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Pages: &zeroValue} + i.GetPages() + i = &InstallationPermissions{} + i.GetPages() + i = nil + i.GetPages() +} + +func TestInstallationPermissions_GetPlan(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Plan: &zeroValue} + i.GetPlan() + i = &InstallationPermissions{} + i.GetPlan() + i = nil + i.GetPlan() +} + +func TestInstallationPermissions_GetProfile(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Profile: &zeroValue} + i.GetProfile() + i = &InstallationPermissions{} + i.GetProfile() + i = nil + i.GetProfile() +} + +func TestInstallationPermissions_GetPullRequests(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{PullRequests: &zeroValue} + i.GetPullRequests() + i = &InstallationPermissions{} + i.GetPullRequests() + i = nil + i.GetPullRequests() +} + +func TestInstallationPermissions_GetRepositoryAdvisories(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{RepositoryAdvisories: &zeroValue} + i.GetRepositoryAdvisories() + i = &InstallationPermissions{} + i.GetRepositoryAdvisories() + i = nil + i.GetRepositoryAdvisories() +} + +func TestInstallationPermissions_GetRepositoryCustomProperties(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{RepositoryCustomProperties: &zeroValue} + i.GetRepositoryCustomProperties() + i = &InstallationPermissions{} + i.GetRepositoryCustomProperties() + i = nil + i.GetRepositoryCustomProperties() +} + +func TestInstallationPermissions_GetRepositoryHooks(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{RepositoryHooks: &zeroValue} + i.GetRepositoryHooks() + i = &InstallationPermissions{} + i.GetRepositoryHooks() + i = nil + i.GetRepositoryHooks() +} + +func TestInstallationPermissions_GetRepositoryPreReceiveHooks(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{RepositoryPreReceiveHooks: &zeroValue} + i.GetRepositoryPreReceiveHooks() + i = &InstallationPermissions{} + i.GetRepositoryPreReceiveHooks() + i = nil + i.GetRepositoryPreReceiveHooks() +} + +func TestInstallationPermissions_GetRepositoryProjects(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{RepositoryProjects: &zeroValue} + i.GetRepositoryProjects() + i = &InstallationPermissions{} + i.GetRepositoryProjects() + i = nil + i.GetRepositoryProjects() +} + +func TestInstallationPermissions_GetSecrets(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Secrets: &zeroValue} + i.GetSecrets() + i = &InstallationPermissions{} + i.GetSecrets() + i = nil + i.GetSecrets() +} + +func TestInstallationPermissions_GetSecretScanningAlerts(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{SecretScanningAlerts: &zeroValue} + i.GetSecretScanningAlerts() + i = &InstallationPermissions{} + i.GetSecretScanningAlerts() + i = nil + i.GetSecretScanningAlerts() +} + +func TestInstallationPermissions_GetSecurityEvents(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{SecurityEvents: &zeroValue} + i.GetSecurityEvents() + i = &InstallationPermissions{} + i.GetSecurityEvents() + i = nil + i.GetSecurityEvents() +} + +func TestInstallationPermissions_GetSingleFile(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{SingleFile: &zeroValue} + i.GetSingleFile() + i = &InstallationPermissions{} + i.GetSingleFile() + i = nil + i.GetSingleFile() +} + +func TestInstallationPermissions_GetStarring(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Starring: &zeroValue} + i.GetStarring() + i = &InstallationPermissions{} + i.GetStarring() + i = nil + i.GetStarring() +} + +func TestInstallationPermissions_GetStatuses(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Statuses: &zeroValue} + i.GetStatuses() + i = &InstallationPermissions{} + i.GetStatuses() + i = nil + i.GetStatuses() +} + +func TestInstallationPermissions_GetTeamDiscussions(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{TeamDiscussions: &zeroValue} + i.GetTeamDiscussions() + i = &InstallationPermissions{} + i.GetTeamDiscussions() + i = nil + i.GetTeamDiscussions() +} + +func TestInstallationPermissions_GetUserEvents(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{UserEvents: &zeroValue} + i.GetUserEvents() + i = &InstallationPermissions{} + i.GetUserEvents() + i = nil + i.GetUserEvents() +} + +func TestInstallationPermissions_GetVulnerabilityAlerts(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{VulnerabilityAlerts: &zeroValue} + i.GetVulnerabilityAlerts() + i = &InstallationPermissions{} + i.GetVulnerabilityAlerts() + i = nil + i.GetVulnerabilityAlerts() +} + +func TestInstallationPermissions_GetWatching(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Watching: &zeroValue} + i.GetWatching() + i = &InstallationPermissions{} + i.GetWatching() + i = nil + i.GetWatching() +} + +func TestInstallationPermissions_GetWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationPermissions{Workflows: &zeroValue} + i.GetWorkflows() + i = &InstallationPermissions{} + i.GetWorkflows() + i = nil + i.GetWorkflows() +} + +func TestInstallationRepositoriesEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationRepositoriesEvent{Action: &zeroValue} + i.GetAction() + i = &InstallationRepositoriesEvent{} + i.GetAction() + i = nil + i.GetAction() +} + +func TestInstallationRepositoriesEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + i := &InstallationRepositoriesEvent{} + i.GetInstallation() + i = nil + i.GetInstallation() +} + +func TestInstallationRepositoriesEvent_GetOrg(tt *testing.T) { + tt.Parallel() + i := &InstallationRepositoriesEvent{} + i.GetOrg() + i = nil + i.GetOrg() +} + +func TestInstallationRepositoriesEvent_GetRepositoriesAdded(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + i := &InstallationRepositoriesEvent{RepositoriesAdded: zeroValue} + i.GetRepositoriesAdded() + i = &InstallationRepositoriesEvent{} + i.GetRepositoriesAdded() + i = nil + i.GetRepositoriesAdded() +} + +func TestInstallationRepositoriesEvent_GetRepositoriesRemoved(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + i := &InstallationRepositoriesEvent{RepositoriesRemoved: zeroValue} + i.GetRepositoriesRemoved() + i = &InstallationRepositoriesEvent{} + i.GetRepositoriesRemoved() + i = nil + i.GetRepositoriesRemoved() +} + +func TestInstallationRepositoriesEvent_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationRepositoriesEvent{RepositorySelection: &zeroValue} + i.GetRepositorySelection() + i = &InstallationRepositoriesEvent{} + i.GetRepositorySelection() + i = nil + i.GetRepositorySelection() +} + +func TestInstallationRepositoriesEvent_GetSender(tt *testing.T) { + tt.Parallel() + i := &InstallationRepositoriesEvent{} + i.GetSender() + i = nil + i.GetSender() +} + +func TestInstallationRequest_GetAccount(tt *testing.T) { + tt.Parallel() + i := &InstallationRequest{} + i.GetAccount() + i = nil + i.GetAccount() +} + +func TestInstallationRequest_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &InstallationRequest{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &InstallationRequest{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestInstallationRequest_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &InstallationRequest{ID: &zeroValue} + i.GetID() + i = &InstallationRequest{} + i.GetID() + i = nil + i.GetID() +} + +func TestInstallationRequest_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationRequest{NodeID: &zeroValue} + i.GetNodeID() + i = &InstallationRequest{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestInstallationRequest_GetRequester(tt *testing.T) { + tt.Parallel() + i := &InstallationRequest{} + i.GetRequester() + i = nil + i.GetRequester() +} + +func TestInstallationSlugChange_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationSlugChange{From: &zeroValue} + i.GetFrom() + i = &InstallationSlugChange{} + i.GetFrom() + i = nil + i.GetFrom() +} + +func TestInstallationTargetEvent_GetAccount(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetAccount() + i = nil + i.GetAccount() +} + +func TestInstallationTargetEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationTargetEvent{Action: &zeroValue} + i.GetAction() + i = &InstallationTargetEvent{} + i.GetAction() + i = nil + i.GetAction() +} + +func TestInstallationTargetEvent_GetChanges(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetChanges() + i = nil + i.GetChanges() +} + +func TestInstallationTargetEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetEnterprise() + i = nil + i.GetEnterprise() +} + +func TestInstallationTargetEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetInstallation() + i = nil + i.GetInstallation() +} + +func TestInstallationTargetEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetOrganization() + i = nil + i.GetOrganization() +} + +func TestInstallationTargetEvent_GetRepository(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetRepository() + i = nil + i.GetRepository() +} + +func TestInstallationTargetEvent_GetSender(tt *testing.T) { + tt.Parallel() + i := &InstallationTargetEvent{} + i.GetSender() + i = nil + i.GetSender() +} + +func TestInstallationTargetEvent_GetTargetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationTargetEvent{TargetType: &zeroValue} + i.GetTargetType() + i = &InstallationTargetEvent{} + i.GetTargetType() + i = nil + i.GetTargetType() +} + +func TestInstallationToken_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &InstallationToken{ExpiresAt: &zeroValue} + i.GetExpiresAt() + i = &InstallationToken{} + i.GetExpiresAt() + i = nil + i.GetExpiresAt() +} + +func TestInstallationToken_GetPermissions(tt *testing.T) { + tt.Parallel() + i := &InstallationToken{} + i.GetPermissions() + i = nil + i.GetPermissions() +} + +func TestInstallationToken_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + i := &InstallationToken{Repositories: zeroValue} + i.GetRepositories() + i = &InstallationToken{} + i.GetRepositories() + i = nil + i.GetRepositories() +} + +func TestInstallationToken_GetToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InstallationToken{Token: &zeroValue} + i.GetToken() + i = &InstallationToken{} + i.GetToken() + i = nil + i.GetToken() +} + +func TestInstallationTokenListRepoOptions_GetPermissions(tt *testing.T) { + tt.Parallel() + i := &InstallationTokenListRepoOptions{} + i.GetPermissions() + i = nil + i.GetPermissions() +} + +func TestInstallationTokenListRepoOptions_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &InstallationTokenListRepoOptions{Repositories: zeroValue} + i.GetRepositories() + i = &InstallationTokenListRepoOptions{} + i.GetRepositories() + i = nil + i.GetRepositories() +} + +func TestInstallationTokenListRepoOptions_GetRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + i := &InstallationTokenListRepoOptions{RepositoryIDs: zeroValue} + i.GetRepositoryIDs() + i = &InstallationTokenListRepoOptions{} + i.GetRepositoryIDs() + i = nil + i.GetRepositoryIDs() +} + +func TestInstallationTokenOptions_GetPermissions(tt *testing.T) { + tt.Parallel() + i := &InstallationTokenOptions{} + i.GetPermissions() + i = nil + i.GetPermissions() +} + +func TestInstallationTokenOptions_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &InstallationTokenOptions{Repositories: zeroValue} + i.GetRepositories() + i = &InstallationTokenOptions{} + i.GetRepositories() + i = nil + i.GetRepositories() +} + +func TestInstallationTokenOptions_GetRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + i := &InstallationTokenOptions{RepositoryIDs: zeroValue} + i.GetRepositoryIDs() + i = &InstallationTokenOptions{} + i.GetRepositoryIDs() + i = nil + i.GetRepositoryIDs() +} + +func TestInteractionRestriction_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &InteractionRestriction{ExpiresAt: &zeroValue} + i.GetExpiresAt() + i = &InteractionRestriction{} + i.GetExpiresAt() + i = nil + i.GetExpiresAt() +} + +func TestInteractionRestriction_GetLimit(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InteractionRestriction{Limit: &zeroValue} + i.GetLimit() + i = &InteractionRestriction{} + i.GetLimit() + i = nil + i.GetLimit() +} + +func TestInteractionRestriction_GetOrigin(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &InteractionRestriction{Origin: &zeroValue} + i.GetOrigin() + i = &InteractionRestriction{} + i.GetOrigin() + i = nil + i.GetOrigin() +} + +func TestInvitation_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Invitation{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &Invitation{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestInvitation_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Invitation{Email: &zeroValue} + i.GetEmail() + i = &Invitation{} + i.GetEmail() + i = nil + i.GetEmail() +} + +func TestInvitation_GetFailedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Invitation{FailedAt: &zeroValue} + i.GetFailedAt() + i = &Invitation{} + i.GetFailedAt() + i = nil + i.GetFailedAt() +} + +func TestInvitation_GetFailedReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Invitation{FailedReason: &zeroValue} + i.GetFailedReason() + i = &Invitation{} + i.GetFailedReason() + i = nil + i.GetFailedReason() +} + +func TestInvitation_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &Invitation{ID: &zeroValue} + i.GetID() + i = &Invitation{} + i.GetID() + i = nil + i.GetID() +} + +func TestInvitation_GetInvitationTeamURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Invitation{InvitationTeamURL: &zeroValue} + i.GetInvitationTeamURL() + i = &Invitation{} + i.GetInvitationTeamURL() + i = nil + i.GetInvitationTeamURL() +} + +func TestInvitation_GetInviter(tt *testing.T) { + tt.Parallel() + i := &Invitation{} + i.GetInviter() + i = nil + i.GetInviter() +} + +func TestInvitation_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Invitation{Login: &zeroValue} + i.GetLogin() + i = &Invitation{} + i.GetLogin() + i = nil + i.GetLogin() +} + +func TestInvitation_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Invitation{NodeID: &zeroValue} + i.GetNodeID() + i = &Invitation{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestInvitation_GetRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Invitation{Role: &zeroValue} + i.GetRole() + i = &Invitation{} + i.GetRole() + i = nil + i.GetRole() +} + +func TestInvitation_GetTeamCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Invitation{TeamCount: &zeroValue} + i.GetTeamCount() + i = &Invitation{} + i.GetTeamCount() + i = nil + i.GetTeamCount() +} + +func TestIssue_GetActiveLockReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{ActiveLockReason: &zeroValue} + i.GetActiveLockReason() + i = &Issue{} + i.GetActiveLockReason() + i = nil + i.GetActiveLockReason() +} + +func TestIssue_GetAssignee(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetAssignee() + i = nil + i.GetAssignee() +} + +func TestIssue_GetAssignees(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + i := &Issue{Assignees: zeroValue} + i.GetAssignees() + i = &Issue{} + i.GetAssignees() + i = nil + i.GetAssignees() +} + +func TestIssue_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{AuthorAssociation: &zeroValue} + i.GetAuthorAssociation() + i = &Issue{} + i.GetAuthorAssociation() + i = nil + i.GetAuthorAssociation() +} + +func TestIssue_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{Body: &zeroValue} + i.GetBody() + i = &Issue{} + i.GetBody() + i = nil + i.GetBody() +} + +func TestIssue_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Issue{ClosedAt: &zeroValue} + i.GetClosedAt() + i = &Issue{} + i.GetClosedAt() + i = nil + i.GetClosedAt() +} + +func TestIssue_GetClosedBy(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetClosedBy() + i = nil + i.GetClosedBy() +} + +func TestIssue_GetComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Issue{Comments: &zeroValue} + i.GetComments() + i = &Issue{} + i.GetComments() + i = nil + i.GetComments() +} + +func TestIssue_GetCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{CommentsURL: &zeroValue} + i.GetCommentsURL() + i = &Issue{} + i.GetCommentsURL() + i = nil + i.GetCommentsURL() +} + +func TestIssue_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Issue{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &Issue{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssue_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + i := &Issue{Draft: &zeroValue} + i.GetDraft() + i = &Issue{} + i.GetDraft() + i = nil + i.GetDraft() +} + +func TestIssue_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{EventsURL: &zeroValue} + i.GetEventsURL() + i = &Issue{} + i.GetEventsURL() + i = nil + i.GetEventsURL() +} + +func TestIssue_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{HTMLURL: &zeroValue} + i.GetHTMLURL() + i = &Issue{} + i.GetHTMLURL() + i = nil + i.GetHTMLURL() +} + +func TestIssue_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &Issue{ID: &zeroValue} + i.GetID() + i = &Issue{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssue_GetIssueDependenciesSummary(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetIssueDependenciesSummary() + i = nil + i.GetIssueDependenciesSummary() +} + +func TestIssue_GetIssueFieldValues(tt *testing.T) { + tt.Parallel() + zeroValue := []*IssueFieldValue{} + i := &Issue{IssueFieldValues: zeroValue} + i.GetIssueFieldValues() + i = &Issue{} + i.GetIssueFieldValues() + i = nil + i.GetIssueFieldValues() +} + +func TestIssue_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []*Label{} + i := &Issue{Labels: zeroValue} + i.GetLabels() + i = &Issue{} + i.GetLabels() + i = nil + i.GetLabels() +} + +func TestIssue_GetLabelsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{LabelsURL: &zeroValue} + i.GetLabelsURL() + i = &Issue{} + i.GetLabelsURL() + i = nil + i.GetLabelsURL() +} + +func TestIssue_GetLocked(tt *testing.T) { + tt.Parallel() + var zeroValue bool + i := &Issue{Locked: &zeroValue} + i.GetLocked() + i = &Issue{} + i.GetLocked() + i = nil + i.GetLocked() +} + +func TestIssue_GetMilestone(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetMilestone() + i = nil + i.GetMilestone() +} + +func TestIssue_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{NodeID: &zeroValue} + i.GetNodeID() + i = &Issue{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestIssue_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &Issue{Number: &zeroValue} + i.GetNumber() + i = &Issue{} + i.GetNumber() + i = nil + i.GetNumber() +} + +func TestIssue_GetParentIssueURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{ParentIssueURL: &zeroValue} + i.GetParentIssueURL() + i = &Issue{} + i.GetParentIssueURL() + i = nil + i.GetParentIssueURL() +} + +func TestIssue_GetPerformedViaGithubApp(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetPerformedViaGithubApp() + i = nil + i.GetPerformedViaGithubApp() +} + +func TestIssue_GetPinnedComment(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetPinnedComment() + i = nil + i.GetPinnedComment() +} + +func TestIssue_GetPullRequestLinks(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetPullRequestLinks() + i = nil + i.GetPullRequestLinks() +} + +func TestIssue_GetReactions(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetReactions() + i = nil + i.GetReactions() +} + +func TestIssue_GetRepository(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetRepository() + i = nil + i.GetRepository() +} + +func TestIssue_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{RepositoryURL: &zeroValue} + i.GetRepositoryURL() + i = &Issue{} + i.GetRepositoryURL() + i = nil + i.GetRepositoryURL() +} + +func TestIssue_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{State: &zeroValue} + i.GetState() + i = &Issue{} + i.GetState() + i = nil + i.GetState() +} + +func TestIssue_GetStateReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{StateReason: &zeroValue} + i.GetStateReason() + i = &Issue{} + i.GetStateReason() + i = nil + i.GetStateReason() +} + +func TestIssue_GetSubIssuesSummary(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetSubIssuesSummary() + i = nil + i.GetSubIssuesSummary() +} + +func TestIssue_GetTextMatches(tt *testing.T) { + tt.Parallel() + zeroValue := []*TextMatch{} + i := &Issue{TextMatches: zeroValue} + i.GetTextMatches() + i = &Issue{} + i.GetTextMatches() + i = nil + i.GetTextMatches() +} + +func TestIssue_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{Title: &zeroValue} + i.GetTitle() + i = &Issue{} + i.GetTitle() + i = nil + i.GetTitle() +} + +func TestIssue_GetType(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetType() + i = nil + i.GetType() +} + +func TestIssue_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &Issue{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &Issue{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestIssue_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &Issue{URL: &zeroValue} + i.GetURL() + i = &Issue{} + i.GetURL() + i = nil + i.GetURL() +} + +func TestIssue_GetUser(tt *testing.T) { + tt.Parallel() + i := &Issue{} + i.GetUser() + i = nil + i.GetUser() +} + +func TestIssueComment_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueComment{AuthorAssociation: &zeroValue} + i.GetAuthorAssociation() + i = &IssueComment{} + i.GetAuthorAssociation() + i = nil + i.GetAuthorAssociation() +} + +func TestIssueComment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueComment{Body: &zeroValue} + i.GetBody() + i = &IssueComment{} + i.GetBody() + i = nil + i.GetBody() +} + +func TestIssueComment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueComment{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueComment{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueComment_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueComment{HTMLURL: &zeroValue} + i.GetHTMLURL() + i = &IssueComment{} + i.GetHTMLURL() + i = nil + i.GetHTMLURL() +} + +func TestIssueComment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &IssueComment{ID: &zeroValue} + i.GetID() + i = &IssueComment{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssueComment_GetIssueURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueComment{IssueURL: &zeroValue} + i.GetIssueURL() + i = &IssueComment{} + i.GetIssueURL() + i = nil + i.GetIssueURL() +} + +func TestIssueComment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueComment{NodeID: &zeroValue} + i.GetNodeID() + i = &IssueComment{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestIssueComment_GetReactions(tt *testing.T) { + tt.Parallel() + i := &IssueComment{} + i.GetReactions() + i = nil + i.GetReactions() +} + +func TestIssueComment_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueComment{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &IssueComment{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestIssueComment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueComment{URL: &zeroValue} + i.GetURL() + i = &IssueComment{} + i.GetURL() + i = nil + i.GetURL() +} + +func TestIssueComment_GetUser(tt *testing.T) { + tt.Parallel() + i := &IssueComment{} + i.GetUser() + i = nil + i.GetUser() +} + +func TestIssueCommentEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueCommentEvent{Action: &zeroValue} + i.GetAction() + i = &IssueCommentEvent{} + i.GetAction() + i = nil + i.GetAction() +} + +func TestIssueCommentEvent_GetChanges(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetChanges() + i = nil + i.GetChanges() +} + +func TestIssueCommentEvent_GetComment(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetComment() + i = nil + i.GetComment() +} + +func TestIssueCommentEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetInstallation() + i = nil + i.GetInstallation() +} + +func TestIssueCommentEvent_GetIssue(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetIssue() + i = nil + i.GetIssue() +} + +func TestIssueCommentEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetOrganization() + i = nil + i.GetOrganization() +} + +func TestIssueCommentEvent_GetRepo(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetRepo() + i = nil + i.GetRepo() +} + +func TestIssueCommentEvent_GetSender(tt *testing.T) { + tt.Parallel() + i := &IssueCommentEvent{} + i.GetSender() + i = nil + i.GetSender() +} + +func TestIssueDependenciesSummary_GetBlockedBy(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueDependenciesSummary{BlockedBy: &zeroValue} + i.GetBlockedBy() + i = &IssueDependenciesSummary{} + i.GetBlockedBy() + i = nil + i.GetBlockedBy() +} + +func TestIssueDependenciesSummary_GetBlocking(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueDependenciesSummary{Blocking: &zeroValue} + i.GetBlocking() + i = &IssueDependenciesSummary{} + i.GetBlocking() + i = nil + i.GetBlocking() +} + +func TestIssueDependenciesSummary_GetTotalBlockedBy(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueDependenciesSummary{TotalBlockedBy: &zeroValue} + i.GetTotalBlockedBy() + i = &IssueDependenciesSummary{} + i.GetTotalBlockedBy() + i = nil + i.GetTotalBlockedBy() +} + +func TestIssueDependenciesSummary_GetTotalBlocking(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueDependenciesSummary{TotalBlocking: &zeroValue} + i.GetTotalBlocking() + i = &IssueDependenciesSummary{} + i.GetTotalBlocking() + i = nil + i.GetTotalBlocking() +} + +func TestIssueDependencyRequest_GetIssueID(tt *testing.T) { + tt.Parallel() + i := &IssueDependencyRequest{} + i.GetIssueID() + i = nil + i.GetIssueID() +} + +func TestIssueEvent_GetAction(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetAction() + i = nil + i.GetAction() +} + +func TestIssueEvent_GetActor(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetActor() + i = nil + i.GetActor() +} + +func TestIssueEvent_GetAssignee(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetAssignee() + i = nil + i.GetAssignee() +} + +func TestIssueEvent_GetAssigner(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetAssigner() + i = nil + i.GetAssigner() +} + +func TestIssueEvent_GetCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueEvent{CommitID: &zeroValue} + i.GetCommitID() + i = &IssueEvent{} + i.GetCommitID() + i = nil + i.GetCommitID() +} + +func TestIssueEvent_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueEvent{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueEvent{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueEvent_GetDismissedReview(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetDismissedReview() + i = nil + i.GetDismissedReview() +} + +func TestIssueEvent_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueEvent{Event: &zeroValue} + i.GetEvent() + i = &IssueEvent{} + i.GetEvent() + i = nil + i.GetEvent() +} + +func TestIssueEvent_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &IssueEvent{ID: &zeroValue} + i.GetID() + i = &IssueEvent{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssueEvent_GetIssue(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetIssue() + i = nil + i.GetIssue() +} + +func TestIssueEvent_GetLabel(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetLabel() + i = nil + i.GetLabel() +} + +func TestIssueEvent_GetLockReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueEvent{LockReason: &zeroValue} + i.GetLockReason() + i = &IssueEvent{} + i.GetLockReason() + i = nil + i.GetLockReason() +} + +func TestIssueEvent_GetMilestone(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetMilestone() + i = nil + i.GetMilestone() +} + +func TestIssueEvent_GetPerformedViaGithubApp(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetPerformedViaGithubApp() + i = nil + i.GetPerformedViaGithubApp() +} + +func TestIssueEvent_GetRename(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetRename() + i = nil + i.GetRename() +} + +func TestIssueEvent_GetRepository(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetRepository() + i = nil + i.GetRepository() +} + +func TestIssueEvent_GetRequestedReviewer(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetRequestedReviewer() + i = nil + i.GetRequestedReviewer() +} + +func TestIssueEvent_GetRequestedTeam(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetRequestedTeam() + i = nil + i.GetRequestedTeam() +} + +func TestIssueEvent_GetReviewRequester(tt *testing.T) { + tt.Parallel() + i := &IssueEvent{} + i.GetReviewRequester() + i = nil + i.GetReviewRequester() +} + +func TestIssueEvent_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueEvent{URL: &zeroValue} + i.GetURL() + i = &IssueEvent{} + i.GetURL() + i = nil + i.GetURL() +} + +func TestIssueFieldValue_GetDataType(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValue{} + i.GetDataType() + i = nil + i.GetDataType() +} + +func TestIssueFieldValue_GetIssueFieldID(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValue{} + i.GetIssueFieldID() + i = nil + i.GetIssueFieldID() +} + +func TestIssueFieldValue_GetNodeID(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValue{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestIssueFieldValue_GetSingleSelectOption(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValue{} + i.GetSingleSelectOption() + i = nil + i.GetSingleSelectOption() +} + +func TestIssueFieldValue_GetValue(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValue{} + i.GetValue() + i = nil + i.GetValue() +} + +func TestIssueFieldValueSingleSelectOption_GetColor(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValueSingleSelectOption{} + i.GetColor() + i = nil + i.GetColor() +} + +func TestIssueFieldValueSingleSelectOption_GetID(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValueSingleSelectOption{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssueFieldValueSingleSelectOption_GetName(tt *testing.T) { + tt.Parallel() + i := &IssueFieldValueSingleSelectOption{} + i.GetName() + i = nil + i.GetName() +} + +func TestIssueImport_GetAssignee(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImport{Assignee: &zeroValue} + i.GetAssignee() + i = &IssueImport{} + i.GetAssignee() + i = nil + i.GetAssignee() +} + +func TestIssueImport_GetBody(tt *testing.T) { + tt.Parallel() + i := &IssueImport{} + i.GetBody() + i = nil + i.GetBody() +} + +func TestIssueImport_GetClosed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + i := &IssueImport{Closed: &zeroValue} + i.GetClosed() + i = &IssueImport{} + i.GetClosed() + i = nil + i.GetClosed() +} + +func TestIssueImport_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueImport{ClosedAt: &zeroValue} + i.GetClosedAt() + i = &IssueImport{} + i.GetClosedAt() + i = nil + i.GetClosedAt() +} + +func TestIssueImport_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueImport{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueImport{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueImport_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &IssueImport{Labels: zeroValue} + i.GetLabels() + i = &IssueImport{} + i.GetLabels() + i = nil + i.GetLabels() +} + +func TestIssueImport_GetMilestone(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueImport{Milestone: &zeroValue} + i.GetMilestone() + i = &IssueImport{} + i.GetMilestone() + i = nil + i.GetMilestone() +} + +func TestIssueImport_GetTitle(tt *testing.T) { + tt.Parallel() + i := &IssueImport{} + i.GetTitle() + i = nil + i.GetTitle() +} + +func TestIssueImport_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueImport{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &IssueImport{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestIssueImportError_GetCode(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportError{Code: &zeroValue} + i.GetCode() + i = &IssueImportError{} + i.GetCode() + i = nil + i.GetCode() +} + +func TestIssueImportError_GetField(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportError{Field: &zeroValue} + i.GetField() + i = &IssueImportError{} + i.GetField() + i = nil + i.GetField() +} + +func TestIssueImportError_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportError{Location: &zeroValue} + i.GetLocation() + i = &IssueImportError{} + i.GetLocation() + i = nil + i.GetLocation() +} + +func TestIssueImportError_GetResource(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportError{Resource: &zeroValue} + i.GetResource() + i = &IssueImportError{} + i.GetResource() + i = nil + i.GetResource() +} + +func TestIssueImportError_GetValue(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportError{Value: &zeroValue} + i.GetValue() + i = &IssueImportError{} + i.GetValue() + i = nil + i.GetValue() +} + +func TestIssueImportRequest_GetComments(tt *testing.T) { + tt.Parallel() + zeroValue := []*Comment{} + i := &IssueImportRequest{Comments: zeroValue} + i.GetComments() + i = &IssueImportRequest{} + i.GetComments() + i = nil + i.GetComments() +} + +func TestIssueImportRequest_GetIssueImport(tt *testing.T) { + tt.Parallel() + i := &IssueImportRequest{} + i.GetIssueImport() + i = nil + i.GetIssueImport() +} + +func TestIssueImportResponse_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueImportResponse{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueImportResponse{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueImportResponse_GetDocumentationURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportResponse{DocumentationURL: &zeroValue} + i.GetDocumentationURL() + i = &IssueImportResponse{} + i.GetDocumentationURL() + i = nil + i.GetDocumentationURL() +} + +func TestIssueImportResponse_GetErrors(tt *testing.T) { + tt.Parallel() + zeroValue := []*IssueImportError{} + i := &IssueImportResponse{Errors: zeroValue} + i.GetErrors() + i = &IssueImportResponse{} + i.GetErrors() + i = nil + i.GetErrors() +} + +func TestIssueImportResponse_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueImportResponse{ID: &zeroValue} + i.GetID() + i = &IssueImportResponse{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssueImportResponse_GetImportIssuesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportResponse{ImportIssuesURL: &zeroValue} + i.GetImportIssuesURL() + i = &IssueImportResponse{} + i.GetImportIssuesURL() + i = nil + i.GetImportIssuesURL() +} + +func TestIssueImportResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportResponse{Message: &zeroValue} + i.GetMessage() + i = &IssueImportResponse{} + i.GetMessage() + i = nil + i.GetMessage() +} + +func TestIssueImportResponse_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportResponse{RepositoryURL: &zeroValue} + i.GetRepositoryURL() + i = &IssueImportResponse{} + i.GetRepositoryURL() + i = nil + i.GetRepositoryURL() +} + +func TestIssueImportResponse_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportResponse{Status: &zeroValue} + i.GetStatus() + i = &IssueImportResponse{} + i.GetStatus() + i = nil + i.GetStatus() +} + +func TestIssueImportResponse_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueImportResponse{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &IssueImportResponse{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestIssueImportResponse_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueImportResponse{URL: &zeroValue} + i.GetURL() + i = &IssueImportResponse{} + i.GetURL() + i = nil + i.GetURL() +} + +func TestIssueListByOrgOptions_GetDirection(tt *testing.T) { + tt.Parallel() + i := &IssueListByOrgOptions{} + i.GetDirection() + i = nil + i.GetDirection() +} + +func TestIssueListByOrgOptions_GetFilter(tt *testing.T) { + tt.Parallel() + i := &IssueListByOrgOptions{} + i.GetFilter() + i = nil + i.GetFilter() +} + +func TestIssueListByOrgOptions_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &IssueListByOrgOptions{Labels: zeroValue} + i.GetLabels() + i = &IssueListByOrgOptions{} + i.GetLabels() + i = nil + i.GetLabels() +} + +func TestIssueListByOrgOptions_GetSince(tt *testing.T) { + tt.Parallel() + i := &IssueListByOrgOptions{} + i.GetSince() + i = nil + i.GetSince() +} + +func TestIssueListByOrgOptions_GetSort(tt *testing.T) { + tt.Parallel() + i := &IssueListByOrgOptions{} + i.GetSort() + i = nil + i.GetSort() +} + +func TestIssueListByOrgOptions_GetState(tt *testing.T) { + tt.Parallel() + i := &IssueListByOrgOptions{} + i.GetState() + i = nil + i.GetState() +} + +func TestIssueListByOrgOptions_GetType(tt *testing.T) { + tt.Parallel() + i := &IssueListByOrgOptions{} + i.GetType() + i = nil + i.GetType() +} + +func TestIssueListByRepoOptions_GetAssignee(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetAssignee() + i = nil + i.GetAssignee() +} + +func TestIssueListByRepoOptions_GetCreator(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetCreator() + i = nil + i.GetCreator() +} + +func TestIssueListByRepoOptions_GetDirection(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetDirection() + i = nil + i.GetDirection() +} + +func TestIssueListByRepoOptions_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + i := &IssueListByRepoOptions{Labels: zeroValue} + i.GetLabels() + i = &IssueListByRepoOptions{} + i.GetLabels() + i = nil + i.GetLabels() +} + +func TestIssueListByRepoOptions_GetMentioned(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetMentioned() + i = nil + i.GetMentioned() +} + +func TestIssueListByRepoOptions_GetMilestone(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetMilestone() + i = nil + i.GetMilestone() +} + +func TestIssueListByRepoOptions_GetSince(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetSince() + i = nil + i.GetSince() +} + +func TestIssueListByRepoOptions_GetSort(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetSort() + i = nil + i.GetSort() +} + +func TestIssueListByRepoOptions_GetState(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetState() + i = nil + i.GetState() +} + +func TestIssueListByRepoOptions_GetType(tt *testing.T) { + tt.Parallel() + i := &IssueListByRepoOptions{} + i.GetType() + i = nil + i.GetType() +} + +func TestIssueListCommentsOptions_GetDirection(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueListCommentsOptions{Direction: &zeroValue} + i.GetDirection() + i = &IssueListCommentsOptions{} + i.GetDirection() + i = nil + i.GetDirection() +} + +func TestIssueListCommentsOptions_GetSince(tt *testing.T) { + tt.Parallel() + var zeroValue time.Time + i := &IssueListCommentsOptions{Since: &zeroValue} + i.GetSince() + i = &IssueListCommentsOptions{} + i.GetSince() + i = nil + i.GetSince() +} + +func TestIssueListCommentsOptions_GetSort(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueListCommentsOptions{Sort: &zeroValue} + i.GetSort() + i = &IssueListCommentsOptions{} + i.GetSort() + i = nil + i.GetSort() +} + +func TestIssueRequestFieldValue_GetFieldID(tt *testing.T) { + tt.Parallel() + i := &IssueRequestFieldValue{} + i.GetFieldID() + i = nil + i.GetFieldID() +} + +func TestIssueRequestFieldValue_GetValue(tt *testing.T) { + tt.Parallel() + i := &IssueRequestFieldValue{} + i.GetValue() + i = nil + i.GetValue() +} + +func TestIssuesEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssuesEvent{Action: &zeroValue} + i.GetAction() + i = &IssuesEvent{} + i.GetAction() + i = nil + i.GetAction() +} + +func TestIssuesEvent_GetAssignee(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetAssignee() + i = nil + i.GetAssignee() +} + +func TestIssuesEvent_GetChanges(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetChanges() + i = nil + i.GetChanges() +} + +func TestIssuesEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetInstallation() + i = nil + i.GetInstallation() +} + +func TestIssuesEvent_GetIssue(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetIssue() + i = nil + i.GetIssue() +} + +func TestIssuesEvent_GetLabel(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetLabel() + i = nil + i.GetLabel() +} + +func TestIssuesEvent_GetMilestone(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetMilestone() + i = nil + i.GetMilestone() +} + +func TestIssuesEvent_GetOrg(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetOrg() + i = nil + i.GetOrg() +} + +func TestIssuesEvent_GetRepo(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetRepo() + i = nil + i.GetRepo() +} + +func TestIssuesEvent_GetSender(tt *testing.T) { + tt.Parallel() + i := &IssuesEvent{} + i.GetSender() + i = nil + i.GetSender() +} + +func TestIssuesSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + i := &IssuesSearchResult{IncompleteResults: &zeroValue} + i.GetIncompleteResults() + i = &IssuesSearchResult{} + i.GetIncompleteResults() + i = nil + i.GetIncompleteResults() +} + +func TestIssuesSearchResult_GetIssues(tt *testing.T) { + tt.Parallel() + zeroValue := []*Issue{} + i := &IssuesSearchResult{Issues: zeroValue} + i.GetIssues() + i = &IssuesSearchResult{} + i.GetIssues() + i = nil + i.GetIssues() +} + +func TestIssuesSearchResult_GetSearchType(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssuesSearchResult{SearchType: &zeroValue} + i.GetSearchType() + i = &IssuesSearchResult{} + i.GetSearchType() + i = nil + i.GetSearchType() +} + +func TestIssuesSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssuesSearchResult{Total: &zeroValue} + i.GetTotal() + i = &IssuesSearchResult{} + i.GetTotal() + i = nil + i.GetTotal() +} + +func TestIssueStats_GetClosedIssues(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueStats{ClosedIssues: &zeroValue} + i.GetClosedIssues() + i = &IssueStats{} + i.GetClosedIssues() + i = nil + i.GetClosedIssues() +} + +func TestIssueStats_GetOpenIssues(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueStats{OpenIssues: &zeroValue} + i.GetOpenIssues() + i = &IssueStats{} + i.GetOpenIssues() + i = nil + i.GetOpenIssues() +} + +func TestIssueStats_GetTotalIssues(tt *testing.T) { + tt.Parallel() + var zeroValue int + i := &IssueStats{TotalIssues: &zeroValue} + i.GetTotalIssues() + i = &IssueStats{} + i.GetTotalIssues() + i = nil + i.GetTotalIssues() +} + +func TestIssueType_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueType{Color: &zeroValue} + i.GetColor() + i = &IssueType{} + i.GetColor() + i = nil + i.GetColor() +} + +func TestIssueType_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueType{CreatedAt: &zeroValue} + i.GetCreatedAt() + i = &IssueType{} + i.GetCreatedAt() + i = nil + i.GetCreatedAt() +} + +func TestIssueType_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueType{Description: &zeroValue} + i.GetDescription() + i = &IssueType{} + i.GetDescription() + i = nil + i.GetDescription() +} + +func TestIssueType_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + i := &IssueType{ID: &zeroValue} + i.GetID() + i = &IssueType{} + i.GetID() + i = nil + i.GetID() +} + +func TestIssueType_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueType{Name: &zeroValue} + i.GetName() + i = &IssueType{} + i.GetName() + i = nil + i.GetName() +} + +func TestIssueType_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + i := &IssueType{NodeID: &zeroValue} + i.GetNodeID() + i = &IssueType{} + i.GetNodeID() + i = nil + i.GetNodeID() +} + +func TestIssueType_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + i := &IssueType{UpdatedAt: &zeroValue} + i.GetUpdatedAt() + i = &IssueType{} + i.GetUpdatedAt() + i = nil + i.GetUpdatedAt() +} + +func TestJITRunnerConfig_GetEncodedJITConfig(tt *testing.T) { + tt.Parallel() + var zeroValue string + j := &JITRunnerConfig{EncodedJITConfig: &zeroValue} + j.GetEncodedJITConfig() + j = &JITRunnerConfig{} + j.GetEncodedJITConfig() + j = nil + j.GetEncodedJITConfig() +} + +func TestJITRunnerConfig_GetRunner(tt *testing.T) { + tt.Parallel() + j := &JITRunnerConfig{} + j.GetRunner() + j = nil + j.GetRunner() +} + +func TestJobs_GetJobs(tt *testing.T) { + tt.Parallel() + zeroValue := []*WorkflowJob{} + j := &Jobs{Jobs: zeroValue} + j.GetJobs() + j = &Jobs{} + j.GetJobs() + j = nil + j.GetJobs() +} + +func TestJobs_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + j := &Jobs{TotalCount: &zeroValue} + j.GetTotalCount() + j = &Jobs{} + j.GetTotalCount() + j = nil + j.GetTotalCount() +} + +func TestKey_GetAddedBy(tt *testing.T) { + tt.Parallel() + var zeroValue string + k := &Key{AddedBy: &zeroValue} + k.GetAddedBy() + k = &Key{} + k.GetAddedBy() + k = nil + k.GetAddedBy() +} + +func TestKey_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + k := &Key{CreatedAt: &zeroValue} + k.GetCreatedAt() + k = &Key{} + k.GetCreatedAt() + k = nil + k.GetCreatedAt() +} + +func TestKey_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + k := &Key{ID: &zeroValue} + k.GetID() + k = &Key{} + k.GetID() + k = nil + k.GetID() +} + +func TestKey_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + k := &Key{Key: &zeroValue} + k.GetKey() + k = &Key{} + k.GetKey() + k = nil + k.GetKey() +} + +func TestKey_GetLastUsed(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + k := &Key{LastUsed: &zeroValue} + k.GetLastUsed() + k = &Key{} + k.GetLastUsed() + k = nil + k.GetLastUsed() +} + +func TestKey_GetReadOnly(tt *testing.T) { + tt.Parallel() + var zeroValue bool + k := &Key{ReadOnly: &zeroValue} + k.GetReadOnly() + k = &Key{} + k.GetReadOnly() + k = nil + k.GetReadOnly() +} + +func TestKey_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + k := &Key{Title: &zeroValue} + k.GetTitle() + k = &Key{} + k.GetTitle() + k = nil + k.GetTitle() +} + +func TestKey_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + k := &Key{URL: &zeroValue} + k.GetURL() + k = &Key{} + k.GetURL() + k = nil + k.GetURL() +} + +func TestKey_GetVerified(tt *testing.T) { + tt.Parallel() + var zeroValue bool + k := &Key{Verified: &zeroValue} + k.GetVerified() + k = &Key{} + k.GetVerified() + k = nil + k.GetVerified() +} + +func TestLabel_GetColor(tt *testing.T) { + tt.Parallel() + l := &Label{} + l.GetColor() + l = nil + l.GetColor() +} + +func TestLabel_GetDefault(tt *testing.T) { + tt.Parallel() + l := &Label{} + l.GetDefault() + l = nil + l.GetDefault() +} + +func TestLabel_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &Label{Description: &zeroValue} + l.GetDescription() + l = &Label{} + l.GetDescription() + l = nil + l.GetDescription() +} + +func TestLabel_GetID(tt *testing.T) { + tt.Parallel() + l := &Label{} + l.GetID() + l = nil + l.GetID() +} + +func TestLabel_GetName(tt *testing.T) { + tt.Parallel() + l := &Label{} + l.GetName() + l = nil + l.GetName() +} + +func TestLabel_GetNodeID(tt *testing.T) { + tt.Parallel() + l := &Label{} + l.GetNodeID() + l = nil + l.GetNodeID() +} + +func TestLabel_GetURL(tt *testing.T) { + tt.Parallel() + l := &Label{} + l.GetURL() + l = nil + l.GetURL() +} + +func TestLabelEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LabelEvent{Action: &zeroValue} + l.GetAction() + l = &LabelEvent{} + l.GetAction() + l = nil + l.GetAction() +} + +func TestLabelEvent_GetChanges(tt *testing.T) { + tt.Parallel() + l := &LabelEvent{} + l.GetChanges() + l = nil + l.GetChanges() +} + +func TestLabelEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + l := &LabelEvent{} + l.GetInstallation() + l = nil + l.GetInstallation() +} + +func TestLabelEvent_GetLabel(tt *testing.T) { + tt.Parallel() + l := &LabelEvent{} + l.GetLabel() + l = nil + l.GetLabel() +} + +func TestLabelEvent_GetOrg(tt *testing.T) { + tt.Parallel() + l := &LabelEvent{} + l.GetOrg() + l = nil + l.GetOrg() +} + +func TestLabelEvent_GetRepo(tt *testing.T) { + tt.Parallel() + l := &LabelEvent{} + l.GetRepo() + l = nil + l.GetRepo() +} + +func TestLabelEvent_GetSender(tt *testing.T) { + tt.Parallel() + l := &LabelEvent{} + l.GetSender() + l = nil + l.GetSender() +} + +func TestLabelResult_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LabelResult{Color: &zeroValue} + l.GetColor() + l = &LabelResult{} + l.GetColor() + l = nil + l.GetColor() +} + +func TestLabelResult_GetDefault(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LabelResult{Default: &zeroValue} + l.GetDefault() + l = &LabelResult{} + l.GetDefault() + l = nil + l.GetDefault() +} + +func TestLabelResult_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LabelResult{Description: &zeroValue} + l.GetDescription() + l = &LabelResult{} + l.GetDescription() + l = nil + l.GetDescription() +} + +func TestLabelResult_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + l := &LabelResult{ID: &zeroValue} + l.GetID() + l = &LabelResult{} + l.GetID() + l = nil + l.GetID() +} + +func TestLabelResult_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LabelResult{Name: &zeroValue} + l.GetName() + l = &LabelResult{} + l.GetName() + l = nil + l.GetName() +} + +func TestLabelResult_GetScore(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + l := &LabelResult{Score: &zeroValue} + l.GetScore() + l = &LabelResult{} + l.GetScore() + l = nil + l.GetScore() +} + +func TestLabelResult_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LabelResult{URL: &zeroValue} + l.GetURL() + l = &LabelResult{} + l.GetURL() + l = nil + l.GetURL() +} + +func TestLabelsSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LabelsSearchResult{IncompleteResults: &zeroValue} + l.GetIncompleteResults() + l = &LabelsSearchResult{} + l.GetIncompleteResults() + l = nil + l.GetIncompleteResults() +} + +func TestLabelsSearchResult_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []*LabelResult{} + l := &LabelsSearchResult{Labels: zeroValue} + l.GetLabels() + l = &LabelsSearchResult{} + l.GetLabels() + l = nil + l.GetLabels() +} + +func TestLabelsSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &LabelsSearchResult{Total: &zeroValue} + l.GetTotal() + l = &LabelsSearchResult{} + l.GetTotal() + l = nil + l.GetTotal() +} + +func TestLargeFile_GetOID(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LargeFile{OID: &zeroValue} + l.GetOID() + l = &LargeFile{} + l.GetOID() + l = nil + l.GetOID() +} + +func TestLargeFile_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LargeFile{Path: &zeroValue} + l.GetPath() + l = &LargeFile{} + l.GetPath() + l = nil + l.GetPath() +} + +func TestLargeFile_GetRefName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LargeFile{RefName: &zeroValue} + l.GetRefName() + l = &LargeFile{} + l.GetRefName() + l = nil + l.GetRefName() +} + +func TestLargeFile_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &LargeFile{Size: &zeroValue} + l.GetSize() + l = &LargeFile{} + l.GetSize() + l = nil + l.GetSize() +} + +func TestLastLicenseSync_GetProperties(tt *testing.T) { + tt.Parallel() + l := &LastLicenseSync{} + l.GetProperties() + l = nil + l.GetProperties() +} + +func TestLastLicenseSync_GetType(tt *testing.T) { + tt.Parallel() + l := &LastLicenseSync{} + l.GetType() + l = nil + l.GetType() +} + +func TestLastLicenseSyncProperties_GetDate(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + l := &LastLicenseSyncProperties{Date: &zeroValue} + l.GetDate() + l = &LastLicenseSyncProperties{} + l.GetDate() + l = nil + l.GetDate() +} + +func TestLastLicenseSyncProperties_GetError(tt *testing.T) { + tt.Parallel() + l := &LastLicenseSyncProperties{} + l.GetError() + l = nil + l.GetError() +} + +func TestLastLicenseSyncProperties_GetStatus(tt *testing.T) { + tt.Parallel() + l := &LastLicenseSyncProperties{} + l.GetStatus() + l = nil + l.GetStatus() +} + +func TestLicense_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{Body: &zeroValue} + l.GetBody() + l = &License{} + l.GetBody() + l = nil + l.GetBody() +} + +func TestLicense_GetConditions(tt *testing.T) { + tt.Parallel() + var zeroValue []string + l := &License{Conditions: &zeroValue} + l.GetConditions() + l = &License{} + l.GetConditions() + l = nil + l.GetConditions() +} + +func TestLicense_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{Description: &zeroValue} + l.GetDescription() + l = &License{} + l.GetDescription() + l = nil + l.GetDescription() +} + +func TestLicense_GetFeatured(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &License{Featured: &zeroValue} + l.GetFeatured() + l = &License{} + l.GetFeatured() + l = nil + l.GetFeatured() +} + +func TestLicense_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{HTMLURL: &zeroValue} + l.GetHTMLURL() + l = &License{} + l.GetHTMLURL() + l = nil + l.GetHTMLURL() +} + +func TestLicense_GetImplementation(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{Implementation: &zeroValue} + l.GetImplementation() + l = &License{} + l.GetImplementation() + l = nil + l.GetImplementation() +} + +func TestLicense_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{Key: &zeroValue} + l.GetKey() + l = &License{} + l.GetKey() + l = nil + l.GetKey() +} + +func TestLicense_GetLimitations(tt *testing.T) { + tt.Parallel() + var zeroValue []string + l := &License{Limitations: &zeroValue} + l.GetLimitations() + l = &License{} + l.GetLimitations() + l = nil + l.GetLimitations() +} + +func TestLicense_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{Name: &zeroValue} + l.GetName() + l = &License{} + l.GetName() + l = nil + l.GetName() +} + +func TestLicense_GetPermissions(tt *testing.T) { + tt.Parallel() + var zeroValue []string + l := &License{Permissions: &zeroValue} + l.GetPermissions() + l = &License{} + l.GetPermissions() + l = nil + l.GetPermissions() +} + +func TestLicense_GetSPDXID(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{SPDXID: &zeroValue} + l.GetSPDXID() + l = &License{} + l.GetSPDXID() + l = nil + l.GetSPDXID() +} + +func TestLicense_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &License{URL: &zeroValue} + l.GetURL() + l = &License{} + l.GetURL() + l = nil + l.GetURL() +} + +func TestLicenseCheck_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LicenseCheck{Status: &zeroValue} + l.GetStatus() + l = &LicenseCheck{} + l.GetStatus() + l = nil + l.GetStatus() +} + +func TestLicenseStatus_GetAdvancedSecurityEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{AdvancedSecurityEnabled: &zeroValue} + l.GetAdvancedSecurityEnabled() + l = &LicenseStatus{} + l.GetAdvancedSecurityEnabled() + l = nil + l.GetAdvancedSecurityEnabled() +} + +func TestLicenseStatus_GetAdvancedSecuritySeats(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &LicenseStatus{AdvancedSecuritySeats: &zeroValue} + l.GetAdvancedSecuritySeats() + l = &LicenseStatus{} + l.GetAdvancedSecuritySeats() + l = nil + l.GetAdvancedSecuritySeats() +} + +func TestLicenseStatus_GetClusterSupport(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{ClusterSupport: &zeroValue} + l.GetClusterSupport() + l = &LicenseStatus{} + l.GetClusterSupport() + l = nil + l.GetClusterSupport() +} + +func TestLicenseStatus_GetCompany(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LicenseStatus{Company: &zeroValue} + l.GetCompany() + l = &LicenseStatus{} + l.GetCompany() + l = nil + l.GetCompany() +} + +func TestLicenseStatus_GetCroquetSupport(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{CroquetSupport: &zeroValue} + l.GetCroquetSupport() + l = &LicenseStatus{} + l.GetCroquetSupport() + l = nil + l.GetCroquetSupport() +} + +func TestLicenseStatus_GetCustomTerms(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{CustomTerms: &zeroValue} + l.GetCustomTerms() + l = &LicenseStatus{} + l.GetCustomTerms() + l = nil + l.GetCustomTerms() +} + +func TestLicenseStatus_GetEvaluation(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{Evaluation: &zeroValue} + l.GetEvaluation() + l = &LicenseStatus{} + l.GetEvaluation() + l = nil + l.GetEvaluation() +} + +func TestLicenseStatus_GetExpireAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + l := &LicenseStatus{ExpireAt: &zeroValue} + l.GetExpireAt() + l = &LicenseStatus{} + l.GetExpireAt() + l = nil + l.GetExpireAt() +} + +func TestLicenseStatus_GetInsightsEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{InsightsEnabled: &zeroValue} + l.GetInsightsEnabled() + l = &LicenseStatus{} + l.GetInsightsEnabled() + l = nil + l.GetInsightsEnabled() +} + +func TestLicenseStatus_GetInsightsExpireAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + l := &LicenseStatus{InsightsExpireAt: &zeroValue} + l.GetInsightsExpireAt() + l = &LicenseStatus{} + l.GetInsightsExpireAt() + l = nil + l.GetInsightsExpireAt() +} + +func TestLicenseStatus_GetLearningLabEvaluationExpires(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + l := &LicenseStatus{LearningLabEvaluationExpires: &zeroValue} + l.GetLearningLabEvaluationExpires() + l = &LicenseStatus{} + l.GetLearningLabEvaluationExpires() + l = nil + l.GetLearningLabEvaluationExpires() +} + +func TestLicenseStatus_GetLearningLabSeats(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &LicenseStatus{LearningLabSeats: &zeroValue} + l.GetLearningLabSeats() + l = &LicenseStatus{} + l.GetLearningLabSeats() + l = nil + l.GetLearningLabSeats() +} + +func TestLicenseStatus_GetPerpetual(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{Perpetual: &zeroValue} + l.GetPerpetual() + l = &LicenseStatus{} + l.GetPerpetual() + l = nil + l.GetPerpetual() +} + +func TestLicenseStatus_GetReferenceNumber(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LicenseStatus{ReferenceNumber: &zeroValue} + l.GetReferenceNumber() + l = &LicenseStatus{} + l.GetReferenceNumber() + l = nil + l.GetReferenceNumber() +} + +func TestLicenseStatus_GetSeats(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &LicenseStatus{Seats: &zeroValue} + l.GetSeats() + l = &LicenseStatus{} + l.GetSeats() + l = nil + l.GetSeats() +} + +func TestLicenseStatus_GetSSHAllowed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{SSHAllowed: &zeroValue} + l.GetSSHAllowed() + l = &LicenseStatus{} + l.GetSSHAllowed() + l = nil + l.GetSSHAllowed() +} + +func TestLicenseStatus_GetSupportKey(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{SupportKey: &zeroValue} + l.GetSupportKey() + l = &LicenseStatus{} + l.GetSupportKey() + l = nil + l.GetSupportKey() +} + +func TestLicenseStatus_GetUnlimitedSeating(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LicenseStatus{UnlimitedSeating: &zeroValue} + l.GetUnlimitedSeating() + l = &LicenseStatus{} + l.GetUnlimitedSeating() + l = nil + l.GetUnlimitedSeating() +} + +func TestLinearHistoryRequirementEnforcementLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &LinearHistoryRequirementEnforcementLevelChanges{From: &zeroValue} + l.GetFrom() + l = &LinearHistoryRequirementEnforcementLevelChanges{} + l.GetFrom() + l = nil + l.GetFrom() +} + +func TestListAlertsOptions_GetDirection(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{Direction: &zeroValue} + l.GetDirection() + l = &ListAlertsOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListAlertsOptions_GetEcosystem(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{Ecosystem: &zeroValue} + l.GetEcosystem() + l = &ListAlertsOptions{} + l.GetEcosystem() + l = nil + l.GetEcosystem() +} + +func TestListAlertsOptions_GetPackage(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{Package: &zeroValue} + l.GetPackage() + l = &ListAlertsOptions{} + l.GetPackage() + l = nil + l.GetPackage() +} + +func TestListAlertsOptions_GetScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{Scope: &zeroValue} + l.GetScope() + l = &ListAlertsOptions{} + l.GetScope() + l = nil + l.GetScope() +} + +func TestListAlertsOptions_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{Severity: &zeroValue} + l.GetSeverity() + l = &ListAlertsOptions{} + l.GetSeverity() + l = nil + l.GetSeverity() +} + +func TestListAlertsOptions_GetSort(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{Sort: &zeroValue} + l.GetSort() + l = &ListAlertsOptions{} + l.GetSort() + l = nil + l.GetSort() +} + +func TestListAlertsOptions_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListAlertsOptions{State: &zeroValue} + l.GetState() + l = &ListAlertsOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListAllIssuesOptions_GetCollab(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetCollab() + l = nil + l.GetCollab() +} + +func TestListAllIssuesOptions_GetDirection(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListAllIssuesOptions_GetFilter(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListAllIssuesOptions_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + l := &ListAllIssuesOptions{Labels: zeroValue} + l.GetLabels() + l = &ListAllIssuesOptions{} + l.GetLabels() + l = nil + l.GetLabels() +} + +func TestListAllIssuesOptions_GetOrgs(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetOrgs() + l = nil + l.GetOrgs() +} + +func TestListAllIssuesOptions_GetOwned(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetOwned() + l = nil + l.GetOwned() +} + +func TestListAllIssuesOptions_GetPulls(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetPulls() + l = nil + l.GetPulls() +} + +func TestListAllIssuesOptions_GetSince(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetSince() + l = nil + l.GetSince() +} + +func TestListAllIssuesOptions_GetSort(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetSort() + l = nil + l.GetSort() +} + +func TestListAllIssuesOptions_GetState(tt *testing.T) { + tt.Parallel() + l := &ListAllIssuesOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListArtifactsOptions_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListArtifactsOptions{Name: &zeroValue} + l.GetName() + l = &ListArtifactsOptions{} + l.GetName() + l = nil + l.GetName() +} + +func TestListCheckRunsOptions_GetAppID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + l := &ListCheckRunsOptions{AppID: &zeroValue} + l.GetAppID() + l = &ListCheckRunsOptions{} + l.GetAppID() + l = nil + l.GetAppID() +} + +func TestListCheckRunsOptions_GetCheckName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListCheckRunsOptions{CheckName: &zeroValue} + l.GetCheckName() + l = &ListCheckRunsOptions{} + l.GetCheckName() + l = nil + l.GetCheckName() +} + +func TestListCheckRunsOptions_GetFilter(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListCheckRunsOptions{Filter: &zeroValue} + l.GetFilter() + l = &ListCheckRunsOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListCheckRunsOptions_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListCheckRunsOptions{Status: &zeroValue} + l.GetStatus() + l = &ListCheckRunsOptions{} + l.GetStatus() + l = nil + l.GetStatus() +} + +func TestListCheckRunsResults_GetCheckRuns(tt *testing.T) { + tt.Parallel() + zeroValue := []*CheckRun{} + l := &ListCheckRunsResults{CheckRuns: zeroValue} + l.GetCheckRuns() + l = &ListCheckRunsResults{} + l.GetCheckRuns() + l = nil + l.GetCheckRuns() +} + +func TestListCheckRunsResults_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListCheckRunsResults{Total: &zeroValue} + l.GetTotal() + l = &ListCheckRunsResults{} + l.GetTotal() + l = nil + l.GetTotal() +} + +func TestListCheckSuiteOptions_GetAppID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + l := &ListCheckSuiteOptions{AppID: &zeroValue} + l.GetAppID() + l = &ListCheckSuiteOptions{} + l.GetAppID() + l = nil + l.GetAppID() +} + +func TestListCheckSuiteOptions_GetCheckName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListCheckSuiteOptions{CheckName: &zeroValue} + l.GetCheckName() + l = &ListCheckSuiteOptions{} + l.GetCheckName() + l = nil + l.GetCheckName() +} + +func TestListCheckSuiteResults_GetCheckSuites(tt *testing.T) { + tt.Parallel() + zeroValue := []*CheckSuite{} + l := &ListCheckSuiteResults{CheckSuites: zeroValue} + l.GetCheckSuites() + l = &ListCheckSuiteResults{} + l.GetCheckSuites() + l = nil + l.GetCheckSuites() +} + +func TestListCheckSuiteResults_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListCheckSuiteResults{Total: &zeroValue} + l.GetTotal() + l = &ListCheckSuiteResults{} + l.GetTotal() + l = nil + l.GetTotal() +} + +func TestListCodeQualityFindingsOptions_GetDirection(tt *testing.T) { + tt.Parallel() + l := &ListCodeQualityFindingsOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListCodeQualityFindingsOptions_GetState(tt *testing.T) { + tt.Parallel() + l := &ListCodeQualityFindingsOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListCodeSecurityConfigurationRepositoriesOptions_GetAfter(tt *testing.T) { + tt.Parallel() + l := &ListCodeSecurityConfigurationRepositoriesOptions{} + l.GetAfter() + l = nil + l.GetAfter() +} + +func TestListCodeSecurityConfigurationRepositoriesOptions_GetBefore(tt *testing.T) { + tt.Parallel() + l := &ListCodeSecurityConfigurationRepositoriesOptions{} + l.GetBefore() + l = nil + l.GetBefore() +} + +func TestListCodeSecurityConfigurationRepositoriesOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListCodeSecurityConfigurationRepositoriesOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListCodeSecurityConfigurationRepositoriesOptions_GetStatus(tt *testing.T) { + tt.Parallel() + l := &ListCodeSecurityConfigurationRepositoriesOptions{} + l.GetStatus() + l = nil + l.GetStatus() +} + +func TestListCodespaces_GetCodespaces(tt *testing.T) { + tt.Parallel() + zeroValue := []*Codespace{} + l := &ListCodespaces{Codespaces: zeroValue} + l.GetCodespaces() + l = &ListCodespaces{} + l.GetCodespaces() + l = nil + l.GetCodespaces() +} + +func TestListCodespaces_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListCodespaces{TotalCount: &zeroValue} + l.GetTotalCount() + l = &ListCodespaces{} + l.GetTotalCount() + l = nil + l.GetTotalCount() +} + +func TestListCodespacesOptions_GetRepositoryID(tt *testing.T) { + tt.Parallel() + l := &ListCodespacesOptions{} + l.GetRepositoryID() + l = nil + l.GetRepositoryID() +} + +func TestListCollaboratorsOptions_GetAffiliation(tt *testing.T) { + tt.Parallel() + l := &ListCollaboratorsOptions{} + l.GetAffiliation() + l = nil + l.GetAffiliation() +} + +func TestListCollaboratorsOptions_GetPermission(tt *testing.T) { + tt.Parallel() + l := &ListCollaboratorsOptions{} + l.GetPermission() + l = nil + l.GetPermission() +} + +func TestListContributorsOptions_GetAnon(tt *testing.T) { + tt.Parallel() + l := &ListContributorsOptions{} + l.GetAnon() + l = nil + l.GetAnon() +} + +func TestListCopilotSeatsResponse_GetSeats(tt *testing.T) { + tt.Parallel() + zeroValue := []*CopilotSeatDetails{} + l := &ListCopilotSeatsResponse{Seats: zeroValue} + l.GetSeats() + l = &ListCopilotSeatsResponse{} + l.GetSeats() + l = nil + l.GetSeats() +} + +func TestListCopilotSeatsResponse_GetTotalSeats(tt *testing.T) { + tt.Parallel() + l := &ListCopilotSeatsResponse{} + l.GetTotalSeats() + l = nil + l.GetTotalSeats() +} + +func TestListCostCenterOptions_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListCostCenterOptions{State: &zeroValue} + l.GetState() + l = &ListCostCenterOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListCursorOptions_GetAfter(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetAfter() + l = nil + l.GetAfter() +} + +func TestListCursorOptions_GetBefore(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetBefore() + l = nil + l.GetBefore() +} + +func TestListCursorOptions_GetCursor(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetCursor() + l = nil + l.GetCursor() +} + +func TestListCursorOptions_GetFirst(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetFirst() + l = nil + l.GetFirst() +} + +func TestListCursorOptions_GetLast(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetLast() + l = nil + l.GetLast() +} + +func TestListCursorOptions_GetPage(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetPage() + l = nil + l.GetPage() +} + +func TestListCursorOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListCursorOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListCustomDeploymentRuleIntegrationsResponse_GetAvailableIntegrations(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomDeploymentProtectionRuleApp{} + l := &ListCustomDeploymentRuleIntegrationsResponse{AvailableIntegrations: zeroValue} + l.GetAvailableIntegrations() + l = &ListCustomDeploymentRuleIntegrationsResponse{} + l.GetAvailableIntegrations() + l = nil + l.GetAvailableIntegrations() +} + +func TestListCustomDeploymentRuleIntegrationsResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListCustomDeploymentRuleIntegrationsResponse{TotalCount: &zeroValue} + l.GetTotalCount() + l = &ListCustomDeploymentRuleIntegrationsResponse{} + l.GetTotalCount() + l = nil + l.GetTotalCount() +} + +func TestListCustomPropertyValuesOptions_GetRepositoryQuery(tt *testing.T) { + tt.Parallel() + l := &ListCustomPropertyValuesOptions{} + l.GetRepositoryQuery() + l = nil + l.GetRepositoryQuery() +} + +func TestListDeploymentProtectionRuleResponse_GetProtectionRules(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomDeploymentProtectionRule{} + l := &ListDeploymentProtectionRuleResponse{ProtectionRules: zeroValue} + l.GetProtectionRules() + l = &ListDeploymentProtectionRuleResponse{} + l.GetProtectionRules() + l = nil + l.GetProtectionRules() +} + +func TestListDeploymentProtectionRuleResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListDeploymentProtectionRuleResponse{TotalCount: &zeroValue} + l.GetTotalCount() + l = &ListDeploymentProtectionRuleResponse{} + l.GetTotalCount() + l = nil + l.GetTotalCount() +} + +func TestListEnterpriseCodeSecurityConfigurationOptions_GetAfter(tt *testing.T) { + tt.Parallel() + l := &ListEnterpriseCodeSecurityConfigurationOptions{} + l.GetAfter() + l = nil + l.GetAfter() +} + +func TestListEnterpriseCodeSecurityConfigurationOptions_GetBefore(tt *testing.T) { + tt.Parallel() + l := &ListEnterpriseCodeSecurityConfigurationOptions{} + l.GetBefore() + l = nil + l.GetBefore() +} + +func TestListEnterpriseCodeSecurityConfigurationOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListEnterpriseCodeSecurityConfigurationOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListEnterpriseRunnerGroupOptions_GetVisibleToOrganization(tt *testing.T) { + tt.Parallel() + l := &ListEnterpriseRunnerGroupOptions{} + l.GetVisibleToOrganization() + l = nil + l.GetVisibleToOrganization() +} + +func TestListExternalGroupsOptions_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListExternalGroupsOptions{DisplayName: &zeroValue} + l.GetDisplayName() + l = &ListExternalGroupsOptions{} + l.GetDisplayName() + l = nil + l.GetDisplayName() +} + +func TestListFineGrainedPATOptions_GetDirection(tt *testing.T) { + tt.Parallel() + l := &ListFineGrainedPATOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListFineGrainedPATOptions_GetLastUsedAfter(tt *testing.T) { + tt.Parallel() + l := &ListFineGrainedPATOptions{} + l.GetLastUsedAfter() + l = nil + l.GetLastUsedAfter() +} + +func TestListFineGrainedPATOptions_GetLastUsedBefore(tt *testing.T) { + tt.Parallel() + l := &ListFineGrainedPATOptions{} + l.GetLastUsedBefore() + l = nil + l.GetLastUsedBefore() +} + +func TestListFineGrainedPATOptions_GetOwner(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + l := &ListFineGrainedPATOptions{Owner: zeroValue} + l.GetOwner() + l = &ListFineGrainedPATOptions{} + l.GetOwner() + l = nil + l.GetOwner() +} + +func TestListFineGrainedPATOptions_GetPermission(tt *testing.T) { + tt.Parallel() + l := &ListFineGrainedPATOptions{} + l.GetPermission() + l = nil + l.GetPermission() +} + +func TestListFineGrainedPATOptions_GetRepository(tt *testing.T) { + tt.Parallel() + l := &ListFineGrainedPATOptions{} + l.GetRepository() + l = nil + l.GetRepository() +} + +func TestListFineGrainedPATOptions_GetSort(tt *testing.T) { + tt.Parallel() + l := &ListFineGrainedPATOptions{} + l.GetSort() + l = nil + l.GetSort() +} + +func TestListFineGrainedPATOptions_GetTokenID(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + l := &ListFineGrainedPATOptions{TokenID: zeroValue} + l.GetTokenID() + l = &ListFineGrainedPATOptions{} + l.GetTokenID() + l = nil + l.GetTokenID() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetAffects(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Affects: &zeroValue} + l.GetAffects() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetAffects() + l = nil + l.GetAffects() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetCVEID(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{CVEID: &zeroValue} + l.GetCVEID() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetCVEID() + l = nil + l.GetCVEID() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetCWEs(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + l := &ListGlobalSecurityAdvisoriesOptions{CWEs: zeroValue} + l.GetCWEs() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetCWEs() + l = nil + l.GetCWEs() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetEcosystem(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Ecosystem: &zeroValue} + l.GetEcosystem() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetEcosystem() + l = nil + l.GetEcosystem() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetGHSAID(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{GHSAID: &zeroValue} + l.GetGHSAID() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetGHSAID() + l = nil + l.GetGHSAID() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetIsWithdrawn(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &ListGlobalSecurityAdvisoriesOptions{IsWithdrawn: &zeroValue} + l.GetIsWithdrawn() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetIsWithdrawn() + l = nil + l.GetIsWithdrawn() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetModified(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Modified: &zeroValue} + l.GetModified() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetModified() + l = nil + l.GetModified() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetPublished(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Published: &zeroValue} + l.GetPublished() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetPublished() + l = nil + l.GetPublished() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Severity: &zeroValue} + l.GetSeverity() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetSeverity() + l = nil + l.GetSeverity() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Type: &zeroValue} + l.GetType() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetType() + l = nil + l.GetType() +} + +func TestListGlobalSecurityAdvisoriesOptions_GetUpdated(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListGlobalSecurityAdvisoriesOptions{Updated: &zeroValue} + l.GetUpdated() + l = &ListGlobalSecurityAdvisoriesOptions{} + l.GetUpdated() + l = nil + l.GetUpdated() +} + +func TestListIDPGroupsOptions_GetQuery(tt *testing.T) { + tt.Parallel() + l := &ListIDPGroupsOptions{} + l.GetQuery() + l = nil + l.GetQuery() +} + +func TestListLicensesOptions_GetFeatured(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &ListLicensesOptions{Featured: &zeroValue} + l.GetFeatured() + l = &ListLicensesOptions{} + l.GetFeatured() + l = nil + l.GetFeatured() +} + +func TestListMembersOptions_GetFilter(tt *testing.T) { + tt.Parallel() + l := &ListMembersOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListMembersOptions_GetPublicOnly(tt *testing.T) { + tt.Parallel() + l := &ListMembersOptions{} + l.GetPublicOnly() + l = nil + l.GetPublicOnly() +} + +func TestListMembersOptions_GetRole(tt *testing.T) { + tt.Parallel() + l := &ListMembersOptions{} + l.GetRole() + l = nil + l.GetRole() +} + +func TestListOptions_GetPage(tt *testing.T) { + tt.Parallel() + l := &ListOptions{} + l.GetPage() + l = nil + l.GetPage() +} + +func TestListOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListOrganizationCopilotCodingAgentRepositoriesResponse_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + l := &ListOrganizationCopilotCodingAgentRepositoriesResponse{Repositories: zeroValue} + l.GetRepositories() + l = &ListOrganizationCopilotCodingAgentRepositoriesResponse{} + l.GetRepositories() + l = nil + l.GetRepositories() +} + +func TestListOrganizationCopilotCodingAgentRepositoriesResponse_GetTotalCount(tt *testing.T) { + tt.Parallel() + l := &ListOrganizationCopilotCodingAgentRepositoriesResponse{} + l.GetTotalCount() + l = nil + l.GetTotalCount() +} + +func TestListOrganizations_GetOrganizations(tt *testing.T) { + tt.Parallel() + zeroValue := []*Organization{} + l := &ListOrganizations{Organizations: zeroValue} + l.GetOrganizations() + l = &ListOrganizations{} + l.GetOrganizations() + l = nil + l.GetOrganizations() +} + +func TestListOrganizations_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListOrganizations{TotalCount: &zeroValue} + l.GetTotalCount() + l = &ListOrganizations{} + l.GetTotalCount() + l = nil + l.GetTotalCount() +} + +func TestListOrgCodeSecurityConfigurationOptions_GetAfter(tt *testing.T) { + tt.Parallel() + l := &ListOrgCodeSecurityConfigurationOptions{} + l.GetAfter() + l = nil + l.GetAfter() +} + +func TestListOrgCodeSecurityConfigurationOptions_GetBefore(tt *testing.T) { + tt.Parallel() + l := &ListOrgCodeSecurityConfigurationOptions{} + l.GetBefore() + l = nil + l.GetBefore() +} + +func TestListOrgCodeSecurityConfigurationOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListOrgCodeSecurityConfigurationOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListOrgCodeSecurityConfigurationOptions_GetTargetType(tt *testing.T) { + tt.Parallel() + l := &ListOrgCodeSecurityConfigurationOptions{} + l.GetTargetType() + l = nil + l.GetTargetType() +} + +func TestListOrgMembershipsOptions_GetState(tt *testing.T) { + tt.Parallel() + l := &ListOrgMembershipsOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListOrgRunnerGroupOptions_GetVisibleToRepository(tt *testing.T) { + tt.Parallel() + l := &ListOrgRunnerGroupOptions{} + l.GetVisibleToRepository() + l = nil + l.GetVisibleToRepository() +} + +func TestListOutsideCollaboratorsOptions_GetFilter(tt *testing.T) { + tt.Parallel() + l := &ListOutsideCollaboratorsOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListPackageVersionsOptions_GetState(tt *testing.T) { + tt.Parallel() + l := &ListPackageVersionsOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListProjectItemsOptions_GetFields(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + l := &ListProjectItemsOptions{Fields: zeroValue} + l.GetFields() + l = &ListProjectItemsOptions{} + l.GetFields() + l = nil + l.GetFields() +} + +func TestListProjectsOptions_GetQuery(tt *testing.T) { + tt.Parallel() + l := &ListProjectsOptions{} + l.GetQuery() + l = nil + l.GetQuery() +} + +func TestListProjectsPaginationOptions_GetAfter(tt *testing.T) { + tt.Parallel() + l := &ListProjectsPaginationOptions{} + l.GetAfter() + l = nil + l.GetAfter() +} + +func TestListProjectsPaginationOptions_GetBefore(tt *testing.T) { + tt.Parallel() + l := &ListProjectsPaginationOptions{} + l.GetBefore() + l = nil + l.GetBefore() +} + +func TestListProjectsPaginationOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListProjectsPaginationOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListProvisionedSCIMGroupsEnterpriseOptions_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListProvisionedSCIMGroupsEnterpriseOptions{Count: &zeroValue} + l.GetCount() + l = &ListProvisionedSCIMGroupsEnterpriseOptions{} + l.GetCount() + l = nil + l.GetCount() +} + +func TestListProvisionedSCIMGroupsEnterpriseOptions_GetExcludedAttributes(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListProvisionedSCIMGroupsEnterpriseOptions{ExcludedAttributes: &zeroValue} + l.GetExcludedAttributes() + l = &ListProvisionedSCIMGroupsEnterpriseOptions{} + l.GetExcludedAttributes() + l = nil + l.GetExcludedAttributes() +} + +func TestListProvisionedSCIMGroupsEnterpriseOptions_GetFilter(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListProvisionedSCIMGroupsEnterpriseOptions{Filter: &zeroValue} + l.GetFilter() + l = &ListProvisionedSCIMGroupsEnterpriseOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListProvisionedSCIMGroupsEnterpriseOptions_GetStartIndex(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListProvisionedSCIMGroupsEnterpriseOptions{StartIndex: &zeroValue} + l.GetStartIndex() + l = &ListProvisionedSCIMGroupsEnterpriseOptions{} + l.GetStartIndex() + l = nil + l.GetStartIndex() +} + +func TestListProvisionedSCIMUsersEnterpriseOptions_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListProvisionedSCIMUsersEnterpriseOptions{Count: &zeroValue} + l.GetCount() + l = &ListProvisionedSCIMUsersEnterpriseOptions{} + l.GetCount() + l = nil + l.GetCount() +} + +func TestListProvisionedSCIMUsersEnterpriseOptions_GetFilter(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListProvisionedSCIMUsersEnterpriseOptions{Filter: &zeroValue} + l.GetFilter() + l = &ListProvisionedSCIMUsersEnterpriseOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListProvisionedSCIMUsersEnterpriseOptions_GetStartIndex(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListProvisionedSCIMUsersEnterpriseOptions{StartIndex: &zeroValue} + l.GetStartIndex() + l = &ListProvisionedSCIMUsersEnterpriseOptions{} + l.GetStartIndex() + l = nil + l.GetStartIndex() +} + +func TestListReactionOptions_GetContent(tt *testing.T) { + tt.Parallel() + l := &ListReactionOptions{} + l.GetContent() + l = nil + l.GetContent() +} + +func TestListRepoMachineTypesOptions_GetClientIP(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListRepoMachineTypesOptions{ClientIP: &zeroValue} + l.GetClientIP() + l = &ListRepoMachineTypesOptions{} + l.GetClientIP() + l = nil + l.GetClientIP() +} + +func TestListRepoMachineTypesOptions_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListRepoMachineTypesOptions{Location: &zeroValue} + l.GetLocation() + l = &ListRepoMachineTypesOptions{} + l.GetLocation() + l = nil + l.GetLocation() +} + +func TestListRepoMachineTypesOptions_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListRepoMachineTypesOptions{Ref: &zeroValue} + l.GetRef() + l = &ListRepoMachineTypesOptions{} + l.GetRef() + l = nil + l.GetRef() +} + +func TestListRepositories_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + l := &ListRepositories{Repositories: zeroValue} + l.GetRepositories() + l = &ListRepositories{} + l.GetRepositories() + l = nil + l.GetRepositories() +} + +func TestListRepositories_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListRepositories{TotalCount: &zeroValue} + l.GetTotalCount() + l = &ListRepositories{} + l.GetTotalCount() + l = nil + l.GetTotalCount() +} + +func TestListRepositoryActivityOptions_GetActivityType(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetActivityType() + l = nil + l.GetActivityType() +} + +func TestListRepositoryActivityOptions_GetActor(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetActor() + l = nil + l.GetActor() +} + +func TestListRepositoryActivityOptions_GetAfter(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetAfter() + l = nil + l.GetAfter() +} + +func TestListRepositoryActivityOptions_GetBefore(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetBefore() + l = nil + l.GetBefore() +} + +func TestListRepositoryActivityOptions_GetDirection(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListRepositoryActivityOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetPerPage() + l = nil + l.GetPerPage() +} + +func TestListRepositoryActivityOptions_GetRef(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetRef() + l = nil + l.GetRef() +} + +func TestListRepositoryActivityOptions_GetTimePeriod(tt *testing.T) { + tt.Parallel() + l := &ListRepositoryActivityOptions{} + l.GetTimePeriod() + l = nil + l.GetTimePeriod() +} + +func TestListRepositorySecurityAdvisoriesOptions_GetDirection(tt *testing.T) { + tt.Parallel() + l := &ListRepositorySecurityAdvisoriesOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListRepositorySecurityAdvisoriesOptions_GetSort(tt *testing.T) { + tt.Parallel() + l := &ListRepositorySecurityAdvisoriesOptions{} + l.GetSort() + l = nil + l.GetSort() +} + +func TestListRepositorySecurityAdvisoriesOptions_GetState(tt *testing.T) { + tt.Parallel() + l := &ListRepositorySecurityAdvisoriesOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListRunnersOptions_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListRunnersOptions{Name: &zeroValue} + l.GetName() + l = &ListRunnersOptions{} + l.GetName() + l = nil + l.GetName() +} + +func TestListSCIMProvisionedIdentitiesOptions_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListSCIMProvisionedIdentitiesOptions{Count: &zeroValue} + l.GetCount() + l = &ListSCIMProvisionedIdentitiesOptions{} + l.GetCount() + l = nil + l.GetCount() +} + +func TestListSCIMProvisionedIdentitiesOptions_GetFilter(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &ListSCIMProvisionedIdentitiesOptions{Filter: &zeroValue} + l.GetFilter() + l = &ListSCIMProvisionedIdentitiesOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListSCIMProvisionedIdentitiesOptions_GetStartIndex(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &ListSCIMProvisionedIdentitiesOptions{StartIndex: &zeroValue} + l.GetStartIndex() + l = &ListSCIMProvisionedIdentitiesOptions{} + l.GetStartIndex() + l = nil + l.GetStartIndex() +} + +func TestListUserIssuesOptions_GetDirection(tt *testing.T) { + tt.Parallel() + l := &ListUserIssuesOptions{} + l.GetDirection() + l = nil + l.GetDirection() +} + +func TestListUserIssuesOptions_GetFilter(tt *testing.T) { + tt.Parallel() + l := &ListUserIssuesOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListUserIssuesOptions_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + l := &ListUserIssuesOptions{Labels: zeroValue} + l.GetLabels() + l = &ListUserIssuesOptions{} + l.GetLabels() + l = nil + l.GetLabels() +} + +func TestListUserIssuesOptions_GetSince(tt *testing.T) { + tt.Parallel() + l := &ListUserIssuesOptions{} + l.GetSince() + l = nil + l.GetSince() +} + +func TestListUserIssuesOptions_GetSort(tt *testing.T) { + tt.Parallel() + l := &ListUserIssuesOptions{} + l.GetSort() + l = nil + l.GetSort() +} + +func TestListUserIssuesOptions_GetState(tt *testing.T) { + tt.Parallel() + l := &ListUserIssuesOptions{} + l.GetState() + l = nil + l.GetState() +} + +func TestListWorkflowJobsOptions_GetFilter(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowJobsOptions{} + l.GetFilter() + l = nil + l.GetFilter() +} + +func TestListWorkflowRunsOptions_GetActor(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetActor() + l = nil + l.GetActor() +} + +func TestListWorkflowRunsOptions_GetBranch(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetBranch() + l = nil + l.GetBranch() +} + +func TestListWorkflowRunsOptions_GetCheckSuiteID(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetCheckSuiteID() + l = nil + l.GetCheckSuiteID() +} + +func TestListWorkflowRunsOptions_GetCreated(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetCreated() + l = nil + l.GetCreated() +} + +func TestListWorkflowRunsOptions_GetEvent(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetEvent() + l = nil + l.GetEvent() +} + +func TestListWorkflowRunsOptions_GetExcludePullRequests(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetExcludePullRequests() + l = nil + l.GetExcludePullRequests() +} + +func TestListWorkflowRunsOptions_GetHeadSHA(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetHeadSHA() + l = nil + l.GetHeadSHA() +} + +func TestListWorkflowRunsOptions_GetStatus(tt *testing.T) { + tt.Parallel() + l := &ListWorkflowRunsOptions{} + l.GetStatus() + l = nil + l.GetStatus() +} + +func TestLocation_GetEndColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &Location{EndColumn: &zeroValue} + l.GetEndColumn() + l = &Location{} + l.GetEndColumn() + l = nil + l.GetEndColumn() +} + +func TestLocation_GetEndLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &Location{EndLine: &zeroValue} + l.GetEndLine() + l = &Location{} + l.GetEndLine() + l = nil + l.GetEndLine() +} + +func TestLocation_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + l := &Location{Path: &zeroValue} + l.GetPath() + l = &Location{} + l.GetPath() + l = nil + l.GetPath() +} + +func TestLocation_GetStartColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &Location{StartColumn: &zeroValue} + l.GetStartColumn() + l = &Location{} + l.GetStartColumn() + l = nil + l.GetStartColumn() +} + +func TestLocation_GetStartLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + l := &Location{StartLine: &zeroValue} + l.GetStartLine() + l = &Location{} + l.GetStartLine() + l = nil + l.GetStartLine() +} + +func TestLockBranch_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &LockBranch{Enabled: &zeroValue} + l.GetEnabled() + l = &LockBranch{} + l.GetEnabled() + l = nil + l.GetEnabled() +} + +func TestLockIssueOptions_GetLockReason(tt *testing.T) { + tt.Parallel() + l := &LockIssueOptions{} + l.GetLockReason() + l = nil + l.GetLockReason() +} + +func TestMaintenanceOperationStatus_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceOperationStatus{Hostname: &zeroValue} + m.GetHostname() + m = &MaintenanceOperationStatus{} + m.GetHostname() + m = nil + m.GetHostname() +} + +func TestMaintenanceOperationStatus_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceOperationStatus{Message: &zeroValue} + m.GetMessage() + m = &MaintenanceOperationStatus{} + m.GetMessage() + m = nil + m.GetMessage() +} + +func TestMaintenanceOperationStatus_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceOperationStatus{UUID: &zeroValue} + m.GetUUID() + m = &MaintenanceOperationStatus{} + m.GetUUID() + m = nil + m.GetUUID() +} + +func TestMaintenanceOptions_GetEnabled(tt *testing.T) { + tt.Parallel() + m := &MaintenanceOptions{} + m.GetEnabled() + m = nil + m.GetEnabled() +} + +func TestMaintenanceOptions_GetIPExceptionList(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + m := &MaintenanceOptions{IPExceptionList: zeroValue} + m.GetIPExceptionList() + m = &MaintenanceOptions{} + m.GetIPExceptionList() + m = nil + m.GetIPExceptionList() +} + +func TestMaintenanceOptions_GetMaintenanceModeMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceOptions{MaintenanceModeMessage: &zeroValue} + m.GetMaintenanceModeMessage() + m = &MaintenanceOptions{} + m.GetMaintenanceModeMessage() + m = nil + m.GetMaintenanceModeMessage() +} + +func TestMaintenanceOptions_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceOptions{UUID: &zeroValue} + m.GetUUID() + m = &MaintenanceOptions{} + m.GetUUID() + m = nil + m.GetUUID() +} + +func TestMaintenanceOptions_GetWhen(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceOptions{When: &zeroValue} + m.GetWhen() + m = &MaintenanceOptions{} + m.GetWhen() + m = nil + m.GetWhen() +} + +func TestMaintenanceStatus_GetCanUnsetMaintenance(tt *testing.T) { + tt.Parallel() + var zeroValue bool + m := &MaintenanceStatus{CanUnsetMaintenance: &zeroValue} + m.GetCanUnsetMaintenance() + m = &MaintenanceStatus{} + m.GetCanUnsetMaintenance() + m = nil + m.GetCanUnsetMaintenance() +} + +func TestMaintenanceStatus_GetConnectionServices(tt *testing.T) { + tt.Parallel() + zeroValue := []*ConnectionServiceItem{} + m := &MaintenanceStatus{ConnectionServices: zeroValue} + m.GetConnectionServices() + m = &MaintenanceStatus{} + m.GetConnectionServices() + m = nil + m.GetConnectionServices() +} + +func TestMaintenanceStatus_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceStatus{Hostname: &zeroValue} + m.GetHostname() + m = &MaintenanceStatus{} + m.GetHostname() + m = nil + m.GetHostname() +} + +func TestMaintenanceStatus_GetIPExceptionList(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + m := &MaintenanceStatus{IPExceptionList: zeroValue} + m.GetIPExceptionList() + m = &MaintenanceStatus{} + m.GetIPExceptionList() + m = nil + m.GetIPExceptionList() +} + +func TestMaintenanceStatus_GetMaintenanceModeMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceStatus{MaintenanceModeMessage: &zeroValue} + m.GetMaintenanceModeMessage() + m = &MaintenanceStatus{} + m.GetMaintenanceModeMessage() + m = nil + m.GetMaintenanceModeMessage() +} + +func TestMaintenanceStatus_GetScheduledTime(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &MaintenanceStatus{ScheduledTime: &zeroValue} + m.GetScheduledTime() + m = &MaintenanceStatus{} + m.GetScheduledTime() + m = nil + m.GetScheduledTime() +} + +func TestMaintenanceStatus_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceStatus{Status: &zeroValue} + m.GetStatus() + m = &MaintenanceStatus{} + m.GetStatus() + m = nil + m.GetStatus() +} + +func TestMaintenanceStatus_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MaintenanceStatus{UUID: &zeroValue} + m.GetUUID() + m = &MaintenanceStatus{} + m.GetUUID() + m = nil + m.GetUUID() +} + +func TestMarkdownOptions_GetContext(tt *testing.T) { + tt.Parallel() + m := &MarkdownOptions{} + m.GetContext() + m = nil + m.GetContext() +} + +func TestMarkdownOptions_GetMode(tt *testing.T) { + tt.Parallel() + m := &MarkdownOptions{} + m.GetMode() + m = nil + m.GetMode() +} + +func TestMarketplacePendingChange_GetEffectiveDate(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &MarketplacePendingChange{EffectiveDate: &zeroValue} + m.GetEffectiveDate() + m = &MarketplacePendingChange{} + m.GetEffectiveDate() + m = nil + m.GetEffectiveDate() +} + +func TestMarketplacePendingChange_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &MarketplacePendingChange{ID: &zeroValue} + m.GetID() + m = &MarketplacePendingChange{} + m.GetID() + m = nil + m.GetID() +} + +func TestMarketplacePendingChange_GetPlan(tt *testing.T) { + tt.Parallel() + m := &MarketplacePendingChange{} + m.GetPlan() + m = nil + m.GetPlan() +} + +func TestMarketplacePendingChange_GetUnitCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MarketplacePendingChange{UnitCount: &zeroValue} + m.GetUnitCount() + m = &MarketplacePendingChange{} + m.GetUnitCount() + m = nil + m.GetUnitCount() +} + +func TestMarketplacePlan_GetAccountsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{AccountsURL: &zeroValue} + m.GetAccountsURL() + m = &MarketplacePlan{} + m.GetAccountsURL() + m = nil + m.GetAccountsURL() +} + +func TestMarketplacePlan_GetBullets(tt *testing.T) { + tt.Parallel() + var zeroValue []string + m := &MarketplacePlan{Bullets: &zeroValue} + m.GetBullets() + m = &MarketplacePlan{} + m.GetBullets() + m = nil + m.GetBullets() +} + +func TestMarketplacePlan_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{Description: &zeroValue} + m.GetDescription() + m = &MarketplacePlan{} + m.GetDescription() + m = nil + m.GetDescription() +} + +func TestMarketplacePlan_GetHasFreeTrial(tt *testing.T) { + tt.Parallel() + var zeroValue bool + m := &MarketplacePlan{HasFreeTrial: &zeroValue} + m.GetHasFreeTrial() + m = &MarketplacePlan{} + m.GetHasFreeTrial() + m = nil + m.GetHasFreeTrial() +} + +func TestMarketplacePlan_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &MarketplacePlan{ID: &zeroValue} + m.GetID() + m = &MarketplacePlan{} + m.GetID() + m = nil + m.GetID() +} + +func TestMarketplacePlan_GetMonthlyPriceInCents(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MarketplacePlan{MonthlyPriceInCents: &zeroValue} + m.GetMonthlyPriceInCents() + m = &MarketplacePlan{} + m.GetMonthlyPriceInCents() + m = nil + m.GetMonthlyPriceInCents() +} + +func TestMarketplacePlan_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{Name: &zeroValue} + m.GetName() + m = &MarketplacePlan{} + m.GetName() + m = nil + m.GetName() +} + +func TestMarketplacePlan_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MarketplacePlan{Number: &zeroValue} + m.GetNumber() + m = &MarketplacePlan{} + m.GetNumber() + m = nil + m.GetNumber() +} + +func TestMarketplacePlan_GetPriceModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{PriceModel: &zeroValue} + m.GetPriceModel() + m = &MarketplacePlan{} + m.GetPriceModel() + m = nil + m.GetPriceModel() +} + +func TestMarketplacePlan_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{State: &zeroValue} + m.GetState() + m = &MarketplacePlan{} + m.GetState() + m = nil + m.GetState() +} + +func TestMarketplacePlan_GetUnitName(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{UnitName: &zeroValue} + m.GetUnitName() + m = &MarketplacePlan{} + m.GetUnitName() + m = nil + m.GetUnitName() +} + +func TestMarketplacePlan_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlan{URL: &zeroValue} + m.GetURL() + m = &MarketplacePlan{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMarketplacePlan_GetYearlyPriceInCents(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MarketplacePlan{YearlyPriceInCents: &zeroValue} + m.GetYearlyPriceInCents() + m = &MarketplacePlan{} + m.GetYearlyPriceInCents() + m = nil + m.GetYearlyPriceInCents() +} + +func TestMarketplacePlanAccount_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &MarketplacePlanAccount{ID: &zeroValue} + m.GetID() + m = &MarketplacePlanAccount{} + m.GetID() + m = nil + m.GetID() +} + +func TestMarketplacePlanAccount_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlanAccount{Login: &zeroValue} + m.GetLogin() + m = &MarketplacePlanAccount{} + m.GetLogin() + m = nil + m.GetLogin() +} + +func TestMarketplacePlanAccount_GetMarketplacePendingChange(tt *testing.T) { + tt.Parallel() + m := &MarketplacePlanAccount{} + m.GetMarketplacePendingChange() + m = nil + m.GetMarketplacePendingChange() +} + +func TestMarketplacePlanAccount_GetMarketplacePurchase(tt *testing.T) { + tt.Parallel() + m := &MarketplacePlanAccount{} + m.GetMarketplacePurchase() + m = nil + m.GetMarketplacePurchase() +} + +func TestMarketplacePlanAccount_GetOrganizationBillingEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlanAccount{OrganizationBillingEmail: &zeroValue} + m.GetOrganizationBillingEmail() + m = &MarketplacePlanAccount{} + m.GetOrganizationBillingEmail() + m = nil + m.GetOrganizationBillingEmail() +} + +func TestMarketplacePlanAccount_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlanAccount{Type: &zeroValue} + m.GetType() + m = &MarketplacePlanAccount{} + m.GetType() + m = nil + m.GetType() +} + +func TestMarketplacePlanAccount_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePlanAccount{URL: &zeroValue} + m.GetURL() + m = &MarketplacePlanAccount{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMarketplacePurchase_GetAccount(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchase{} + m.GetAccount() + m = nil + m.GetAccount() +} + +func TestMarketplacePurchase_GetBillingCycle(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchase{BillingCycle: &zeroValue} + m.GetBillingCycle() + m = &MarketplacePurchase{} + m.GetBillingCycle() + m = nil + m.GetBillingCycle() +} + +func TestMarketplacePurchase_GetFreeTrialEndsOn(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &MarketplacePurchase{FreeTrialEndsOn: &zeroValue} + m.GetFreeTrialEndsOn() + m = &MarketplacePurchase{} + m.GetFreeTrialEndsOn() + m = nil + m.GetFreeTrialEndsOn() +} + +func TestMarketplacePurchase_GetNextBillingDate(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &MarketplacePurchase{NextBillingDate: &zeroValue} + m.GetNextBillingDate() + m = &MarketplacePurchase{} + m.GetNextBillingDate() + m = nil + m.GetNextBillingDate() +} + +func TestMarketplacePurchase_GetOnFreeTrial(tt *testing.T) { + tt.Parallel() + var zeroValue bool + m := &MarketplacePurchase{OnFreeTrial: &zeroValue} + m.GetOnFreeTrial() + m = &MarketplacePurchase{} + m.GetOnFreeTrial() + m = nil + m.GetOnFreeTrial() +} + +func TestMarketplacePurchase_GetPlan(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchase{} + m.GetPlan() + m = nil + m.GetPlan() +} + +func TestMarketplacePurchase_GetUnitCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MarketplacePurchase{UnitCount: &zeroValue} + m.GetUnitCount() + m = &MarketplacePurchase{} + m.GetUnitCount() + m = nil + m.GetUnitCount() +} + +func TestMarketplacePurchase_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &MarketplacePurchase{UpdatedAt: &zeroValue} + m.GetUpdatedAt() + m = &MarketplacePurchase{} + m.GetUpdatedAt() + m = nil + m.GetUpdatedAt() +} + +func TestMarketplacePurchaseAccount_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseAccount{Email: &zeroValue} + m.GetEmail() + m = &MarketplacePurchaseAccount{} + m.GetEmail() + m = nil + m.GetEmail() +} + +func TestMarketplacePurchaseAccount_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &MarketplacePurchaseAccount{ID: &zeroValue} + m.GetID() + m = &MarketplacePurchaseAccount{} + m.GetID() + m = nil + m.GetID() +} + +func TestMarketplacePurchaseAccount_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseAccount{Login: &zeroValue} + m.GetLogin() + m = &MarketplacePurchaseAccount{} + m.GetLogin() + m = nil + m.GetLogin() +} + +func TestMarketplacePurchaseAccount_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseAccount{NodeID: &zeroValue} + m.GetNodeID() + m = &MarketplacePurchaseAccount{} + m.GetNodeID() + m = nil + m.GetNodeID() +} + +func TestMarketplacePurchaseAccount_GetOrganizationBillingEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseAccount{OrganizationBillingEmail: &zeroValue} + m.GetOrganizationBillingEmail() + m = &MarketplacePurchaseAccount{} + m.GetOrganizationBillingEmail() + m = nil + m.GetOrganizationBillingEmail() +} + +func TestMarketplacePurchaseAccount_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseAccount{Type: &zeroValue} + m.GetType() + m = &MarketplacePurchaseAccount{} + m.GetType() + m = nil + m.GetType() +} + +func TestMarketplacePurchaseAccount_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseAccount{URL: &zeroValue} + m.GetURL() + m = &MarketplacePurchaseAccount{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMarketplacePurchaseEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MarketplacePurchaseEvent{Action: &zeroValue} + m.GetAction() + m = &MarketplacePurchaseEvent{} + m.GetAction() + m = nil + m.GetAction() +} + +func TestMarketplacePurchaseEvent_GetEffectiveDate(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &MarketplacePurchaseEvent{EffectiveDate: &zeroValue} + m.GetEffectiveDate() + m = &MarketplacePurchaseEvent{} + m.GetEffectiveDate() + m = nil + m.GetEffectiveDate() +} + +func TestMarketplacePurchaseEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchaseEvent{} + m.GetInstallation() + m = nil + m.GetInstallation() +} + +func TestMarketplacePurchaseEvent_GetMarketplacePurchase(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchaseEvent{} + m.GetMarketplacePurchase() + m = nil + m.GetMarketplacePurchase() +} + +func TestMarketplacePurchaseEvent_GetOrg(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchaseEvent{} + m.GetOrg() + m = nil + m.GetOrg() +} + +func TestMarketplacePurchaseEvent_GetPreviousMarketplacePurchase(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchaseEvent{} + m.GetPreviousMarketplacePurchase() + m = nil + m.GetPreviousMarketplacePurchase() +} + +func TestMarketplacePurchaseEvent_GetSender(tt *testing.T) { + tt.Parallel() + m := &MarketplacePurchaseEvent{} + m.GetSender() + m = nil + m.GetSender() +} + +func TestMatch_GetIndices(tt *testing.T) { + tt.Parallel() + zeroValue := []int{} + m := &Match{Indices: zeroValue} + m.GetIndices() + m = &Match{} + m.GetIndices() + m = nil + m.GetIndices() +} + +func TestMatch_GetText(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Match{Text: &zeroValue} + m.GetText() + m = &Match{} + m.GetText() + m = nil + m.GetText() +} + +func TestMaxFilePathLengthBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + m := &MaxFilePathLengthBranchRule{} + m.GetParameters() + m = nil + m.GetParameters() +} + +func TestMaxFilePathLengthRuleParameters_GetMaxFilePathLength(tt *testing.T) { + tt.Parallel() + m := &MaxFilePathLengthRuleParameters{} + m.GetMaxFilePathLength() + m = nil + m.GetMaxFilePathLength() +} + +func TestMaxFileSizeBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + m := &MaxFileSizeBranchRule{} + m.GetParameters() + m = nil + m.GetParameters() +} + +func TestMaxFileSizeRuleParameters_GetMaxFileSize(tt *testing.T) { + tt.Parallel() + m := &MaxFileSizeRuleParameters{} + m.GetMaxFileSize() + m = nil + m.GetMaxFileSize() +} + +func TestMemberChanges_GetPermission(tt *testing.T) { + tt.Parallel() + m := &MemberChanges{} + m.GetPermission() + m = nil + m.GetPermission() +} + +func TestMemberChanges_GetRoleName(tt *testing.T) { + tt.Parallel() + m := &MemberChanges{} + m.GetRoleName() + m = nil + m.GetRoleName() +} + +func TestMemberChangesPermission_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MemberChangesPermission{From: &zeroValue} + m.GetFrom() + m = &MemberChangesPermission{} + m.GetFrom() + m = nil + m.GetFrom() +} + +func TestMemberChangesPermission_GetTo(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MemberChangesPermission{To: &zeroValue} + m.GetTo() + m = &MemberChangesPermission{} + m.GetTo() + m = nil + m.GetTo() +} + +func TestMemberChangesRoleName_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MemberChangesRoleName{From: &zeroValue} + m.GetFrom() + m = &MemberChangesRoleName{} + m.GetFrom() + m = nil + m.GetFrom() +} + +func TestMemberChangesRoleName_GetTo(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MemberChangesRoleName{To: &zeroValue} + m.GetTo() + m = &MemberChangesRoleName{} + m.GetTo() + m = nil + m.GetTo() +} + +func TestMemberEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MemberEvent{Action: &zeroValue} + m.GetAction() + m = &MemberEvent{} + m.GetAction() + m = nil + m.GetAction() +} + +func TestMemberEvent_GetChanges(tt *testing.T) { + tt.Parallel() + m := &MemberEvent{} + m.GetChanges() + m = nil + m.GetChanges() +} + +func TestMemberEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + m := &MemberEvent{} + m.GetInstallation() + m = nil + m.GetInstallation() +} + +func TestMemberEvent_GetMember(tt *testing.T) { + tt.Parallel() + m := &MemberEvent{} + m.GetMember() + m = nil + m.GetMember() +} + +func TestMemberEvent_GetOrg(tt *testing.T) { + tt.Parallel() + m := &MemberEvent{} + m.GetOrg() + m = nil + m.GetOrg() +} + +func TestMemberEvent_GetRepo(tt *testing.T) { + tt.Parallel() + m := &MemberEvent{} + m.GetRepo() + m = nil + m.GetRepo() +} + +func TestMemberEvent_GetSender(tt *testing.T) { + tt.Parallel() + m := &MemberEvent{} + m.GetSender() + m = nil + m.GetSender() +} + +func TestMembership_GetOrganization(tt *testing.T) { + tt.Parallel() + m := &Membership{} + m.GetOrganization() + m = nil + m.GetOrganization() +} + +func TestMembership_GetOrganizationURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Membership{OrganizationURL: &zeroValue} + m.GetOrganizationURL() + m = &Membership{} + m.GetOrganizationURL() + m = nil + m.GetOrganizationURL() +} + +func TestMembership_GetRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Membership{Role: &zeroValue} + m.GetRole() + m = &Membership{} + m.GetRole() + m = nil + m.GetRole() +} + +func TestMembership_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Membership{State: &zeroValue} + m.GetState() + m = &Membership{} + m.GetState() + m = nil + m.GetState() +} + +func TestMembership_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Membership{URL: &zeroValue} + m.GetURL() + m = &Membership{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMembership_GetUser(tt *testing.T) { + tt.Parallel() + m := &Membership{} + m.GetUser() + m = nil + m.GetUser() +} + +func TestMembershipEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MembershipEvent{Action: &zeroValue} + m.GetAction() + m = &MembershipEvent{} + m.GetAction() + m = nil + m.GetAction() +} + +func TestMembershipEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + m := &MembershipEvent{} + m.GetInstallation() + m = nil + m.GetInstallation() +} + +func TestMembershipEvent_GetMember(tt *testing.T) { + tt.Parallel() + m := &MembershipEvent{} + m.GetMember() + m = nil + m.GetMember() +} + +func TestMembershipEvent_GetOrg(tt *testing.T) { + tt.Parallel() + m := &MembershipEvent{} + m.GetOrg() + m = nil + m.GetOrg() +} + +func TestMembershipEvent_GetScope(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MembershipEvent{Scope: &zeroValue} + m.GetScope() + m = &MembershipEvent{} + m.GetScope() + m = nil + m.GetScope() +} + +func TestMembershipEvent_GetSender(tt *testing.T) { + tt.Parallel() + m := &MembershipEvent{} + m.GetSender() + m = nil + m.GetSender() +} + +func TestMembershipEvent_GetTeam(tt *testing.T) { + tt.Parallel() + m := &MembershipEvent{} + m.GetTeam() + m = nil + m.GetTeam() +} + +func TestMergeGroup_GetBaseRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MergeGroup{BaseRef: &zeroValue} + m.GetBaseRef() + m = &MergeGroup{} + m.GetBaseRef() + m = nil + m.GetBaseRef() +} + +func TestMergeGroup_GetBaseSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MergeGroup{BaseSHA: &zeroValue} + m.GetBaseSHA() + m = &MergeGroup{} + m.GetBaseSHA() + m = nil + m.GetBaseSHA() +} + +func TestMergeGroup_GetHeadCommit(tt *testing.T) { + tt.Parallel() + m := &MergeGroup{} + m.GetHeadCommit() + m = nil + m.GetHeadCommit() +} + +func TestMergeGroup_GetHeadRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MergeGroup{HeadRef: &zeroValue} + m.GetHeadRef() + m = &MergeGroup{} + m.GetHeadRef() + m = nil + m.GetHeadRef() +} + +func TestMergeGroup_GetHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MergeGroup{HeadSHA: &zeroValue} + m.GetHeadSHA() + m = &MergeGroup{} + m.GetHeadSHA() + m = nil + m.GetHeadSHA() +} + +func TestMergeGroupEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MergeGroupEvent{Action: &zeroValue} + m.GetAction() + m = &MergeGroupEvent{} + m.GetAction() + m = nil + m.GetAction() +} + +func TestMergeGroupEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + m := &MergeGroupEvent{} + m.GetInstallation() + m = nil + m.GetInstallation() +} + +func TestMergeGroupEvent_GetMergeGroup(tt *testing.T) { + tt.Parallel() + m := &MergeGroupEvent{} + m.GetMergeGroup() + m = nil + m.GetMergeGroup() +} + +func TestMergeGroupEvent_GetOrg(tt *testing.T) { + tt.Parallel() + m := &MergeGroupEvent{} + m.GetOrg() + m = nil + m.GetOrg() +} + +func TestMergeGroupEvent_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MergeGroupEvent{Reason: &zeroValue} + m.GetReason() + m = &MergeGroupEvent{} + m.GetReason() + m = nil + m.GetReason() +} + +func TestMergeGroupEvent_GetRepo(tt *testing.T) { + tt.Parallel() + m := &MergeGroupEvent{} + m.GetRepo() + m = nil + m.GetRepo() +} + +func TestMergeGroupEvent_GetSender(tt *testing.T) { + tt.Parallel() + m := &MergeGroupEvent{} + m.GetSender() + m = nil + m.GetSender() +} + +func TestMergeQueueBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + m := &MergeQueueBranchRule{} + m.GetParameters() + m = nil + m.GetParameters() +} + +func TestMergeQueueRuleParameters_GetCheckResponseTimeoutMinutes(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetCheckResponseTimeoutMinutes() + m = nil + m.GetCheckResponseTimeoutMinutes() +} + +func TestMergeQueueRuleParameters_GetGroupingStrategy(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetGroupingStrategy() + m = nil + m.GetGroupingStrategy() +} + +func TestMergeQueueRuleParameters_GetMaxEntriesToBuild(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetMaxEntriesToBuild() + m = nil + m.GetMaxEntriesToBuild() +} + +func TestMergeQueueRuleParameters_GetMaxEntriesToMerge(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetMaxEntriesToMerge() + m = nil + m.GetMaxEntriesToMerge() +} + +func TestMergeQueueRuleParameters_GetMergeMethod(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetMergeMethod() + m = nil + m.GetMergeMethod() +} + +func TestMergeQueueRuleParameters_GetMinEntriesToMerge(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetMinEntriesToMerge() + m = nil + m.GetMinEntriesToMerge() +} + +func TestMergeQueueRuleParameters_GetMinEntriesToMergeWaitMinutes(tt *testing.T) { + tt.Parallel() + m := &MergeQueueRuleParameters{} + m.GetMinEntriesToMergeWaitMinutes() + m = nil + m.GetMinEntriesToMergeWaitMinutes() +} + +func TestMessage_GetText(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Message{Text: &zeroValue} + m.GetText() + m = &Message{} + m.GetText() + m = nil + m.GetText() +} + +func TestMetaEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MetaEvent{Action: &zeroValue} + m.GetAction() + m = &MetaEvent{} + m.GetAction() + m = nil + m.GetAction() +} + +func TestMetaEvent_GetHook(tt *testing.T) { + tt.Parallel() + m := &MetaEvent{} + m.GetHook() + m = nil + m.GetHook() +} + +func TestMetaEvent_GetHookID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &MetaEvent{HookID: &zeroValue} + m.GetHookID() + m = &MetaEvent{} + m.GetHookID() + m = nil + m.GetHookID() +} + +func TestMetaEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + m := &MetaEvent{} + m.GetInstallation() + m = nil + m.GetInstallation() +} + +func TestMetaEvent_GetOrg(tt *testing.T) { + tt.Parallel() + m := &MetaEvent{} + m.GetOrg() + m = nil + m.GetOrg() +} + +func TestMetaEvent_GetRepo(tt *testing.T) { + tt.Parallel() + m := &MetaEvent{} + m.GetRepo() + m = nil + m.GetRepo() +} + +func TestMetaEvent_GetSender(tt *testing.T) { + tt.Parallel() + m := &MetaEvent{} + m.GetSender() + m = nil + m.GetSender() +} + +func TestMetric_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Metric{HTMLURL: &zeroValue} + m.GetHTMLURL() + m = &Metric{} + m.GetHTMLURL() + m = nil + m.GetHTMLURL() +} + +func TestMetric_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Metric{Key: &zeroValue} + m.GetKey() + m = &Metric{} + m.GetKey() + m = nil + m.GetKey() +} + +func TestMetric_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Metric{Name: &zeroValue} + m.GetName() + m = &Metric{} + m.GetName() + m = nil + m.GetName() +} + +func TestMetric_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Metric{NodeID: &zeroValue} + m.GetNodeID() + m = &Metric{} + m.GetNodeID() + m = nil + m.GetNodeID() +} + +func TestMetric_GetSPDXID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Metric{SPDXID: &zeroValue} + m.GetSPDXID() + m = &Metric{} + m.GetSPDXID() + m = nil + m.GetSPDXID() +} + +func TestMetric_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Metric{URL: &zeroValue} + m.GetURL() + m = &Metric{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMigration_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Migration{CreatedAt: &zeroValue} + m.GetCreatedAt() + m = &Migration{} + m.GetCreatedAt() + m = nil + m.GetCreatedAt() +} + +func TestMigration_GetExcludeAttachments(tt *testing.T) { + tt.Parallel() + var zeroValue bool + m := &Migration{ExcludeAttachments: &zeroValue} + m.GetExcludeAttachments() + m = &Migration{} + m.GetExcludeAttachments() + m = nil + m.GetExcludeAttachments() +} + +func TestMigration_GetGUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Migration{GUID: &zeroValue} + m.GetGUID() + m = &Migration{} + m.GetGUID() + m = nil + m.GetGUID() +} + +func TestMigration_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &Migration{ID: &zeroValue} + m.GetID() + m = &Migration{} + m.GetID() + m = nil + m.GetID() +} + +func TestMigration_GetLockRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + m := &Migration{LockRepositories: &zeroValue} + m.GetLockRepositories() + m = &Migration{} + m.GetLockRepositories() + m = nil + m.GetLockRepositories() +} + +func TestMigration_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + m := &Migration{Repositories: zeroValue} + m.GetRepositories() + m = &Migration{} + m.GetRepositories() + m = nil + m.GetRepositories() +} + +func TestMigration_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Migration{State: &zeroValue} + m.GetState() + m = &Migration{} + m.GetState() + m = nil + m.GetState() +} + +func TestMigration_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Migration{UpdatedAt: &zeroValue} + m.GetUpdatedAt() + m = &Migration{} + m.GetUpdatedAt() + m = nil + m.GetUpdatedAt() +} + +func TestMigration_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Migration{URL: &zeroValue} + m.GetURL() + m = &Migration{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMigrationOptions_GetExclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + m := &MigrationOptions{Exclude: zeroValue} + m.GetExclude() + m = &MigrationOptions{} + m.GetExclude() + m = nil + m.GetExclude() +} + +func TestMigrationOptions_GetExcludeAttachments(tt *testing.T) { + tt.Parallel() + m := &MigrationOptions{} + m.GetExcludeAttachments() + m = nil + m.GetExcludeAttachments() +} + +func TestMigrationOptions_GetExcludeReleases(tt *testing.T) { + tt.Parallel() + m := &MigrationOptions{} + m.GetExcludeReleases() + m = nil + m.GetExcludeReleases() +} + +func TestMigrationOptions_GetLockRepositories(tt *testing.T) { + tt.Parallel() + m := &MigrationOptions{} + m.GetLockRepositories() + m = nil + m.GetLockRepositories() +} + +func TestMilestone_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &Milestone{ClosedAt: &zeroValue} + m.GetClosedAt() + m = &Milestone{} + m.GetClosedAt() + m = nil + m.GetClosedAt() +} + +func TestMilestone_GetClosedIssues(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &Milestone{ClosedIssues: &zeroValue} + m.GetClosedIssues() + m = &Milestone{} + m.GetClosedIssues() + m = nil + m.GetClosedIssues() +} + +func TestMilestone_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &Milestone{CreatedAt: &zeroValue} + m.GetCreatedAt() + m = &Milestone{} + m.GetCreatedAt() + m = nil + m.GetCreatedAt() +} + +func TestMilestone_GetCreator(tt *testing.T) { + tt.Parallel() + m := &Milestone{} + m.GetCreator() + m = nil + m.GetCreator() +} + +func TestMilestone_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{Description: &zeroValue} + m.GetDescription() + m = &Milestone{} + m.GetDescription() + m = nil + m.GetDescription() +} + +func TestMilestone_GetDueOn(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &Milestone{DueOn: &zeroValue} + m.GetDueOn() + m = &Milestone{} + m.GetDueOn() + m = nil + m.GetDueOn() +} + +func TestMilestone_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{HTMLURL: &zeroValue} + m.GetHTMLURL() + m = &Milestone{} + m.GetHTMLURL() + m = nil + m.GetHTMLURL() +} + +func TestMilestone_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + m := &Milestone{ID: &zeroValue} + m.GetID() + m = &Milestone{} + m.GetID() + m = nil + m.GetID() +} + +func TestMilestone_GetLabelsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{LabelsURL: &zeroValue} + m.GetLabelsURL() + m = &Milestone{} + m.GetLabelsURL() + m = nil + m.GetLabelsURL() +} + +func TestMilestone_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{NodeID: &zeroValue} + m.GetNodeID() + m = &Milestone{} + m.GetNodeID() + m = nil + m.GetNodeID() +} + +func TestMilestone_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &Milestone{Number: &zeroValue} + m.GetNumber() + m = &Milestone{} + m.GetNumber() + m = nil + m.GetNumber() +} + +func TestMilestone_GetOpenIssues(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &Milestone{OpenIssues: &zeroValue} + m.GetOpenIssues() + m = &Milestone{} + m.GetOpenIssues() + m = nil + m.GetOpenIssues() +} + +func TestMilestone_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{State: &zeroValue} + m.GetState() + m = &Milestone{} + m.GetState() + m = nil + m.GetState() +} + +func TestMilestone_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{Title: &zeroValue} + m.GetTitle() + m = &Milestone{} + m.GetTitle() + m = nil + m.GetTitle() +} + +func TestMilestone_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + m := &Milestone{UpdatedAt: &zeroValue} + m.GetUpdatedAt() + m = &Milestone{} + m.GetUpdatedAt() + m = nil + m.GetUpdatedAt() +} + +func TestMilestone_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &Milestone{URL: &zeroValue} + m.GetURL() + m = &Milestone{} + m.GetURL() + m = nil + m.GetURL() +} + +func TestMilestoneEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MilestoneEvent{Action: &zeroValue} + m.GetAction() + m = &MilestoneEvent{} + m.GetAction() + m = nil + m.GetAction() +} + +func TestMilestoneEvent_GetChanges(tt *testing.T) { + tt.Parallel() + m := &MilestoneEvent{} + m.GetChanges() + m = nil + m.GetChanges() +} + +func TestMilestoneEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + m := &MilestoneEvent{} + m.GetInstallation() + m = nil + m.GetInstallation() +} + +func TestMilestoneEvent_GetMilestone(tt *testing.T) { + tt.Parallel() + m := &MilestoneEvent{} + m.GetMilestone() + m = nil + m.GetMilestone() +} + +func TestMilestoneEvent_GetOrg(tt *testing.T) { + tt.Parallel() + m := &MilestoneEvent{} + m.GetOrg() + m = nil + m.GetOrg() +} + +func TestMilestoneEvent_GetRepo(tt *testing.T) { + tt.Parallel() + m := &MilestoneEvent{} + m.GetRepo() + m = nil + m.GetRepo() +} + +func TestMilestoneEvent_GetSender(tt *testing.T) { + tt.Parallel() + m := &MilestoneEvent{} + m.GetSender() + m = nil + m.GetSender() +} + +func TestMilestoneListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + m := &MilestoneListOptions{} + m.GetDirection() + m = nil + m.GetDirection() +} + +func TestMilestoneListOptions_GetSort(tt *testing.T) { + tt.Parallel() + m := &MilestoneListOptions{} + m.GetSort() + m = nil + m.GetSort() +} + +func TestMilestoneListOptions_GetState(tt *testing.T) { + tt.Parallel() + m := &MilestoneListOptions{} + m.GetState() + m = nil + m.GetState() +} + +func TestMilestoneStats_GetClosedMilestones(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MilestoneStats{ClosedMilestones: &zeroValue} + m.GetClosedMilestones() + m = &MilestoneStats{} + m.GetClosedMilestones() + m = nil + m.GetClosedMilestones() +} + +func TestMilestoneStats_GetOpenMilestones(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MilestoneStats{OpenMilestones: &zeroValue} + m.GetOpenMilestones() + m = &MilestoneStats{} + m.GetOpenMilestones() + m = nil + m.GetOpenMilestones() +} + +func TestMilestoneStats_GetTotalMilestones(tt *testing.T) { + tt.Parallel() + var zeroValue int + m := &MilestoneStats{TotalMilestones: &zeroValue} + m.GetTotalMilestones() + m = &MilestoneStats{} + m.GetTotalMilestones() + m = nil + m.GetTotalMilestones() +} + +func TestMostRecentInstance_GetAnalysisKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{AnalysisKey: &zeroValue} + m.GetAnalysisKey() + m = &MostRecentInstance{} + m.GetAnalysisKey() + m = nil + m.GetAnalysisKey() +} + +func TestMostRecentInstance_GetCategory(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{Category: &zeroValue} + m.GetCategory() + m = &MostRecentInstance{} + m.GetCategory() + m = nil + m.GetCategory() +} + +func TestMostRecentInstance_GetClassifications(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + m := &MostRecentInstance{Classifications: zeroValue} + m.GetClassifications() + m = &MostRecentInstance{} + m.GetClassifications() + m = nil + m.GetClassifications() +} + +func TestMostRecentInstance_GetCommitSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{CommitSHA: &zeroValue} + m.GetCommitSHA() + m = &MostRecentInstance{} + m.GetCommitSHA() + m = nil + m.GetCommitSHA() +} + +func TestMostRecentInstance_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{Environment: &zeroValue} + m.GetEnvironment() + m = &MostRecentInstance{} + m.GetEnvironment() + m = nil + m.GetEnvironment() +} + +func TestMostRecentInstance_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{HTMLURL: &zeroValue} + m.GetHTMLURL() + m = &MostRecentInstance{} + m.GetHTMLURL() + m = nil + m.GetHTMLURL() +} + +func TestMostRecentInstance_GetLocation(tt *testing.T) { + tt.Parallel() + m := &MostRecentInstance{} + m.GetLocation() + m = nil + m.GetLocation() +} + +func TestMostRecentInstance_GetMessage(tt *testing.T) { + tt.Parallel() + m := &MostRecentInstance{} + m.GetMessage() + m = nil + m.GetMessage() +} + +func TestMostRecentInstance_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{Ref: &zeroValue} + m.GetRef() + m = &MostRecentInstance{} + m.GetRef() + m = nil + m.GetRef() +} + +func TestMostRecentInstance_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + m := &MostRecentInstance{State: &zeroValue} + m.GetState() + m = &MostRecentInstance{} + m.GetState() + m = nil + m.GetState() +} + +func TestNetworkConfiguration_GetComputeService(tt *testing.T) { + tt.Parallel() + n := &NetworkConfiguration{} + n.GetComputeService() + n = nil + n.GetComputeService() +} + +func TestNetworkConfiguration_GetCreatedOn(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + n := &NetworkConfiguration{CreatedOn: &zeroValue} + n.GetCreatedOn() + n = &NetworkConfiguration{} + n.GetCreatedOn() + n = nil + n.GetCreatedOn() +} + +func TestNetworkConfiguration_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkConfiguration{ID: &zeroValue} + n.GetID() + n = &NetworkConfiguration{} + n.GetID() + n = nil + n.GetID() +} + +func TestNetworkConfiguration_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkConfiguration{Name: &zeroValue} + n.GetName() + n = &NetworkConfiguration{} + n.GetName() + n = nil + n.GetName() +} + +func TestNetworkConfiguration_GetNetworkSettingsIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + n := &NetworkConfiguration{NetworkSettingsIDs: zeroValue} + n.GetNetworkSettingsIDs() + n = &NetworkConfiguration{} + n.GetNetworkSettingsIDs() + n = nil + n.GetNetworkSettingsIDs() +} + +func TestNetworkConfigurationRequest_GetComputeService(tt *testing.T) { + tt.Parallel() + n := &NetworkConfigurationRequest{} + n.GetComputeService() + n = nil + n.GetComputeService() +} + +func TestNetworkConfigurationRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkConfigurationRequest{Name: &zeroValue} + n.GetName() + n = &NetworkConfigurationRequest{} + n.GetName() + n = nil + n.GetName() +} + +func TestNetworkConfigurationRequest_GetNetworkSettingsIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + n := &NetworkConfigurationRequest{NetworkSettingsIDs: zeroValue} + n.GetNetworkSettingsIDs() + n = &NetworkConfigurationRequest{} + n.GetNetworkSettingsIDs() + n = nil + n.GetNetworkSettingsIDs() +} + +func TestNetworkConfigurations_GetNetworkConfigurations(tt *testing.T) { + tt.Parallel() + zeroValue := []*NetworkConfiguration{} + n := &NetworkConfigurations{NetworkConfigurations: zeroValue} + n.GetNetworkConfigurations() + n = &NetworkConfigurations{} + n.GetNetworkConfigurations() + n = nil + n.GetNetworkConfigurations() +} + +func TestNetworkConfigurations_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + n := &NetworkConfigurations{TotalCount: &zeroValue} + n.GetTotalCount() + n = &NetworkConfigurations{} + n.GetTotalCount() + n = nil + n.GetTotalCount() +} + +func TestNetworkSettingsResource_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkSettingsResource{ID: &zeroValue} + n.GetID() + n = &NetworkSettingsResource{} + n.GetID() + n = nil + n.GetID() +} + +func TestNetworkSettingsResource_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkSettingsResource{Name: &zeroValue} + n.GetName() + n = &NetworkSettingsResource{} + n.GetName() + n = nil + n.GetName() +} + +func TestNetworkSettingsResource_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkSettingsResource{NetworkConfigurationID: &zeroValue} + n.GetNetworkConfigurationID() + n = &NetworkSettingsResource{} + n.GetNetworkConfigurationID() + n = nil + n.GetNetworkConfigurationID() +} + +func TestNetworkSettingsResource_GetRegion(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkSettingsResource{Region: &zeroValue} + n.GetRegion() + n = &NetworkSettingsResource{} + n.GetRegion() + n = nil + n.GetRegion() +} + +func TestNetworkSettingsResource_GetSubnetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NetworkSettingsResource{SubnetID: &zeroValue} + n.GetSubnetID() + n = &NetworkSettingsResource{} + n.GetSubnetID() + n = nil + n.GetSubnetID() +} + +func TestNodeDetails_GetClusterRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + n := &NodeDetails{ClusterRoles: zeroValue} + n.GetClusterRoles() + n = &NodeDetails{} + n.GetClusterRoles() + n = nil + n.GetClusterRoles() +} + +func TestNodeDetails_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NodeDetails{Hostname: &zeroValue} + n.GetHostname() + n = &NodeDetails{} + n.GetHostname() + n = nil + n.GetHostname() +} + +func TestNodeDetails_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NodeDetails{UUID: &zeroValue} + n.GetUUID() + n = &NodeDetails{} + n.GetUUID() + n = nil + n.GetUUID() +} + +func TestNodeMetadataStatus_GetNodes(tt *testing.T) { + tt.Parallel() + zeroValue := []*NodeDetails{} + n := &NodeMetadataStatus{Nodes: zeroValue} + n.GetNodes() + n = &NodeMetadataStatus{} + n.GetNodes() + n = nil + n.GetNodes() +} + +func TestNodeMetadataStatus_GetTopology(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NodeMetadataStatus{Topology: &zeroValue} + n.GetTopology() + n = &NodeMetadataStatus{} + n.GetTopology() + n = nil + n.GetTopology() +} + +func TestNodeQueryOptions_GetClusterRoles(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NodeQueryOptions{ClusterRoles: &zeroValue} + n.GetClusterRoles() + n = &NodeQueryOptions{} + n.GetClusterRoles() + n = nil + n.GetClusterRoles() +} + +func TestNodeQueryOptions_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NodeQueryOptions{UUID: &zeroValue} + n.GetUUID() + n = &NodeQueryOptions{} + n.GetUUID() + n = nil + n.GetUUID() +} + +func TestNodeReleaseVersion_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NodeReleaseVersion{Hostname: &zeroValue} + n.GetHostname() + n = &NodeReleaseVersion{} + n.GetHostname() + n = nil + n.GetHostname() +} + +func TestNodeReleaseVersion_GetVersion(tt *testing.T) { + tt.Parallel() + n := &NodeReleaseVersion{} + n.GetVersion() + n = nil + n.GetVersion() +} + +func TestNotification_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &Notification{ID: &zeroValue} + n.GetID() + n = &Notification{} + n.GetID() + n = nil + n.GetID() +} + +func TestNotification_GetLastReadAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + n := &Notification{LastReadAt: &zeroValue} + n.GetLastReadAt() + n = &Notification{} + n.GetLastReadAt() + n = nil + n.GetLastReadAt() +} + +func TestNotification_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &Notification{Reason: &zeroValue} + n.GetReason() + n = &Notification{} + n.GetReason() + n = nil + n.GetReason() +} + +func TestNotification_GetRepository(tt *testing.T) { + tt.Parallel() + n := &Notification{} + n.GetRepository() + n = nil + n.GetRepository() +} + +func TestNotification_GetSubject(tt *testing.T) { + tt.Parallel() + n := &Notification{} + n.GetSubject() + n = nil + n.GetSubject() +} + +func TestNotification_GetUnread(tt *testing.T) { + tt.Parallel() + var zeroValue bool + n := &Notification{Unread: &zeroValue} + n.GetUnread() + n = &Notification{} + n.GetUnread() + n = nil + n.GetUnread() +} + +func TestNotification_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + n := &Notification{UpdatedAt: &zeroValue} + n.GetUpdatedAt() + n = &Notification{} + n.GetUpdatedAt() + n = nil + n.GetUpdatedAt() +} + +func TestNotification_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &Notification{URL: &zeroValue} + n.GetURL() + n = &Notification{} + n.GetURL() + n = nil + n.GetURL() +} + +func TestNotificationListOptions_GetAll(tt *testing.T) { + tt.Parallel() + n := &NotificationListOptions{} + n.GetAll() + n = nil + n.GetAll() +} + +func TestNotificationListOptions_GetBefore(tt *testing.T) { + tt.Parallel() + n := &NotificationListOptions{} + n.GetBefore() + n = nil + n.GetBefore() +} + +func TestNotificationListOptions_GetParticipating(tt *testing.T) { + tt.Parallel() + n := &NotificationListOptions{} + n.GetParticipating() + n = nil + n.GetParticipating() +} + +func TestNotificationListOptions_GetSince(tt *testing.T) { + tt.Parallel() + n := &NotificationListOptions{} + n.GetSince() + n = nil + n.GetSince() +} + +func TestNotificationSubject_GetLatestCommentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NotificationSubject{LatestCommentURL: &zeroValue} + n.GetLatestCommentURL() + n = &NotificationSubject{} + n.GetLatestCommentURL() + n = nil + n.GetLatestCommentURL() +} + +func TestNotificationSubject_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NotificationSubject{Title: &zeroValue} + n.GetTitle() + n = &NotificationSubject{} + n.GetTitle() + n = nil + n.GetTitle() +} + +func TestNotificationSubject_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NotificationSubject{Type: &zeroValue} + n.GetType() + n = &NotificationSubject{} + n.GetType() + n = nil + n.GetType() +} + +func TestNotificationSubject_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + n := &NotificationSubject{URL: &zeroValue} + n.GetURL() + n = &NotificationSubject{} + n.GetURL() + n = nil + n.GetURL() +} + +func TestOAuthAPP_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &OAuthAPP{ClientID: &zeroValue} + o.GetClientID() + o = &OAuthAPP{} + o.GetClientID() + o = nil + o.GetClientID() +} + +func TestOAuthAPP_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &OAuthAPP{Name: &zeroValue} + o.GetName() + o = &OAuthAPP{} + o.GetName() + o = nil + o.GetName() +} + +func TestOAuthAPP_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &OAuthAPP{URL: &zeroValue} + o.GetURL() + o = &OAuthAPP{} + o.GetURL() + o = nil + o.GetURL() +} + +func TestOIDCCustomPropertyClaim_GetCustomPropertyName(tt *testing.T) { + tt.Parallel() + o := &OIDCCustomPropertyClaim{} + o.GetCustomPropertyName() + o = nil + o.GetCustomPropertyName() +} + +func TestOIDCCustomPropertyClaimResponse_GetInclusionSource(tt *testing.T) { + tt.Parallel() + o := &OIDCCustomPropertyClaimResponse{} + o.GetInclusionSource() + o = nil + o.GetInclusionSource() +} + +func TestOIDCSubjectClaimCustomTemplate_GetIncludeClaimKeys(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + o := &OIDCSubjectClaimCustomTemplate{IncludeClaimKeys: zeroValue} + o.GetIncludeClaimKeys() + o = &OIDCSubjectClaimCustomTemplate{} + o.GetIncludeClaimKeys() + o = nil + o.GetIncludeClaimKeys() +} + +func TestOIDCSubjectClaimCustomTemplate_GetSubClaimPrefix(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &OIDCSubjectClaimCustomTemplate{SubClaimPrefix: &zeroValue} + o.GetSubClaimPrefix() + o = &OIDCSubjectClaimCustomTemplate{} + o.GetSubClaimPrefix() + o = nil + o.GetSubClaimPrefix() +} + +func TestOIDCSubjectClaimCustomTemplate_GetUseDefault(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &OIDCSubjectClaimCustomTemplate{UseDefault: &zeroValue} + o.GetUseDefault() + o = &OIDCSubjectClaimCustomTemplate{} + o.GetUseDefault() + o = nil + o.GetUseDefault() +} + +func TestOIDCSubjectClaimCustomTemplate_GetUseImmutableSubject(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &OIDCSubjectClaimCustomTemplate{UseImmutableSubject: &zeroValue} + o.GetUseImmutableSubject() + o = &OIDCSubjectClaimCustomTemplate{} + o.GetUseImmutableSubject() + o = nil + o.GetUseImmutableSubject() +} + +func TestOrganization_GetAdvancedSecurityEnabledForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{AdvancedSecurityEnabledForNewRepos: &zeroValue} + o.GetAdvancedSecurityEnabledForNewRepos() + o = &Organization{} + o.GetAdvancedSecurityEnabledForNewRepos() + o = nil + o.GetAdvancedSecurityEnabledForNewRepos() +} + +func TestOrganization_GetArchivedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + o := &Organization{ArchivedAt: &zeroValue} + o.GetArchivedAt() + o = &Organization{} + o.GetArchivedAt() + o = nil + o.GetArchivedAt() +} + +func TestOrganization_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{AvatarURL: &zeroValue} + o.GetAvatarURL() + o = &Organization{} + o.GetAvatarURL() + o = nil + o.GetAvatarURL() +} + +func TestOrganization_GetBillingEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{BillingEmail: &zeroValue} + o.GetBillingEmail() + o = &Organization{} + o.GetBillingEmail() + o = nil + o.GetBillingEmail() +} + +func TestOrganization_GetBlog(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Blog: &zeroValue} + o.GetBlog() + o = &Organization{} + o.GetBlog() + o = nil + o.GetBlog() +} + +func TestOrganization_GetCollaborators(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{Collaborators: &zeroValue} + o.GetCollaborators() + o = &Organization{} + o.GetCollaborators() + o = nil + o.GetCollaborators() +} + +func TestOrganization_GetCompany(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Company: &zeroValue} + o.GetCompany() + o = &Organization{} + o.GetCompany() + o = nil + o.GetCompany() +} + +func TestOrganization_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + o := &Organization{CreatedAt: &zeroValue} + o.GetCreatedAt() + o = &Organization{} + o.GetCreatedAt() + o = nil + o.GetCreatedAt() +} + +func TestOrganization_GetDefaultRepoPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{DefaultRepoPermission: &zeroValue} + o.GetDefaultRepoPermission() + o = &Organization{} + o.GetDefaultRepoPermission() + o = nil + o.GetDefaultRepoPermission() +} + +func TestOrganization_GetDefaultRepoSettings(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{DefaultRepoSettings: &zeroValue} + o.GetDefaultRepoSettings() + o = &Organization{} + o.GetDefaultRepoSettings() + o = nil + o.GetDefaultRepoSettings() +} + +func TestOrganization_GetDefaultRepositoryBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{DefaultRepositoryBranch: &zeroValue} + o.GetDefaultRepositoryBranch() + o = &Organization{} + o.GetDefaultRepositoryBranch() + o = nil + o.GetDefaultRepositoryBranch() +} + +func TestOrganization_GetDependabotAlertsEnabledForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{DependabotAlertsEnabledForNewRepos: &zeroValue} + o.GetDependabotAlertsEnabledForNewRepos() + o = &Organization{} + o.GetDependabotAlertsEnabledForNewRepos() + o = nil + o.GetDependabotAlertsEnabledForNewRepos() +} + +func TestOrganization_GetDependabotSecurityUpdatesEnabledForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{DependabotSecurityUpdatesEnabledForNewRepos: &zeroValue} + o.GetDependabotSecurityUpdatesEnabledForNewRepos() + o = &Organization{} + o.GetDependabotSecurityUpdatesEnabledForNewRepos() + o = nil + o.GetDependabotSecurityUpdatesEnabledForNewRepos() +} + +func TestOrganization_GetDependencyGraphEnabledForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{DependencyGraphEnabledForNewRepos: &zeroValue} + o.GetDependencyGraphEnabledForNewRepos() + o = &Organization{} + o.GetDependencyGraphEnabledForNewRepos() + o = nil + o.GetDependencyGraphEnabledForNewRepos() +} + +func TestOrganization_GetDeployKeysEnabledForRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{DeployKeysEnabledForRepositories: &zeroValue} + o.GetDeployKeysEnabledForRepositories() + o = &Organization{} + o.GetDeployKeysEnabledForRepositories() + o = nil + o.GetDeployKeysEnabledForRepositories() +} + +func TestOrganization_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Description: &zeroValue} + o.GetDescription() + o = &Organization{} + o.GetDescription() + o = nil + o.GetDescription() +} + +func TestOrganization_GetDiskUsage(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{DiskUsage: &zeroValue} + o.GetDiskUsage() + o = &Organization{} + o.GetDiskUsage() + o = nil + o.GetDiskUsage() +} + +func TestOrganization_GetDisplayCommenterFullNameSettingEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{DisplayCommenterFullNameSettingEnabled: &zeroValue} + o.GetDisplayCommenterFullNameSettingEnabled() + o = &Organization{} + o.GetDisplayCommenterFullNameSettingEnabled() + o = nil + o.GetDisplayCommenterFullNameSettingEnabled() +} + +func TestOrganization_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Email: &zeroValue} + o.GetEmail() + o = &Organization{} + o.GetEmail() + o = nil + o.GetEmail() +} + +func TestOrganization_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{EventsURL: &zeroValue} + o.GetEventsURL() + o = &Organization{} + o.GetEventsURL() + o = nil + o.GetEventsURL() +} + +func TestOrganization_GetFollowers(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{Followers: &zeroValue} + o.GetFollowers() + o = &Organization{} + o.GetFollowers() + o = nil + o.GetFollowers() +} + +func TestOrganization_GetFollowing(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{Following: &zeroValue} + o.GetFollowing() + o = &Organization{} + o.GetFollowing() + o = nil + o.GetFollowing() +} + +func TestOrganization_GetHasOrganizationProjects(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{HasOrganizationProjects: &zeroValue} + o.GetHasOrganizationProjects() + o = &Organization{} + o.GetHasOrganizationProjects() + o = nil + o.GetHasOrganizationProjects() +} + +func TestOrganization_GetHasRepositoryProjects(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{HasRepositoryProjects: &zeroValue} + o.GetHasRepositoryProjects() + o = &Organization{} + o.GetHasRepositoryProjects() + o = nil + o.GetHasRepositoryProjects() +} + +func TestOrganization_GetHooksURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{HooksURL: &zeroValue} + o.GetHooksURL() + o = &Organization{} + o.GetHooksURL() + o = nil + o.GetHooksURL() +} + +func TestOrganization_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{HTMLURL: &zeroValue} + o.GetHTMLURL() + o = &Organization{} + o.GetHTMLURL() + o = nil + o.GetHTMLURL() +} + +func TestOrganization_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + o := &Organization{ID: &zeroValue} + o.GetID() + o = &Organization{} + o.GetID() + o = nil + o.GetID() +} + +func TestOrganization_GetIssuesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{IssuesURL: &zeroValue} + o.GetIssuesURL() + o = &Organization{} + o.GetIssuesURL() + o = nil + o.GetIssuesURL() +} + +func TestOrganization_GetIsVerified(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{IsVerified: &zeroValue} + o.GetIsVerified() + o = &Organization{} + o.GetIsVerified() + o = nil + o.GetIsVerified() +} + +func TestOrganization_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Location: &zeroValue} + o.GetLocation() + o = &Organization{} + o.GetLocation() + o = nil + o.GetLocation() +} + +func TestOrganization_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Login: &zeroValue} + o.GetLogin() + o = &Organization{} + o.GetLogin() + o = nil + o.GetLogin() +} + +func TestOrganization_GetMembersAllowedRepositoryCreationType(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{MembersAllowedRepositoryCreationType: &zeroValue} + o.GetMembersAllowedRepositoryCreationType() + o = &Organization{} + o.GetMembersAllowedRepositoryCreationType() + o = nil + o.GetMembersAllowedRepositoryCreationType() +} + +func TestOrganization_GetMembersCanChangeRepoVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanChangeRepoVisibility: &zeroValue} + o.GetMembersCanChangeRepoVisibility() + o = &Organization{} + o.GetMembersCanChangeRepoVisibility() + o = nil + o.GetMembersCanChangeRepoVisibility() +} + +func TestOrganization_GetMembersCanCreateInternalRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreateInternalRepos: &zeroValue} + o.GetMembersCanCreateInternalRepos() + o = &Organization{} + o.GetMembersCanCreateInternalRepos() + o = nil + o.GetMembersCanCreateInternalRepos() +} + +func TestOrganization_GetMembersCanCreatePages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreatePages: &zeroValue} + o.GetMembersCanCreatePages() + o = &Organization{} + o.GetMembersCanCreatePages() + o = nil + o.GetMembersCanCreatePages() +} + +func TestOrganization_GetMembersCanCreatePrivatePages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreatePrivatePages: &zeroValue} + o.GetMembersCanCreatePrivatePages() + o = &Organization{} + o.GetMembersCanCreatePrivatePages() + o = nil + o.GetMembersCanCreatePrivatePages() +} + +func TestOrganization_GetMembersCanCreatePrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreatePrivateRepos: &zeroValue} + o.GetMembersCanCreatePrivateRepos() + o = &Organization{} + o.GetMembersCanCreatePrivateRepos() + o = nil + o.GetMembersCanCreatePrivateRepos() +} + +func TestOrganization_GetMembersCanCreatePublicPages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreatePublicPages: &zeroValue} + o.GetMembersCanCreatePublicPages() + o = &Organization{} + o.GetMembersCanCreatePublicPages() + o = nil + o.GetMembersCanCreatePublicPages() +} + +func TestOrganization_GetMembersCanCreatePublicRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreatePublicRepos: &zeroValue} + o.GetMembersCanCreatePublicRepos() + o = &Organization{} + o.GetMembersCanCreatePublicRepos() + o = nil + o.GetMembersCanCreatePublicRepos() +} + +func TestOrganization_GetMembersCanCreateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreateRepos: &zeroValue} + o.GetMembersCanCreateRepos() + o = &Organization{} + o.GetMembersCanCreateRepos() + o = nil + o.GetMembersCanCreateRepos() +} + +func TestOrganization_GetMembersCanCreateTeams(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanCreateTeams: &zeroValue} + o.GetMembersCanCreateTeams() + o = &Organization{} + o.GetMembersCanCreateTeams() + o = nil + o.GetMembersCanCreateTeams() +} + +func TestOrganization_GetMembersCanDeleteIssues(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanDeleteIssues: &zeroValue} + o.GetMembersCanDeleteIssues() + o = &Organization{} + o.GetMembersCanDeleteIssues() + o = nil + o.GetMembersCanDeleteIssues() +} + +func TestOrganization_GetMembersCanDeleteRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanDeleteRepositories: &zeroValue} + o.GetMembersCanDeleteRepositories() + o = &Organization{} + o.GetMembersCanDeleteRepositories() + o = nil + o.GetMembersCanDeleteRepositories() +} + +func TestOrganization_GetMembersCanForkPrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanForkPrivateRepos: &zeroValue} + o.GetMembersCanForkPrivateRepos() + o = &Organization{} + o.GetMembersCanForkPrivateRepos() + o = nil + o.GetMembersCanForkPrivateRepos() +} + +func TestOrganization_GetMembersCanInviteOutsideCollaborators(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanInviteOutsideCollaborators: &zeroValue} + o.GetMembersCanInviteOutsideCollaborators() + o = &Organization{} + o.GetMembersCanInviteOutsideCollaborators() + o = nil + o.GetMembersCanInviteOutsideCollaborators() +} + +func TestOrganization_GetMembersCanViewDependencyInsights(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{MembersCanViewDependencyInsights: &zeroValue} + o.GetMembersCanViewDependencyInsights() + o = &Organization{} + o.GetMembersCanViewDependencyInsights() + o = nil + o.GetMembersCanViewDependencyInsights() +} + +func TestOrganization_GetMembersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{MembersURL: &zeroValue} + o.GetMembersURL() + o = &Organization{} + o.GetMembersURL() + o = nil + o.GetMembersURL() +} + +func TestOrganization_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Name: &zeroValue} + o.GetName() + o = &Organization{} + o.GetName() + o = nil + o.GetName() +} + +func TestOrganization_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{NodeID: &zeroValue} + o.GetNodeID() + o = &Organization{} + o.GetNodeID() + o = nil + o.GetNodeID() +} + +func TestOrganization_GetOwnedPrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + o := &Organization{OwnedPrivateRepos: &zeroValue} + o.GetOwnedPrivateRepos() + o = &Organization{} + o.GetOwnedPrivateRepos() + o = nil + o.GetOwnedPrivateRepos() +} + +func TestOrganization_GetPlan(tt *testing.T) { + tt.Parallel() + o := &Organization{} + o.GetPlan() + o = nil + o.GetPlan() +} + +func TestOrganization_GetPrivateGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{PrivateGists: &zeroValue} + o.GetPrivateGists() + o = &Organization{} + o.GetPrivateGists() + o = nil + o.GetPrivateGists() +} + +func TestOrganization_GetPublicGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{PublicGists: &zeroValue} + o.GetPublicGists() + o = &Organization{} + o.GetPublicGists() + o = nil + o.GetPublicGists() +} + +func TestOrganization_GetPublicMembersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{PublicMembersURL: &zeroValue} + o.GetPublicMembersURL() + o = &Organization{} + o.GetPublicMembersURL() + o = nil + o.GetPublicMembersURL() +} + +func TestOrganization_GetPublicRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &Organization{PublicRepos: &zeroValue} + o.GetPublicRepos() + o = &Organization{} + o.GetPublicRepos() + o = nil + o.GetPublicRepos() +} + +func TestOrganization_GetReadersCanCreateDiscussions(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{ReadersCanCreateDiscussions: &zeroValue} + o.GetReadersCanCreateDiscussions() + o = &Organization{} + o.GetReadersCanCreateDiscussions() + o = nil + o.GetReadersCanCreateDiscussions() +} + +func TestOrganization_GetReposURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{ReposURL: &zeroValue} + o.GetReposURL() + o = &Organization{} + o.GetReposURL() + o = nil + o.GetReposURL() +} + +func TestOrganization_GetSecretScanningEnabledForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{SecretScanningEnabledForNewRepos: &zeroValue} + o.GetSecretScanningEnabledForNewRepos() + o = &Organization{} + o.GetSecretScanningEnabledForNewRepos() + o = nil + o.GetSecretScanningEnabledForNewRepos() +} + +func TestOrganization_GetSecretScanningPushProtectionCustomLink(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{SecretScanningPushProtectionCustomLink: &zeroValue} + o.GetSecretScanningPushProtectionCustomLink() + o = &Organization{} + o.GetSecretScanningPushProtectionCustomLink() + o = nil + o.GetSecretScanningPushProtectionCustomLink() +} + +func TestOrganization_GetSecretScanningPushProtectionCustomLinkEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{SecretScanningPushProtectionCustomLinkEnabled: &zeroValue} + o.GetSecretScanningPushProtectionCustomLinkEnabled() + o = &Organization{} + o.GetSecretScanningPushProtectionCustomLinkEnabled() + o = nil + o.GetSecretScanningPushProtectionCustomLinkEnabled() +} + +func TestOrganization_GetSecretScanningPushProtectionEnabledForNewRepos(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{SecretScanningPushProtectionEnabledForNewRepos: &zeroValue} + o.GetSecretScanningPushProtectionEnabledForNewRepos() + o = &Organization{} + o.GetSecretScanningPushProtectionEnabledForNewRepos() + o = nil + o.GetSecretScanningPushProtectionEnabledForNewRepos() +} + +func TestOrganization_GetSecretScanningValidityChecksEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{SecretScanningValidityChecksEnabled: &zeroValue} + o.GetSecretScanningValidityChecksEnabled() + o = &Organization{} + o.GetSecretScanningValidityChecksEnabled() + o = nil + o.GetSecretScanningValidityChecksEnabled() +} + +func TestOrganization_GetTotalPrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + o := &Organization{TotalPrivateRepos: &zeroValue} + o.GetTotalPrivateRepos() + o = &Organization{} + o.GetTotalPrivateRepos() + o = nil + o.GetTotalPrivateRepos() +} + +func TestOrganization_GetTwitterUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{TwitterUsername: &zeroValue} + o.GetTwitterUsername() + o = &Organization{} + o.GetTwitterUsername() + o = nil + o.GetTwitterUsername() +} + +func TestOrganization_GetTwoFactorRequirementEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{TwoFactorRequirementEnabled: &zeroValue} + o.GetTwoFactorRequirementEnabled() + o = &Organization{} + o.GetTwoFactorRequirementEnabled() + o = nil + o.GetTwoFactorRequirementEnabled() +} + +func TestOrganization_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{Type: &zeroValue} + o.GetType() + o = &Organization{} + o.GetType() + o = nil + o.GetType() +} + +func TestOrganization_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + o := &Organization{UpdatedAt: &zeroValue} + o.GetUpdatedAt() + o = &Organization{} + o.GetUpdatedAt() + o = nil + o.GetUpdatedAt() +} + +func TestOrganization_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &Organization{URL: &zeroValue} + o.GetURL() + o = &Organization{} + o.GetURL() + o = nil + o.GetURL() +} + +func TestOrganization_GetWebCommitSignoffRequired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + o := &Organization{WebCommitSignoffRequired: &zeroValue} + o.GetWebCommitSignoffRequired() + o = &Organization{} + o.GetWebCommitSignoffRequired() + o = nil + o.GetWebCommitSignoffRequired() +} + +func TestOrganizationCustomPropertyValues_GetProperties(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPropertyValue{} + o := &OrganizationCustomPropertyValues{Properties: zeroValue} + o.GetProperties() + o = &OrganizationCustomPropertyValues{} + o.GetProperties() + o = nil + o.GetProperties() +} + +func TestOrganizationCustomRepoRoles_GetCustomRepoRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomRepoRoles{} + o := &OrganizationCustomRepoRoles{CustomRepoRoles: zeroValue} + o.GetCustomRepoRoles() + o = &OrganizationCustomRepoRoles{} + o.GetCustomRepoRoles() + o = nil + o.GetCustomRepoRoles() +} + +func TestOrganizationCustomRepoRoles_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrganizationCustomRepoRoles{TotalCount: &zeroValue} + o.GetTotalCount() + o = &OrganizationCustomRepoRoles{} + o.GetTotalCount() + o = nil + o.GetTotalCount() +} + +func TestOrganizationCustomRoles_GetCustomRepoRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomOrgRole{} + o := &OrganizationCustomRoles{CustomRepoRoles: zeroValue} + o.GetCustomRepoRoles() + o = &OrganizationCustomRoles{} + o.GetCustomRepoRoles() + o = nil + o.GetCustomRepoRoles() +} + +func TestOrganizationCustomRoles_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrganizationCustomRoles{TotalCount: &zeroValue} + o.GetTotalCount() + o = &OrganizationCustomRoles{} + o.GetTotalCount() + o = nil + o.GetTotalCount() +} + +func TestOrganizationEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &OrganizationEvent{Action: &zeroValue} + o.GetAction() + o = &OrganizationEvent{} + o.GetAction() + o = nil + o.GetAction() +} + +func TestOrganizationEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + o := &OrganizationEvent{} + o.GetInstallation() + o = nil + o.GetInstallation() +} + +func TestOrganizationEvent_GetInvitation(tt *testing.T) { + tt.Parallel() + o := &OrganizationEvent{} + o.GetInvitation() + o = nil + o.GetInvitation() +} + +func TestOrganizationEvent_GetMembership(tt *testing.T) { + tt.Parallel() + o := &OrganizationEvent{} + o.GetMembership() + o = nil + o.GetMembership() +} + +func TestOrganizationEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + o := &OrganizationEvent{} + o.GetOrganization() + o = nil + o.GetOrganization() +} + +func TestOrganizationEvent_GetSender(tt *testing.T) { + tt.Parallel() + o := &OrganizationEvent{} + o.GetSender() + o = nil + o.GetSender() +} + +func TestOrganizationFineGrainedPermission_GetDescription(tt *testing.T) { + tt.Parallel() + o := &OrganizationFineGrainedPermission{} + o.GetDescription() + o = nil + o.GetDescription() +} + +func TestOrganizationFineGrainedPermission_GetName(tt *testing.T) { + tt.Parallel() + o := &OrganizationFineGrainedPermission{} + o.GetName() + o = nil + o.GetName() +} + +func TestOrganizationInstallations_GetInstallations(tt *testing.T) { + tt.Parallel() + zeroValue := []*Installation{} + o := &OrganizationInstallations{Installations: zeroValue} + o.GetInstallations() + o = &OrganizationInstallations{} + o.GetInstallations() + o = nil + o.GetInstallations() +} + +func TestOrganizationInstallations_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrganizationInstallations{TotalCount: &zeroValue} + o.GetTotalCount() + o = &OrganizationInstallations{} + o.GetTotalCount() + o = nil + o.GetTotalCount() +} + +func TestOrganizationsListOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + o := &OrganizationsListOptions{} + o.GetPerPage() + o = nil + o.GetPerPage() +} + +func TestOrganizationsListOptions_GetSince(tt *testing.T) { + tt.Parallel() + o := &OrganizationsListOptions{} + o.GetSince() + o = nil + o.GetSince() +} + +func TestOrgBlockEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + o := &OrgBlockEvent{Action: &zeroValue} + o.GetAction() + o = &OrgBlockEvent{} + o.GetAction() + o = nil + o.GetAction() +} + +func TestOrgBlockEvent_GetBlockedUser(tt *testing.T) { + tt.Parallel() + o := &OrgBlockEvent{} + o.GetBlockedUser() + o = nil + o.GetBlockedUser() +} + +func TestOrgBlockEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + o := &OrgBlockEvent{} + o.GetInstallation() + o = nil + o.GetInstallation() +} + +func TestOrgBlockEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + o := &OrgBlockEvent{} + o.GetOrganization() + o = nil + o.GetOrganization() +} + +func TestOrgBlockEvent_GetSender(tt *testing.T) { + tt.Parallel() + o := &OrgBlockEvent{} + o.GetSender() + o = nil + o.GetSender() +} + +func TestOrgStats_GetDisabledOrgs(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrgStats{DisabledOrgs: &zeroValue} + o.GetDisabledOrgs() + o = &OrgStats{} + o.GetDisabledOrgs() + o = nil + o.GetDisabledOrgs() +} + +func TestOrgStats_GetTotalOrgs(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrgStats{TotalOrgs: &zeroValue} + o.GetTotalOrgs() + o = &OrgStats{} + o.GetTotalOrgs() + o = nil + o.GetTotalOrgs() +} + +func TestOrgStats_GetTotalTeamMembers(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrgStats{TotalTeamMembers: &zeroValue} + o.GetTotalTeamMembers() + o = &OrgStats{} + o.GetTotalTeamMembers() + o = nil + o.GetTotalTeamMembers() +} + +func TestOrgStats_GetTotalTeams(tt *testing.T) { + tt.Parallel() + var zeroValue int + o := &OrgStats{TotalTeams: &zeroValue} + o.GetTotalTeams() + o = &OrgStats{} + o.GetTotalTeams() + o = nil + o.GetTotalTeams() +} + +func TestOwnerInfo_GetOrg(tt *testing.T) { + tt.Parallel() + o := &OwnerInfo{} + o.GetOrg() + o = nil + o.GetOrg() +} + +func TestOwnerInfo_GetUser(tt *testing.T) { + tt.Parallel() + o := &OwnerInfo{} + o.GetUser() + o = nil + o.GetUser() +} + +func TestPackage_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &Package{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &Package{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPackage_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{Description: &zeroValue} + p.GetDescription() + p = &Package{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestPackage_GetEcosystem(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{Ecosystem: &zeroValue} + p.GetEcosystem() + p = &Package{} + p.GetEcosystem() + p = nil + p.GetEcosystem() +} + +func TestPackage_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &Package{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPackage_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &Package{ID: &zeroValue} + p.GetID() + p = &Package{} + p.GetID() + p = nil + p.GetID() +} + +func TestPackage_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{Name: &zeroValue} + p.GetName() + p = &Package{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackage_GetNamespace(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{Namespace: &zeroValue} + p.GetNamespace() + p = &Package{} + p.GetNamespace() + p = nil + p.GetNamespace() +} + +func TestPackage_GetOwner(tt *testing.T) { + tt.Parallel() + p := &Package{} + p.GetOwner() + p = nil + p.GetOwner() +} + +func TestPackage_GetPackageType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{PackageType: &zeroValue} + p.GetPackageType() + p = &Package{} + p.GetPackageType() + p = nil + p.GetPackageType() +} + +func TestPackage_GetPackageVersion(tt *testing.T) { + tt.Parallel() + p := &Package{} + p.GetPackageVersion() + p = nil + p.GetPackageVersion() +} + +func TestPackage_GetRegistry(tt *testing.T) { + tt.Parallel() + p := &Package{} + p.GetRegistry() + p = nil + p.GetRegistry() +} + +func TestPackage_GetRepository(tt *testing.T) { + tt.Parallel() + p := &Package{} + p.GetRepository() + p = nil + p.GetRepository() +} + +func TestPackage_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &Package{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &Package{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPackage_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{URL: &zeroValue} + p.GetURL() + p = &Package{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPackage_GetVersionCount(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &Package{VersionCount: &zeroValue} + p.GetVersionCount() + p = &Package{} + p.GetVersionCount() + p = nil + p.GetVersionCount() +} + +func TestPackage_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Package{Visibility: &zeroValue} + p.GetVisibility() + p = &Package{} + p.GetVisibility() + p = nil + p.GetVisibility() +} + +func TestPackageContainerMetadata_GetTags(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PackageContainerMetadata{Tags: zeroValue} + p.GetTags() + p = &PackageContainerMetadata{} + p.GetTags() + p = nil + p.GetTags() +} + +func TestPackageEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageEvent{Action: &zeroValue} + p.GetAction() + p = &PackageEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPackageEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PackageEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPackageEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PackageEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPackageEvent_GetPackage(tt *testing.T) { + tt.Parallel() + p := &PackageEvent{} + p.GetPackage() + p = nil + p.GetPackage() +} + +func TestPackageEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PackageEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPackageEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PackageEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPackageEventContainerMetadata_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PackageEventContainerMetadata{Labels: zeroValue} + p.GetLabels() + p = &PackageEventContainerMetadata{} + p.GetLabels() + p = nil + p.GetLabels() +} + +func TestPackageEventContainerMetadata_GetManifest(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PackageEventContainerMetadata{Manifest: zeroValue} + p.GetManifest() + p = &PackageEventContainerMetadata{} + p.GetManifest() + p = nil + p.GetManifest() +} + +func TestPackageEventContainerMetadata_GetTag(tt *testing.T) { + tt.Parallel() + p := &PackageEventContainerMetadata{} + p.GetTag() + p = nil + p.GetTag() +} + +func TestPackageEventContainerMetadataTag_GetDigest(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageEventContainerMetadataTag{Digest: &zeroValue} + p.GetDigest() + p = &PackageEventContainerMetadataTag{} + p.GetDigest() + p = nil + p.GetDigest() +} + +func TestPackageEventContainerMetadataTag_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageEventContainerMetadataTag{Name: &zeroValue} + p.GetName() + p = &PackageEventContainerMetadataTag{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageExternalRef_GetReferenceCategory(tt *testing.T) { + tt.Parallel() + p := &PackageExternalRef{} + p.GetReferenceCategory() + p = nil + p.GetReferenceCategory() +} + +func TestPackageExternalRef_GetReferenceLocator(tt *testing.T) { + tt.Parallel() + p := &PackageExternalRef{} + p.GetReferenceLocator() + p = nil + p.GetReferenceLocator() +} + +func TestPackageExternalRef_GetReferenceType(tt *testing.T) { + tt.Parallel() + p := &PackageExternalRef{} + p.GetReferenceType() + p = nil + p.GetReferenceType() +} + +func TestPackageFile_GetAuthor(tt *testing.T) { + tt.Parallel() + p := &PackageFile{} + p.GetAuthor() + p = nil + p.GetAuthor() +} + +func TestPackageFile_GetContentType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{ContentType: &zeroValue} + p.GetContentType() + p = &PackageFile{} + p.GetContentType() + p = nil + p.GetContentType() +} + +func TestPackageFile_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageFile{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PackageFile{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPackageFile_GetDownloadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{DownloadURL: &zeroValue} + p.GetDownloadURL() + p = &PackageFile{} + p.GetDownloadURL() + p = nil + p.GetDownloadURL() +} + +func TestPackageFile_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageFile{ID: &zeroValue} + p.GetID() + p = &PackageFile{} + p.GetID() + p = nil + p.GetID() +} + +func TestPackageFile_GetMD5(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{MD5: &zeroValue} + p.GetMD5() + p = &PackageFile{} + p.GetMD5() + p = nil + p.GetMD5() +} + +func TestPackageFile_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{Name: &zeroValue} + p.GetName() + p = &PackageFile{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageFile_GetSHA1(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{SHA1: &zeroValue} + p.GetSHA1() + p = &PackageFile{} + p.GetSHA1() + p = nil + p.GetSHA1() +} + +func TestPackageFile_GetSHA256(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{SHA256: &zeroValue} + p.GetSHA256() + p = &PackageFile{} + p.GetSHA256() + p = nil + p.GetSHA256() +} + +func TestPackageFile_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageFile{Size: &zeroValue} + p.GetSize() + p = &PackageFile{} + p.GetSize() + p = nil + p.GetSize() +} + +func TestPackageFile_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageFile{State: &zeroValue} + p.GetState() + p = &PackageFile{} + p.GetState() + p = nil + p.GetState() +} + +func TestPackageFile_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageFile{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PackageFile{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPackageListOptions_GetPackageType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageListOptions{PackageType: &zeroValue} + p.GetPackageType() + p = &PackageListOptions{} + p.GetPackageType() + p = nil + p.GetPackageType() +} + +func TestPackageListOptions_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageListOptions{State: &zeroValue} + p.GetState() + p = &PackageListOptions{} + p.GetState() + p = nil + p.GetState() +} + +func TestPackageListOptions_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageListOptions{Visibility: &zeroValue} + p.GetVisibility() + p = &PackageListOptions{} + p.GetVisibility() + p = nil + p.GetVisibility() +} + +func TestPackageMetadata_GetContainer(tt *testing.T) { + tt.Parallel() + p := &PackageMetadata{} + p.GetContainer() + p = nil + p.GetContainer() +} + +func TestPackageMetadata_GetPackageType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageMetadata{PackageType: &zeroValue} + p.GetPackageType() + p = &PackageMetadata{} + p.GetPackageType() + p = nil + p.GetPackageType() +} + +func TestPackageNPMMetadata_GetAuthor(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Author: zeroValue} + p.GetAuthor() + p = &PackageNPMMetadata{} + p.GetAuthor() + p = nil + p.GetAuthor() +} + +func TestPackageNPMMetadata_GetBin(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PackageNPMMetadata{Bin: zeroValue} + p.GetBin() + p = &PackageNPMMetadata{} + p.GetBin() + p = nil + p.GetBin() +} + +func TestPackageNPMMetadata_GetBugs(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Bugs: zeroValue} + p.GetBugs() + p = &PackageNPMMetadata{} + p.GetBugs() + p = nil + p.GetBugs() +} + +func TestPackageNPMMetadata_GetCommitOID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{CommitOID: &zeroValue} + p.GetCommitOID() + p = &PackageNPMMetadata{} + p.GetCommitOID() + p = nil + p.GetCommitOID() +} + +func TestPackageNPMMetadata_GetContributors(tt *testing.T) { + tt.Parallel() + zeroValue := []any{} + p := &PackageNPMMetadata{Contributors: zeroValue} + p.GetContributors() + p = &PackageNPMMetadata{} + p.GetContributors() + p = nil + p.GetContributors() +} + +func TestPackageNPMMetadata_GetCPU(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PackageNPMMetadata{CPU: zeroValue} + p.GetCPU() + p = &PackageNPMMetadata{} + p.GetCPU() + p = nil + p.GetCPU() +} + +func TestPackageNPMMetadata_GetDeletedByID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageNPMMetadata{DeletedByID: &zeroValue} + p.GetDeletedByID() + p = &PackageNPMMetadata{} + p.GetDeletedByID() + p = nil + p.GetDeletedByID() +} + +func TestPackageNPMMetadata_GetDependencies(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Dependencies: zeroValue} + p.GetDependencies() + p = &PackageNPMMetadata{} + p.GetDependencies() + p = nil + p.GetDependencies() +} + +func TestPackageNPMMetadata_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{Description: &zeroValue} + p.GetDescription() + p = &PackageNPMMetadata{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestPackageNPMMetadata_GetDevDependencies(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{DevDependencies: zeroValue} + p.GetDevDependencies() + p = &PackageNPMMetadata{} + p.GetDevDependencies() + p = nil + p.GetDevDependencies() +} + +func TestPackageNPMMetadata_GetDirectories(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Directories: zeroValue} + p.GetDirectories() + p = &PackageNPMMetadata{} + p.GetDirectories() + p = nil + p.GetDirectories() +} + +func TestPackageNPMMetadata_GetDist(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Dist: zeroValue} + p.GetDist() + p = &PackageNPMMetadata{} + p.GetDist() + p = nil + p.GetDist() +} + +func TestPackageNPMMetadata_GetEngines(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Engines: zeroValue} + p.GetEngines() + p = &PackageNPMMetadata{} + p.GetEngines() + p = nil + p.GetEngines() +} + +func TestPackageNPMMetadata_GetFiles(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PackageNPMMetadata{Files: zeroValue} + p.GetFiles() + p = &PackageNPMMetadata{} + p.GetFiles() + p = nil + p.GetFiles() +} + +func TestPackageNPMMetadata_GetGitHead(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{GitHead: &zeroValue} + p.GetGitHead() + p = &PackageNPMMetadata{} + p.GetGitHead() + p = nil + p.GetGitHead() +} + +func TestPackageNPMMetadata_GetHasShrinkwrap(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageNPMMetadata{HasShrinkwrap: &zeroValue} + p.GetHasShrinkwrap() + p = &PackageNPMMetadata{} + p.GetHasShrinkwrap() + p = nil + p.GetHasShrinkwrap() +} + +func TestPackageNPMMetadata_GetHomepage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{Homepage: &zeroValue} + p.GetHomepage() + p = &PackageNPMMetadata{} + p.GetHomepage() + p = nil + p.GetHomepage() +} + +func TestPackageNPMMetadata_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{ID: &zeroValue} + p.GetID() + p = &PackageNPMMetadata{} + p.GetID() + p = nil + p.GetID() +} + +func TestPackageNPMMetadata_GetInstallationCommand(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{InstallationCommand: &zeroValue} + p.GetInstallationCommand() + p = &PackageNPMMetadata{} + p.GetInstallationCommand() + p = nil + p.GetInstallationCommand() +} + +func TestPackageNPMMetadata_GetKeywords(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PackageNPMMetadata{Keywords: zeroValue} + p.GetKeywords() + p = &PackageNPMMetadata{} + p.GetKeywords() + p = nil + p.GetKeywords() +} + +func TestPackageNPMMetadata_GetLicense(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{License: &zeroValue} + p.GetLicense() + p = &PackageNPMMetadata{} + p.GetLicense() + p = nil + p.GetLicense() +} + +func TestPackageNPMMetadata_GetMain(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{Main: &zeroValue} + p.GetMain() + p = &PackageNPMMetadata{} + p.GetMain() + p = nil + p.GetMain() +} + +func TestPackageNPMMetadata_GetMaintainers(tt *testing.T) { + tt.Parallel() + zeroValue := []any{} + p := &PackageNPMMetadata{Maintainers: zeroValue} + p.GetMaintainers() + p = &PackageNPMMetadata{} + p.GetMaintainers() + p = nil + p.GetMaintainers() +} + +func TestPackageNPMMetadata_GetMan(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PackageNPMMetadata{Man: zeroValue} + p.GetMan() + p = &PackageNPMMetadata{} + p.GetMan() + p = nil + p.GetMan() +} + +func TestPackageNPMMetadata_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{Name: &zeroValue} + p.GetName() + p = &PackageNPMMetadata{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageNPMMetadata_GetNodeVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{NodeVersion: &zeroValue} + p.GetNodeVersion() + p = &PackageNPMMetadata{} + p.GetNodeVersion() + p = nil + p.GetNodeVersion() +} + +func TestPackageNPMMetadata_GetNPMUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{NPMUser: &zeroValue} + p.GetNPMUser() + p = &PackageNPMMetadata{} + p.GetNPMUser() + p = nil + p.GetNPMUser() +} + +func TestPackageNPMMetadata_GetNPMVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{NPMVersion: &zeroValue} + p.GetNPMVersion() + p = &PackageNPMMetadata{} + p.GetNPMVersion() + p = nil + p.GetNPMVersion() +} + +func TestPackageNPMMetadata_GetOptionalDependencies(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{OptionalDependencies: zeroValue} + p.GetOptionalDependencies() + p = &PackageNPMMetadata{} + p.GetOptionalDependencies() + p = nil + p.GetOptionalDependencies() +} + +func TestPackageNPMMetadata_GetOS(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PackageNPMMetadata{OS: zeroValue} + p.GetOS() + p = &PackageNPMMetadata{} + p.GetOS() + p = nil + p.GetOS() +} + +func TestPackageNPMMetadata_GetPeerDependencies(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{PeerDependencies: zeroValue} + p.GetPeerDependencies() + p = &PackageNPMMetadata{} + p.GetPeerDependencies() + p = nil + p.GetPeerDependencies() +} + +func TestPackageNPMMetadata_GetPublishedViaActions(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageNPMMetadata{PublishedViaActions: &zeroValue} + p.GetPublishedViaActions() + p = &PackageNPMMetadata{} + p.GetPublishedViaActions() + p = nil + p.GetPublishedViaActions() +} + +func TestPackageNPMMetadata_GetReadme(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{Readme: &zeroValue} + p.GetReadme() + p = &PackageNPMMetadata{} + p.GetReadme() + p = nil + p.GetReadme() +} + +func TestPackageNPMMetadata_GetReleaseID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageNPMMetadata{ReleaseID: &zeroValue} + p.GetReleaseID() + p = &PackageNPMMetadata{} + p.GetReleaseID() + p = nil + p.GetReleaseID() +} + +func TestPackageNPMMetadata_GetRepository(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PackageNPMMetadata{Repository: zeroValue} + p.GetRepository() + p = &PackageNPMMetadata{} + p.GetRepository() + p = nil + p.GetRepository() +} + +func TestPackageNPMMetadata_GetScripts(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PackageNPMMetadata{Scripts: zeroValue} + p.GetScripts() + p = &PackageNPMMetadata{} + p.GetScripts() + p = nil + p.GetScripts() +} + +func TestPackageNPMMetadata_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNPMMetadata{Version: &zeroValue} + p.GetVersion() + p = &PackageNPMMetadata{} + p.GetVersion() + p = nil + p.GetVersion() +} + +func TestPackageNugetMetadata_GetID(tt *testing.T) { + tt.Parallel() + p := &PackageNugetMetadata{} + p.GetID() + p = nil + p.GetID() +} + +func TestPackageNugetMetadata_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageNugetMetadata{Name: &zeroValue} + p.GetName() + p = &PackageNugetMetadata{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageNugetMetadata_GetValue(tt *testing.T) { + tt.Parallel() + p := &PackageNugetMetadata{} + p.GetValue() + p = nil + p.GetValue() +} + +func TestPackageRegistry_GetAboutURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRegistry{AboutURL: &zeroValue} + p.GetAboutURL() + p = &PackageRegistry{} + p.GetAboutURL() + p = nil + p.GetAboutURL() +} + +func TestPackageRegistry_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRegistry{Name: &zeroValue} + p.GetName() + p = &PackageRegistry{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageRegistry_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRegistry{Type: &zeroValue} + p.GetType() + p = &PackageRegistry{} + p.GetType() + p = nil + p.GetType() +} + +func TestPackageRegistry_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRegistry{URL: &zeroValue} + p.GetURL() + p = &PackageRegistry{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPackageRegistry_GetVendor(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRegistry{Vendor: &zeroValue} + p.GetVendor() + p = &PackageRegistry{} + p.GetVendor() + p = nil + p.GetVendor() +} + +func TestPackageRelease_GetAuthor(tt *testing.T) { + tt.Parallel() + p := &PackageRelease{} + p.GetAuthor() + p = nil + p.GetAuthor() +} + +func TestPackageRelease_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageRelease{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PackageRelease{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPackageRelease_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageRelease{Draft: &zeroValue} + p.GetDraft() + p = &PackageRelease{} + p.GetDraft() + p = nil + p.GetDraft() +} + +func TestPackageRelease_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRelease{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PackageRelease{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPackageRelease_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageRelease{ID: &zeroValue} + p.GetID() + p = &PackageRelease{} + p.GetID() + p = nil + p.GetID() +} + +func TestPackageRelease_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRelease{Name: &zeroValue} + p.GetName() + p = &PackageRelease{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageRelease_GetPrerelease(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageRelease{Prerelease: &zeroValue} + p.GetPrerelease() + p = &PackageRelease{} + p.GetPrerelease() + p = nil + p.GetPrerelease() +} + +func TestPackageRelease_GetPublishedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageRelease{PublishedAt: &zeroValue} + p.GetPublishedAt() + p = &PackageRelease{} + p.GetPublishedAt() + p = nil + p.GetPublishedAt() +} + +func TestPackageRelease_GetTagName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRelease{TagName: &zeroValue} + p.GetTagName() + p = &PackageRelease{} + p.GetTagName() + p = nil + p.GetTagName() +} + +func TestPackageRelease_GetTargetCommitish(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRelease{TargetCommitish: &zeroValue} + p.GetTargetCommitish() + p = &PackageRelease{} + p.GetTargetCommitish() + p = nil + p.GetTargetCommitish() +} + +func TestPackageRelease_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageRelease{URL: &zeroValue} + p.GetURL() + p = &PackageRelease{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPackagesBilling_GetIncludedGigabytesBandwidth(tt *testing.T) { + tt.Parallel() + p := &PackagesBilling{} + p.GetIncludedGigabytesBandwidth() + p = nil + p.GetIncludedGigabytesBandwidth() +} + +func TestPackagesBilling_GetTotalGigabytesBandwidthUsed(tt *testing.T) { + tt.Parallel() + p := &PackagesBilling{} + p.GetTotalGigabytesBandwidthUsed() + p = nil + p.GetTotalGigabytesBandwidthUsed() +} + +func TestPackagesBilling_GetTotalPaidGigabytesBandwidthUsed(tt *testing.T) { + tt.Parallel() + p := &PackagesBilling{} + p.GetTotalPaidGigabytesBandwidthUsed() + p = nil + p.GetTotalPaidGigabytesBandwidthUsed() +} + +func TestPackageVersion_GetAuthor(tt *testing.T) { + tt.Parallel() + p := &PackageVersion{} + p.GetAuthor() + p = nil + p.GetAuthor() +} + +func TestPackageVersion_GetBodyHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{BodyHTML: &zeroValue} + p.GetBodyHTML() + p = &PackageVersion{} + p.GetBodyHTML() + p = nil + p.GetBodyHTML() +} + +func TestPackageVersion_GetContainerMetadata(tt *testing.T) { + tt.Parallel() + p := &PackageVersion{} + p.GetContainerMetadata() + p = nil + p.GetContainerMetadata() +} + +func TestPackageVersion_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageVersion{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PackageVersion{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPackageVersion_GetDeletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageVersion{DeletedAt: &zeroValue} + p.GetDeletedAt() + p = &PackageVersion{} + p.GetDeletedAt() + p = nil + p.GetDeletedAt() +} + +func TestPackageVersion_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{Description: &zeroValue} + p.GetDescription() + p = &PackageVersion{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestPackageVersion_GetDockerMetadata(tt *testing.T) { + tt.Parallel() + zeroValue := []any{} + p := &PackageVersion{DockerMetadata: zeroValue} + p.GetDockerMetadata() + p = &PackageVersion{} + p.GetDockerMetadata() + p = nil + p.GetDockerMetadata() +} + +func TestPackageVersion_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageVersion{Draft: &zeroValue} + p.GetDraft() + p = &PackageVersion{} + p.GetDraft() + p = nil + p.GetDraft() +} + +func TestPackageVersion_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PackageVersion{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPackageVersion_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageVersion{ID: &zeroValue} + p.GetID() + p = &PackageVersion{} + p.GetID() + p = nil + p.GetID() +} + +func TestPackageVersion_GetInstallationCommand(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{InstallationCommand: &zeroValue} + p.GetInstallationCommand() + p = &PackageVersion{} + p.GetInstallationCommand() + p = nil + p.GetInstallationCommand() +} + +func TestPackageVersion_GetLicense(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{License: &zeroValue} + p.GetLicense() + p = &PackageVersion{} + p.GetLicense() + p = nil + p.GetLicense() +} + +func TestPackageVersion_GetManifest(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{Manifest: &zeroValue} + p.GetManifest() + p = &PackageVersion{} + p.GetManifest() + p = nil + p.GetManifest() +} + +func TestPackageVersion_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{Name: &zeroValue} + p.GetName() + p = &PackageVersion{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageVersion_GetNPMMetadata(tt *testing.T) { + tt.Parallel() + p := &PackageVersion{} + p.GetNPMMetadata() + p = nil + p.GetNPMMetadata() +} + +func TestPackageVersion_GetNugetMetadata(tt *testing.T) { + tt.Parallel() + zeroValue := []*PackageNugetMetadata{} + p := &PackageVersion{NugetMetadata: zeroValue} + p.GetNugetMetadata() + p = &PackageVersion{} + p.GetNugetMetadata() + p = nil + p.GetNugetMetadata() +} + +func TestPackageVersion_GetPackageFiles(tt *testing.T) { + tt.Parallel() + zeroValue := []*PackageFile{} + p := &PackageVersion{PackageFiles: zeroValue} + p.GetPackageFiles() + p = &PackageVersion{} + p.GetPackageFiles() + p = nil + p.GetPackageFiles() +} + +func TestPackageVersion_GetPackageHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{PackageHTMLURL: &zeroValue} + p.GetPackageHTMLURL() + p = &PackageVersion{} + p.GetPackageHTMLURL() + p = nil + p.GetPackageHTMLURL() +} + +func TestPackageVersion_GetPackageURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{PackageURL: &zeroValue} + p.GetPackageURL() + p = &PackageVersion{} + p.GetPackageURL() + p = nil + p.GetPackageURL() +} + +func TestPackageVersion_GetPrerelease(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageVersion{Prerelease: &zeroValue} + p.GetPrerelease() + p = &PackageVersion{} + p.GetPrerelease() + p = nil + p.GetPrerelease() +} + +func TestPackageVersion_GetRelease(tt *testing.T) { + tt.Parallel() + p := &PackageVersion{} + p.GetRelease() + p = nil + p.GetRelease() +} + +func TestPackageVersion_GetRubyMetadata(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PackageVersion{RubyMetadata: zeroValue} + p.GetRubyMetadata() + p = &PackageVersion{} + p.GetRubyMetadata() + p = nil + p.GetRubyMetadata() +} + +func TestPackageVersion_GetSourceURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{SourceURL: &zeroValue} + p.GetSourceURL() + p = &PackageVersion{} + p.GetSourceURL() + p = nil + p.GetSourceURL() +} + +func TestPackageVersion_GetSummary(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{Summary: &zeroValue} + p.GetSummary() + p = &PackageVersion{} + p.GetSummary() + p = nil + p.GetSummary() +} + +func TestPackageVersion_GetTagName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{TagName: &zeroValue} + p.GetTagName() + p = &PackageVersion{} + p.GetTagName() + p = nil + p.GetTagName() +} + +func TestPackageVersion_GetTargetCommitish(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{TargetCommitish: &zeroValue} + p.GetTargetCommitish() + p = &PackageVersion{} + p.GetTargetCommitish() + p = nil + p.GetTargetCommitish() +} + +func TestPackageVersion_GetTargetOID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{TargetOID: &zeroValue} + p.GetTargetOID() + p = &PackageVersion{} + p.GetTargetOID() + p = nil + p.GetTargetOID() +} + +func TestPackageVersion_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PackageVersion{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PackageVersion{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPackageVersion_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{URL: &zeroValue} + p.GetURL() + p = &PackageVersion{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPackageVersion_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersion{Version: &zeroValue} + p.GetVersion() + p = &PackageVersion{} + p.GetVersion() + p = nil + p.GetVersion() +} + +func TestPackageVersionBody_GetInfo(tt *testing.T) { + tt.Parallel() + p := &PackageVersionBody{} + p.GetInfo() + p = nil + p.GetInfo() +} + +func TestPackageVersionBody_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PackageVersionBody{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPackageVersionBodyInfo_GetCollection(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PackageVersionBodyInfo{Collection: &zeroValue} + p.GetCollection() + p = &PackageVersionBodyInfo{} + p.GetCollection() + p = nil + p.GetCollection() +} + +func TestPackageVersionBodyInfo_GetMode(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageVersionBodyInfo{Mode: &zeroValue} + p.GetMode() + p = &PackageVersionBodyInfo{} + p.GetMode() + p = nil + p.GetMode() +} + +func TestPackageVersionBodyInfo_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersionBodyInfo{Name: &zeroValue} + p.GetName() + p = &PackageVersionBodyInfo{} + p.GetName() + p = nil + p.GetName() +} + +func TestPackageVersionBodyInfo_GetOID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersionBodyInfo{OID: &zeroValue} + p.GetOID() + p = &PackageVersionBodyInfo{} + p.GetOID() + p = nil + p.GetOID() +} + +func TestPackageVersionBodyInfo_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersionBodyInfo{Path: &zeroValue} + p.GetPath() + p = &PackageVersionBodyInfo{} + p.GetPath() + p = nil + p.GetPath() +} + +func TestPackageVersionBodyInfo_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PackageVersionBodyInfo{Size: &zeroValue} + p.GetSize() + p = &PackageVersionBodyInfo{} + p.GetSize() + p = nil + p.GetSize() +} + +func TestPackageVersionBodyInfo_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PackageVersionBodyInfo{Type: &zeroValue} + p.GetType() + p = &PackageVersionBodyInfo{} + p.GetType() + p = nil + p.GetType() +} + +func TestPage_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Page{Action: &zeroValue} + p.GetAction() + p = &Page{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPage_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Page{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &Page{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPage_GetPageName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Page{PageName: &zeroValue} + p.GetPageName() + p = &Page{} + p.GetPageName() + p = nil + p.GetPageName() +} + +func TestPage_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Page{SHA: &zeroValue} + p.GetSHA() + p = &Page{} + p.GetSHA() + p = nil + p.GetSHA() +} + +func TestPage_GetSummary(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Page{Summary: &zeroValue} + p.GetSummary() + p = &Page{} + p.GetSummary() + p = nil + p.GetSummary() +} + +func TestPage_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Page{Title: &zeroValue} + p.GetTitle() + p = &Page{} + p.GetTitle() + p = nil + p.GetTitle() +} + +func TestPageBuildEvent_GetBuild(tt *testing.T) { + tt.Parallel() + p := &PageBuildEvent{} + p.GetBuild() + p = nil + p.GetBuild() +} + +func TestPageBuildEvent_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PageBuildEvent{ID: &zeroValue} + p.GetID() + p = &PageBuildEvent{} + p.GetID() + p = nil + p.GetID() +} + +func TestPageBuildEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PageBuildEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPageBuildEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PageBuildEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPageBuildEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PageBuildEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPageBuildEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PageBuildEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPages_GetBuildType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Pages{BuildType: &zeroValue} + p.GetBuildType() + p = &Pages{} + p.GetBuildType() + p = nil + p.GetBuildType() +} + +func TestPages_GetCNAME(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Pages{CNAME: &zeroValue} + p.GetCNAME() + p = &Pages{} + p.GetCNAME() + p = nil + p.GetCNAME() +} + +func TestPages_GetCustom404(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &Pages{Custom404: &zeroValue} + p.GetCustom404() + p = &Pages{} + p.GetCustom404() + p = nil + p.GetCustom404() +} + +func TestPages_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Pages{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &Pages{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPages_GetHTTPSCertificate(tt *testing.T) { + tt.Parallel() + p := &Pages{} + p.GetHTTPSCertificate() + p = nil + p.GetHTTPSCertificate() +} + +func TestPages_GetHTTPSEnforced(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &Pages{HTTPSEnforced: &zeroValue} + p.GetHTTPSEnforced() + p = &Pages{} + p.GetHTTPSEnforced() + p = nil + p.GetHTTPSEnforced() +} + +func TestPages_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &Pages{Public: &zeroValue} + p.GetPublic() + p = &Pages{} + p.GetPublic() + p = nil + p.GetPublic() +} + +func TestPages_GetSource(tt *testing.T) { + tt.Parallel() + p := &Pages{} + p.GetSource() + p = nil + p.GetSource() +} + +func TestPages_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Pages{Status: &zeroValue} + p.GetStatus() + p = &Pages{} + p.GetStatus() + p = nil + p.GetStatus() +} + +func TestPages_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Pages{URL: &zeroValue} + p.GetURL() + p = &Pages{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPagesBuild_GetCommit(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesBuild{Commit: &zeroValue} + p.GetCommit() + p = &PagesBuild{} + p.GetCommit() + p = nil + p.GetCommit() +} + +func TestPagesBuild_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PagesBuild{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PagesBuild{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPagesBuild_GetDuration(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PagesBuild{Duration: &zeroValue} + p.GetDuration() + p = &PagesBuild{} + p.GetDuration() + p = nil + p.GetDuration() +} + +func TestPagesBuild_GetError(tt *testing.T) { + tt.Parallel() + p := &PagesBuild{} + p.GetError() + p = nil + p.GetError() +} + +func TestPagesBuild_GetPusher(tt *testing.T) { + tt.Parallel() + p := &PagesBuild{} + p.GetPusher() + p = nil + p.GetPusher() +} + +func TestPagesBuild_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesBuild{Status: &zeroValue} + p.GetStatus() + p = &PagesBuild{} + p.GetStatus() + p = nil + p.GetStatus() +} + +func TestPagesBuild_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PagesBuild{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PagesBuild{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPagesBuild_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesBuild{URL: &zeroValue} + p.GetURL() + p = &PagesBuild{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPagesDomain_GetCAAError(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesDomain{CAAError: &zeroValue} + p.GetCAAError() + p = &PagesDomain{} + p.GetCAAError() + p = nil + p.GetCAAError() +} + +func TestPagesDomain_GetDNSResolves(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{DNSResolves: &zeroValue} + p.GetDNSResolves() + p = &PagesDomain{} + p.GetDNSResolves() + p = nil + p.GetDNSResolves() +} + +func TestPagesDomain_GetEnforcesHTTPS(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{EnforcesHTTPS: &zeroValue} + p.GetEnforcesHTTPS() + p = &PagesDomain{} + p.GetEnforcesHTTPS() + p = nil + p.GetEnforcesHTTPS() +} + +func TestPagesDomain_GetHasCNAMERecord(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{HasCNAMERecord: &zeroValue} + p.GetHasCNAMERecord() + p = &PagesDomain{} + p.GetHasCNAMERecord() + p = nil + p.GetHasCNAMERecord() +} + +func TestPagesDomain_GetHasMXRecordsPresent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{HasMXRecordsPresent: &zeroValue} + p.GetHasMXRecordsPresent() + p = &PagesDomain{} + p.GetHasMXRecordsPresent() + p = nil + p.GetHasMXRecordsPresent() +} + +func TestPagesDomain_GetHost(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesDomain{Host: &zeroValue} + p.GetHost() + p = &PagesDomain{} + p.GetHost() + p = nil + p.GetHost() +} + +func TestPagesDomain_GetHTTPSError(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesDomain{HTTPSError: &zeroValue} + p.GetHTTPSError() + p = &PagesDomain{} + p.GetHTTPSError() + p = nil + p.GetHTTPSError() +} + +func TestPagesDomain_GetIsApexDomain(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsApexDomain: &zeroValue} + p.GetIsApexDomain() + p = &PagesDomain{} + p.GetIsApexDomain() + p = nil + p.GetIsApexDomain() +} + +func TestPagesDomain_GetIsARecord(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsARecord: &zeroValue} + p.GetIsARecord() + p = &PagesDomain{} + p.GetIsARecord() + p = nil + p.GetIsARecord() +} + +func TestPagesDomain_GetIsCloudflareIP(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsCloudflareIP: &zeroValue} + p.GetIsCloudflareIP() + p = &PagesDomain{} + p.GetIsCloudflareIP() + p = nil + p.GetIsCloudflareIP() +} + +func TestPagesDomain_GetIsCNAMEToFastly(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsCNAMEToFastly: &zeroValue} + p.GetIsCNAMEToFastly() + p = &PagesDomain{} + p.GetIsCNAMEToFastly() + p = nil + p.GetIsCNAMEToFastly() +} + +func TestPagesDomain_GetIsCNAMEToGithubUserDomain(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsCNAMEToGithubUserDomain: &zeroValue} + p.GetIsCNAMEToGithubUserDomain() + p = &PagesDomain{} + p.GetIsCNAMEToGithubUserDomain() + p = nil + p.GetIsCNAMEToGithubUserDomain() +} + +func TestPagesDomain_GetIsCNAMEToPagesDotGithubDotCom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsCNAMEToPagesDotGithubDotCom: &zeroValue} + p.GetIsCNAMEToPagesDotGithubDotCom() + p = &PagesDomain{} + p.GetIsCNAMEToPagesDotGithubDotCom() + p = nil + p.GetIsCNAMEToPagesDotGithubDotCom() +} + +func TestPagesDomain_GetIsFastlyIP(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsFastlyIP: &zeroValue} + p.GetIsFastlyIP() + p = &PagesDomain{} + p.GetIsFastlyIP() + p = nil + p.GetIsFastlyIP() +} + +func TestPagesDomain_GetIsHTTPSEligible(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsHTTPSEligible: &zeroValue} + p.GetIsHTTPSEligible() + p = &PagesDomain{} + p.GetIsHTTPSEligible() + p = nil + p.GetIsHTTPSEligible() +} + +func TestPagesDomain_GetIsNonGithubPagesIPPresent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsNonGithubPagesIPPresent: &zeroValue} + p.GetIsNonGithubPagesIPPresent() + p = &PagesDomain{} + p.GetIsNonGithubPagesIPPresent() + p = nil + p.GetIsNonGithubPagesIPPresent() +} + +func TestPagesDomain_GetIsOldIPAddress(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsOldIPAddress: &zeroValue} + p.GetIsOldIPAddress() + p = &PagesDomain{} + p.GetIsOldIPAddress() + p = nil + p.GetIsOldIPAddress() +} + +func TestPagesDomain_GetIsPagesDomain(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsPagesDomain: &zeroValue} + p.GetIsPagesDomain() + p = &PagesDomain{} + p.GetIsPagesDomain() + p = nil + p.GetIsPagesDomain() +} + +func TestPagesDomain_GetIsPointedToGithubPagesIP(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsPointedToGithubPagesIP: &zeroValue} + p.GetIsPointedToGithubPagesIP() + p = &PagesDomain{} + p.GetIsPointedToGithubPagesIP() + p = nil + p.GetIsPointedToGithubPagesIP() +} + +func TestPagesDomain_GetIsProxied(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsProxied: &zeroValue} + p.GetIsProxied() + p = &PagesDomain{} + p.GetIsProxied() + p = nil + p.GetIsProxied() +} + +func TestPagesDomain_GetIsServedByPages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsServedByPages: &zeroValue} + p.GetIsServedByPages() + p = &PagesDomain{} + p.GetIsServedByPages() + p = nil + p.GetIsServedByPages() +} + +func TestPagesDomain_GetIsValid(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsValid: &zeroValue} + p.GetIsValid() + p = &PagesDomain{} + p.GetIsValid() + p = nil + p.GetIsValid() +} + +func TestPagesDomain_GetIsValidDomain(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{IsValidDomain: &zeroValue} + p.GetIsValidDomain() + p = &PagesDomain{} + p.GetIsValidDomain() + p = nil + p.GetIsValidDomain() +} + +func TestPagesDomain_GetNameservers(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesDomain{Nameservers: &zeroValue} + p.GetNameservers() + p = &PagesDomain{} + p.GetNameservers() + p = nil + p.GetNameservers() +} + +func TestPagesDomain_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesDomain{Reason: &zeroValue} + p.GetReason() + p = &PagesDomain{} + p.GetReason() + p = nil + p.GetReason() +} + +func TestPagesDomain_GetRespondsToHTTPS(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{RespondsToHTTPS: &zeroValue} + p.GetRespondsToHTTPS() + p = &PagesDomain{} + p.GetRespondsToHTTPS() + p = nil + p.GetRespondsToHTTPS() +} + +func TestPagesDomain_GetShouldBeARecord(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesDomain{ShouldBeARecord: &zeroValue} + p.GetShouldBeARecord() + p = &PagesDomain{} + p.GetShouldBeARecord() + p = nil + p.GetShouldBeARecord() +} + +func TestPagesDomain_GetURI(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesDomain{URI: &zeroValue} + p.GetURI() + p = &PagesDomain{} + p.GetURI() + p = nil + p.GetURI() +} + +func TestPagesError_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesError{Message: &zeroValue} + p.GetMessage() + p = &PagesError{} + p.GetMessage() + p = nil + p.GetMessage() +} + +func TestPagesHealthCheckResponse_GetAltDomain(tt *testing.T) { + tt.Parallel() + p := &PagesHealthCheckResponse{} + p.GetAltDomain() + p = nil + p.GetAltDomain() +} + +func TestPagesHealthCheckResponse_GetDomain(tt *testing.T) { + tt.Parallel() + p := &PagesHealthCheckResponse{} + p.GetDomain() + p = nil + p.GetDomain() +} + +func TestPagesHTTPSCertificate_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesHTTPSCertificate{Description: &zeroValue} + p.GetDescription() + p = &PagesHTTPSCertificate{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestPagesHTTPSCertificate_GetDomains(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PagesHTTPSCertificate{Domains: zeroValue} + p.GetDomains() + p = &PagesHTTPSCertificate{} + p.GetDomains() + p = nil + p.GetDomains() +} + +func TestPagesHTTPSCertificate_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesHTTPSCertificate{ExpiresAt: &zeroValue} + p.GetExpiresAt() + p = &PagesHTTPSCertificate{} + p.GetExpiresAt() + p = nil + p.GetExpiresAt() +} + +func TestPagesHTTPSCertificate_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesHTTPSCertificate{State: &zeroValue} + p.GetState() + p = &PagesHTTPSCertificate{} + p.GetState() + p = nil + p.GetState() +} + +func TestPagesSource_GetBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesSource{Branch: &zeroValue} + p.GetBranch() + p = &PagesSource{} + p.GetBranch() + p = nil + p.GetBranch() +} + +func TestPagesSource_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesSource{Path: &zeroValue} + p.GetPath() + p = &PagesSource{} + p.GetPath() + p = nil + p.GetPath() +} + +func TestPageStats_GetTotalPages(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PageStats{TotalPages: &zeroValue} + p.GetTotalPages() + p = &PageStats{} + p.GetTotalPages() + p = nil + p.GetTotalPages() +} + +func TestPagesUpdate_GetBuildType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesUpdate{BuildType: &zeroValue} + p.GetBuildType() + p = &PagesUpdate{} + p.GetBuildType() + p = nil + p.GetBuildType() +} + +func TestPagesUpdate_GetCNAME(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesUpdate{CNAME: &zeroValue} + p.GetCNAME() + p = &PagesUpdate{} + p.GetCNAME() + p = nil + p.GetCNAME() +} + +func TestPagesUpdate_GetHTTPSEnforced(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesUpdate{HTTPSEnforced: &zeroValue} + p.GetHTTPSEnforced() + p = &PagesUpdate{} + p.GetHTTPSEnforced() + p = nil + p.GetHTTPSEnforced() +} + +func TestPagesUpdate_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesUpdate{Public: &zeroValue} + p.GetPublic() + p = &PagesUpdate{} + p.GetPublic() + p = nil + p.GetPublic() +} + +func TestPagesUpdate_GetSource(tt *testing.T) { + tt.Parallel() + p := &PagesUpdate{} + p.GetSource() + p = nil + p.GetSource() +} + +func TestPagesUpdateWithoutCNAME_GetBuildType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PagesUpdateWithoutCNAME{BuildType: &zeroValue} + p.GetBuildType() + p = &PagesUpdateWithoutCNAME{} + p.GetBuildType() + p = nil + p.GetBuildType() +} + +func TestPagesUpdateWithoutCNAME_GetHTTPSEnforced(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesUpdateWithoutCNAME{HTTPSEnforced: &zeroValue} + p.GetHTTPSEnforced() + p = &PagesUpdateWithoutCNAME{} + p.GetHTTPSEnforced() + p = nil + p.GetHTTPSEnforced() +} + +func TestPagesUpdateWithoutCNAME_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PagesUpdateWithoutCNAME{Public: &zeroValue} + p.GetPublic() + p = &PagesUpdateWithoutCNAME{} + p.GetPublic() + p = nil + p.GetPublic() +} + +func TestPagesUpdateWithoutCNAME_GetSource(tt *testing.T) { + tt.Parallel() + p := &PagesUpdateWithoutCNAME{} + p.GetSource() + p = nil + p.GetSource() +} + +func TestPatternBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + p := &PatternBranchRule{} + p.GetParameters() + p = nil + p.GetParameters() +} + +func TestPatternRuleParameters_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PatternRuleParameters{Name: &zeroValue} + p.GetName() + p = &PatternRuleParameters{} + p.GetName() + p = nil + p.GetName() +} + +func TestPatternRuleParameters_GetNegate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PatternRuleParameters{Negate: &zeroValue} + p.GetNegate() + p = &PatternRuleParameters{} + p.GetNegate() + p = nil + p.GetNegate() +} + +func TestPatternRuleParameters_GetOperator(tt *testing.T) { + tt.Parallel() + p := &PatternRuleParameters{} + p.GetOperator() + p = nil + p.GetOperator() +} + +func TestPatternRuleParameters_GetPattern(tt *testing.T) { + tt.Parallel() + p := &PatternRuleParameters{} + p.GetPattern() + p = nil + p.GetPattern() +} + +func TestPendingDeployment_GetCurrentUserCanApprove(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PendingDeployment{CurrentUserCanApprove: &zeroValue} + p.GetCurrentUserCanApprove() + p = &PendingDeployment{} + p.GetCurrentUserCanApprove() + p = nil + p.GetCurrentUserCanApprove() +} + +func TestPendingDeployment_GetEnvironment(tt *testing.T) { + tt.Parallel() + p := &PendingDeployment{} + p.GetEnvironment() + p = nil + p.GetEnvironment() +} + +func TestPendingDeployment_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*RequiredReviewer{} + p := &PendingDeployment{Reviewers: zeroValue} + p.GetReviewers() + p = &PendingDeployment{} + p.GetReviewers() + p = nil + p.GetReviewers() +} + +func TestPendingDeployment_GetWaitTimer(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PendingDeployment{WaitTimer: &zeroValue} + p.GetWaitTimer() + p = &PendingDeployment{} + p.GetWaitTimer() + p = nil + p.GetWaitTimer() +} + +func TestPendingDeployment_GetWaitTimerStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PendingDeployment{WaitTimerStartedAt: &zeroValue} + p.GetWaitTimerStartedAt() + p = &PendingDeployment{} + p.GetWaitTimerStartedAt() + p = nil + p.GetWaitTimerStartedAt() +} + +func TestPendingDeploymentEnvironment_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PendingDeploymentEnvironment{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PendingDeploymentEnvironment{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPendingDeploymentEnvironment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PendingDeploymentEnvironment{ID: &zeroValue} + p.GetID() + p = &PendingDeploymentEnvironment{} + p.GetID() + p = nil + p.GetID() +} + +func TestPendingDeploymentEnvironment_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PendingDeploymentEnvironment{Name: &zeroValue} + p.GetName() + p = &PendingDeploymentEnvironment{} + p.GetName() + p = nil + p.GetName() +} + +func TestPendingDeploymentEnvironment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PendingDeploymentEnvironment{NodeID: &zeroValue} + p.GetNodeID() + p = &PendingDeploymentEnvironment{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPendingDeploymentEnvironment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PendingDeploymentEnvironment{URL: &zeroValue} + p.GetURL() + p = &PendingDeploymentEnvironment{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPendingDeploymentsRequest_GetComment(tt *testing.T) { + tt.Parallel() + p := &PendingDeploymentsRequest{} + p.GetComment() + p = nil + p.GetComment() +} + +func TestPendingDeploymentsRequest_GetEnvironmentIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + p := &PendingDeploymentsRequest{EnvironmentIDs: zeroValue} + p.GetEnvironmentIDs() + p = &PendingDeploymentsRequest{} + p.GetEnvironmentIDs() + p = nil + p.GetEnvironmentIDs() +} + +func TestPendingDeploymentsRequest_GetState(tt *testing.T) { + tt.Parallel() + p := &PendingDeploymentsRequest{} + p.GetState() + p = nil + p.GetState() +} + +func TestPersonalAccessToken_GetAccessGrantedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PersonalAccessToken{AccessGrantedAt: &zeroValue} + p.GetAccessGrantedAt() + p = &PersonalAccessToken{} + p.GetAccessGrantedAt() + p = nil + p.GetAccessGrantedAt() +} + +func TestPersonalAccessToken_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PersonalAccessToken{ID: &zeroValue} + p.GetID() + p = &PersonalAccessToken{} + p.GetID() + p = nil + p.GetID() +} + +func TestPersonalAccessToken_GetOwner(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessToken{} + p.GetOwner() + p = nil + p.GetOwner() +} + +func TestPersonalAccessToken_GetPermissions(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessToken{} + p.GetPermissions() + p = nil + p.GetPermissions() +} + +func TestPersonalAccessToken_GetRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PersonalAccessToken{RepositoriesURL: &zeroValue} + p.GetRepositoriesURL() + p = &PersonalAccessToken{} + p.GetRepositoriesURL() + p = nil + p.GetRepositoriesURL() +} + +func TestPersonalAccessToken_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PersonalAccessToken{RepositorySelection: &zeroValue} + p.GetRepositorySelection() + p = &PersonalAccessToken{} + p.GetRepositorySelection() + p = nil + p.GetRepositorySelection() +} + +func TestPersonalAccessToken_GetTokenExpired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PersonalAccessToken{TokenExpired: &zeroValue} + p.GetTokenExpired() + p = &PersonalAccessToken{} + p.GetTokenExpired() + p = nil + p.GetTokenExpired() +} + +func TestPersonalAccessToken_GetTokenExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PersonalAccessToken{TokenExpiresAt: &zeroValue} + p.GetTokenExpiresAt() + p = &PersonalAccessToken{} + p.GetTokenExpiresAt() + p = nil + p.GetTokenExpiresAt() +} + +func TestPersonalAccessToken_GetTokenID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PersonalAccessToken{TokenID: &zeroValue} + p.GetTokenID() + p = &PersonalAccessToken{} + p.GetTokenID() + p = nil + p.GetTokenID() +} + +func TestPersonalAccessToken_GetTokenLastUsedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PersonalAccessToken{TokenLastUsedAt: &zeroValue} + p.GetTokenLastUsedAt() + p = &PersonalAccessToken{} + p.GetTokenLastUsedAt() + p = nil + p.GetTokenLastUsedAt() +} + +func TestPersonalAccessToken_GetTokenName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PersonalAccessToken{TokenName: &zeroValue} + p.GetTokenName() + p = &PersonalAccessToken{} + p.GetTokenName() + p = nil + p.GetTokenName() +} + +func TestPersonalAccessTokenPermissions_GetOrg(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PersonalAccessTokenPermissions{Org: zeroValue} + p.GetOrg() + p = &PersonalAccessTokenPermissions{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPersonalAccessTokenPermissions_GetOther(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PersonalAccessTokenPermissions{Other: zeroValue} + p.GetOther() + p = &PersonalAccessTokenPermissions{} + p.GetOther() + p = nil + p.GetOther() +} + +func TestPersonalAccessTokenPermissions_GetRepo(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]string{} + p := &PersonalAccessTokenPermissions{Repo: zeroValue} + p.GetRepo() + p = &PersonalAccessTokenPermissions{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPersonalAccessTokenRequest_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PersonalAccessTokenRequest{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PersonalAccessTokenRequest{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPersonalAccessTokenRequest_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PersonalAccessTokenRequest{ID: &zeroValue} + p.GetID() + p = &PersonalAccessTokenRequest{} + p.GetID() + p = nil + p.GetID() +} + +func TestPersonalAccessTokenRequest_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequest{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPersonalAccessTokenRequest_GetOwner(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequest{} + p.GetOwner() + p = nil + p.GetOwner() +} + +func TestPersonalAccessTokenRequest_GetPermissionsAdded(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequest{} + p.GetPermissionsAdded() + p = nil + p.GetPermissionsAdded() +} + +func TestPersonalAccessTokenRequest_GetPermissionsResult(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequest{} + p.GetPermissionsResult() + p = nil + p.GetPermissionsResult() +} + +func TestPersonalAccessTokenRequest_GetPermissionsUpgraded(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequest{} + p.GetPermissionsUpgraded() + p = nil + p.GetPermissionsUpgraded() +} + +func TestPersonalAccessTokenRequest_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + p := &PersonalAccessTokenRequest{Repositories: zeroValue} + p.GetRepositories() + p = &PersonalAccessTokenRequest{} + p.GetRepositories() + p = nil + p.GetRepositories() +} + +func TestPersonalAccessTokenRequest_GetRepositoryCount(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PersonalAccessTokenRequest{RepositoryCount: &zeroValue} + p.GetRepositoryCount() + p = &PersonalAccessTokenRequest{} + p.GetRepositoryCount() + p = nil + p.GetRepositoryCount() +} + +func TestPersonalAccessTokenRequest_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PersonalAccessTokenRequest{RepositorySelection: &zeroValue} + p.GetRepositorySelection() + p = &PersonalAccessTokenRequest{} + p.GetRepositorySelection() + p = nil + p.GetRepositorySelection() +} + +func TestPersonalAccessTokenRequest_GetTokenExpired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PersonalAccessTokenRequest{TokenExpired: &zeroValue} + p.GetTokenExpired() + p = &PersonalAccessTokenRequest{} + p.GetTokenExpired() + p = nil + p.GetTokenExpired() +} + +func TestPersonalAccessTokenRequest_GetTokenExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PersonalAccessTokenRequest{TokenExpiresAt: &zeroValue} + p.GetTokenExpiresAt() + p = &PersonalAccessTokenRequest{} + p.GetTokenExpiresAt() + p = nil + p.GetTokenExpiresAt() +} + +func TestPersonalAccessTokenRequest_GetTokenLastUsedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PersonalAccessTokenRequest{TokenLastUsedAt: &zeroValue} + p.GetTokenLastUsedAt() + p = &PersonalAccessTokenRequest{} + p.GetTokenLastUsedAt() + p = nil + p.GetTokenLastUsedAt() +} + +func TestPersonalAccessTokenRequestEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PersonalAccessTokenRequestEvent{Action: &zeroValue} + p.GetAction() + p = &PersonalAccessTokenRequestEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPersonalAccessTokenRequestEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequestEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPersonalAccessTokenRequestEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequestEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPersonalAccessTokenRequestEvent_GetPersonalAccessTokenRequest(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequestEvent{} + p.GetPersonalAccessTokenRequest() + p = nil + p.GetPersonalAccessTokenRequest() +} + +func TestPersonalAccessTokenRequestEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PersonalAccessTokenRequestEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPingEvent_GetHook(tt *testing.T) { + tt.Parallel() + p := &PingEvent{} + p.GetHook() + p = nil + p.GetHook() +} + +func TestPingEvent_GetHookID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PingEvent{HookID: &zeroValue} + p.GetHookID() + p = &PingEvent{} + p.GetHookID() + p = nil + p.GetHookID() +} + +func TestPingEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PingEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPingEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PingEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPingEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PingEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPingEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PingEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPingEvent_GetZen(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PingEvent{Zen: &zeroValue} + p.GetZen() + p = &PingEvent{} + p.GetZen() + p = nil + p.GetZen() +} + +func TestPlan_GetCollaborators(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &Plan{Collaborators: &zeroValue} + p.GetCollaborators() + p = &Plan{} + p.GetCollaborators() + p = nil + p.GetCollaborators() +} + +func TestPlan_GetFilledSeats(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &Plan{FilledSeats: &zeroValue} + p.GetFilledSeats() + p = &Plan{} + p.GetFilledSeats() + p = nil + p.GetFilledSeats() +} + +func TestPlan_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Plan{Name: &zeroValue} + p.GetName() + p = &Plan{} + p.GetName() + p = nil + p.GetName() +} + +func TestPlan_GetPrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &Plan{PrivateRepos: &zeroValue} + p.GetPrivateRepos() + p = &Plan{} + p.GetPrivateRepos() + p = nil + p.GetPrivateRepos() +} + +func TestPlan_GetSeats(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &Plan{Seats: &zeroValue} + p.GetSeats() + p = &Plan{} + p.GetSeats() + p = nil + p.GetSeats() +} + +func TestPlan_GetSpace(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &Plan{Space: &zeroValue} + p.GetSpace() + p = &Plan{} + p.GetSpace() + p = nil + p.GetSpace() +} + +func TestPreferenceList_GetAutoTriggerChecks(tt *testing.T) { + tt.Parallel() + zeroValue := []*AutoTriggerCheck{} + p := &PreferenceList{AutoTriggerChecks: zeroValue} + p.GetAutoTriggerChecks() + p = &PreferenceList{} + p.GetAutoTriggerChecks() + p = nil + p.GetAutoTriggerChecks() +} + +func TestPremiumRequestUsageItem_GetDiscountAmount(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetDiscountAmount() + p = nil + p.GetDiscountAmount() +} + +func TestPremiumRequestUsageItem_GetDiscountQuantity(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetDiscountQuantity() + p = nil + p.GetDiscountQuantity() +} + +func TestPremiumRequestUsageItem_GetGrossAmount(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetGrossAmount() + p = nil + p.GetGrossAmount() +} + +func TestPremiumRequestUsageItem_GetGrossQuantity(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetGrossQuantity() + p = nil + p.GetGrossQuantity() +} + +func TestPremiumRequestUsageItem_GetModel(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetModel() + p = nil + p.GetModel() +} + +func TestPremiumRequestUsageItem_GetNetAmount(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetNetAmount() + p = nil + p.GetNetAmount() +} + +func TestPremiumRequestUsageItem_GetNetQuantity(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetNetQuantity() + p = nil + p.GetNetQuantity() +} + +func TestPremiumRequestUsageItem_GetPricePerUnit(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetPricePerUnit() + p = nil + p.GetPricePerUnit() +} + +func TestPremiumRequestUsageItem_GetProduct(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetProduct() + p = nil + p.GetProduct() +} + +func TestPremiumRequestUsageItem_GetSKU(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetSKU() + p = nil + p.GetSKU() +} + +func TestPremiumRequestUsageItem_GetUnitType(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageItem{} + p.GetUnitType() + p = nil + p.GetUnitType() +} + +func TestPremiumRequestUsageReport_GetModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{Model: &zeroValue} + p.GetModel() + p = &PremiumRequestUsageReport{} + p.GetModel() + p = nil + p.GetModel() +} + +func TestPremiumRequestUsageReport_GetOrganization(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{Organization: &zeroValue} + p.GetOrganization() + p = &PremiumRequestUsageReport{} + p.GetOrganization() + p = nil + p.GetOrganization() +} + +func TestPremiumRequestUsageReport_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{Product: &zeroValue} + p.GetProduct() + p = &PremiumRequestUsageReport{} + p.GetProduct() + p = nil + p.GetProduct() +} + +func TestPremiumRequestUsageReport_GetTimePeriod(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageReport{} + p.GetTimePeriod() + p = nil + p.GetTimePeriod() +} + +func TestPremiumRequestUsageReport_GetUsageItems(tt *testing.T) { + tt.Parallel() + zeroValue := []*PremiumRequestUsageItem{} + p := &PremiumRequestUsageReport{UsageItems: zeroValue} + p.GetUsageItems() + p = &PremiumRequestUsageReport{} + p.GetUsageItems() + p = nil + p.GetUsageItems() +} + +func TestPremiumRequestUsageReport_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReport{User: &zeroValue} + p.GetUser() + p = &PremiumRequestUsageReport{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPremiumRequestUsageReportOptions_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageReportOptions{Day: &zeroValue} + p.GetDay() + p = &PremiumRequestUsageReportOptions{} + p.GetDay() + p = nil + p.GetDay() +} + +func TestPremiumRequestUsageReportOptions_GetModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReportOptions{Model: &zeroValue} + p.GetModel() + p = &PremiumRequestUsageReportOptions{} + p.GetModel() + p = nil + p.GetModel() +} + +func TestPremiumRequestUsageReportOptions_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageReportOptions{Month: &zeroValue} + p.GetMonth() + p = &PremiumRequestUsageReportOptions{} + p.GetMonth() + p = nil + p.GetMonth() +} + +func TestPremiumRequestUsageReportOptions_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReportOptions{Product: &zeroValue} + p.GetProduct() + p = &PremiumRequestUsageReportOptions{} + p.GetProduct() + p = nil + p.GetProduct() +} + +func TestPremiumRequestUsageReportOptions_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PremiumRequestUsageReportOptions{User: &zeroValue} + p.GetUser() + p = &PremiumRequestUsageReportOptions{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPremiumRequestUsageReportOptions_GetYear(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageReportOptions{Year: &zeroValue} + p.GetYear() + p = &PremiumRequestUsageReportOptions{} + p.GetYear() + p = nil + p.GetYear() +} + +func TestPremiumRequestUsageTimePeriod_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageTimePeriod{Day: &zeroValue} + p.GetDay() + p = &PremiumRequestUsageTimePeriod{} + p.GetDay() + p = nil + p.GetDay() +} + +func TestPremiumRequestUsageTimePeriod_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PremiumRequestUsageTimePeriod{Month: &zeroValue} + p.GetMonth() + p = &PremiumRequestUsageTimePeriod{} + p.GetMonth() + p = nil + p.GetMonth() +} + +func TestPremiumRequestUsageTimePeriod_GetYear(tt *testing.T) { + tt.Parallel() + p := &PremiumRequestUsageTimePeriod{} + p.GetYear() + p = nil + p.GetYear() +} + +func TestPreReceiveHook_GetConfigURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PreReceiveHook{ConfigURL: &zeroValue} + p.GetConfigURL() + p = &PreReceiveHook{} + p.GetConfigURL() + p = nil + p.GetConfigURL() +} + +func TestPreReceiveHook_GetEnforcement(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PreReceiveHook{Enforcement: &zeroValue} + p.GetEnforcement() + p = &PreReceiveHook{} + p.GetEnforcement() + p = nil + p.GetEnforcement() +} + +func TestPreReceiveHook_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PreReceiveHook{ID: &zeroValue} + p.GetID() + p = &PreReceiveHook{} + p.GetID() + p = nil + p.GetID() +} + +func TestPreReceiveHook_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PreReceiveHook{Name: &zeroValue} + p.GetName() + p = &PreReceiveHook{} + p.GetName() + p = nil + p.GetName() +} + +func TestPrivateRegistries_GetConfigurations(tt *testing.T) { + tt.Parallel() + zeroValue := []*PrivateRegistry{} + p := &PrivateRegistries{Configurations: zeroValue} + p.GetConfigurations() + p = &PrivateRegistries{} + p.GetConfigurations() + p = nil + p.GetConfigurations() +} + +func TestPrivateRegistries_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PrivateRegistries{TotalCount: &zeroValue} + p.GetTotalCount() + p = &PrivateRegistries{} + p.GetTotalCount() + p = nil + p.GetTotalCount() +} + +func TestPrivateRegistry_GetAccountID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{AccountID: &zeroValue} + p.GetAccountID() + p = &PrivateRegistry{} + p.GetAccountID() + p = nil + p.GetAccountID() +} + +func TestPrivateRegistry_GetAudience(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{Audience: &zeroValue} + p.GetAudience() + p = &PrivateRegistry{} + p.GetAudience() + p = nil + p.GetAudience() +} + +func TestPrivateRegistry_GetAuthType(tt *testing.T) { + tt.Parallel() + p := &PrivateRegistry{} + p.GetAuthType() + p = nil + p.GetAuthType() +} + +func TestPrivateRegistry_GetAWSRegion(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{AWSRegion: &zeroValue} + p.GetAWSRegion() + p = &PrivateRegistry{} + p.GetAWSRegion() + p = nil + p.GetAWSRegion() +} + +func TestPrivateRegistry_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{ClientID: &zeroValue} + p.GetClientID() + p = &PrivateRegistry{} + p.GetClientID() + p = nil + p.GetClientID() +} + +func TestPrivateRegistry_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PrivateRegistry{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PrivateRegistry{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPrivateRegistry_GetDomain(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{Domain: &zeroValue} + p.GetDomain() + p = &PrivateRegistry{} + p.GetDomain() + p = nil + p.GetDomain() +} + +func TestPrivateRegistry_GetDomainOwner(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{DomainOwner: &zeroValue} + p.GetDomainOwner() + p = &PrivateRegistry{} + p.GetDomainOwner() + p = nil + p.GetDomainOwner() +} + +func TestPrivateRegistry_GetIdentityMappingName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{IdentityMappingName: &zeroValue} + p.GetIdentityMappingName() + p = &PrivateRegistry{} + p.GetIdentityMappingName() + p = nil + p.GetIdentityMappingName() +} + +func TestPrivateRegistry_GetJFrogOIDCProviderName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{JFrogOIDCProviderName: &zeroValue} + p.GetJFrogOIDCProviderName() + p = &PrivateRegistry{} + p.GetJFrogOIDCProviderName() + p = nil + p.GetJFrogOIDCProviderName() +} + +func TestPrivateRegistry_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{Name: &zeroValue} + p.GetName() + p = &PrivateRegistry{} + p.GetName() + p = nil + p.GetName() +} + +func TestPrivateRegistry_GetRegistryType(tt *testing.T) { + tt.Parallel() + p := &PrivateRegistry{} + p.GetRegistryType() + p = nil + p.GetRegistryType() +} + +func TestPrivateRegistry_GetReplacesBase(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PrivateRegistry{ReplacesBase: &zeroValue} + p.GetReplacesBase() + p = &PrivateRegistry{} + p.GetReplacesBase() + p = nil + p.GetReplacesBase() +} + +func TestPrivateRegistry_GetRoleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{RoleName: &zeroValue} + p.GetRoleName() + p = &PrivateRegistry{} + p.GetRoleName() + p = nil + p.GetRoleName() +} + +func TestPrivateRegistry_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + p := &PrivateRegistry{SelectedRepositoryIDs: zeroValue} + p.GetSelectedRepositoryIDs() + p = &PrivateRegistry{} + p.GetSelectedRepositoryIDs() + p = nil + p.GetSelectedRepositoryIDs() +} + +func TestPrivateRegistry_GetTenantID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{TenantID: &zeroValue} + p.GetTenantID() + p = &PrivateRegistry{} + p.GetTenantID() + p = nil + p.GetTenantID() +} + +func TestPrivateRegistry_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PrivateRegistry{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PrivateRegistry{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPrivateRegistry_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{URL: &zeroValue} + p.GetURL() + p = &PrivateRegistry{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPrivateRegistry_GetUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PrivateRegistry{Username: &zeroValue} + p.GetUsername() + p = &PrivateRegistry{} + p.GetUsername() + p = nil + p.GetUsername() +} + +func TestPrivateRegistry_GetVisibility(tt *testing.T) { + tt.Parallel() + p := &PrivateRegistry{} + p.GetVisibility() + p = nil + p.GetVisibility() +} + +func TestPRLink_GetHRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PRLink{HRef: &zeroValue} + p.GetHRef() + p = &PRLink{} + p.GetHRef() + p = nil + p.GetHRef() +} + +func TestPRLinks_GetComments(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetComments() + p = nil + p.GetComments() +} + +func TestPRLinks_GetCommits(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetCommits() + p = nil + p.GetCommits() +} + +func TestPRLinks_GetHTML(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetHTML() + p = nil + p.GetHTML() +} + +func TestPRLinks_GetIssue(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetIssue() + p = nil + p.GetIssue() +} + +func TestPRLinks_GetReviewComment(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetReviewComment() + p = nil + p.GetReviewComment() +} + +func TestPRLinks_GetReviewComments(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetReviewComments() + p = nil + p.GetReviewComments() +} + +func TestPRLinks_GetSelf(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetSelf() + p = nil + p.GetSelf() +} + +func TestPRLinks_GetStatuses(tt *testing.T) { + tt.Parallel() + p := &PRLinks{} + p.GetStatuses() + p = nil + p.GetStatuses() +} + +func TestProjectBody_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectBody{From: &zeroValue} + p.GetFrom() + p = &ProjectBody{} + p.GetFrom() + p = nil + p.GetFrom() +} + +func TestProjectCardChange_GetNote(tt *testing.T) { + tt.Parallel() + p := &ProjectCardChange{} + p.GetNote() + p = nil + p.GetNote() +} + +func TestProjectCardNote_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectCardNote{From: &zeroValue} + p.GetFrom() + p = &ProjectCardNote{} + p.GetFrom() + p = nil + p.GetFrom() +} + +func TestProjectChange_GetBody(tt *testing.T) { + tt.Parallel() + p := &ProjectChange{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestProjectChange_GetName(tt *testing.T) { + tt.Parallel() + p := &ProjectChange{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectColumnChange_GetName(tt *testing.T) { + tt.Parallel() + p := &ProjectColumnChange{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectColumnName_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectColumnName{From: &zeroValue} + p.GetFrom() + p = &ProjectColumnName{} + p.GetFrom() + p = nil + p.GetFrom() +} + +func TestProjectName_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectName{From: &zeroValue} + p.GetFrom() + p = &ProjectName{} + p.GetFrom() + p = nil + p.GetFrom() +} + +func TestProjectV2_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{Body: &zeroValue} + p.GetBody() + p = &ProjectV2{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestProjectV2_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2{ClosedAt: &zeroValue} + p.GetClosedAt() + p = &ProjectV2{} + p.GetClosedAt() + p = nil + p.GetClosedAt() +} + +func TestProjectV2_GetColumnsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{ColumnsURL: &zeroValue} + p.GetColumnsURL() + p = &ProjectV2{} + p.GetColumnsURL() + p = nil + p.GetColumnsURL() +} + +func TestProjectV2_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &ProjectV2{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestProjectV2_GetCreator(tt *testing.T) { + tt.Parallel() + p := &ProjectV2{} + p.GetCreator() + p = nil + p.GetCreator() +} + +func TestProjectV2_GetDeletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2{DeletedAt: &zeroValue} + p.GetDeletedAt() + p = &ProjectV2{} + p.GetDeletedAt() + p = nil + p.GetDeletedAt() +} + +func TestProjectV2_GetDeletedBy(tt *testing.T) { + tt.Parallel() + p := &ProjectV2{} + p.GetDeletedBy() + p = nil + p.GetDeletedBy() +} + +func TestProjectV2_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{Description: &zeroValue} + p.GetDescription() + p = &ProjectV2{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestProjectV2_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &ProjectV2{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestProjectV2_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2{ID: &zeroValue} + p.GetID() + p = &ProjectV2{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2_GetIsTemplate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProjectV2{IsTemplate: &zeroValue} + p.GetIsTemplate() + p = &ProjectV2{} + p.GetIsTemplate() + p = nil + p.GetIsTemplate() +} + +func TestProjectV2_GetLatestStatusUpdate(tt *testing.T) { + tt.Parallel() + p := &ProjectV2{} + p.GetLatestStatusUpdate() + p = nil + p.GetLatestStatusUpdate() +} + +func TestProjectV2_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{Name: &zeroValue} + p.GetName() + p = &ProjectV2{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectV2_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{NodeID: &zeroValue} + p.GetNodeID() + p = &ProjectV2{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProjectV2_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProjectV2{Number: &zeroValue} + p.GetNumber() + p = &ProjectV2{} + p.GetNumber() + p = nil + p.GetNumber() +} + +func TestProjectV2_GetOrganizationPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{OrganizationPermission: &zeroValue} + p.GetOrganizationPermission() + p = &ProjectV2{} + p.GetOrganizationPermission() + p = nil + p.GetOrganizationPermission() +} + +func TestProjectV2_GetOwner(tt *testing.T) { + tt.Parallel() + p := &ProjectV2{} + p.GetOwner() + p = nil + p.GetOwner() +} + +func TestProjectV2_GetOwnerURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{OwnerURL: &zeroValue} + p.GetOwnerURL() + p = &ProjectV2{} + p.GetOwnerURL() + p = nil + p.GetOwnerURL() +} + +func TestProjectV2_GetPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProjectV2{Private: &zeroValue} + p.GetPrivate() + p = &ProjectV2{} + p.GetPrivate() + p = nil + p.GetPrivate() +} + +func TestProjectV2_GetPublic(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProjectV2{Public: &zeroValue} + p.GetPublic() + p = &ProjectV2{} + p.GetPublic() + p = nil + p.GetPublic() +} + +func TestProjectV2_GetShortDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{ShortDescription: &zeroValue} + p.GetShortDescription() + p = &ProjectV2{} + p.GetShortDescription() + p = nil + p.GetShortDescription() +} + +func TestProjectV2_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{State: &zeroValue} + p.GetState() + p = &ProjectV2{} + p.GetState() + p = nil + p.GetState() +} + +func TestProjectV2_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{Title: &zeroValue} + p.GetTitle() + p = &ProjectV2{} + p.GetTitle() + p = nil + p.GetTitle() +} + +func TestProjectV2_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &ProjectV2{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestProjectV2_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2{URL: &zeroValue} + p.GetURL() + p = &ProjectV2{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestProjectV2DraftIssue_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2DraftIssue{Body: &zeroValue} + p.GetBody() + p = &ProjectV2DraftIssue{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestProjectV2DraftIssue_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2DraftIssue{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &ProjectV2DraftIssue{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestProjectV2DraftIssue_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2DraftIssue{ID: &zeroValue} + p.GetID() + p = &ProjectV2DraftIssue{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2DraftIssue_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2DraftIssue{NodeID: &zeroValue} + p.GetNodeID() + p = &ProjectV2DraftIssue{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProjectV2DraftIssue_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2DraftIssue{Title: &zeroValue} + p.GetTitle() + p = &ProjectV2DraftIssue{} + p.GetTitle() + p = nil + p.GetTitle() +} + +func TestProjectV2DraftIssue_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2DraftIssue{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &ProjectV2DraftIssue{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestProjectV2DraftIssue_GetUser(tt *testing.T) { + tt.Parallel() + p := &ProjectV2DraftIssue{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestProjectV2Event_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Event{Action: &zeroValue} + p.GetAction() + p = &ProjectV2Event{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestProjectV2Event_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Event{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestProjectV2Event_GetOrg(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Event{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestProjectV2Event_GetProjectsV2(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Event{} + p.GetProjectsV2() + p = nil + p.GetProjectsV2() +} + +func TestProjectV2Event_GetSender(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Event{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestProjectV2Field_GetConfiguration(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Field{} + p.GetConfiguration() + p = nil + p.GetConfiguration() +} + +func TestProjectV2Field_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2Field{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &ProjectV2Field{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestProjectV2Field_GetDataType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Field{DataType: &zeroValue} + p.GetDataType() + p = &ProjectV2Field{} + p.GetDataType() + p = nil + p.GetDataType() +} + +func TestProjectV2Field_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2Field{ID: &zeroValue} + p.GetID() + p = &ProjectV2Field{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2Field_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Field{Name: &zeroValue} + p.GetName() + p = &ProjectV2Field{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectV2Field_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Field{NodeID: &zeroValue} + p.GetNodeID() + p = &ProjectV2Field{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProjectV2Field_GetOptions(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProjectV2FieldOption{} + p := &ProjectV2Field{Options: zeroValue} + p.GetOptions() + p = &ProjectV2Field{} + p.GetOptions() + p = nil + p.GetOptions() +} + +func TestProjectV2Field_GetProjectURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Field{ProjectURL: &zeroValue} + p.GetProjectURL() + p = &ProjectV2Field{} + p.GetProjectURL() + p = nil + p.GetProjectURL() +} + +func TestProjectV2Field_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2Field{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &ProjectV2Field{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestProjectV2FieldConfiguration_GetDuration(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProjectV2FieldConfiguration{Duration: &zeroValue} + p.GetDuration() + p = &ProjectV2FieldConfiguration{} + p.GetDuration() + p = nil + p.GetDuration() +} + +func TestProjectV2FieldConfiguration_GetIterations(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProjectV2FieldIteration{} + p := &ProjectV2FieldConfiguration{Iterations: zeroValue} + p.GetIterations() + p = &ProjectV2FieldConfiguration{} + p.GetIterations() + p = nil + p.GetIterations() +} + +func TestProjectV2FieldConfiguration_GetStartDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProjectV2FieldConfiguration{StartDay: &zeroValue} + p.GetStartDay() + p = &ProjectV2FieldConfiguration{} + p.GetStartDay() + p = nil + p.GetStartDay() +} + +func TestProjectV2FieldIteration_GetDuration(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProjectV2FieldIteration{Duration: &zeroValue} + p.GetDuration() + p = &ProjectV2FieldIteration{} + p.GetDuration() + p = nil + p.GetDuration() +} + +func TestProjectV2FieldIteration_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldIteration{ID: &zeroValue} + p.GetID() + p = &ProjectV2FieldIteration{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2FieldIteration_GetStartDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldIteration{StartDate: &zeroValue} + p.GetStartDate() + p = &ProjectV2FieldIteration{} + p.GetStartDate() + p = nil + p.GetStartDate() +} + +func TestProjectV2FieldIteration_GetTitle(tt *testing.T) { + tt.Parallel() + p := &ProjectV2FieldIteration{} + p.GetTitle() + p = nil + p.GetTitle() +} + +func TestProjectV2FieldIterationConfiguration_GetDuration(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProjectV2FieldIterationConfiguration{Duration: &zeroValue} + p.GetDuration() + p = &ProjectV2FieldIterationConfiguration{} + p.GetDuration() + p = nil + p.GetDuration() +} + +func TestProjectV2FieldIterationConfiguration_GetIterations(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProjectV2FieldIterationConfigurationIteration{} + p := &ProjectV2FieldIterationConfiguration{Iterations: zeroValue} + p.GetIterations() + p = &ProjectV2FieldIterationConfiguration{} + p.GetIterations() + p = nil + p.GetIterations() +} + +func TestProjectV2FieldIterationConfiguration_GetStartDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldIterationConfiguration{StartDate: &zeroValue} + p.GetStartDate() + p = &ProjectV2FieldIterationConfiguration{} + p.GetStartDate() + p = nil + p.GetStartDate() +} + +func TestProjectV2FieldIterationConfigurationIteration_GetDuration(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProjectV2FieldIterationConfigurationIteration{Duration: &zeroValue} + p.GetDuration() + p = &ProjectV2FieldIterationConfigurationIteration{} + p.GetDuration() + p = nil + p.GetDuration() +} + +func TestProjectV2FieldIterationConfigurationIteration_GetStartDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldIterationConfigurationIteration{StartDate: &zeroValue} + p.GetStartDate() + p = &ProjectV2FieldIterationConfigurationIteration{} + p.GetStartDate() + p = nil + p.GetStartDate() +} + +func TestProjectV2FieldIterationConfigurationIteration_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldIterationConfigurationIteration{Title: &zeroValue} + p.GetTitle() + p = &ProjectV2FieldIterationConfigurationIteration{} + p.GetTitle() + p = nil + p.GetTitle() +} + +func TestProjectV2FieldOption_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldOption{Color: &zeroValue} + p.GetColor() + p = &ProjectV2FieldOption{} + p.GetColor() + p = nil + p.GetColor() +} + +func TestProjectV2FieldOption_GetDescription(tt *testing.T) { + tt.Parallel() + p := &ProjectV2FieldOption{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestProjectV2FieldOption_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldOption{ID: &zeroValue} + p.GetID() + p = &ProjectV2FieldOption{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2FieldOption_GetName(tt *testing.T) { + tt.Parallel() + p := &ProjectV2FieldOption{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectV2FieldSingleSelectOption_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldSingleSelectOption{Color: &zeroValue} + p.GetColor() + p = &ProjectV2FieldSingleSelectOption{} + p.GetColor() + p = nil + p.GetColor() +} + +func TestProjectV2FieldSingleSelectOption_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2FieldSingleSelectOption{Description: &zeroValue} + p.GetDescription() + p = &ProjectV2FieldSingleSelectOption{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestProjectV2FieldSingleSelectOption_GetName(tt *testing.T) { + tt.Parallel() + p := &ProjectV2FieldSingleSelectOption{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectV2Item_GetArchivedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2Item{ArchivedAt: &zeroValue} + p.GetArchivedAt() + p = &ProjectV2Item{} + p.GetArchivedAt() + p = nil + p.GetArchivedAt() +} + +func TestProjectV2Item_GetContent(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Item{} + p.GetContent() + p = nil + p.GetContent() +} + +func TestProjectV2Item_GetContentNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Item{ContentNodeID: &zeroValue} + p.GetContentNodeID() + p = &ProjectV2Item{} + p.GetContentNodeID() + p = nil + p.GetContentNodeID() +} + +func TestProjectV2Item_GetContentType(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Item{} + p.GetContentType() + p = nil + p.GetContentType() +} + +func TestProjectV2Item_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2Item{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &ProjectV2Item{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestProjectV2Item_GetCreator(tt *testing.T) { + tt.Parallel() + p := &ProjectV2Item{} + p.GetCreator() + p = nil + p.GetCreator() +} + +func TestProjectV2Item_GetFields(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProjectV2ItemFieldValue{} + p := &ProjectV2Item{Fields: zeroValue} + p.GetFields() + p = &ProjectV2Item{} + p.GetFields() + p = nil + p.GetFields() +} + +func TestProjectV2Item_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2Item{ID: &zeroValue} + p.GetID() + p = &ProjectV2Item{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2Item_GetItemURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Item{ItemURL: &zeroValue} + p.GetItemURL() + p = &ProjectV2Item{} + p.GetItemURL() + p = nil + p.GetItemURL() +} + +func TestProjectV2Item_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Item{NodeID: &zeroValue} + p.GetNodeID() + p = &ProjectV2Item{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProjectV2Item_GetProjectNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Item{ProjectNodeID: &zeroValue} + p.GetProjectNodeID() + p = &ProjectV2Item{} + p.GetProjectNodeID() + p = nil + p.GetProjectNodeID() +} + +func TestProjectV2Item_GetProjectURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2Item{ProjectURL: &zeroValue} + p.GetProjectURL() + p = &ProjectV2Item{} + p.GetProjectURL() + p = nil + p.GetProjectURL() +} + +func TestProjectV2Item_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2Item{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &ProjectV2Item{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestProjectV2ItemChange_GetArchivedAt(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemChange{} + p.GetArchivedAt() + p = nil + p.GetArchivedAt() +} + +func TestProjectV2ItemChange_GetFieldValue(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemChange{} + p.GetFieldValue() + p = nil + p.GetFieldValue() +} + +func TestProjectV2ItemContent_GetDraftIssue(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemContent{} + p.GetDraftIssue() + p = nil + p.GetDraftIssue() +} + +func TestProjectV2ItemContent_GetIssue(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemContent{} + p.GetIssue() + p = nil + p.GetIssue() +} + +func TestProjectV2ItemContent_GetPullRequest(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemContent{} + p.GetPullRequest() + p = nil + p.GetPullRequest() +} + +func TestProjectV2ItemEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2ItemEvent{Action: &zeroValue} + p.GetAction() + p = &ProjectV2ItemEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestProjectV2ItemEvent_GetChanges(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemEvent{} + p.GetChanges() + p = nil + p.GetChanges() +} + +func TestProjectV2ItemEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestProjectV2ItemEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestProjectV2ItemEvent_GetProjectV2Item(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemEvent{} + p.GetProjectV2Item() + p = nil + p.GetProjectV2Item() +} + +func TestProjectV2ItemEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestProjectV2ItemFieldValue_GetDataType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2ItemFieldValue{DataType: &zeroValue} + p.GetDataType() + p = &ProjectV2ItemFieldValue{} + p.GetDataType() + p = nil + p.GetDataType() +} + +func TestProjectV2ItemFieldValue_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2ItemFieldValue{ID: &zeroValue} + p.GetID() + p = &ProjectV2ItemFieldValue{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2ItemFieldValue_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2ItemFieldValue{Name: &zeroValue} + p.GetName() + p = &ProjectV2ItemFieldValue{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectV2ItemFieldValue_GetValue(tt *testing.T) { + tt.Parallel() + p := &ProjectV2ItemFieldValue{} + p.GetValue() + p = nil + p.GetValue() +} + +func TestProjectV2StatusUpdate_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2StatusUpdate{Body: &zeroValue} + p.GetBody() + p = &ProjectV2StatusUpdate{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestProjectV2StatusUpdate_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2StatusUpdate{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &ProjectV2StatusUpdate{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestProjectV2StatusUpdate_GetCreator(tt *testing.T) { + tt.Parallel() + p := &ProjectV2StatusUpdate{} + p.GetCreator() + p = nil + p.GetCreator() +} + +func TestProjectV2StatusUpdate_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2StatusUpdate{ID: &zeroValue} + p.GetID() + p = &ProjectV2StatusUpdate{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2StatusUpdate_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2StatusUpdate{NodeID: &zeroValue} + p.GetNodeID() + p = &ProjectV2StatusUpdate{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProjectV2StatusUpdate_GetProjectNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2StatusUpdate{ProjectNodeID: &zeroValue} + p.GetProjectNodeID() + p = &ProjectV2StatusUpdate{} + p.GetProjectNodeID() + p = nil + p.GetProjectNodeID() +} + +func TestProjectV2StatusUpdate_GetStartDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2StatusUpdate{StartDate: &zeroValue} + p.GetStartDate() + p = &ProjectV2StatusUpdate{} + p.GetStartDate() + p = nil + p.GetStartDate() +} + +func TestProjectV2StatusUpdate_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2StatusUpdate{Status: &zeroValue} + p.GetStatus() + p = &ProjectV2StatusUpdate{} + p.GetStatus() + p = nil + p.GetStatus() +} + +func TestProjectV2StatusUpdate_GetTargetDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2StatusUpdate{TargetDate: &zeroValue} + p.GetTargetDate() + p = &ProjectV2StatusUpdate{} + p.GetTargetDate() + p = nil + p.GetTargetDate() +} + +func TestProjectV2StatusUpdate_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &ProjectV2StatusUpdate{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &ProjectV2StatusUpdate{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestProjectV2TextContent_GetHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2TextContent{HTML: &zeroValue} + p.GetHTML() + p = &ProjectV2TextContent{} + p.GetHTML() + p = nil + p.GetHTML() +} + +func TestProjectV2TextContent_GetRaw(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2TextContent{Raw: &zeroValue} + p.GetRaw() + p = &ProjectV2TextContent{} + p.GetRaw() + p = nil + p.GetRaw() +} + +func TestProjectV2View_GetCreatedAt(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestProjectV2View_GetCreator(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetCreator() + p = nil + p.GetCreator() +} + +func TestProjectV2View_GetFilter(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2View{Filter: &zeroValue} + p.GetFilter() + p = &ProjectV2View{} + p.GetFilter() + p = nil + p.GetFilter() +} + +func TestProjectV2View_GetGroupBy(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + p := &ProjectV2View{GroupBy: zeroValue} + p.GetGroupBy() + p = &ProjectV2View{} + p.GetGroupBy() + p = nil + p.GetGroupBy() +} + +func TestProjectV2View_GetHTMLURL(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestProjectV2View_GetID(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetID() + p = nil + p.GetID() +} + +func TestProjectV2View_GetLayout(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetLayout() + p = nil + p.GetLayout() +} + +func TestProjectV2View_GetName(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetName() + p = nil + p.GetName() +} + +func TestProjectV2View_GetNodeID(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProjectV2View_GetNumber(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetNumber() + p = nil + p.GetNumber() +} + +func TestProjectV2View_GetProjectURL(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetProjectURL() + p = nil + p.GetProjectURL() +} + +func TestProjectV2View_GetSortBy(tt *testing.T) { + tt.Parallel() + zeroValue := []*ProjectV2ViewSortBy{} + p := &ProjectV2View{SortBy: zeroValue} + p.GetSortBy() + p = &ProjectV2View{} + p.GetSortBy() + p = nil + p.GetSortBy() +} + +func TestProjectV2View_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + p := &ProjectV2View{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestProjectV2View_GetVerticalGroupBy(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + p := &ProjectV2View{VerticalGroupBy: zeroValue} + p.GetVerticalGroupBy() + p = &ProjectV2View{} + p.GetVerticalGroupBy() + p = nil + p.GetVerticalGroupBy() +} + +func TestProjectV2View_GetVisibleFields(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + p := &ProjectV2View{VisibleFields: zeroValue} + p.GetVisibleFields() + p = &ProjectV2View{} + p.GetVisibleFields() + p = nil + p.GetVisibleFields() +} + +func TestProjectV2ViewSortBy_GetDirection(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProjectV2ViewSortBy{Direction: &zeroValue} + p.GetDirection() + p = &ProjectV2ViewSortBy{} + p.GetDirection() + p = nil + p.GetDirection() +} + +func TestProjectV2ViewSortBy_GetFieldID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProjectV2ViewSortBy{FieldID: &zeroValue} + p.GetFieldID() + p = &ProjectV2ViewSortBy{} + p.GetFieldID() + p = nil + p.GetFieldID() +} + +func TestProtection_GetAllowDeletions(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetAllowDeletions() + p = nil + p.GetAllowDeletions() +} + +func TestProtection_GetAllowForcePushes(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetAllowForcePushes() + p = nil + p.GetAllowForcePushes() +} + +func TestProtection_GetAllowForkSyncing(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetAllowForkSyncing() + p = nil + p.GetAllowForkSyncing() +} + +func TestProtection_GetBlockCreations(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetBlockCreations() + p = nil + p.GetBlockCreations() +} + +func TestProtection_GetEnforceAdmins(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetEnforceAdmins() + p = nil + p.GetEnforceAdmins() +} + +func TestProtection_GetLockBranch(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetLockBranch() + p = nil + p.GetLockBranch() +} + +func TestProtection_GetRequiredConversationResolution(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetRequiredConversationResolution() + p = nil + p.GetRequiredConversationResolution() +} + +func TestProtection_GetRequiredPullRequestReviews(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetRequiredPullRequestReviews() + p = nil + p.GetRequiredPullRequestReviews() +} + +func TestProtection_GetRequiredSignatures(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetRequiredSignatures() + p = nil + p.GetRequiredSignatures() +} + +func TestProtection_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetRequiredStatusChecks() + p = nil + p.GetRequiredStatusChecks() +} + +func TestProtection_GetRequireLinearHistory(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetRequireLinearHistory() + p = nil + p.GetRequireLinearHistory() +} + +func TestProtection_GetRestrictions(tt *testing.T) { + tt.Parallel() + p := &Protection{} + p.GetRestrictions() + p = nil + p.GetRestrictions() +} + +func TestProtection_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &Protection{URL: &zeroValue} + p.GetURL() + p = &Protection{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestProtectionChanges_GetAdminEnforced(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetAdminEnforced() + p = nil + p.GetAdminEnforced() +} + +func TestProtectionChanges_GetAllowDeletionsEnforcementLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetAllowDeletionsEnforcementLevel() + p = nil + p.GetAllowDeletionsEnforcementLevel() +} + +func TestProtectionChanges_GetAuthorizedActorNames(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetAuthorizedActorNames() + p = nil + p.GetAuthorizedActorNames() +} + +func TestProtectionChanges_GetAuthorizedActorsOnly(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetAuthorizedActorsOnly() + p = nil + p.GetAuthorizedActorsOnly() +} + +func TestProtectionChanges_GetAuthorizedDismissalActorsOnly(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetAuthorizedDismissalActorsOnly() + p = nil + p.GetAuthorizedDismissalActorsOnly() +} + +func TestProtectionChanges_GetCreateProtected(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetCreateProtected() + p = nil + p.GetCreateProtected() +} + +func TestProtectionChanges_GetDismissStaleReviewsOnPush(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetDismissStaleReviewsOnPush() + p = nil + p.GetDismissStaleReviewsOnPush() +} + +func TestProtectionChanges_GetLinearHistoryRequirementEnforcementLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetLinearHistoryRequirementEnforcementLevel() + p = nil + p.GetLinearHistoryRequirementEnforcementLevel() +} + +func TestProtectionChanges_GetPullRequestReviewsEnforcementLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetPullRequestReviewsEnforcementLevel() + p = nil + p.GetPullRequestReviewsEnforcementLevel() +} + +func TestProtectionChanges_GetRequireCodeOwnerReview(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetRequireCodeOwnerReview() + p = nil + p.GetRequireCodeOwnerReview() +} + +func TestProtectionChanges_GetRequiredConversationResolutionLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetRequiredConversationResolutionLevel() + p = nil + p.GetRequiredConversationResolutionLevel() +} + +func TestProtectionChanges_GetRequiredDeploymentsEnforcementLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetRequiredDeploymentsEnforcementLevel() + p = nil + p.GetRequiredDeploymentsEnforcementLevel() +} + +func TestProtectionChanges_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetRequiredStatusChecks() + p = nil + p.GetRequiredStatusChecks() +} + +func TestProtectionChanges_GetRequiredStatusChecksEnforcementLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetRequiredStatusChecksEnforcementLevel() + p = nil + p.GetRequiredStatusChecksEnforcementLevel() +} + +func TestProtectionChanges_GetRequireLastPushApproval(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetRequireLastPushApproval() + p = nil + p.GetRequireLastPushApproval() +} + +func TestProtectionChanges_GetSignatureRequirementEnforcementLevel(tt *testing.T) { + tt.Parallel() + p := &ProtectionChanges{} + p.GetSignatureRequirementEnforcementLevel() + p = nil + p.GetSignatureRequirementEnforcementLevel() +} + +func TestProtectionRequest_GetAllowDeletions(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{AllowDeletions: &zeroValue} + p.GetAllowDeletions() + p = &ProtectionRequest{} + p.GetAllowDeletions() + p = nil + p.GetAllowDeletions() +} + +func TestProtectionRequest_GetAllowForcePushes(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{AllowForcePushes: &zeroValue} + p.GetAllowForcePushes() + p = &ProtectionRequest{} + p.GetAllowForcePushes() + p = nil + p.GetAllowForcePushes() +} + +func TestProtectionRequest_GetAllowForkSyncing(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{AllowForkSyncing: &zeroValue} + p.GetAllowForkSyncing() + p = &ProtectionRequest{} + p.GetAllowForkSyncing() + p = nil + p.GetAllowForkSyncing() +} + +func TestProtectionRequest_GetBlockCreations(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{BlockCreations: &zeroValue} + p.GetBlockCreations() + p = &ProtectionRequest{} + p.GetBlockCreations() + p = nil + p.GetBlockCreations() +} + +func TestProtectionRequest_GetEnforceAdmins(tt *testing.T) { + tt.Parallel() + p := &ProtectionRequest{} + p.GetEnforceAdmins() + p = nil + p.GetEnforceAdmins() +} + +func TestProtectionRequest_GetLockBranch(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{LockBranch: &zeroValue} + p.GetLockBranch() + p = &ProtectionRequest{} + p.GetLockBranch() + p = nil + p.GetLockBranch() +} + +func TestProtectionRequest_GetRequiredConversationResolution(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{RequiredConversationResolution: &zeroValue} + p.GetRequiredConversationResolution() + p = &ProtectionRequest{} + p.GetRequiredConversationResolution() + p = nil + p.GetRequiredConversationResolution() +} + +func TestProtectionRequest_GetRequiredPullRequestReviews(tt *testing.T) { + tt.Parallel() + p := &ProtectionRequest{} + p.GetRequiredPullRequestReviews() + p = nil + p.GetRequiredPullRequestReviews() +} + +func TestProtectionRequest_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + p := &ProtectionRequest{} + p.GetRequiredStatusChecks() + p = nil + p.GetRequiredStatusChecks() +} + +func TestProtectionRequest_GetRequireLinearHistory(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRequest{RequireLinearHistory: &zeroValue} + p.GetRequireLinearHistory() + p = &ProtectionRequest{} + p.GetRequireLinearHistory() + p = nil + p.GetRequireLinearHistory() +} + +func TestProtectionRequest_GetRestrictions(tt *testing.T) { + tt.Parallel() + p := &ProtectionRequest{} + p.GetRestrictions() + p = nil + p.GetRestrictions() +} + +func TestProtectionRule_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &ProtectionRule{ID: &zeroValue} + p.GetID() + p = &ProtectionRule{} + p.GetID() + p = nil + p.GetID() +} + +func TestProtectionRule_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProtectionRule{NodeID: &zeroValue} + p.GetNodeID() + p = &ProtectionRule{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestProtectionRule_GetPreventSelfReview(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &ProtectionRule{PreventSelfReview: &zeroValue} + p.GetPreventSelfReview() + p = &ProtectionRule{} + p.GetPreventSelfReview() + p = nil + p.GetPreventSelfReview() +} + +func TestProtectionRule_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*RequiredReviewer{} + p := &ProtectionRule{Reviewers: zeroValue} + p.GetReviewers() + p = &ProtectionRule{} + p.GetReviewers() + p = nil + p.GetReviewers() +} + +func TestProtectionRule_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &ProtectionRule{Type: &zeroValue} + p.GetType() + p = &ProtectionRule{} + p.GetType() + p = nil + p.GetType() +} + +func TestProtectionRule_GetWaitTimer(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &ProtectionRule{WaitTimer: &zeroValue} + p.GetWaitTimer() + p = &ProtectionRule{} + p.GetWaitTimer() + p = nil + p.GetWaitTimer() +} + +func TestPublicEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PublicEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPublicEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PublicEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPublicEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PublicEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPublicEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PublicEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPublicIPUsage_GetCurrentUsage(tt *testing.T) { + tt.Parallel() + p := &PublicIPUsage{} + p.GetCurrentUsage() + p = nil + p.GetCurrentUsage() +} + +func TestPublicIPUsage_GetMaximum(tt *testing.T) { + tt.Parallel() + p := &PublicIPUsage{} + p.GetMaximum() + p = nil + p.GetMaximum() +} + +func TestPublicKey_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PublicKey{Key: &zeroValue} + p.GetKey() + p = &PublicKey{} + p.GetKey() + p = nil + p.GetKey() +} + +func TestPublicKey_GetKeyID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PublicKey{KeyID: &zeroValue} + p.GetKeyID() + p = &PublicKey{} + p.GetKeyID() + p = nil + p.GetKeyID() +} + +func TestPublishCodespaceOptions_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PublishCodespaceOptions{Name: &zeroValue} + p.GetName() + p = &PublishCodespaceOptions{} + p.GetName() + p = nil + p.GetName() +} + +func TestPublishCodespaceOptions_GetPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PublishCodespaceOptions{Private: &zeroValue} + p.GetPrivate() + p = &PublishCodespaceOptions{} + p.GetPrivate() + p = nil + p.GetPrivate() +} + +func TestPullRequest_GetActiveLockReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{ActiveLockReason: &zeroValue} + p.GetActiveLockReason() + p = &PullRequest{} + p.GetActiveLockReason() + p = nil + p.GetActiveLockReason() +} + +func TestPullRequest_GetAdditions(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{Additions: &zeroValue} + p.GetAdditions() + p = &PullRequest{} + p.GetAdditions() + p = nil + p.GetAdditions() +} + +func TestPullRequest_GetAssignee(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetAssignee() + p = nil + p.GetAssignee() +} + +func TestPullRequest_GetAssignees(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + p := &PullRequest{Assignees: zeroValue} + p.GetAssignees() + p = &PullRequest{} + p.GetAssignees() + p = nil + p.GetAssignees() +} + +func TestPullRequest_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{AuthorAssociation: &zeroValue} + p.GetAuthorAssociation() + p = &PullRequest{} + p.GetAuthorAssociation() + p = nil + p.GetAuthorAssociation() +} + +func TestPullRequest_GetAutoMerge(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetAutoMerge() + p = nil + p.GetAutoMerge() +} + +func TestPullRequest_GetBase(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetBase() + p = nil + p.GetBase() +} + +func TestPullRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{Body: &zeroValue} + p.GetBody() + p = &PullRequest{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestPullRequest_GetChangedFiles(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{ChangedFiles: &zeroValue} + p.GetChangedFiles() + p = &PullRequest{} + p.GetChangedFiles() + p = nil + p.GetChangedFiles() +} + +func TestPullRequest_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequest{ClosedAt: &zeroValue} + p.GetClosedAt() + p = &PullRequest{} + p.GetClosedAt() + p = nil + p.GetClosedAt() +} + +func TestPullRequest_GetComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{Comments: &zeroValue} + p.GetComments() + p = &PullRequest{} + p.GetComments() + p = nil + p.GetComments() +} + +func TestPullRequest_GetCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{CommentsURL: &zeroValue} + p.GetCommentsURL() + p = &PullRequest{} + p.GetCommentsURL() + p = nil + p.GetCommentsURL() +} + +func TestPullRequest_GetCommits(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{Commits: &zeroValue} + p.GetCommits() + p = &PullRequest{} + p.GetCommits() + p = nil + p.GetCommits() +} + +func TestPullRequest_GetCommitsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{CommitsURL: &zeroValue} + p.GetCommitsURL() + p = &PullRequest{} + p.GetCommitsURL() + p = nil + p.GetCommitsURL() +} + +func TestPullRequest_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequest{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PullRequest{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPullRequest_GetDeletions(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{Deletions: &zeroValue} + p.GetDeletions() + p = &PullRequest{} + p.GetDeletions() + p = nil + p.GetDeletions() +} + +func TestPullRequest_GetDiffURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{DiffURL: &zeroValue} + p.GetDiffURL() + p = &PullRequest{} + p.GetDiffURL() + p = nil + p.GetDiffURL() +} + +func TestPullRequest_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequest{Draft: &zeroValue} + p.GetDraft() + p = &PullRequest{} + p.GetDraft() + p = nil + p.GetDraft() +} + +func TestPullRequest_GetHead(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetHead() + p = nil + p.GetHead() +} + +func TestPullRequest_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PullRequest{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPullRequest_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequest{ID: &zeroValue} + p.GetID() + p = &PullRequest{} + p.GetID() + p = nil + p.GetID() +} + +func TestPullRequest_GetIssueURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{IssueURL: &zeroValue} + p.GetIssueURL() + p = &PullRequest{} + p.GetIssueURL() + p = nil + p.GetIssueURL() +} + +func TestPullRequest_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []*Label{} + p := &PullRequest{Labels: zeroValue} + p.GetLabels() + p = &PullRequest{} + p.GetLabels() + p = nil + p.GetLabels() +} + +func TestPullRequest_GetLinks(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetLinks() + p = nil + p.GetLinks() +} + +func TestPullRequest_GetLocked(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequest{Locked: &zeroValue} + p.GetLocked() + p = &PullRequest{} + p.GetLocked() + p = nil + p.GetLocked() +} + +func TestPullRequest_GetMaintainerCanModify(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequest{MaintainerCanModify: &zeroValue} + p.GetMaintainerCanModify() + p = &PullRequest{} + p.GetMaintainerCanModify() + p = nil + p.GetMaintainerCanModify() +} + +func TestPullRequest_GetMergeable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequest{Mergeable: &zeroValue} + p.GetMergeable() + p = &PullRequest{} + p.GetMergeable() + p = nil + p.GetMergeable() +} + +func TestPullRequest_GetMergeableState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{MergeableState: &zeroValue} + p.GetMergeableState() + p = &PullRequest{} + p.GetMergeableState() + p = nil + p.GetMergeableState() +} + +func TestPullRequest_GetMergeCommitSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{MergeCommitSHA: &zeroValue} + p.GetMergeCommitSHA() + p = &PullRequest{} + p.GetMergeCommitSHA() + p = nil + p.GetMergeCommitSHA() +} + +func TestPullRequest_GetMerged(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequest{Merged: &zeroValue} + p.GetMerged() + p = &PullRequest{} + p.GetMerged() + p = nil + p.GetMerged() +} + +func TestPullRequest_GetMergedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequest{MergedAt: &zeroValue} + p.GetMergedAt() + p = &PullRequest{} + p.GetMergedAt() + p = nil + p.GetMergedAt() +} + +func TestPullRequest_GetMergedBy(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetMergedBy() + p = nil + p.GetMergedBy() +} + +func TestPullRequest_GetMilestone(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetMilestone() + p = nil + p.GetMilestone() +} + +func TestPullRequest_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{NodeID: &zeroValue} + p.GetNodeID() + p = &PullRequest{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPullRequest_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{Number: &zeroValue} + p.GetNumber() + p = &PullRequest{} + p.GetNumber() + p = nil + p.GetNumber() +} + +func TestPullRequest_GetPatchURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{PatchURL: &zeroValue} + p.GetPatchURL() + p = &PullRequest{} + p.GetPatchURL() + p = nil + p.GetPatchURL() +} + +func TestPullRequest_GetRebaseable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequest{Rebaseable: &zeroValue} + p.GetRebaseable() + p = &PullRequest{} + p.GetRebaseable() + p = nil + p.GetRebaseable() +} + +func TestPullRequest_GetRequestedReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + p := &PullRequest{RequestedReviewers: zeroValue} + p.GetRequestedReviewers() + p = &PullRequest{} + p.GetRequestedReviewers() + p = nil + p.GetRequestedReviewers() +} + +func TestPullRequest_GetRequestedTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + p := &PullRequest{RequestedTeams: zeroValue} + p.GetRequestedTeams() + p = &PullRequest{} + p.GetRequestedTeams() + p = nil + p.GetRequestedTeams() +} + +func TestPullRequest_GetReviewComments(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequest{ReviewComments: &zeroValue} + p.GetReviewComments() + p = &PullRequest{} + p.GetReviewComments() + p = nil + p.GetReviewComments() +} + +func TestPullRequest_GetReviewCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{ReviewCommentsURL: &zeroValue} + p.GetReviewCommentsURL() + p = &PullRequest{} + p.GetReviewCommentsURL() + p = nil + p.GetReviewCommentsURL() +} + +func TestPullRequest_GetReviewCommentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{ReviewCommentURL: &zeroValue} + p.GetReviewCommentURL() + p = &PullRequest{} + p.GetReviewCommentURL() + p = nil + p.GetReviewCommentURL() +} + +func TestPullRequest_GetStack(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetStack() + p = nil + p.GetStack() +} + +func TestPullRequest_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{State: &zeroValue} + p.GetState() + p = &PullRequest{} + p.GetState() + p = nil + p.GetState() +} + +func TestPullRequest_GetStatusesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{StatusesURL: &zeroValue} + p.GetStatusesURL() + p = &PullRequest{} + p.GetStatusesURL() + p = nil + p.GetStatusesURL() +} + +func TestPullRequest_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{Title: &zeroValue} + p.GetTitle() + p = &PullRequest{} + p.GetTitle() + p = nil + p.GetTitle() +} + +func TestPullRequest_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequest{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PullRequest{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPullRequest_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequest{URL: &zeroValue} + p.GetURL() + p = &PullRequest{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPullRequest_GetUser(tt *testing.T) { + tt.Parallel() + p := &PullRequest{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPullRequestAutoMerge_GetCommitMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestAutoMerge{CommitMessage: &zeroValue} + p.GetCommitMessage() + p = &PullRequestAutoMerge{} + p.GetCommitMessage() + p = nil + p.GetCommitMessage() +} + +func TestPullRequestAutoMerge_GetCommitTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestAutoMerge{CommitTitle: &zeroValue} + p.GetCommitTitle() + p = &PullRequestAutoMerge{} + p.GetCommitTitle() + p = nil + p.GetCommitTitle() +} + +func TestPullRequestAutoMerge_GetEnabledBy(tt *testing.T) { + tt.Parallel() + p := &PullRequestAutoMerge{} + p.GetEnabledBy() + p = nil + p.GetEnabledBy() +} + +func TestPullRequestAutoMerge_GetMergeMethod(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestAutoMerge{MergeMethod: &zeroValue} + p.GetMergeMethod() + p = &PullRequestAutoMerge{} + p.GetMergeMethod() + p = nil + p.GetMergeMethod() +} + +func TestPullRequestBranch_GetLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestBranch{Label: &zeroValue} + p.GetLabel() + p = &PullRequestBranch{} + p.GetLabel() + p = nil + p.GetLabel() +} + +func TestPullRequestBranch_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestBranch{Ref: &zeroValue} + p.GetRef() + p = &PullRequestBranch{} + p.GetRef() + p = nil + p.GetRef() +} + +func TestPullRequestBranch_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PullRequestBranch{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPullRequestBranch_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestBranch{SHA: &zeroValue} + p.GetSHA() + p = &PullRequestBranch{} + p.GetSHA() + p = nil + p.GetSHA() +} + +func TestPullRequestBranch_GetUser(tt *testing.T) { + tt.Parallel() + p := &PullRequestBranch{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPullRequestBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + p := &PullRequestBranchRule{} + p.GetParameters() + p = nil + p.GetParameters() +} + +func TestPullRequestBranchUpdateOptions_GetExpectedHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestBranchUpdateOptions{ExpectedHeadSHA: &zeroValue} + p.GetExpectedHeadSHA() + p = &PullRequestBranchUpdateOptions{} + p.GetExpectedHeadSHA() + p = nil + p.GetExpectedHeadSHA() +} + +func TestPullRequestBranchUpdateResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestBranchUpdateResponse{Message: &zeroValue} + p.GetMessage() + p = &PullRequestBranchUpdateResponse{} + p.GetMessage() + p = nil + p.GetMessage() +} + +func TestPullRequestBranchUpdateResponse_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestBranchUpdateResponse{URL: &zeroValue} + p.GetURL() + p = &PullRequestBranchUpdateResponse{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPullRequestComment_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{AuthorAssociation: &zeroValue} + p.GetAuthorAssociation() + p = &PullRequestComment{} + p.GetAuthorAssociation() + p = nil + p.GetAuthorAssociation() +} + +func TestPullRequestComment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{Body: &zeroValue} + p.GetBody() + p = &PullRequestComment{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestPullRequestComment_GetCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{CommitID: &zeroValue} + p.GetCommitID() + p = &PullRequestComment{} + p.GetCommitID() + p = nil + p.GetCommitID() +} + +func TestPullRequestComment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequestComment{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PullRequestComment{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPullRequestComment_GetDiffHunk(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{DiffHunk: &zeroValue} + p.GetDiffHunk() + p = &PullRequestComment{} + p.GetDiffHunk() + p = nil + p.GetDiffHunk() +} + +func TestPullRequestComment_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PullRequestComment{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPullRequestComment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequestComment{ID: &zeroValue} + p.GetID() + p = &PullRequestComment{} + p.GetID() + p = nil + p.GetID() +} + +func TestPullRequestComment_GetInReplyTo(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequestComment{InReplyTo: &zeroValue} + p.GetInReplyTo() + p = &PullRequestComment{} + p.GetInReplyTo() + p = nil + p.GetInReplyTo() +} + +func TestPullRequestComment_GetLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestComment{Line: &zeroValue} + p.GetLine() + p = &PullRequestComment{} + p.GetLine() + p = nil + p.GetLine() +} + +func TestPullRequestComment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{NodeID: &zeroValue} + p.GetNodeID() + p = &PullRequestComment{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPullRequestComment_GetOriginalCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{OriginalCommitID: &zeroValue} + p.GetOriginalCommitID() + p = &PullRequestComment{} + p.GetOriginalCommitID() + p = nil + p.GetOriginalCommitID() +} + +func TestPullRequestComment_GetOriginalLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestComment{OriginalLine: &zeroValue} + p.GetOriginalLine() + p = &PullRequestComment{} + p.GetOriginalLine() + p = nil + p.GetOriginalLine() +} + +func TestPullRequestComment_GetOriginalPosition(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestComment{OriginalPosition: &zeroValue} + p.GetOriginalPosition() + p = &PullRequestComment{} + p.GetOriginalPosition() + p = nil + p.GetOriginalPosition() +} + +func TestPullRequestComment_GetOriginalStartLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestComment{OriginalStartLine: &zeroValue} + p.GetOriginalStartLine() + p = &PullRequestComment{} + p.GetOriginalStartLine() + p = nil + p.GetOriginalStartLine() +} + +func TestPullRequestComment_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{Path: &zeroValue} + p.GetPath() + p = &PullRequestComment{} + p.GetPath() + p = nil + p.GetPath() +} + +func TestPullRequestComment_GetPosition(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestComment{Position: &zeroValue} + p.GetPosition() + p = &PullRequestComment{} + p.GetPosition() + p = nil + p.GetPosition() +} + +func TestPullRequestComment_GetPullRequestReviewID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequestComment{PullRequestReviewID: &zeroValue} + p.GetPullRequestReviewID() + p = &PullRequestComment{} + p.GetPullRequestReviewID() + p = nil + p.GetPullRequestReviewID() +} + +func TestPullRequestComment_GetPullRequestURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{PullRequestURL: &zeroValue} + p.GetPullRequestURL() + p = &PullRequestComment{} + p.GetPullRequestURL() + p = nil + p.GetPullRequestURL() +} + +func TestPullRequestComment_GetReactions(tt *testing.T) { + tt.Parallel() + p := &PullRequestComment{} + p.GetReactions() + p = nil + p.GetReactions() +} + +func TestPullRequestComment_GetSide(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{Side: &zeroValue} + p.GetSide() + p = &PullRequestComment{} + p.GetSide() + p = nil + p.GetSide() +} + +func TestPullRequestComment_GetStartLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestComment{StartLine: &zeroValue} + p.GetStartLine() + p = &PullRequestComment{} + p.GetStartLine() + p = nil + p.GetStartLine() +} + +func TestPullRequestComment_GetStartSide(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{StartSide: &zeroValue} + p.GetStartSide() + p = &PullRequestComment{} + p.GetStartSide() + p = nil + p.GetStartSide() +} + +func TestPullRequestComment_GetSubjectType(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{SubjectType: &zeroValue} + p.GetSubjectType() + p = &PullRequestComment{} + p.GetSubjectType() + p = nil + p.GetSubjectType() +} + +func TestPullRequestComment_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequestComment{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PullRequestComment{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPullRequestComment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestComment{URL: &zeroValue} + p.GetURL() + p = &PullRequestComment{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPullRequestComment_GetUser(tt *testing.T) { + tt.Parallel() + p := &PullRequestComment{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPullRequestDismissReviewRequest_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestDismissReviewRequest{Event: &zeroValue} + p.GetEvent() + p = &PullRequestDismissReviewRequest{} + p.GetEvent() + p = nil + p.GetEvent() +} + +func TestPullRequestDismissReviewRequest_GetMessage(tt *testing.T) { + tt.Parallel() + p := &PullRequestDismissReviewRequest{} + p.GetMessage() + p = nil + p.GetMessage() +} + +func TestPullRequestEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestEvent{Action: &zeroValue} + p.GetAction() + p = &PullRequestEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPullRequestEvent_GetAfter(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestEvent{After: &zeroValue} + p.GetAfter() + p = &PullRequestEvent{} + p.GetAfter() + p = nil + p.GetAfter() +} + +func TestPullRequestEvent_GetAssignee(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetAssignee() + p = nil + p.GetAssignee() +} + +func TestPullRequestEvent_GetBefore(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestEvent{Before: &zeroValue} + p.GetBefore() + p = &PullRequestEvent{} + p.GetBefore() + p = nil + p.GetBefore() +} + +func TestPullRequestEvent_GetChanges(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetChanges() + p = nil + p.GetChanges() +} + +func TestPullRequestEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPullRequestEvent_GetLabel(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetLabel() + p = nil + p.GetLabel() +} + +func TestPullRequestEvent_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestEvent{Number: &zeroValue} + p.GetNumber() + p = &PullRequestEvent{} + p.GetNumber() + p = nil + p.GetNumber() +} + +func TestPullRequestEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetOrganization() + p = nil + p.GetOrganization() +} + +func TestPullRequestEvent_GetPerformedViaGithubApp(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetPerformedViaGithubApp() + p = nil + p.GetPerformedViaGithubApp() +} + +func TestPullRequestEvent_GetPullRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetPullRequest() + p = nil + p.GetPullRequest() +} + +func TestPullRequestEvent_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestEvent{Reason: &zeroValue} + p.GetReason() + p = &PullRequestEvent{} + p.GetReason() + p = nil + p.GetReason() +} + +func TestPullRequestEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPullRequestEvent_GetRequestedReviewer(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetRequestedReviewer() + p = nil + p.GetRequestedReviewer() +} + +func TestPullRequestEvent_GetRequestedTeam(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetRequestedTeam() + p = nil + p.GetRequestedTeam() +} + +func TestPullRequestEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PullRequestEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPullRequestLinks_GetDiffURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestLinks{DiffURL: &zeroValue} + p.GetDiffURL() + p = &PullRequestLinks{} + p.GetDiffURL() + p = nil + p.GetDiffURL() +} + +func TestPullRequestLinks_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestLinks{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PullRequestLinks{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPullRequestLinks_GetMergedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequestLinks{MergedAt: &zeroValue} + p.GetMergedAt() + p = &PullRequestLinks{} + p.GetMergedAt() + p = nil + p.GetMergedAt() +} + +func TestPullRequestLinks_GetPatchURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestLinks{PatchURL: &zeroValue} + p.GetPatchURL() + p = &PullRequestLinks{} + p.GetPatchURL() + p = nil + p.GetPatchURL() +} + +func TestPullRequestLinks_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestLinks{URL: &zeroValue} + p.GetURL() + p = &PullRequestLinks{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPullRequestListCommentsOptions_GetDirection(tt *testing.T) { + tt.Parallel() + p := &PullRequestListCommentsOptions{} + p.GetDirection() + p = nil + p.GetDirection() +} + +func TestPullRequestListCommentsOptions_GetSince(tt *testing.T) { + tt.Parallel() + p := &PullRequestListCommentsOptions{} + p.GetSince() + p = nil + p.GetSince() +} + +func TestPullRequestListCommentsOptions_GetSort(tt *testing.T) { + tt.Parallel() + p := &PullRequestListCommentsOptions{} + p.GetSort() + p = nil + p.GetSort() +} + +func TestPullRequestListOptions_GetBase(tt *testing.T) { + tt.Parallel() + p := &PullRequestListOptions{} + p.GetBase() + p = nil + p.GetBase() +} + +func TestPullRequestListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + p := &PullRequestListOptions{} + p.GetDirection() + p = nil + p.GetDirection() +} + +func TestPullRequestListOptions_GetHead(tt *testing.T) { + tt.Parallel() + p := &PullRequestListOptions{} + p.GetHead() + p = nil + p.GetHead() +} + +func TestPullRequestListOptions_GetSort(tt *testing.T) { + tt.Parallel() + p := &PullRequestListOptions{} + p.GetSort() + p = nil + p.GetSort() +} + +func TestPullRequestListOptions_GetState(tt *testing.T) { + tt.Parallel() + p := &PullRequestListOptions{} + p.GetState() + p = nil + p.GetState() +} + +func TestPullRequestMergeResult_GetMerged(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequestMergeResult{Merged: &zeroValue} + p.GetMerged() + p = &PullRequestMergeResult{} + p.GetMerged() + p = nil + p.GetMerged() +} + +func TestPullRequestMergeResult_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestMergeResult{Message: &zeroValue} + p.GetMessage() + p = &PullRequestMergeResult{} + p.GetMessage() + p = nil + p.GetMessage() +} + +func TestPullRequestMergeResult_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestMergeResult{SHA: &zeroValue} + p.GetSHA() + p = &PullRequestMergeResult{} + p.GetSHA() + p = nil + p.GetSHA() +} + +func TestPullRequestOptions_GetCommitTitle(tt *testing.T) { + tt.Parallel() + p := &PullRequestOptions{} + p.GetCommitTitle() + p = nil + p.GetCommitTitle() +} + +func TestPullRequestOptions_GetDontDefaultIfBlank(tt *testing.T) { + tt.Parallel() + p := &PullRequestOptions{} + p.GetDontDefaultIfBlank() + p = nil + p.GetDontDefaultIfBlank() +} + +func TestPullRequestOptions_GetMergeMethod(tt *testing.T) { + tt.Parallel() + p := &PullRequestOptions{} + p.GetMergeMethod() + p = nil + p.GetMergeMethod() +} + +func TestPullRequestOptions_GetSHA(tt *testing.T) { + tt.Parallel() + p := &PullRequestOptions{} + p.GetSHA() + p = nil + p.GetSHA() +} + +func TestPullRequestReview_GetAuthorAssociation(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{AuthorAssociation: &zeroValue} + p.GetAuthorAssociation() + p = &PullRequestReview{} + p.GetAuthorAssociation() + p = nil + p.GetAuthorAssociation() +} + +func TestPullRequestReview_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{Body: &zeroValue} + p.GetBody() + p = &PullRequestReview{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestPullRequestReview_GetCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{CommitID: &zeroValue} + p.GetCommitID() + p = &PullRequestReview{} + p.GetCommitID() + p = nil + p.GetCommitID() +} + +func TestPullRequestReview_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PullRequestReview{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPullRequestReview_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequestReview{ID: &zeroValue} + p.GetID() + p = &PullRequestReview{} + p.GetID() + p = nil + p.GetID() +} + +func TestPullRequestReview_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{NodeID: &zeroValue} + p.GetNodeID() + p = &PullRequestReview{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPullRequestReview_GetPullRequestURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{PullRequestURL: &zeroValue} + p.GetPullRequestURL() + p = &PullRequestReview{} + p.GetPullRequestURL() + p = nil + p.GetPullRequestURL() +} + +func TestPullRequestReview_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReview{State: &zeroValue} + p.GetState() + p = &PullRequestReview{} + p.GetState() + p = nil + p.GetState() +} + +func TestPullRequestReview_GetSubmittedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PullRequestReview{SubmittedAt: &zeroValue} + p.GetSubmittedAt() + p = &PullRequestReview{} + p.GetSubmittedAt() + p = nil + p.GetSubmittedAt() +} + +func TestPullRequestReview_GetUser(tt *testing.T) { + tt.Parallel() + p := &PullRequestReview{} + p.GetUser() + p = nil + p.GetUser() +} + +func TestPullRequestReviewCommentEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewCommentEvent{Action: &zeroValue} + p.GetAction() + p = &PullRequestReviewCommentEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPullRequestReviewCommentEvent_GetChanges(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetChanges() + p = nil + p.GetChanges() +} + +func TestPullRequestReviewCommentEvent_GetComment(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetComment() + p = nil + p.GetComment() +} + +func TestPullRequestReviewCommentEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPullRequestReviewCommentEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPullRequestReviewCommentEvent_GetPullRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetPullRequest() + p = nil + p.GetPullRequest() +} + +func TestPullRequestReviewCommentEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPullRequestReviewCommentEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewCommentEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPullRequestReviewEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewEvent{Action: &zeroValue} + p.GetAction() + p = &PullRequestReviewEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPullRequestReviewEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPullRequestReviewEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewEvent{} + p.GetOrganization() + p = nil + p.GetOrganization() +} + +func TestPullRequestReviewEvent_GetPullRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewEvent{} + p.GetPullRequest() + p = nil + p.GetPullRequest() +} + +func TestPullRequestReviewEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPullRequestReviewEvent_GetReview(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewEvent{} + p.GetReview() + p = nil + p.GetReview() +} + +func TestPullRequestReviewEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPullRequestReviewRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewRequest{Body: &zeroValue} + p.GetBody() + p = &PullRequestReviewRequest{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestPullRequestReviewRequest_GetComments(tt *testing.T) { + tt.Parallel() + zeroValue := []*DraftReviewComment{} + p := &PullRequestReviewRequest{Comments: zeroValue} + p.GetComments() + p = &PullRequestReviewRequest{} + p.GetComments() + p = nil + p.GetComments() +} + +func TestPullRequestReviewRequest_GetCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewRequest{CommitID: &zeroValue} + p.GetCommitID() + p = &PullRequestReviewRequest{} + p.GetCommitID() + p = nil + p.GetCommitID() +} + +func TestPullRequestReviewRequest_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewRequest{Event: &zeroValue} + p.GetEvent() + p = &PullRequestReviewRequest{} + p.GetEvent() + p = nil + p.GetEvent() +} + +func TestPullRequestReviewRequest_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewRequest{NodeID: &zeroValue} + p.GetNodeID() + p = &PullRequestReviewRequest{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPullRequestReviewsEnforcement_GetBypassPullRequestAllowances(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcement{} + p.GetBypassPullRequestAllowances() + p = nil + p.GetBypassPullRequestAllowances() +} + +func TestPullRequestReviewsEnforcement_GetDismissalRestrictions(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcement{} + p.GetDismissalRestrictions() + p = nil + p.GetDismissalRestrictions() +} + +func TestPullRequestReviewsEnforcement_GetDismissStaleReviews(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcement{} + p.GetDismissStaleReviews() + p = nil + p.GetDismissStaleReviews() +} + +func TestPullRequestReviewsEnforcement_GetRequireCodeOwnerReviews(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcement{} + p.GetRequireCodeOwnerReviews() + p = nil + p.GetRequireCodeOwnerReviews() +} + +func TestPullRequestReviewsEnforcement_GetRequiredApprovingReviewCount(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcement{} + p.GetRequiredApprovingReviewCount() + p = nil + p.GetRequiredApprovingReviewCount() +} + +func TestPullRequestReviewsEnforcement_GetRequireLastPushApproval(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcement{} + p.GetRequireLastPushApproval() + p = nil + p.GetRequireLastPushApproval() +} + +func TestPullRequestReviewsEnforcementLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewsEnforcementLevelChanges{From: &zeroValue} + p.GetFrom() + p = &PullRequestReviewsEnforcementLevelChanges{} + p.GetFrom() + p = nil + p.GetFrom() +} + +func TestPullRequestReviewsEnforcementRequest_GetBypassPullRequestAllowancesRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementRequest{} + p.GetBypassPullRequestAllowancesRequest() + p = nil + p.GetBypassPullRequestAllowancesRequest() +} + +func TestPullRequestReviewsEnforcementRequest_GetDismissalRestrictionsRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementRequest{} + p.GetDismissalRestrictionsRequest() + p = nil + p.GetDismissalRestrictionsRequest() +} + +func TestPullRequestReviewsEnforcementRequest_GetDismissStaleReviews(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementRequest{} + p.GetDismissStaleReviews() + p = nil + p.GetDismissStaleReviews() +} + +func TestPullRequestReviewsEnforcementRequest_GetRequireCodeOwnerReviews(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementRequest{} + p.GetRequireCodeOwnerReviews() + p = nil + p.GetRequireCodeOwnerReviews() +} + +func TestPullRequestReviewsEnforcementRequest_GetRequiredApprovingReviewCount(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementRequest{} + p.GetRequiredApprovingReviewCount() + p = nil + p.GetRequiredApprovingReviewCount() +} + +func TestPullRequestReviewsEnforcementRequest_GetRequireLastPushApproval(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequestReviewsEnforcementRequest{RequireLastPushApproval: &zeroValue} + p.GetRequireLastPushApproval() + p = &PullRequestReviewsEnforcementRequest{} + p.GetRequireLastPushApproval() + p = nil + p.GetRequireLastPushApproval() +} + +func TestPullRequestReviewsEnforcementUpdate_GetBypassPullRequestAllowancesRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementUpdate{} + p.GetBypassPullRequestAllowancesRequest() + p = nil + p.GetBypassPullRequestAllowancesRequest() +} + +func TestPullRequestReviewsEnforcementUpdate_GetDismissalRestrictionsRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementUpdate{} + p.GetDismissalRestrictionsRequest() + p = nil + p.GetDismissalRestrictionsRequest() +} + +func TestPullRequestReviewsEnforcementUpdate_GetDismissStaleReviews(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequestReviewsEnforcementUpdate{DismissStaleReviews: &zeroValue} + p.GetDismissStaleReviews() + p = &PullRequestReviewsEnforcementUpdate{} + p.GetDismissStaleReviews() + p = nil + p.GetDismissStaleReviews() +} + +func TestPullRequestReviewsEnforcementUpdate_GetRequireCodeOwnerReviews(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequestReviewsEnforcementUpdate{RequireCodeOwnerReviews: &zeroValue} + p.GetRequireCodeOwnerReviews() + p = &PullRequestReviewsEnforcementUpdate{} + p.GetRequireCodeOwnerReviews() + p = nil + p.GetRequireCodeOwnerReviews() +} + +func TestPullRequestReviewsEnforcementUpdate_GetRequiredApprovingReviewCount(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewsEnforcementUpdate{} + p.GetRequiredApprovingReviewCount() + p = nil + p.GetRequiredApprovingReviewCount() +} + +func TestPullRequestReviewsEnforcementUpdate_GetRequireLastPushApproval(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PullRequestReviewsEnforcementUpdate{RequireLastPushApproval: &zeroValue} + p.GetRequireLastPushApproval() + p = &PullRequestReviewsEnforcementUpdate{} + p.GetRequireLastPushApproval() + p = nil + p.GetRequireLastPushApproval() +} + +func TestPullRequestReviewThreadEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestReviewThreadEvent{Action: &zeroValue} + p.GetAction() + p = &PullRequestReviewThreadEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPullRequestReviewThreadEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewThreadEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPullRequestReviewThreadEvent_GetOrg(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewThreadEvent{} + p.GetOrg() + p = nil + p.GetOrg() +} + +func TestPullRequestReviewThreadEvent_GetPullRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewThreadEvent{} + p.GetPullRequest() + p = nil + p.GetPullRequest() +} + +func TestPullRequestReviewThreadEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewThreadEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPullRequestReviewThreadEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewThreadEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPullRequestReviewThreadEvent_GetThread(tt *testing.T) { + tt.Parallel() + p := &PullRequestReviewThreadEvent{} + p.GetThread() + p = nil + p.GetThread() +} + +func TestPullRequestRuleParameters_GetAllowedMergeMethods(tt *testing.T) { + tt.Parallel() + zeroValue := []PullRequestMergeMethod{} + p := &PullRequestRuleParameters{AllowedMergeMethods: zeroValue} + p.GetAllowedMergeMethods() + p = &PullRequestRuleParameters{} + p.GetAllowedMergeMethods() + p = nil + p.GetAllowedMergeMethods() +} + +func TestPullRequestRuleParameters_GetDismissStaleReviewsOnPush(tt *testing.T) { + tt.Parallel() + p := &PullRequestRuleParameters{} + p.GetDismissStaleReviewsOnPush() + p = nil + p.GetDismissStaleReviewsOnPush() +} + +func TestPullRequestRuleParameters_GetRequireCodeOwnerReview(tt *testing.T) { + tt.Parallel() + p := &PullRequestRuleParameters{} + p.GetRequireCodeOwnerReview() + p = nil + p.GetRequireCodeOwnerReview() +} + +func TestPullRequestRuleParameters_GetRequiredApprovingReviewCount(tt *testing.T) { + tt.Parallel() + p := &PullRequestRuleParameters{} + p.GetRequiredApprovingReviewCount() + p = nil + p.GetRequiredApprovingReviewCount() +} + +func TestPullRequestRuleParameters_GetRequiredReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*RulesetRequiredReviewer{} + p := &PullRequestRuleParameters{RequiredReviewers: zeroValue} + p.GetRequiredReviewers() + p = &PullRequestRuleParameters{} + p.GetRequiredReviewers() + p = nil + p.GetRequiredReviewers() +} + +func TestPullRequestRuleParameters_GetRequiredReviewThreadResolution(tt *testing.T) { + tt.Parallel() + p := &PullRequestRuleParameters{} + p.GetRequiredReviewThreadResolution() + p = nil + p.GetRequiredReviewThreadResolution() +} + +func TestPullRequestRuleParameters_GetRequireLastPushApproval(tt *testing.T) { + tt.Parallel() + p := &PullRequestRuleParameters{} + p.GetRequireLastPushApproval() + p = nil + p.GetRequireLastPushApproval() +} + +func TestPullRequestStack_GetBase(tt *testing.T) { + tt.Parallel() + p := &PullRequestStack{} + p.GetBase() + p = nil + p.GetBase() +} + +func TestPullRequestStack_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequestStack{ID: &zeroValue} + p.GetID() + p = &PullRequestStack{} + p.GetID() + p = nil + p.GetID() +} + +func TestPullRequestStack_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestStack{Number: &zeroValue} + p.GetNumber() + p = &PullRequestStack{} + p.GetNumber() + p = nil + p.GetNumber() +} + +func TestPullRequestStack_GetPosition(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestStack{Position: &zeroValue} + p.GetPosition() + p = &PullRequestStack{} + p.GetPosition() + p = nil + p.GetPosition() +} + +func TestPullRequestStack_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestStack{Size: &zeroValue} + p.GetSize() + p = &PullRequestStack{} + p.GetSize() + p = nil + p.GetSize() +} + +func TestPullRequestStackBase_GetRef(tt *testing.T) { + tt.Parallel() + p := &PullRequestStackBase{} + p.GetRef() + p = nil + p.GetRef() +} + +func TestPullRequestStackBase_GetSHA(tt *testing.T) { + tt.Parallel() + p := &PullRequestStackBase{} + p.GetSHA() + p = nil + p.GetSHA() +} + +func TestPullRequestSubmitReviewRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestSubmitReviewRequest{Body: &zeroValue} + p.GetBody() + p = &PullRequestSubmitReviewRequest{} + p.GetBody() + p = nil + p.GetBody() +} + +func TestPullRequestSubmitReviewRequest_GetEvent(tt *testing.T) { + tt.Parallel() + p := &PullRequestSubmitReviewRequest{} + p.GetEvent() + p = nil + p.GetEvent() +} + +func TestPullRequestTargetEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestTargetEvent{Action: &zeroValue} + p.GetAction() + p = &PullRequestTargetEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPullRequestTargetEvent_GetAfter(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestTargetEvent{After: &zeroValue} + p.GetAfter() + p = &PullRequestTargetEvent{} + p.GetAfter() + p = nil + p.GetAfter() +} + +func TestPullRequestTargetEvent_GetAssignee(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetAssignee() + p = nil + p.GetAssignee() +} + +func TestPullRequestTargetEvent_GetBefore(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestTargetEvent{Before: &zeroValue} + p.GetBefore() + p = &PullRequestTargetEvent{} + p.GetBefore() + p = nil + p.GetBefore() +} + +func TestPullRequestTargetEvent_GetChanges(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetChanges() + p = nil + p.GetChanges() +} + +func TestPullRequestTargetEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPullRequestTargetEvent_GetLabel(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetLabel() + p = nil + p.GetLabel() +} + +func TestPullRequestTargetEvent_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullRequestTargetEvent{Number: &zeroValue} + p.GetNumber() + p = &PullRequestTargetEvent{} + p.GetNumber() + p = nil + p.GetNumber() +} + +func TestPullRequestTargetEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetOrganization() + p = nil + p.GetOrganization() +} + +func TestPullRequestTargetEvent_GetPerformedViaGithubApp(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetPerformedViaGithubApp() + p = nil + p.GetPerformedViaGithubApp() +} + +func TestPullRequestTargetEvent_GetPullRequest(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetPullRequest() + p = nil + p.GetPullRequest() +} + +func TestPullRequestTargetEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPullRequestTargetEvent_GetRequestedReviewer(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetRequestedReviewer() + p = nil + p.GetRequestedReviewer() +} + +func TestPullRequestTargetEvent_GetRequestedTeam(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetRequestedTeam() + p = nil + p.GetRequestedTeam() +} + +func TestPullRequestTargetEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PullRequestTargetEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPullRequestThread_GetComments(tt *testing.T) { + tt.Parallel() + zeroValue := []*PullRequestComment{} + p := &PullRequestThread{Comments: zeroValue} + p.GetComments() + p = &PullRequestThread{} + p.GetComments() + p = nil + p.GetComments() +} + +func TestPullRequestThread_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PullRequestThread{ID: &zeroValue} + p.GetID() + p = &PullRequestThread{} + p.GetID() + p = nil + p.GetID() +} + +func TestPullRequestThread_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PullRequestThread{NodeID: &zeroValue} + p.GetNodeID() + p = &PullRequestThread{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPullStats_GetMergeablePulls(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullStats{MergeablePulls: &zeroValue} + p.GetMergeablePulls() + p = &PullStats{} + p.GetMergeablePulls() + p = nil + p.GetMergeablePulls() +} + +func TestPullStats_GetMergedPulls(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullStats{MergedPulls: &zeroValue} + p.GetMergedPulls() + p = &PullStats{} + p.GetMergedPulls() + p = nil + p.GetMergedPulls() +} + +func TestPullStats_GetTotalPulls(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullStats{TotalPulls: &zeroValue} + p.GetTotalPulls() + p = &PullStats{} + p.GetTotalPulls() + p = nil + p.GetTotalPulls() +} + +func TestPullStats_GetUnmergeablePulls(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PullStats{UnmergeablePulls: &zeroValue} + p.GetUnmergeablePulls() + p = &PullStats{} + p.GetUnmergeablePulls() + p = nil + p.GetUnmergeablePulls() +} + +func TestPunchCard_GetCommits(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PunchCard{Commits: &zeroValue} + p.GetCommits() + p = &PunchCard{} + p.GetCommits() + p = nil + p.GetCommits() +} + +func TestPunchCard_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PunchCard{Day: &zeroValue} + p.GetDay() + p = &PunchCard{} + p.GetDay() + p = nil + p.GetDay() +} + +func TestPunchCard_GetHour(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PunchCard{Hour: &zeroValue} + p.GetHour() + p = &PunchCard{} + p.GetHour() + p = nil + p.GetHour() +} + +func TestPushEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{Action: &zeroValue} + p.GetAction() + p = &PushEvent{} + p.GetAction() + p = nil + p.GetAction() +} + +func TestPushEvent_GetAfter(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{After: &zeroValue} + p.GetAfter() + p = &PushEvent{} + p.GetAfter() + p = nil + p.GetAfter() +} + +func TestPushEvent_GetBaseRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{BaseRef: &zeroValue} + p.GetBaseRef() + p = &PushEvent{} + p.GetBaseRef() + p = nil + p.GetBaseRef() +} + +func TestPushEvent_GetBefore(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{Before: &zeroValue} + p.GetBefore() + p = &PushEvent{} + p.GetBefore() + p = nil + p.GetBefore() +} + +func TestPushEvent_GetCommits(tt *testing.T) { + tt.Parallel() + zeroValue := []*HeadCommit{} + p := &PushEvent{Commits: zeroValue} + p.GetCommits() + p = &PushEvent{} + p.GetCommits() + p = nil + p.GetCommits() +} + +func TestPushEvent_GetCompare(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{Compare: &zeroValue} + p.GetCompare() + p = &PushEvent{} + p.GetCompare() + p = nil + p.GetCompare() +} + +func TestPushEvent_GetCreated(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEvent{Created: &zeroValue} + p.GetCreated() + p = &PushEvent{} + p.GetCreated() + p = nil + p.GetCreated() +} + +func TestPushEvent_GetDeleted(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEvent{Deleted: &zeroValue} + p.GetDeleted() + p = &PushEvent{} + p.GetDeleted() + p = nil + p.GetDeleted() +} + +func TestPushEvent_GetDistinctSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEvent{DistinctSize: &zeroValue} + p.GetDistinctSize() + p = &PushEvent{} + p.GetDistinctSize() + p = nil + p.GetDistinctSize() +} + +func TestPushEvent_GetForced(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEvent{Forced: &zeroValue} + p.GetForced() + p = &PushEvent{} + p.GetForced() + p = nil + p.GetForced() +} + +func TestPushEvent_GetHead(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{Head: &zeroValue} + p.GetHead() + p = &PushEvent{} + p.GetHead() + p = nil + p.GetHead() +} + +func TestPushEvent_GetHeadCommit(tt *testing.T) { + tt.Parallel() + p := &PushEvent{} + p.GetHeadCommit() + p = nil + p.GetHeadCommit() +} + +func TestPushEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + p := &PushEvent{} + p.GetInstallation() + p = nil + p.GetInstallation() +} + +func TestPushEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + p := &PushEvent{} + p.GetOrganization() + p = nil + p.GetOrganization() +} + +func TestPushEvent_GetPusher(tt *testing.T) { + tt.Parallel() + p := &PushEvent{} + p.GetPusher() + p = nil + p.GetPusher() +} + +func TestPushEvent_GetPushID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PushEvent{PushID: &zeroValue} + p.GetPushID() + p = &PushEvent{} + p.GetPushID() + p = nil + p.GetPushID() +} + +func TestPushEvent_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEvent{Ref: &zeroValue} + p.GetRef() + p = &PushEvent{} + p.GetRef() + p = nil + p.GetRef() +} + +func TestPushEvent_GetRepo(tt *testing.T) { + tt.Parallel() + p := &PushEvent{} + p.GetRepo() + p = nil + p.GetRepo() +} + +func TestPushEvent_GetSender(tt *testing.T) { + tt.Parallel() + p := &PushEvent{} + p.GetSender() + p = nil + p.GetSender() +} + +func TestPushEvent_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEvent{Size: &zeroValue} + p.GetSize() + p = &PushEvent{} + p.GetSize() + p = nil + p.GetSize() +} + +func TestPushEventRepoOwner_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepoOwner{Email: &zeroValue} + p.GetEmail() + p = &PushEventRepoOwner{} + p.GetEmail() + p = nil + p.GetEmail() +} + +func TestPushEventRepoOwner_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepoOwner{Name: &zeroValue} + p.GetName() + p = &PushEventRepoOwner{} + p.GetName() + p = nil + p.GetName() +} + +func TestPushEventRepository_GetArchived(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{Archived: &zeroValue} + p.GetArchived() + p = &PushEventRepository{} + p.GetArchived() + p = nil + p.GetArchived() +} + +func TestPushEventRepository_GetArchiveURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{ArchiveURL: &zeroValue} + p.GetArchiveURL() + p = &PushEventRepository{} + p.GetArchiveURL() + p = nil + p.GetArchiveURL() +} + +func TestPushEventRepository_GetCloneURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{CloneURL: &zeroValue} + p.GetCloneURL() + p = &PushEventRepository{} + p.GetCloneURL() + p = nil + p.GetCloneURL() +} + +func TestPushEventRepository_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PushEventRepository{CreatedAt: &zeroValue} + p.GetCreatedAt() + p = &PushEventRepository{} + p.GetCreatedAt() + p = nil + p.GetCreatedAt() +} + +func TestPushEventRepository_GetCustomProperties(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + p := &PushEventRepository{CustomProperties: zeroValue} + p.GetCustomProperties() + p = &PushEventRepository{} + p.GetCustomProperties() + p = nil + p.GetCustomProperties() +} + +func TestPushEventRepository_GetDefaultBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{DefaultBranch: &zeroValue} + p.GetDefaultBranch() + p = &PushEventRepository{} + p.GetDefaultBranch() + p = nil + p.GetDefaultBranch() +} + +func TestPushEventRepository_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{Description: &zeroValue} + p.GetDescription() + p = &PushEventRepository{} + p.GetDescription() + p = nil + p.GetDescription() +} + +func TestPushEventRepository_GetDisabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{Disabled: &zeroValue} + p.GetDisabled() + p = &PushEventRepository{} + p.GetDisabled() + p = nil + p.GetDisabled() +} + +func TestPushEventRepository_GetFork(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{Fork: &zeroValue} + p.GetFork() + p = &PushEventRepository{} + p.GetFork() + p = nil + p.GetFork() +} + +func TestPushEventRepository_GetForksCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEventRepository{ForksCount: &zeroValue} + p.GetForksCount() + p = &PushEventRepository{} + p.GetForksCount() + p = nil + p.GetForksCount() +} + +func TestPushEventRepository_GetFullName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{FullName: &zeroValue} + p.GetFullName() + p = &PushEventRepository{} + p.GetFullName() + p = nil + p.GetFullName() +} + +func TestPushEventRepository_GetGitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{GitURL: &zeroValue} + p.GetGitURL() + p = &PushEventRepository{} + p.GetGitURL() + p = nil + p.GetGitURL() +} + +func TestPushEventRepository_GetHasDownloads(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{HasDownloads: &zeroValue} + p.GetHasDownloads() + p = &PushEventRepository{} + p.GetHasDownloads() + p = nil + p.GetHasDownloads() +} + +func TestPushEventRepository_GetHasIssues(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{HasIssues: &zeroValue} + p.GetHasIssues() + p = &PushEventRepository{} + p.GetHasIssues() + p = nil + p.GetHasIssues() +} + +func TestPushEventRepository_GetHasPages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{HasPages: &zeroValue} + p.GetHasPages() + p = &PushEventRepository{} + p.GetHasPages() + p = nil + p.GetHasPages() +} + +func TestPushEventRepository_GetHasWiki(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{HasWiki: &zeroValue} + p.GetHasWiki() + p = &PushEventRepository{} + p.GetHasWiki() + p = nil + p.GetHasWiki() +} + +func TestPushEventRepository_GetHomepage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{Homepage: &zeroValue} + p.GetHomepage() + p = &PushEventRepository{} + p.GetHomepage() + p = nil + p.GetHomepage() +} + +func TestPushEventRepository_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{HTMLURL: &zeroValue} + p.GetHTMLURL() + p = &PushEventRepository{} + p.GetHTMLURL() + p = nil + p.GetHTMLURL() +} + +func TestPushEventRepository_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + p := &PushEventRepository{ID: &zeroValue} + p.GetID() + p = &PushEventRepository{} + p.GetID() + p = nil + p.GetID() +} + +func TestPushEventRepository_GetLanguage(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{Language: &zeroValue} + p.GetLanguage() + p = &PushEventRepository{} + p.GetLanguage() + p = nil + p.GetLanguage() +} + +func TestPushEventRepository_GetMasterBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{MasterBranch: &zeroValue} + p.GetMasterBranch() + p = &PushEventRepository{} + p.GetMasterBranch() + p = nil + p.GetMasterBranch() +} + +func TestPushEventRepository_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{Name: &zeroValue} + p.GetName() + p = &PushEventRepository{} + p.GetName() + p = nil + p.GetName() +} + +func TestPushEventRepository_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{NodeID: &zeroValue} + p.GetNodeID() + p = &PushEventRepository{} + p.GetNodeID() + p = nil + p.GetNodeID() +} + +func TestPushEventRepository_GetOpenIssuesCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEventRepository{OpenIssuesCount: &zeroValue} + p.GetOpenIssuesCount() + p = &PushEventRepository{} + p.GetOpenIssuesCount() + p = nil + p.GetOpenIssuesCount() +} + +func TestPushEventRepository_GetOrganization(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{Organization: &zeroValue} + p.GetOrganization() + p = &PushEventRepository{} + p.GetOrganization() + p = nil + p.GetOrganization() +} + +func TestPushEventRepository_GetOwner(tt *testing.T) { + tt.Parallel() + p := &PushEventRepository{} + p.GetOwner() + p = nil + p.GetOwner() +} + +func TestPushEventRepository_GetPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + p := &PushEventRepository{Private: &zeroValue} + p.GetPrivate() + p = &PushEventRepository{} + p.GetPrivate() + p = nil + p.GetPrivate() +} + +func TestPushEventRepository_GetPullsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{PullsURL: &zeroValue} + p.GetPullsURL() + p = &PushEventRepository{} + p.GetPullsURL() + p = nil + p.GetPullsURL() +} + +func TestPushEventRepository_GetPushedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PushEventRepository{PushedAt: &zeroValue} + p.GetPushedAt() + p = &PushEventRepository{} + p.GetPushedAt() + p = nil + p.GetPushedAt() +} + +func TestPushEventRepository_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEventRepository{Size: &zeroValue} + p.GetSize() + p = &PushEventRepository{} + p.GetSize() + p = nil + p.GetSize() +} + +func TestPushEventRepository_GetSSHURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{SSHURL: &zeroValue} + p.GetSSHURL() + p = &PushEventRepository{} + p.GetSSHURL() + p = nil + p.GetSSHURL() +} + +func TestPushEventRepository_GetStargazersCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEventRepository{StargazersCount: &zeroValue} + p.GetStargazersCount() + p = &PushEventRepository{} + p.GetStargazersCount() + p = nil + p.GetStargazersCount() +} + +func TestPushEventRepository_GetStatusesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{StatusesURL: &zeroValue} + p.GetStatusesURL() + p = &PushEventRepository{} + p.GetStatusesURL() + p = nil + p.GetStatusesURL() +} + +func TestPushEventRepository_GetSVNURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{SVNURL: &zeroValue} + p.GetSVNURL() + p = &PushEventRepository{} + p.GetSVNURL() + p = nil + p.GetSVNURL() +} + +func TestPushEventRepository_GetTopics(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + p := &PushEventRepository{Topics: zeroValue} + p.GetTopics() + p = &PushEventRepository{} + p.GetTopics() + p = nil + p.GetTopics() +} + +func TestPushEventRepository_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PushEventRepository{UpdatedAt: &zeroValue} + p.GetUpdatedAt() + p = &PushEventRepository{} + p.GetUpdatedAt() + p = nil + p.GetUpdatedAt() +} + +func TestPushEventRepository_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + p := &PushEventRepository{URL: &zeroValue} + p.GetURL() + p = &PushEventRepository{} + p.GetURL() + p = nil + p.GetURL() +} + +func TestPushEventRepository_GetWatchersCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + p := &PushEventRepository{WatchersCount: &zeroValue} + p.GetWatchersCount() + p = &PushEventRepository{} + p.GetWatchersCount() + p = nil + p.GetWatchersCount() +} + +func TestPushProtectionBypass_GetExpireAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + p := &PushProtectionBypass{ExpireAt: &zeroValue} + p.GetExpireAt() + p = &PushProtectionBypass{} + p.GetExpireAt() + p = nil + p.GetExpireAt() +} + +func TestPushProtectionBypass_GetReason(tt *testing.T) { + tt.Parallel() + p := &PushProtectionBypass{} + p.GetReason() + p = nil + p.GetReason() +} + +func TestPushProtectionBypass_GetTokenType(tt *testing.T) { + tt.Parallel() + p := &PushProtectionBypass{} + p.GetTokenType() + p = nil + p.GetTokenType() +} + +func TestPushProtectionBypassRequest_GetPlaceholderID(tt *testing.T) { + tt.Parallel() + p := &PushProtectionBypassRequest{} + p.GetPlaceholderID() + p = nil + p.GetPlaceholderID() +} + +func TestPushProtectionBypassRequest_GetReason(tt *testing.T) { + tt.Parallel() + p := &PushProtectionBypassRequest{} + p.GetReason() + p = nil + p.GetReason() +} + +func TestRate_GetLimit(tt *testing.T) { + tt.Parallel() + r := &Rate{} + r.GetLimit() + r = nil + r.GetLimit() +} + +func TestRate_GetRemaining(tt *testing.T) { + tt.Parallel() + r := &Rate{} + r.GetRemaining() + r = nil + r.GetRemaining() +} + +func TestRate_GetReset(tt *testing.T) { + tt.Parallel() + r := &Rate{} + r.GetReset() + r = nil + r.GetReset() +} + +func TestRate_GetResource(tt *testing.T) { + tt.Parallel() + r := &Rate{} + r.GetResource() + r = nil + r.GetResource() +} + +func TestRate_GetUsed(tt *testing.T) { + tt.Parallel() + r := &Rate{} + r.GetUsed() + r = nil + r.GetUsed() +} + +func TestRateLimitError_GetMessage(tt *testing.T) { + tt.Parallel() + r := &RateLimitError{} + r.GetMessage() + r = nil + r.GetMessage() +} + +func TestRateLimitError_GetRate(tt *testing.T) { + tt.Parallel() + r := &RateLimitError{} + r.GetRate() + r = nil + r.GetRate() +} + +func TestRateLimits_GetActionsRunnerRegistration(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetActionsRunnerRegistration() + r = nil + r.GetActionsRunnerRegistration() +} + +func TestRateLimits_GetAuditLog(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetAuditLog() + r = nil + r.GetAuditLog() +} + +func TestRateLimits_GetCodeScanningUpload(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetCodeScanningUpload() + r = nil + r.GetCodeScanningUpload() +} + +func TestRateLimits_GetCodeSearch(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetCodeSearch() + r = nil + r.GetCodeSearch() +} + +func TestRateLimits_GetCore(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetCore() + r = nil + r.GetCore() +} + +func TestRateLimits_GetDependencySBOM(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetDependencySBOM() + r = nil + r.GetDependencySBOM() +} + +func TestRateLimits_GetDependencySnapshots(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetDependencySnapshots() + r = nil + r.GetDependencySnapshots() +} + +func TestRateLimits_GetGraphQL(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetGraphQL() + r = nil + r.GetGraphQL() +} + +func TestRateLimits_GetIntegrationManifest(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetIntegrationManifest() + r = nil + r.GetIntegrationManifest() +} + +func TestRateLimits_GetSCIM(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetSCIM() + r = nil + r.GetSCIM() +} + +func TestRateLimits_GetSearch(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetSearch() + r = nil + r.GetSearch() +} + +func TestRateLimits_GetSourceImport(tt *testing.T) { + tt.Parallel() + r := &RateLimits{} + r.GetSourceImport() + r = nil + r.GetSourceImport() +} + +func TestRawOptions_GetType(tt *testing.T) { + tt.Parallel() + r := &RawOptions{} + r.GetType() + r = nil + r.GetType() +} + +func TestReaction_GetContent(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Reaction{Content: &zeroValue} + r.GetContent() + r = &Reaction{} + r.GetContent() + r = nil + r.GetContent() +} + +func TestReaction_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &Reaction{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &Reaction{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestReaction_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &Reaction{ID: &zeroValue} + r.GetID() + r = &Reaction{} + r.GetID() + r = nil + r.GetID() +} + +func TestReaction_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Reaction{NodeID: &zeroValue} + r.GetNodeID() + r = &Reaction{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestReaction_GetUser(tt *testing.T) { + tt.Parallel() + r := &Reaction{} + r.GetUser() + r = nil + r.GetUser() +} + +func TestReactions_GetConfused(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{Confused: &zeroValue} + r.GetConfused() + r = &Reactions{} + r.GetConfused() + r = nil + r.GetConfused() +} + +func TestReactions_GetEyes(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{Eyes: &zeroValue} + r.GetEyes() + r = &Reactions{} + r.GetEyes() + r = nil + r.GetEyes() +} + +func TestReactions_GetHeart(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{Heart: &zeroValue} + r.GetHeart() + r = &Reactions{} + r.GetHeart() + r = nil + r.GetHeart() +} + +func TestReactions_GetHooray(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{Hooray: &zeroValue} + r.GetHooray() + r = &Reactions{} + r.GetHooray() + r = nil + r.GetHooray() +} + +func TestReactions_GetLaugh(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{Laugh: &zeroValue} + r.GetLaugh() + r = &Reactions{} + r.GetLaugh() + r = nil + r.GetLaugh() +} + +func TestReactions_GetMinusOne(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{MinusOne: &zeroValue} + r.GetMinusOne() + r = &Reactions{} + r.GetMinusOne() + r = nil + r.GetMinusOne() +} + +func TestReactions_GetPlusOne(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{PlusOne: &zeroValue} + r.GetPlusOne() + r = &Reactions{} + r.GetPlusOne() + r = nil + r.GetPlusOne() +} + +func TestReactions_GetRocket(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{Rocket: &zeroValue} + r.GetRocket() + r = &Reactions{} + r.GetRocket() + r = nil + r.GetRocket() +} + +func TestReactions_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Reactions{TotalCount: &zeroValue} + r.GetTotalCount() + r = &Reactions{} + r.GetTotalCount() + r = nil + r.GetTotalCount() +} + +func TestReactions_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Reactions{URL: &zeroValue} + r.GetURL() + r = &Reactions{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestReassignedResource_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReassignedResource{Name: &zeroValue} + r.GetName() + r = &ReassignedResource{} + r.GetName() + r = nil + r.GetName() +} + +func TestReassignedResource_GetPreviousCostCenter(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReassignedResource{PreviousCostCenter: &zeroValue} + r.GetPreviousCostCenter() + r = &ReassignedResource{} + r.GetPreviousCostCenter() + r = nil + r.GetPreviousCostCenter() +} + +func TestReassignedResource_GetResourceType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReassignedResource{ResourceType: &zeroValue} + r.GetResourceType() + r = &ReassignedResource{} + r.GetResourceType() + r = nil + r.GetResourceType() +} + +func TestRedirectionError_GetStatusCode(tt *testing.T) { + tt.Parallel() + r := &RedirectionError{} + r.GetStatusCode() + r = nil + r.GetStatusCode() +} + +func TestReference_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Reference{NodeID: &zeroValue} + r.GetNodeID() + r = &Reference{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestReference_GetObject(tt *testing.T) { + tt.Parallel() + r := &Reference{} + r.GetObject() + r = nil + r.GetObject() +} + +func TestReference_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Reference{Ref: &zeroValue} + r.GetRef() + r = &Reference{} + r.GetRef() + r = nil + r.GetRef() +} + +func TestReference_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Reference{URL: &zeroValue} + r.GetURL() + r = &Reference{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestReferencedWorkflow_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReferencedWorkflow{Path: &zeroValue} + r.GetPath() + r = &ReferencedWorkflow{} + r.GetPath() + r = nil + r.GetPath() +} + +func TestReferencedWorkflow_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReferencedWorkflow{Ref: &zeroValue} + r.GetRef() + r = &ReferencedWorkflow{} + r.GetRef() + r = nil + r.GetRef() +} + +func TestReferencedWorkflow_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReferencedWorkflow{SHA: &zeroValue} + r.GetSHA() + r = &ReferencedWorkflow{} + r.GetSHA() + r = nil + r.GetSHA() +} + +func TestRegistrationToken_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RegistrationToken{ExpiresAt: &zeroValue} + r.GetExpiresAt() + r = &RegistrationToken{} + r.GetExpiresAt() + r = nil + r.GetExpiresAt() +} + +func TestRegistrationToken_GetToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RegistrationToken{Token: &zeroValue} + r.GetToken() + r = &RegistrationToken{} + r.GetToken() + r = nil + r.GetToken() +} + +func TestRegistryPackageEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RegistryPackageEvent{Action: &zeroValue} + r.GetAction() + r = &RegistryPackageEvent{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestRegistryPackageEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + r := &RegistryPackageEvent{} + r.GetEnterprise() + r = nil + r.GetEnterprise() +} + +func TestRegistryPackageEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + r := &RegistryPackageEvent{} + r.GetInstallation() + r = nil + r.GetInstallation() +} + +func TestRegistryPackageEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + r := &RegistryPackageEvent{} + r.GetOrganization() + r = nil + r.GetOrganization() +} + +func TestRegistryPackageEvent_GetRegistryPackage(tt *testing.T) { + tt.Parallel() + r := &RegistryPackageEvent{} + r.GetRegistryPackage() + r = nil + r.GetRegistryPackage() +} + +func TestRegistryPackageEvent_GetRepository(tt *testing.T) { + tt.Parallel() + r := &RegistryPackageEvent{} + r.GetRepository() + r = nil + r.GetRepository() +} + +func TestRegistryPackageEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &RegistryPackageEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestReleaseAsset_GetBrowserDownloadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{BrowserDownloadURL: &zeroValue} + r.GetBrowserDownloadURL() + r = &ReleaseAsset{} + r.GetBrowserDownloadURL() + r = nil + r.GetBrowserDownloadURL() +} + +func TestReleaseAsset_GetContentType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{ContentType: &zeroValue} + r.GetContentType() + r = &ReleaseAsset{} + r.GetContentType() + r = nil + r.GetContentType() +} + +func TestReleaseAsset_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &ReleaseAsset{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &ReleaseAsset{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestReleaseAsset_GetDigest(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{Digest: &zeroValue} + r.GetDigest() + r = &ReleaseAsset{} + r.GetDigest() + r = nil + r.GetDigest() +} + +func TestReleaseAsset_GetDownloadCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &ReleaseAsset{DownloadCount: &zeroValue} + r.GetDownloadCount() + r = &ReleaseAsset{} + r.GetDownloadCount() + r = nil + r.GetDownloadCount() +} + +func TestReleaseAsset_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &ReleaseAsset{ID: &zeroValue} + r.GetID() + r = &ReleaseAsset{} + r.GetID() + r = nil + r.GetID() +} + +func TestReleaseAsset_GetLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{Label: &zeroValue} + r.GetLabel() + r = &ReleaseAsset{} + r.GetLabel() + r = nil + r.GetLabel() +} + +func TestReleaseAsset_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{Name: &zeroValue} + r.GetName() + r = &ReleaseAsset{} + r.GetName() + r = nil + r.GetName() +} + +func TestReleaseAsset_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{NodeID: &zeroValue} + r.GetNodeID() + r = &ReleaseAsset{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestReleaseAsset_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &ReleaseAsset{Size: &zeroValue} + r.GetSize() + r = &ReleaseAsset{} + r.GetSize() + r = nil + r.GetSize() +} + +func TestReleaseAsset_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{State: &zeroValue} + r.GetState() + r = &ReleaseAsset{} + r.GetState() + r = nil + r.GetState() +} + +func TestReleaseAsset_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &ReleaseAsset{UpdatedAt: &zeroValue} + r.GetUpdatedAt() + r = &ReleaseAsset{} + r.GetUpdatedAt() + r = nil + r.GetUpdatedAt() +} + +func TestReleaseAsset_GetUploader(tt *testing.T) { + tt.Parallel() + r := &ReleaseAsset{} + r.GetUploader() + r = nil + r.GetUploader() +} + +func TestReleaseAsset_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseAsset{URL: &zeroValue} + r.GetURL() + r = &ReleaseAsset{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestReleaseEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseEvent{Action: &zeroValue} + r.GetAction() + r = &ReleaseEvent{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestReleaseEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetInstallation() + r = nil + r.GetInstallation() +} + +func TestReleaseEvent_GetOrg(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetOrg() + r = nil + r.GetOrg() +} + +func TestReleaseEvent_GetRelease(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetRelease() + r = nil + r.GetRelease() +} + +func TestReleaseEvent_GetRepo(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetRepo() + r = nil + r.GetRepo() +} + +func TestReleaseEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &ReleaseEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestReleaseVersion_GetBuildDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseVersion{BuildDate: &zeroValue} + r.GetBuildDate() + r = &ReleaseVersion{} + r.GetBuildDate() + r = nil + r.GetBuildDate() +} + +func TestReleaseVersion_GetBuildID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseVersion{BuildID: &zeroValue} + r.GetBuildID() + r = &ReleaseVersion{} + r.GetBuildID() + r = nil + r.GetBuildID() +} + +func TestReleaseVersion_GetPlatform(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseVersion{Platform: &zeroValue} + r.GetPlatform() + r = &ReleaseVersion{} + r.GetPlatform() + r = nil + r.GetPlatform() +} + +func TestReleaseVersion_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReleaseVersion{Version: &zeroValue} + r.GetVersion() + r = &ReleaseVersion{} + r.GetVersion() + r = nil + r.GetVersion() +} + +func TestRemoveResourcesFromCostCenterResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RemoveResourcesFromCostCenterResponse{Message: &zeroValue} + r.GetMessage() + r = &RemoveResourcesFromCostCenterResponse{} + r.GetMessage() + r = nil + r.GetMessage() +} + +func TestRemoveToken_GetExpiresAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RemoveToken{ExpiresAt: &zeroValue} + r.GetExpiresAt() + r = &RemoveToken{} + r.GetExpiresAt() + r = nil + r.GetExpiresAt() +} + +func TestRemoveToken_GetToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RemoveToken{Token: &zeroValue} + r.GetToken() + r = &RemoveToken{} + r.GetToken() + r = nil + r.GetToken() +} + +func TestRename_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rename{From: &zeroValue} + r.GetFrom() + r = &Rename{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRename_GetTo(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rename{To: &zeroValue} + r.GetTo() + r = &Rename{} + r.GetTo() + r = nil + r.GetTo() +} + +func TestRenameOrgResponse_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RenameOrgResponse{Message: &zeroValue} + r.GetMessage() + r = &RenameOrgResponse{} + r.GetMessage() + r = nil + r.GetMessage() +} + +func TestRenameOrgResponse_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RenameOrgResponse{URL: &zeroValue} + r.GetURL() + r = &RenameOrgResponse{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepoAdvisoryCredit_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoAdvisoryCredit{Login: &zeroValue} + r.GetLogin() + r = &RepoAdvisoryCredit{} + r.GetLogin() + r = nil + r.GetLogin() +} + +func TestRepoAdvisoryCredit_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoAdvisoryCredit{Type: &zeroValue} + r.GetType() + r = &RepoAdvisoryCredit{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepoAdvisoryCreditDetailed_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoAdvisoryCreditDetailed{State: &zeroValue} + r.GetState() + r = &RepoAdvisoryCreditDetailed{} + r.GetState() + r = nil + r.GetState() +} + +func TestRepoAdvisoryCreditDetailed_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoAdvisoryCreditDetailed{Type: &zeroValue} + r.GetType() + r = &RepoAdvisoryCreditDetailed{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepoAdvisoryCreditDetailed_GetUser(tt *testing.T) { + tt.Parallel() + r := &RepoAdvisoryCreditDetailed{} + r.GetUser() + r = nil + r.GetUser() +} + +func TestRepoCustomPropertyValue_GetProperties(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPropertyValue{} + r := &RepoCustomPropertyValue{Properties: zeroValue} + r.GetProperties() + r = &RepoCustomPropertyValue{} + r.GetProperties() + r = nil + r.GetProperties() +} + +func TestRepoCustomPropertyValue_GetRepositoryFullName(tt *testing.T) { + tt.Parallel() + r := &RepoCustomPropertyValue{} + r.GetRepositoryFullName() + r = nil + r.GetRepositoryFullName() +} + +func TestRepoCustomPropertyValue_GetRepositoryID(tt *testing.T) { + tt.Parallel() + r := &RepoCustomPropertyValue{} + r.GetRepositoryID() + r = nil + r.GetRepositoryID() +} + +func TestRepoCustomPropertyValue_GetRepositoryName(tt *testing.T) { + tt.Parallel() + r := &RepoCustomPropertyValue{} + r.GetRepositoryName() + r = nil + r.GetRepositoryName() +} + +func TestRepoDependencies_GetDownloadLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoDependencies{DownloadLocation: &zeroValue} + r.GetDownloadLocation() + r = &RepoDependencies{} + r.GetDownloadLocation() + r = nil + r.GetDownloadLocation() +} + +func TestRepoDependencies_GetExternalRefs(tt *testing.T) { + tt.Parallel() + zeroValue := []*PackageExternalRef{} + r := &RepoDependencies{ExternalRefs: zeroValue} + r.GetExternalRefs() + r = &RepoDependencies{} + r.GetExternalRefs() + r = nil + r.GetExternalRefs() +} + +func TestRepoDependencies_GetFilesAnalyzed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepoDependencies{FilesAnalyzed: &zeroValue} + r.GetFilesAnalyzed() + r = &RepoDependencies{} + r.GetFilesAnalyzed() + r = nil + r.GetFilesAnalyzed() +} + +func TestRepoDependencies_GetLicenseConcluded(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoDependencies{LicenseConcluded: &zeroValue} + r.GetLicenseConcluded() + r = &RepoDependencies{} + r.GetLicenseConcluded() + r = nil + r.GetLicenseConcluded() +} + +func TestRepoDependencies_GetLicenseDeclared(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoDependencies{LicenseDeclared: &zeroValue} + r.GetLicenseDeclared() + r = &RepoDependencies{} + r.GetLicenseDeclared() + r = nil + r.GetLicenseDeclared() +} + +func TestRepoDependencies_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoDependencies{Name: &zeroValue} + r.GetName() + r = &RepoDependencies{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepoDependencies_GetSPDXID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoDependencies{SPDXID: &zeroValue} + r.GetSPDXID() + r = &RepoDependencies{} + r.GetSPDXID() + r = nil + r.GetSPDXID() +} + +func TestRepoDependencies_GetVersionInfo(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoDependencies{VersionInfo: &zeroValue} + r.GetVersionInfo() + r = &RepoDependencies{} + r.GetVersionInfo() + r = nil + r.GetVersionInfo() +} + +func TestRepoFineGrainedPermission_GetDescription(tt *testing.T) { + tt.Parallel() + r := &RepoFineGrainedPermission{} + r.GetDescription() + r = nil + r.GetDescription() +} + +func TestRepoFineGrainedPermission_GetName(tt *testing.T) { + tt.Parallel() + r := &RepoFineGrainedPermission{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepoImmutableReleasesStatus_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepoImmutableReleasesStatus{Enabled: &zeroValue} + r.GetEnabled() + r = &RepoImmutableReleasesStatus{} + r.GetEnabled() + r = nil + r.GetEnabled() +} + +func TestRepoImmutableReleasesStatus_GetEnforcedByOwner(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepoImmutableReleasesStatus{EnforcedByOwner: &zeroValue} + r.GetEnforcedByOwner() + r = &RepoImmutableReleasesStatus{} + r.GetEnforcedByOwner() + r = nil + r.GetEnforcedByOwner() +} + +func TestRepoMergeUpstreamRequest_GetBranch(tt *testing.T) { + tt.Parallel() + r := &RepoMergeUpstreamRequest{} + r.GetBranch() + r = nil + r.GetBranch() +} + +func TestRepoMergeUpstreamResult_GetBaseBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoMergeUpstreamResult{BaseBranch: &zeroValue} + r.GetBaseBranch() + r = &RepoMergeUpstreamResult{} + r.GetBaseBranch() + r = nil + r.GetBaseBranch() +} + +func TestRepoMergeUpstreamResult_GetMergeType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoMergeUpstreamResult{MergeType: &zeroValue} + r.GetMergeType() + r = &RepoMergeUpstreamResult{} + r.GetMergeType() + r = nil + r.GetMergeType() +} + +func TestRepoMergeUpstreamResult_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoMergeUpstreamResult{Message: &zeroValue} + r.GetMessage() + r = &RepoMergeUpstreamResult{} + r.GetMessage() + r = nil + r.GetMessage() +} + +func TestRepoName_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoName{From: &zeroValue} + r.GetFrom() + r = &RepoName{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRepositoriesSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoriesSearchResult{IncompleteResults: &zeroValue} + r.GetIncompleteResults() + r = &RepositoriesSearchResult{} + r.GetIncompleteResults() + r = nil + r.GetIncompleteResults() +} + +func TestRepositoriesSearchResult_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + r := &RepositoriesSearchResult{Repositories: zeroValue} + r.GetRepositories() + r = &RepositoriesSearchResult{} + r.GetRepositories() + r = nil + r.GetRepositories() +} + +func TestRepositoriesSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepositoriesSearchResult{Total: &zeroValue} + r.GetTotal() + r = &RepositoriesSearchResult{} + r.GetTotal() + r = nil + r.GetTotal() +} + +func TestRepository_GetAllowAutoMerge(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AllowAutoMerge: &zeroValue} + r.GetAllowAutoMerge() + r = &Repository{} + r.GetAllowAutoMerge() + r = nil + r.GetAllowAutoMerge() +} + +func TestRepository_GetAllowForking(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AllowForking: &zeroValue} + r.GetAllowForking() + r = &Repository{} + r.GetAllowForking() + r = nil + r.GetAllowForking() +} + +func TestRepository_GetAllowMergeCommit(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AllowMergeCommit: &zeroValue} + r.GetAllowMergeCommit() + r = &Repository{} + r.GetAllowMergeCommit() + r = nil + r.GetAllowMergeCommit() +} + +func TestRepository_GetAllowRebaseMerge(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AllowRebaseMerge: &zeroValue} + r.GetAllowRebaseMerge() + r = &Repository{} + r.GetAllowRebaseMerge() + r = nil + r.GetAllowRebaseMerge() +} + +func TestRepository_GetAllowSquashMerge(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AllowSquashMerge: &zeroValue} + r.GetAllowSquashMerge() + r = &Repository{} + r.GetAllowSquashMerge() + r = nil + r.GetAllowSquashMerge() +} + +func TestRepository_GetAllowUpdateBranch(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AllowUpdateBranch: &zeroValue} + r.GetAllowUpdateBranch() + r = &Repository{} + r.GetAllowUpdateBranch() + r = nil + r.GetAllowUpdateBranch() +} + +func TestRepository_GetArchived(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{Archived: &zeroValue} + r.GetArchived() + r = &Repository{} + r.GetArchived() + r = nil + r.GetArchived() +} + +func TestRepository_GetArchiveURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{ArchiveURL: &zeroValue} + r.GetArchiveURL() + r = &Repository{} + r.GetArchiveURL() + r = nil + r.GetArchiveURL() +} + +func TestRepository_GetAssigneesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{AssigneesURL: &zeroValue} + r.GetAssigneesURL() + r = &Repository{} + r.GetAssigneesURL() + r = nil + r.GetAssigneesURL() +} + +func TestRepository_GetAutoInit(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{AutoInit: &zeroValue} + r.GetAutoInit() + r = &Repository{} + r.GetAutoInit() + r = nil + r.GetAutoInit() +} + +func TestRepository_GetBlobsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{BlobsURL: &zeroValue} + r.GetBlobsURL() + r = &Repository{} + r.GetBlobsURL() + r = nil + r.GetBlobsURL() +} + +func TestRepository_GetBranchesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{BranchesURL: &zeroValue} + r.GetBranchesURL() + r = &Repository{} + r.GetBranchesURL() + r = nil + r.GetBranchesURL() +} + +func TestRepository_GetCloneURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{CloneURL: &zeroValue} + r.GetCloneURL() + r = &Repository{} + r.GetCloneURL() + r = nil + r.GetCloneURL() +} + +func TestRepository_GetCodeOfConduct(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetCodeOfConduct() + r = nil + r.GetCodeOfConduct() +} + +func TestRepository_GetCollaboratorsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{CollaboratorsURL: &zeroValue} + r.GetCollaboratorsURL() + r = &Repository{} + r.GetCollaboratorsURL() + r = nil + r.GetCollaboratorsURL() +} + +func TestRepository_GetCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{CommentsURL: &zeroValue} + r.GetCommentsURL() + r = &Repository{} + r.GetCommentsURL() + r = nil + r.GetCommentsURL() +} + +func TestRepository_GetCommitsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{CommitsURL: &zeroValue} + r.GetCommitsURL() + r = &Repository{} + r.GetCommitsURL() + r = nil + r.GetCommitsURL() +} + +func TestRepository_GetCompareURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{CompareURL: &zeroValue} + r.GetCompareURL() + r = &Repository{} + r.GetCompareURL() + r = nil + r.GetCompareURL() +} + +func TestRepository_GetContentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{ContentsURL: &zeroValue} + r.GetContentsURL() + r = &Repository{} + r.GetContentsURL() + r = nil + r.GetContentsURL() +} + +func TestRepository_GetContributorsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{ContributorsURL: &zeroValue} + r.GetContributorsURL() + r = &Repository{} + r.GetContributorsURL() + r = nil + r.GetContributorsURL() +} + +func TestRepository_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &Repository{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &Repository{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepository_GetCustomProperties(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]any{} + r := &Repository{CustomProperties: zeroValue} + r.GetCustomProperties() + r = &Repository{} + r.GetCustomProperties() + r = nil + r.GetCustomProperties() +} + +func TestRepository_GetDefaultBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{DefaultBranch: &zeroValue} + r.GetDefaultBranch() + r = &Repository{} + r.GetDefaultBranch() + r = nil + r.GetDefaultBranch() +} + +func TestRepository_GetDeleteBranchOnMerge(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{DeleteBranchOnMerge: &zeroValue} + r.GetDeleteBranchOnMerge() + r = &Repository{} + r.GetDeleteBranchOnMerge() + r = nil + r.GetDeleteBranchOnMerge() +} + +func TestRepository_GetDeploymentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{DeploymentsURL: &zeroValue} + r.GetDeploymentsURL() + r = &Repository{} + r.GetDeploymentsURL() + r = nil + r.GetDeploymentsURL() +} + +func TestRepository_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{Description: &zeroValue} + r.GetDescription() + r = &Repository{} + r.GetDescription() + r = nil + r.GetDescription() +} + +func TestRepository_GetDisabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{Disabled: &zeroValue} + r.GetDisabled() + r = &Repository{} + r.GetDisabled() + r = nil + r.GetDisabled() +} + +func TestRepository_GetDownloadsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{DownloadsURL: &zeroValue} + r.GetDownloadsURL() + r = &Repository{} + r.GetDownloadsURL() + r = nil + r.GetDownloadsURL() +} + +func TestRepository_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{EventsURL: &zeroValue} + r.GetEventsURL() + r = &Repository{} + r.GetEventsURL() + r = nil + r.GetEventsURL() +} + +func TestRepository_GetFork(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{Fork: &zeroValue} + r.GetFork() + r = &Repository{} + r.GetFork() + r = nil + r.GetFork() +} + +func TestRepository_GetForksCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{ForksCount: &zeroValue} + r.GetForksCount() + r = &Repository{} + r.GetForksCount() + r = nil + r.GetForksCount() +} + +func TestRepository_GetForksURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{ForksURL: &zeroValue} + r.GetForksURL() + r = &Repository{} + r.GetForksURL() + r = nil + r.GetForksURL() +} + +func TestRepository_GetFullName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{FullName: &zeroValue} + r.GetFullName() + r = &Repository{} + r.GetFullName() + r = nil + r.GetFullName() +} + +func TestRepository_GetGitCommitsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{GitCommitsURL: &zeroValue} + r.GetGitCommitsURL() + r = &Repository{} + r.GetGitCommitsURL() + r = nil + r.GetGitCommitsURL() +} + +func TestRepository_GetGitignoreTemplate(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{GitignoreTemplate: &zeroValue} + r.GetGitignoreTemplate() + r = &Repository{} + r.GetGitignoreTemplate() + r = nil + r.GetGitignoreTemplate() +} + +func TestRepository_GetGitRefsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{GitRefsURL: &zeroValue} + r.GetGitRefsURL() + r = &Repository{} + r.GetGitRefsURL() + r = nil + r.GetGitRefsURL() +} + +func TestRepository_GetGitTagsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{GitTagsURL: &zeroValue} + r.GetGitTagsURL() + r = &Repository{} + r.GetGitTagsURL() + r = nil + r.GetGitTagsURL() +} + +func TestRepository_GetGitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{GitURL: &zeroValue} + r.GetGitURL() + r = &Repository{} + r.GetGitURL() + r = nil + r.GetGitURL() +} + +func TestRepository_GetHasDiscussions(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasDiscussions: &zeroValue} + r.GetHasDiscussions() + r = &Repository{} + r.GetHasDiscussions() + r = nil + r.GetHasDiscussions() +} + +func TestRepository_GetHasDownloads(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasDownloads: &zeroValue} + r.GetHasDownloads() + r = &Repository{} + r.GetHasDownloads() + r = nil + r.GetHasDownloads() +} + +func TestRepository_GetHasIssues(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasIssues: &zeroValue} + r.GetHasIssues() + r = &Repository{} + r.GetHasIssues() + r = nil + r.GetHasIssues() +} + +func TestRepository_GetHasPages(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasPages: &zeroValue} + r.GetHasPages() + r = &Repository{} + r.GetHasPages() + r = nil + r.GetHasPages() +} + +func TestRepository_GetHasProjects(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasProjects: &zeroValue} + r.GetHasProjects() + r = &Repository{} + r.GetHasProjects() + r = nil + r.GetHasProjects() +} + +func TestRepository_GetHasPullRequests(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasPullRequests: &zeroValue} + r.GetHasPullRequests() + r = &Repository{} + r.GetHasPullRequests() + r = nil + r.GetHasPullRequests() +} + +func TestRepository_GetHasWiki(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{HasWiki: &zeroValue} + r.GetHasWiki() + r = &Repository{} + r.GetHasWiki() + r = nil + r.GetHasWiki() +} + +func TestRepository_GetHomepage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{Homepage: &zeroValue} + r.GetHomepage() + r = &Repository{} + r.GetHomepage() + r = nil + r.GetHomepage() +} + +func TestRepository_GetHooksURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{HooksURL: &zeroValue} + r.GetHooksURL() + r = &Repository{} + r.GetHooksURL() + r = nil + r.GetHooksURL() +} + +func TestRepository_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &Repository{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepository_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &Repository{ID: &zeroValue} + r.GetID() + r = &Repository{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepository_GetIssueCommentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{IssueCommentURL: &zeroValue} + r.GetIssueCommentURL() + r = &Repository{} + r.GetIssueCommentURL() + r = nil + r.GetIssueCommentURL() +} + +func TestRepository_GetIssueEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{IssueEventsURL: &zeroValue} + r.GetIssueEventsURL() + r = &Repository{} + r.GetIssueEventsURL() + r = nil + r.GetIssueEventsURL() +} + +func TestRepository_GetIssuesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{IssuesURL: &zeroValue} + r.GetIssuesURL() + r = &Repository{} + r.GetIssuesURL() + r = nil + r.GetIssuesURL() +} + +func TestRepository_GetIsTemplate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{IsTemplate: &zeroValue} + r.GetIsTemplate() + r = &Repository{} + r.GetIsTemplate() + r = nil + r.GetIsTemplate() +} + +func TestRepository_GetKeysURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{KeysURL: &zeroValue} + r.GetKeysURL() + r = &Repository{} + r.GetKeysURL() + r = nil + r.GetKeysURL() +} + +func TestRepository_GetLabelsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{LabelsURL: &zeroValue} + r.GetLabelsURL() + r = &Repository{} + r.GetLabelsURL() + r = nil + r.GetLabelsURL() +} + +func TestRepository_GetLanguage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{Language: &zeroValue} + r.GetLanguage() + r = &Repository{} + r.GetLanguage() + r = nil + r.GetLanguage() +} + +func TestRepository_GetLanguagesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{LanguagesURL: &zeroValue} + r.GetLanguagesURL() + r = &Repository{} + r.GetLanguagesURL() + r = nil + r.GetLanguagesURL() +} + +func TestRepository_GetLicense(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetLicense() + r = nil + r.GetLicense() +} + +func TestRepository_GetLicenseTemplate(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{LicenseTemplate: &zeroValue} + r.GetLicenseTemplate() + r = &Repository{} + r.GetLicenseTemplate() + r = nil + r.GetLicenseTemplate() +} + +func TestRepository_GetMasterBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{MasterBranch: &zeroValue} + r.GetMasterBranch() + r = &Repository{} + r.GetMasterBranch() + r = nil + r.GetMasterBranch() +} + +func TestRepository_GetMergeCommitMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{MergeCommitMessage: &zeroValue} + r.GetMergeCommitMessage() + r = &Repository{} + r.GetMergeCommitMessage() + r = nil + r.GetMergeCommitMessage() +} + +func TestRepository_GetMergeCommitTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{MergeCommitTitle: &zeroValue} + r.GetMergeCommitTitle() + r = &Repository{} + r.GetMergeCommitTitle() + r = nil + r.GetMergeCommitTitle() +} + +func TestRepository_GetMergesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{MergesURL: &zeroValue} + r.GetMergesURL() + r = &Repository{} + r.GetMergesURL() + r = nil + r.GetMergesURL() +} + +func TestRepository_GetMilestonesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{MilestonesURL: &zeroValue} + r.GetMilestonesURL() + r = &Repository{} + r.GetMilestonesURL() + r = nil + r.GetMilestonesURL() +} + +func TestRepository_GetMirrorURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{MirrorURL: &zeroValue} + r.GetMirrorURL() + r = &Repository{} + r.GetMirrorURL() + r = nil + r.GetMirrorURL() +} + +func TestRepository_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{Name: &zeroValue} + r.GetName() + r = &Repository{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepository_GetNetworkCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{NetworkCount: &zeroValue} + r.GetNetworkCount() + r = &Repository{} + r.GetNetworkCount() + r = nil + r.GetNetworkCount() +} + +func TestRepository_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{NodeID: &zeroValue} + r.GetNodeID() + r = &Repository{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepository_GetNotificationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{NotificationsURL: &zeroValue} + r.GetNotificationsURL() + r = &Repository{} + r.GetNotificationsURL() + r = nil + r.GetNotificationsURL() +} + +func TestRepository_GetOpenIssues(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{OpenIssues: &zeroValue} + r.GetOpenIssues() + r = &Repository{} + r.GetOpenIssues() + r = nil + r.GetOpenIssues() +} + +func TestRepository_GetOpenIssuesCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{OpenIssuesCount: &zeroValue} + r.GetOpenIssuesCount() + r = &Repository{} + r.GetOpenIssuesCount() + r = nil + r.GetOpenIssuesCount() +} + +func TestRepository_GetOrganization(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetOrganization() + r = nil + r.GetOrganization() +} + +func TestRepository_GetOwner(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetOwner() + r = nil + r.GetOwner() +} + +func TestRepository_GetParent(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetParent() + r = nil + r.GetParent() +} + +func TestRepository_GetPermissions(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetPermissions() + r = nil + r.GetPermissions() +} + +func TestRepository_GetPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{Private: &zeroValue} + r.GetPrivate() + r = &Repository{} + r.GetPrivate() + r = nil + r.GetPrivate() +} + +func TestRepository_GetPullRequestCreationPolicy(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{PullRequestCreationPolicy: &zeroValue} + r.GetPullRequestCreationPolicy() + r = &Repository{} + r.GetPullRequestCreationPolicy() + r = nil + r.GetPullRequestCreationPolicy() +} + +func TestRepository_GetPullsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{PullsURL: &zeroValue} + r.GetPullsURL() + r = &Repository{} + r.GetPullsURL() + r = nil + r.GetPullsURL() +} + +func TestRepository_GetPushedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &Repository{PushedAt: &zeroValue} + r.GetPushedAt() + r = &Repository{} + r.GetPushedAt() + r = nil + r.GetPushedAt() +} + +func TestRepository_GetReleasesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{ReleasesURL: &zeroValue} + r.GetReleasesURL() + r = &Repository{} + r.GetReleasesURL() + r = nil + r.GetReleasesURL() +} + +func TestRepository_GetRoleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{RoleName: &zeroValue} + r.GetRoleName() + r = &Repository{} + r.GetRoleName() + r = nil + r.GetRoleName() +} + +func TestRepository_GetSecurityAndAnalysis(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetSecurityAndAnalysis() + r = nil + r.GetSecurityAndAnalysis() +} + +func TestRepository_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{Size: &zeroValue} + r.GetSize() + r = &Repository{} + r.GetSize() + r = nil + r.GetSize() +} + +func TestRepository_GetSource(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetSource() + r = nil + r.GetSource() +} + +func TestRepository_GetSquashMergeCommitMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{SquashMergeCommitMessage: &zeroValue} + r.GetSquashMergeCommitMessage() + r = &Repository{} + r.GetSquashMergeCommitMessage() + r = nil + r.GetSquashMergeCommitMessage() +} + +func TestRepository_GetSquashMergeCommitTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{SquashMergeCommitTitle: &zeroValue} + r.GetSquashMergeCommitTitle() + r = &Repository{} + r.GetSquashMergeCommitTitle() + r = nil + r.GetSquashMergeCommitTitle() +} + +func TestRepository_GetSSHURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{SSHURL: &zeroValue} + r.GetSSHURL() + r = &Repository{} + r.GetSSHURL() + r = nil + r.GetSSHURL() +} + +func TestRepository_GetStargazersCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{StargazersCount: &zeroValue} + r.GetStargazersCount() + r = &Repository{} + r.GetStargazersCount() + r = nil + r.GetStargazersCount() +} + +func TestRepository_GetStargazersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{StargazersURL: &zeroValue} + r.GetStargazersURL() + r = &Repository{} + r.GetStargazersURL() + r = nil + r.GetStargazersURL() +} + +func TestRepository_GetStatusesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{StatusesURL: &zeroValue} + r.GetStatusesURL() + r = &Repository{} + r.GetStatusesURL() + r = nil + r.GetStatusesURL() +} + +func TestRepository_GetSubscribersCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{SubscribersCount: &zeroValue} + r.GetSubscribersCount() + r = &Repository{} + r.GetSubscribersCount() + r = nil + r.GetSubscribersCount() +} + +func TestRepository_GetSubscribersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{SubscribersURL: &zeroValue} + r.GetSubscribersURL() + r = &Repository{} + r.GetSubscribersURL() + r = nil + r.GetSubscribersURL() +} + +func TestRepository_GetSubscriptionURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{SubscriptionURL: &zeroValue} + r.GetSubscriptionURL() + r = &Repository{} + r.GetSubscriptionURL() + r = nil + r.GetSubscriptionURL() +} + +func TestRepository_GetSVNURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{SVNURL: &zeroValue} + r.GetSVNURL() + r = &Repository{} + r.GetSVNURL() + r = nil + r.GetSVNURL() +} + +func TestRepository_GetTagsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{TagsURL: &zeroValue} + r.GetTagsURL() + r = &Repository{} + r.GetTagsURL() + r = nil + r.GetTagsURL() +} + +func TestRepository_GetTeamID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &Repository{TeamID: &zeroValue} + r.GetTeamID() + r = &Repository{} + r.GetTeamID() + r = nil + r.GetTeamID() +} + +func TestRepository_GetTeamsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{TeamsURL: &zeroValue} + r.GetTeamsURL() + r = &Repository{} + r.GetTeamsURL() + r = nil + r.GetTeamsURL() +} + +func TestRepository_GetTemplateRepository(tt *testing.T) { + tt.Parallel() + r := &Repository{} + r.GetTemplateRepository() + r = nil + r.GetTemplateRepository() +} + +func TestRepository_GetTextMatches(tt *testing.T) { + tt.Parallel() + zeroValue := []*TextMatch{} + r := &Repository{TextMatches: zeroValue} + r.GetTextMatches() + r = &Repository{} + r.GetTextMatches() + r = nil + r.GetTextMatches() +} + +func TestRepository_GetTopics(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &Repository{Topics: zeroValue} + r.GetTopics() + r = &Repository{} + r.GetTopics() + r = nil + r.GetTopics() +} + +func TestRepository_GetTreesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{TreesURL: &zeroValue} + r.GetTreesURL() + r = &Repository{} + r.GetTreesURL() + r = nil + r.GetTreesURL() +} + +func TestRepository_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &Repository{UpdatedAt: &zeroValue} + r.GetUpdatedAt() + r = &Repository{} + r.GetUpdatedAt() + r = nil + r.GetUpdatedAt() +} + +func TestRepository_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{URL: &zeroValue} + r.GetURL() + r = &Repository{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepository_GetUseSquashPRTitleAsDefault(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{UseSquashPRTitleAsDefault: &zeroValue} + r.GetUseSquashPRTitleAsDefault() + r = &Repository{} + r.GetUseSquashPRTitleAsDefault() + r = nil + r.GetUseSquashPRTitleAsDefault() +} + +func TestRepository_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Repository{Visibility: &zeroValue} + r.GetVisibility() + r = &Repository{} + r.GetVisibility() + r = nil + r.GetVisibility() +} + +func TestRepository_GetWatchers(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{Watchers: &zeroValue} + r.GetWatchers() + r = &Repository{} + r.GetWatchers() + r = nil + r.GetWatchers() +} + +func TestRepository_GetWatchersCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &Repository{WatchersCount: &zeroValue} + r.GetWatchersCount() + r = &Repository{} + r.GetWatchersCount() + r = nil + r.GetWatchersCount() +} + +func TestRepository_GetWebCommitSignoffRequired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Repository{WebCommitSignoffRequired: &zeroValue} + r.GetWebCommitSignoffRequired() + r = &Repository{} + r.GetWebCommitSignoffRequired() + r = nil + r.GetWebCommitSignoffRequired() +} + +func TestRepositoryActionsAccessLevel_GetAccessLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActionsAccessLevel{AccessLevel: &zeroValue} + r.GetAccessLevel() + r = &RepositoryActionsAccessLevel{} + r.GetAccessLevel() + r = nil + r.GetAccessLevel() +} + +func TestRepositoryActiveCommitters_GetAdvancedSecurityCommitters(tt *testing.T) { + tt.Parallel() + r := &RepositoryActiveCommitters{} + r.GetAdvancedSecurityCommitters() + r = nil + r.GetAdvancedSecurityCommitters() +} + +func TestRepositoryActiveCommitters_GetAdvancedSecurityCommittersBreakdown(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvancedSecurityCommittersBreakdown{} + r := &RepositoryActiveCommitters{AdvancedSecurityCommittersBreakdown: zeroValue} + r.GetAdvancedSecurityCommittersBreakdown() + r = &RepositoryActiveCommitters{} + r.GetAdvancedSecurityCommittersBreakdown() + r = nil + r.GetAdvancedSecurityCommittersBreakdown() +} + +func TestRepositoryActiveCommitters_GetName(tt *testing.T) { + tt.Parallel() + r := &RepositoryActiveCommitters{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryActivity_GetActivityType(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetActivityType() + r = nil + r.GetActivityType() +} + +func TestRepositoryActivity_GetActor(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetActor() + r = nil + r.GetActor() +} + +func TestRepositoryActivity_GetAfter(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetAfter() + r = nil + r.GetAfter() +} + +func TestRepositoryActivity_GetBefore(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetBefore() + r = nil + r.GetBefore() +} + +func TestRepositoryActivity_GetID(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryActivity_GetNodeID(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepositoryActivity_GetRef(tt *testing.T) { + tt.Parallel() + r := &RepositoryActivity{} + r.GetRef() + r = nil + r.GetRef() +} + +func TestRepositoryActivity_GetTimestamp(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryActivity{Timestamp: &zeroValue} + r.GetTimestamp() + r = &RepositoryActivity{} + r.GetTimestamp() + r = nil + r.GetTimestamp() +} + +func TestRepositoryActor_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{AvatarURL: &zeroValue} + r.GetAvatarURL() + r = &RepositoryActor{} + r.GetAvatarURL() + r = nil + r.GetAvatarURL() +} + +func TestRepositoryActor_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{EventsURL: &zeroValue} + r.GetEventsURL() + r = &RepositoryActor{} + r.GetEventsURL() + r = nil + r.GetEventsURL() +} + +func TestRepositoryActor_GetFollowersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{FollowersURL: &zeroValue} + r.GetFollowersURL() + r = &RepositoryActor{} + r.GetFollowersURL() + r = nil + r.GetFollowersURL() +} + +func TestRepositoryActor_GetFollowingURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{FollowingURL: &zeroValue} + r.GetFollowingURL() + r = &RepositoryActor{} + r.GetFollowingURL() + r = nil + r.GetFollowingURL() +} + +func TestRepositoryActor_GetGistsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{GistsURL: &zeroValue} + r.GetGistsURL() + r = &RepositoryActor{} + r.GetGistsURL() + r = nil + r.GetGistsURL() +} + +func TestRepositoryActor_GetGravatarID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{GravatarID: &zeroValue} + r.GetGravatarID() + r = &RepositoryActor{} + r.GetGravatarID() + r = nil + r.GetGravatarID() +} + +func TestRepositoryActor_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &RepositoryActor{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryActor_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RepositoryActor{ID: &zeroValue} + r.GetID() + r = &RepositoryActor{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryActor_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{Login: &zeroValue} + r.GetLogin() + r = &RepositoryActor{} + r.GetLogin() + r = nil + r.GetLogin() +} + +func TestRepositoryActor_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{NodeID: &zeroValue} + r.GetNodeID() + r = &RepositoryActor{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepositoryActor_GetOrganizationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{OrganizationsURL: &zeroValue} + r.GetOrganizationsURL() + r = &RepositoryActor{} + r.GetOrganizationsURL() + r = nil + r.GetOrganizationsURL() +} + +func TestRepositoryActor_GetReceivedEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{ReceivedEventsURL: &zeroValue} + r.GetReceivedEventsURL() + r = &RepositoryActor{} + r.GetReceivedEventsURL() + r = nil + r.GetReceivedEventsURL() +} + +func TestRepositoryActor_GetReposURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{ReposURL: &zeroValue} + r.GetReposURL() + r = &RepositoryActor{} + r.GetReposURL() + r = nil + r.GetReposURL() +} + +func TestRepositoryActor_GetSiteAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryActor{SiteAdmin: &zeroValue} + r.GetSiteAdmin() + r = &RepositoryActor{} + r.GetSiteAdmin() + r = nil + r.GetSiteAdmin() +} + +func TestRepositoryActor_GetStarredURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{StarredURL: &zeroValue} + r.GetStarredURL() + r = &RepositoryActor{} + r.GetStarredURL() + r = nil + r.GetStarredURL() +} + +func TestRepositoryActor_GetSubscriptionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{SubscriptionsURL: &zeroValue} + r.GetSubscriptionsURL() + r = &RepositoryActor{} + r.GetSubscriptionsURL() + r = nil + r.GetSubscriptionsURL() +} + +func TestRepositoryActor_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{Type: &zeroValue} + r.GetType() + r = &RepositoryActor{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryActor_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{URL: &zeroValue} + r.GetURL() + r = &RepositoryActor{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryActor_GetUserViewType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryActor{UserViewType: &zeroValue} + r.GetUserViewType() + r = &RepositoryActor{} + r.GetUserViewType() + r = nil + r.GetUserViewType() +} + +func TestRepositoryAddCollaboratorOptions_GetPermission(tt *testing.T) { + tt.Parallel() + r := &RepositoryAddCollaboratorOptions{} + r.GetPermission() + r = nil + r.GetPermission() +} + +func TestRepositoryAttachment_GetRepository(tt *testing.T) { + tt.Parallel() + r := &RepositoryAttachment{} + r.GetRepository() + r = nil + r.GetRepository() +} + +func TestRepositoryAttachment_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryAttachment{Status: &zeroValue} + r.GetStatus() + r = &RepositoryAttachment{} + r.GetStatus() + r = nil + r.GetStatus() +} + +func TestRepositoryCodeSecurityConfiguration_GetConfiguration(tt *testing.T) { + tt.Parallel() + r := &RepositoryCodeSecurityConfiguration{} + r.GetConfiguration() + r = nil + r.GetConfiguration() +} + +func TestRepositoryCodeSecurityConfiguration_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryCodeSecurityConfiguration{State: &zeroValue} + r.GetState() + r = &RepositoryCodeSecurityConfiguration{} + r.GetState() + r = nil + r.GetState() +} + +func TestRepositoryComment_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryComment{Body: &zeroValue} + r.GetBody() + r = &RepositoryComment{} + r.GetBody() + r = nil + r.GetBody() +} + +func TestRepositoryComment_GetCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryComment{CommitID: &zeroValue} + r.GetCommitID() + r = &RepositoryComment{} + r.GetCommitID() + r = nil + r.GetCommitID() +} + +func TestRepositoryComment_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryComment{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &RepositoryComment{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepositoryComment_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryComment{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &RepositoryComment{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryComment_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RepositoryComment{ID: &zeroValue} + r.GetID() + r = &RepositoryComment{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryComment_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryComment{NodeID: &zeroValue} + r.GetNodeID() + r = &RepositoryComment{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepositoryComment_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryComment{Path: &zeroValue} + r.GetPath() + r = &RepositoryComment{} + r.GetPath() + r = nil + r.GetPath() +} + +func TestRepositoryComment_GetPosition(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepositoryComment{Position: &zeroValue} + r.GetPosition() + r = &RepositoryComment{} + r.GetPosition() + r = nil + r.GetPosition() +} + +func TestRepositoryComment_GetReactions(tt *testing.T) { + tt.Parallel() + r := &RepositoryComment{} + r.GetReactions() + r = nil + r.GetReactions() +} + +func TestRepositoryComment_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryComment{UpdatedAt: &zeroValue} + r.GetUpdatedAt() + r = &RepositoryComment{} + r.GetUpdatedAt() + r = nil + r.GetUpdatedAt() +} + +func TestRepositoryComment_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryComment{URL: &zeroValue} + r.GetURL() + r = &RepositoryComment{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryComment_GetUser(tt *testing.T) { + tt.Parallel() + r := &RepositoryComment{} + r.GetUser() + r = nil + r.GetUser() +} + +func TestRepositoryCommit_GetAuthor(tt *testing.T) { + tt.Parallel() + r := &RepositoryCommit{} + r.GetAuthor() + r = nil + r.GetAuthor() +} + +func TestRepositoryCommit_GetCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryCommit{CommentsURL: &zeroValue} + r.GetCommentsURL() + r = &RepositoryCommit{} + r.GetCommentsURL() + r = nil + r.GetCommentsURL() +} + +func TestRepositoryCommit_GetCommit(tt *testing.T) { + tt.Parallel() + r := &RepositoryCommit{} + r.GetCommit() + r = nil + r.GetCommit() +} + +func TestRepositoryCommit_GetCommitter(tt *testing.T) { + tt.Parallel() + r := &RepositoryCommit{} + r.GetCommitter() + r = nil + r.GetCommitter() +} + +func TestRepositoryCommit_GetFiles(tt *testing.T) { + tt.Parallel() + zeroValue := []*CommitFile{} + r := &RepositoryCommit{Files: zeroValue} + r.GetFiles() + r = &RepositoryCommit{} + r.GetFiles() + r = nil + r.GetFiles() +} + +func TestRepositoryCommit_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryCommit{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &RepositoryCommit{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryCommit_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryCommit{NodeID: &zeroValue} + r.GetNodeID() + r = &RepositoryCommit{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepositoryCommit_GetParents(tt *testing.T) { + tt.Parallel() + zeroValue := []*Commit{} + r := &RepositoryCommit{Parents: zeroValue} + r.GetParents() + r = &RepositoryCommit{} + r.GetParents() + r = nil + r.GetParents() +} + +func TestRepositoryCommit_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryCommit{SHA: &zeroValue} + r.GetSHA() + r = &RepositoryCommit{} + r.GetSHA() + r = nil + r.GetSHA() +} + +func TestRepositoryCommit_GetStats(tt *testing.T) { + tt.Parallel() + r := &RepositoryCommit{} + r.GetStats() + r = nil + r.GetStats() +} + +func TestRepositoryCommit_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryCommit{URL: &zeroValue} + r.GetURL() + r = &RepositoryCommit{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryContent_GetDownloadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{DownloadURL: &zeroValue} + r.GetDownloadURL() + r = &RepositoryContent{} + r.GetDownloadURL() + r = nil + r.GetDownloadURL() +} + +func TestRepositoryContent_GetEncoding(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{Encoding: &zeroValue} + r.GetEncoding() + r = &RepositoryContent{} + r.GetEncoding() + r = nil + r.GetEncoding() +} + +func TestRepositoryContent_GetGitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{GitURL: &zeroValue} + r.GetGitURL() + r = &RepositoryContent{} + r.GetGitURL() + r = nil + r.GetGitURL() +} + +func TestRepositoryContent_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &RepositoryContent{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryContent_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{Name: &zeroValue} + r.GetName() + r = &RepositoryContent{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryContent_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{Path: &zeroValue} + r.GetPath() + r = &RepositoryContent{} + r.GetPath() + r = nil + r.GetPath() +} + +func TestRepositoryContent_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{SHA: &zeroValue} + r.GetSHA() + r = &RepositoryContent{} + r.GetSHA() + r = nil + r.GetSHA() +} + +func TestRepositoryContent_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepositoryContent{Size: &zeroValue} + r.GetSize() + r = &RepositoryContent{} + r.GetSize() + r = nil + r.GetSize() +} + +func TestRepositoryContent_GetSubmoduleGitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{SubmoduleGitURL: &zeroValue} + r.GetSubmoduleGitURL() + r = &RepositoryContent{} + r.GetSubmoduleGitURL() + r = nil + r.GetSubmoduleGitURL() +} + +func TestRepositoryContent_GetTarget(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{Target: &zeroValue} + r.GetTarget() + r = &RepositoryContent{} + r.GetTarget() + r = nil + r.GetTarget() +} + +func TestRepositoryContent_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{Type: &zeroValue} + r.GetType() + r = &RepositoryContent{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryContent_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContent{URL: &zeroValue} + r.GetURL() + r = &RepositoryContent{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryContentFileOptions_GetAuthor(tt *testing.T) { + tt.Parallel() + r := &RepositoryContentFileOptions{} + r.GetAuthor() + r = nil + r.GetAuthor() +} + +func TestRepositoryContentFileOptions_GetBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContentFileOptions{Branch: &zeroValue} + r.GetBranch() + r = &RepositoryContentFileOptions{} + r.GetBranch() + r = nil + r.GetBranch() +} + +func TestRepositoryContentFileOptions_GetCommitter(tt *testing.T) { + tt.Parallel() + r := &RepositoryContentFileOptions{} + r.GetCommitter() + r = nil + r.GetCommitter() +} + +func TestRepositoryContentFileOptions_GetContent(tt *testing.T) { + tt.Parallel() + zeroValue := []byte{} + r := &RepositoryContentFileOptions{Content: zeroValue} + r.GetContent() + r = &RepositoryContentFileOptions{} + r.GetContent() + r = nil + r.GetContent() +} + +func TestRepositoryContentFileOptions_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContentFileOptions{Message: &zeroValue} + r.GetMessage() + r = &RepositoryContentFileOptions{} + r.GetMessage() + r = nil + r.GetMessage() +} + +func TestRepositoryContentFileOptions_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryContentFileOptions{SHA: &zeroValue} + r.GetSHA() + r = &RepositoryContentFileOptions{} + r.GetSHA() + r = nil + r.GetSHA() +} + +func TestRepositoryContentGetOptions_GetRef(tt *testing.T) { + tt.Parallel() + r := &RepositoryContentGetOptions{} + r.GetRef() + r = nil + r.GetRef() +} + +func TestRepositoryContentResponse_GetContent(tt *testing.T) { + tt.Parallel() + r := &RepositoryContentResponse{} + r.GetContent() + r = nil + r.GetContent() +} + +func TestRepositoryCreateForkOptions_GetDefaultBranchOnly(tt *testing.T) { + tt.Parallel() + r := &RepositoryCreateForkOptions{} + r.GetDefaultBranchOnly() + r = nil + r.GetDefaultBranchOnly() +} + +func TestRepositoryCreateForkOptions_GetName(tt *testing.T) { + tt.Parallel() + r := &RepositoryCreateForkOptions{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryCreateForkOptions_GetOrganization(tt *testing.T) { + tt.Parallel() + r := &RepositoryCreateForkOptions{} + r.GetOrganization() + r = nil + r.GetOrganization() +} + +func TestRepositoryDispatchEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryDispatchEvent{Action: &zeroValue} + r.GetAction() + r = &RepositoryDispatchEvent{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestRepositoryDispatchEvent_GetBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryDispatchEvent{Branch: &zeroValue} + r.GetBranch() + r = &RepositoryDispatchEvent{} + r.GetBranch() + r = nil + r.GetBranch() +} + +func TestRepositoryDispatchEvent_GetClientPayload(tt *testing.T) { + tt.Parallel() + r := &RepositoryDispatchEvent{} + r.GetClientPayload() + r = nil + r.GetClientPayload() +} + +func TestRepositoryDispatchEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + r := &RepositoryDispatchEvent{} + r.GetInstallation() + r = nil + r.GetInstallation() +} + +func TestRepositoryDispatchEvent_GetOrg(tt *testing.T) { + tt.Parallel() + r := &RepositoryDispatchEvent{} + r.GetOrg() + r = nil + r.GetOrg() +} + +func TestRepositoryDispatchEvent_GetRepo(tt *testing.T) { + tt.Parallel() + r := &RepositoryDispatchEvent{} + r.GetRepo() + r = nil + r.GetRepo() +} + +func TestRepositoryDispatchEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &RepositoryDispatchEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestRepositoryEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryEvent{Action: &zeroValue} + r.GetAction() + r = &RepositoryEvent{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestRepositoryEvent_GetChanges(tt *testing.T) { + tt.Parallel() + r := &RepositoryEvent{} + r.GetChanges() + r = nil + r.GetChanges() +} + +func TestRepositoryEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + r := &RepositoryEvent{} + r.GetInstallation() + r = nil + r.GetInstallation() +} + +func TestRepositoryEvent_GetOrg(tt *testing.T) { + tt.Parallel() + r := &RepositoryEvent{} + r.GetOrg() + r = nil + r.GetOrg() +} + +func TestRepositoryEvent_GetRepo(tt *testing.T) { + tt.Parallel() + r := &RepositoryEvent{} + r.GetRepo() + r = nil + r.GetRepo() +} + +func TestRepositoryEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &RepositoryEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestRepositoryImportEvent_GetOrg(tt *testing.T) { + tt.Parallel() + r := &RepositoryImportEvent{} + r.GetOrg() + r = nil + r.GetOrg() +} + +func TestRepositoryImportEvent_GetRepo(tt *testing.T) { + tt.Parallel() + r := &RepositoryImportEvent{} + r.GetRepo() + r = nil + r.GetRepo() +} + +func TestRepositoryImportEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &RepositoryImportEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestRepositoryImportEvent_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryImportEvent{Status: &zeroValue} + r.GetStatus() + r = &RepositoryImportEvent{} + r.GetStatus() + r = nil + r.GetStatus() +} + +func TestRepositoryInvitation_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryInvitation{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &RepositoryInvitation{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepositoryInvitation_GetExpired(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryInvitation{Expired: &zeroValue} + r.GetExpired() + r = &RepositoryInvitation{} + r.GetExpired() + r = nil + r.GetExpired() +} + +func TestRepositoryInvitation_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryInvitation{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &RepositoryInvitation{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryInvitation_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RepositoryInvitation{ID: &zeroValue} + r.GetID() + r = &RepositoryInvitation{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryInvitation_GetInvitee(tt *testing.T) { + tt.Parallel() + r := &RepositoryInvitation{} + r.GetInvitee() + r = nil + r.GetInvitee() +} + +func TestRepositoryInvitation_GetInviter(tt *testing.T) { + tt.Parallel() + r := &RepositoryInvitation{} + r.GetInviter() + r = nil + r.GetInviter() +} + +func TestRepositoryInvitation_GetPermissions(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryInvitation{Permissions: &zeroValue} + r.GetPermissions() + r = &RepositoryInvitation{} + r.GetPermissions() + r = nil + r.GetPermissions() +} + +func TestRepositoryInvitation_GetRepo(tt *testing.T) { + tt.Parallel() + r := &RepositoryInvitation{} + r.GetRepo() + r = nil + r.GetRepo() +} + +func TestRepositoryInvitation_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryInvitation{URL: &zeroValue} + r.GetURL() + r = &RepositoryInvitation{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryLicense_GetContent(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{Content: &zeroValue} + r.GetContent() + r = &RepositoryLicense{} + r.GetContent() + r = nil + r.GetContent() +} + +func TestRepositoryLicense_GetDownloadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{DownloadURL: &zeroValue} + r.GetDownloadURL() + r = &RepositoryLicense{} + r.GetDownloadURL() + r = nil + r.GetDownloadURL() +} + +func TestRepositoryLicense_GetEncoding(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{Encoding: &zeroValue} + r.GetEncoding() + r = &RepositoryLicense{} + r.GetEncoding() + r = nil + r.GetEncoding() +} + +func TestRepositoryLicense_GetGitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{GitURL: &zeroValue} + r.GetGitURL() + r = &RepositoryLicense{} + r.GetGitURL() + r = nil + r.GetGitURL() +} + +func TestRepositoryLicense_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{HTMLURL: &zeroValue} + r.GetHTMLURL() + r = &RepositoryLicense{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryLicense_GetLicense(tt *testing.T) { + tt.Parallel() + r := &RepositoryLicense{} + r.GetLicense() + r = nil + r.GetLicense() +} + +func TestRepositoryLicense_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{Name: &zeroValue} + r.GetName() + r = &RepositoryLicense{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryLicense_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{Path: &zeroValue} + r.GetPath() + r = &RepositoryLicense{} + r.GetPath() + r = nil + r.GetPath() +} + +func TestRepositoryLicense_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{SHA: &zeroValue} + r.GetSHA() + r = &RepositoryLicense{} + r.GetSHA() + r = nil + r.GetSHA() +} + +func TestRepositoryLicense_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepositoryLicense{Size: &zeroValue} + r.GetSize() + r = &RepositoryLicense{} + r.GetSize() + r = nil + r.GetSize() +} + +func TestRepositoryLicense_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{Type: &zeroValue} + r.GetType() + r = &RepositoryLicense{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryLicense_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryLicense{URL: &zeroValue} + r.GetURL() + r = &RepositoryLicense{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryListAllOptions_GetSince(tt *testing.T) { + tt.Parallel() + r := &RepositoryListAllOptions{} + r.GetSince() + r = nil + r.GetSince() +} + +func TestRepositoryListByAuthenticatedUserOptions_GetAffiliation(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByAuthenticatedUserOptions{} + r.GetAffiliation() + r = nil + r.GetAffiliation() +} + +func TestRepositoryListByAuthenticatedUserOptions_GetDirection(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByAuthenticatedUserOptions{} + r.GetDirection() + r = nil + r.GetDirection() +} + +func TestRepositoryListByAuthenticatedUserOptions_GetSort(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByAuthenticatedUserOptions{} + r.GetSort() + r = nil + r.GetSort() +} + +func TestRepositoryListByAuthenticatedUserOptions_GetType(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByAuthenticatedUserOptions{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryListByAuthenticatedUserOptions_GetVisibility(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByAuthenticatedUserOptions{} + r.GetVisibility() + r = nil + r.GetVisibility() +} + +func TestRepositoryListByOrgOptions_GetDirection(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByOrgOptions{} + r.GetDirection() + r = nil + r.GetDirection() +} + +func TestRepositoryListByOrgOptions_GetSort(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByOrgOptions{} + r.GetSort() + r = nil + r.GetSort() +} + +func TestRepositoryListByOrgOptions_GetType(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByOrgOptions{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryListByUserOptions_GetDirection(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByUserOptions{} + r.GetDirection() + r = nil + r.GetDirection() +} + +func TestRepositoryListByUserOptions_GetSort(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByUserOptions{} + r.GetSort() + r = nil + r.GetSort() +} + +func TestRepositoryListByUserOptions_GetType(tt *testing.T) { + tt.Parallel() + r := &RepositoryListByUserOptions{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryListForksOptions_GetSort(tt *testing.T) { + tt.Parallel() + r := &RepositoryListForksOptions{} + r.GetSort() + r = nil + r.GetSort() +} + +func TestRepositoryListOptions_GetAffiliation(tt *testing.T) { + tt.Parallel() + r := &RepositoryListOptions{} + r.GetAffiliation() + r = nil + r.GetAffiliation() +} + +func TestRepositoryListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + r := &RepositoryListOptions{} + r.GetDirection() + r = nil + r.GetDirection() +} + +func TestRepositoryListOptions_GetSort(tt *testing.T) { + tt.Parallel() + r := &RepositoryListOptions{} + r.GetSort() + r = nil + r.GetSort() +} + +func TestRepositoryListOptions_GetType(tt *testing.T) { + tt.Parallel() + r := &RepositoryListOptions{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryListOptions_GetVisibility(tt *testing.T) { + tt.Parallel() + r := &RepositoryListOptions{} + r.GetVisibility() + r = nil + r.GetVisibility() +} + +func TestRepositoryListRulesetsOptions_GetIncludesParents(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryListRulesetsOptions{IncludesParents: &zeroValue} + r.GetIncludesParents() + r = &RepositoryListRulesetsOptions{} + r.GetIncludesParents() + r = nil + r.GetIncludesParents() +} + +func TestRepositoryMergeRequest_GetBase(tt *testing.T) { + tt.Parallel() + r := &RepositoryMergeRequest{} + r.GetBase() + r = nil + r.GetBase() +} + +func TestRepositoryMergeRequest_GetCommitMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryMergeRequest{CommitMessage: &zeroValue} + r.GetCommitMessage() + r = &RepositoryMergeRequest{} + r.GetCommitMessage() + r = nil + r.GetCommitMessage() +} + +func TestRepositoryMergeRequest_GetHead(tt *testing.T) { + tt.Parallel() + r := &RepositoryMergeRequest{} + r.GetHead() + r = nil + r.GetHead() +} + +func TestRepositoryParticipation_GetAll(tt *testing.T) { + tt.Parallel() + zeroValue := []int{} + r := &RepositoryParticipation{All: zeroValue} + r.GetAll() + r = &RepositoryParticipation{} + r.GetAll() + r = nil + r.GetAll() +} + +func TestRepositoryParticipation_GetOwner(tt *testing.T) { + tt.Parallel() + zeroValue := []int{} + r := &RepositoryParticipation{Owner: zeroValue} + r.GetOwner() + r = &RepositoryParticipation{} + r.GetOwner() + r = nil + r.GetOwner() +} + +func TestRepositoryPermissionLevel_GetPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryPermissionLevel{Permission: &zeroValue} + r.GetPermission() + r = &RepositoryPermissionLevel{} + r.GetPermission() + r = nil + r.GetPermission() +} + +func TestRepositoryPermissionLevel_GetRoleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryPermissionLevel{RoleName: &zeroValue} + r.GetRoleName() + r = &RepositoryPermissionLevel{} + r.GetRoleName() + r = nil + r.GetRoleName() +} + +func TestRepositoryPermissionLevel_GetUser(tt *testing.T) { + tt.Parallel() + r := &RepositoryPermissionLevel{} + r.GetUser() + r = nil + r.GetUser() +} + +func TestRepositoryPermissions_GetAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryPermissions{Admin: &zeroValue} + r.GetAdmin() + r = &RepositoryPermissions{} + r.GetAdmin() + r = nil + r.GetAdmin() +} + +func TestRepositoryPermissions_GetMaintain(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryPermissions{Maintain: &zeroValue} + r.GetMaintain() + r = &RepositoryPermissions{} + r.GetMaintain() + r = nil + r.GetMaintain() +} + +func TestRepositoryPermissions_GetPull(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryPermissions{Pull: &zeroValue} + r.GetPull() + r = &RepositoryPermissions{} + r.GetPull() + r = nil + r.GetPull() +} + +func TestRepositoryPermissions_GetPush(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryPermissions{Push: &zeroValue} + r.GetPush() + r = &RepositoryPermissions{} + r.GetPush() + r = nil + r.GetPush() +} + +func TestRepositoryPermissions_GetTriage(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryPermissions{Triage: &zeroValue} + r.GetTriage() + r = &RepositoryPermissions{} + r.GetTriage() + r = nil + r.GetTriage() +} + +func TestRepositoryRelease_GetAssets(tt *testing.T) { + tt.Parallel() + zeroValue := []*ReleaseAsset{} + r := &RepositoryRelease{Assets: zeroValue} + r.GetAssets() + r = &RepositoryRelease{} + r.GetAssets() + r = nil + r.GetAssets() +} + +func TestRepositoryRelease_GetAssetsURL(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetAssetsURL() + r = nil + r.GetAssetsURL() +} + +func TestRepositoryRelease_GetAuthor(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetAuthor() + r = nil + r.GetAuthor() +} + +func TestRepositoryRelease_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{Body: &zeroValue} + r.GetBody() + r = &RepositoryRelease{} + r.GetBody() + r = nil + r.GetBody() +} + +func TestRepositoryRelease_GetBodyHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{BodyHTML: &zeroValue} + r.GetBodyHTML() + r = &RepositoryRelease{} + r.GetBodyHTML() + r = nil + r.GetBodyHTML() +} + +func TestRepositoryRelease_GetBodyText(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{BodyText: &zeroValue} + r.GetBodyText() + r = &RepositoryRelease{} + r.GetBodyText() + r = nil + r.GetBodyText() +} + +func TestRepositoryRelease_GetCreatedAt(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepositoryRelease_GetDiscussionURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{DiscussionURL: &zeroValue} + r.GetDiscussionURL() + r = &RepositoryRelease{} + r.GetDiscussionURL() + r = nil + r.GetDiscussionURL() +} + +func TestRepositoryRelease_GetDraft(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetDraft() + r = nil + r.GetDraft() +} + +func TestRepositoryRelease_GetHTMLURL(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetHTMLURL() + r = nil + r.GetHTMLURL() +} + +func TestRepositoryRelease_GetID(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryRelease_GetImmutable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryRelease{Immutable: &zeroValue} + r.GetImmutable() + r = &RepositoryRelease{} + r.GetImmutable() + r = nil + r.GetImmutable() +} + +func TestRepositoryRelease_GetMentionsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepositoryRelease{MentionsCount: &zeroValue} + r.GetMentionsCount() + r = &RepositoryRelease{} + r.GetMentionsCount() + r = nil + r.GetMentionsCount() +} + +func TestRepositoryRelease_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{Name: &zeroValue} + r.GetName() + r = &RepositoryRelease{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryRelease_GetNodeID(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepositoryRelease_GetPrerelease(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetPrerelease() + r = nil + r.GetPrerelease() +} + +func TestRepositoryRelease_GetPublishedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryRelease{PublishedAt: &zeroValue} + r.GetPublishedAt() + r = &RepositoryRelease{} + r.GetPublishedAt() + r = nil + r.GetPublishedAt() +} + +func TestRepositoryRelease_GetReactions(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetReactions() + r = nil + r.GetReactions() +} + +func TestRepositoryRelease_GetTagName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetTagName() + r = nil + r.GetTagName() +} + +func TestRepositoryRelease_GetTarballURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{TarballURL: &zeroValue} + r.GetTarballURL() + r = &RepositoryRelease{} + r.GetTarballURL() + r = nil + r.GetTarballURL() +} + +func TestRepositoryRelease_GetTargetCommitish(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetTargetCommitish() + r = nil + r.GetTargetCommitish() +} + +func TestRepositoryRelease_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryRelease{UpdatedAt: &zeroValue} + r.GetUpdatedAt() + r = &RepositoryRelease{} + r.GetUpdatedAt() + r = nil + r.GetUpdatedAt() +} + +func TestRepositoryRelease_GetUploadURL(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetUploadURL() + r = nil + r.GetUploadURL() +} + +func TestRepositoryRelease_GetURL(tt *testing.T) { + tt.Parallel() + r := &RepositoryRelease{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRepositoryRelease_GetZipballURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRelease{ZipballURL: &zeroValue} + r.GetZipballURL() + r = &RepositoryRelease{} + r.GetZipballURL() + r = nil + r.GetZipballURL() +} + +func TestRepositoryReleaseNotes_GetBody(tt *testing.T) { + tt.Parallel() + r := &RepositoryReleaseNotes{} + r.GetBody() + r = nil + r.GetBody() +} + +func TestRepositoryReleaseNotes_GetName(tt *testing.T) { + tt.Parallel() + r := &RepositoryReleaseNotes{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryRule_GetParameters(tt *testing.T) { + tt.Parallel() + r := &RepositoryRule{} + r.GetParameters() + r = nil + r.GetParameters() +} + +func TestRepositoryRule_GetType(tt *testing.T) { + tt.Parallel() + r := &RepositoryRule{} + r.GetType() + r = nil + r.GetType() +} + +func TestRepositoryRuleset_GetBypassActors(tt *testing.T) { + tt.Parallel() + zeroValue := []*BypassActor{} + r := &RepositoryRuleset{BypassActors: zeroValue} + r.GetBypassActors() + r = &RepositoryRuleset{} + r.GetBypassActors() + r = nil + r.GetBypassActors() +} + +func TestRepositoryRuleset_GetConditions(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetConditions() + r = nil + r.GetConditions() +} + +func TestRepositoryRuleset_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryRuleset{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &RepositoryRuleset{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepositoryRuleset_GetCurrentUserCanBypass(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetCurrentUserCanBypass() + r = nil + r.GetCurrentUserCanBypass() +} + +func TestRepositoryRuleset_GetEnforcement(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetEnforcement() + r = nil + r.GetEnforcement() +} + +func TestRepositoryRuleset_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RepositoryRuleset{ID: &zeroValue} + r.GetID() + r = &RepositoryRuleset{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryRuleset_GetLinks(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetLinks() + r = nil + r.GetLinks() +} + +func TestRepositoryRuleset_GetName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryRuleset_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRuleset{NodeID: &zeroValue} + r.GetNodeID() + r = &RepositoryRuleset{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepositoryRuleset_GetRules(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetRules() + r = nil + r.GetRules() +} + +func TestRepositoryRuleset_GetSource(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetSource() + r = nil + r.GetSource() +} + +func TestRepositoryRuleset_GetSourceType(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetSourceType() + r = nil + r.GetSourceType() +} + +func TestRepositoryRuleset_GetTarget(tt *testing.T) { + tt.Parallel() + r := &RepositoryRuleset{} + r.GetTarget() + r = nil + r.GetTarget() +} + +func TestRepositoryRuleset_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryRuleset{UpdatedAt: &zeroValue} + r.GetUpdatedAt() + r = &RepositoryRuleset{} + r.GetUpdatedAt() + r = nil + r.GetUpdatedAt() +} + +func TestRepositoryRulesetChangedConditions_GetAdded(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetConditions{} + r := &RepositoryRulesetChangedConditions{Added: zeroValue} + r.GetAdded() + r = &RepositoryRulesetChangedConditions{} + r.GetAdded() + r = nil + r.GetAdded() +} + +func TestRepositoryRulesetChangedConditions_GetDeleted(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetConditions{} + r := &RepositoryRulesetChangedConditions{Deleted: zeroValue} + r.GetDeleted() + r = &RepositoryRulesetChangedConditions{} + r.GetDeleted() + r = nil + r.GetDeleted() +} + +func TestRepositoryRulesetChangedConditions_GetUpdated(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetUpdatedConditions{} + r := &RepositoryRulesetChangedConditions{Updated: zeroValue} + r.GetUpdated() + r = &RepositoryRulesetChangedConditions{} + r.GetUpdated() + r = nil + r.GetUpdated() +} + +func TestRepositoryRulesetChangedRule_GetConfiguration(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChangedRule{} + r.GetConfiguration() + r = nil + r.GetConfiguration() +} + +func TestRepositoryRulesetChangedRule_GetPattern(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChangedRule{} + r.GetPattern() + r = nil + r.GetPattern() +} + +func TestRepositoryRulesetChangedRule_GetRuleType(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChangedRule{} + r.GetRuleType() + r = nil + r.GetRuleType() +} + +func TestRepositoryRulesetChangedRules_GetAdded(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRule{} + r := &RepositoryRulesetChangedRules{Added: zeroValue} + r.GetAdded() + r = &RepositoryRulesetChangedRules{} + r.GetAdded() + r = nil + r.GetAdded() +} + +func TestRepositoryRulesetChangedRules_GetDeleted(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRule{} + r := &RepositoryRulesetChangedRules{Deleted: zeroValue} + r.GetDeleted() + r = &RepositoryRulesetChangedRules{} + r.GetDeleted() + r = nil + r.GetDeleted() +} + +func TestRepositoryRulesetChangedRules_GetUpdated(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetUpdatedRules{} + r := &RepositoryRulesetChangedRules{Updated: zeroValue} + r.GetUpdated() + r = &RepositoryRulesetChangedRules{} + r.GetUpdated() + r = nil + r.GetUpdated() +} + +func TestRepositoryRulesetChanges_GetConditions(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChanges{} + r.GetConditions() + r = nil + r.GetConditions() +} + +func TestRepositoryRulesetChanges_GetEnforcement(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChanges{} + r.GetEnforcement() + r = nil + r.GetEnforcement() +} + +func TestRepositoryRulesetChanges_GetName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChanges{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryRulesetChanges_GetRules(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetChanges{} + r.GetRules() + r = nil + r.GetRules() +} + +func TestRepositoryRulesetChangeSource_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRulesetChangeSource{From: &zeroValue} + r.GetFrom() + r = &RepositoryRulesetChangeSource{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRepositoryRulesetChangeSources_GetFrom(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetChangeSources{From: zeroValue} + r.GetFrom() + r = &RepositoryRulesetChangeSources{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRepositoryRulesetConditions_GetOrganizationID(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetOrganizationID() + r = nil + r.GetOrganizationID() +} + +func TestRepositoryRulesetConditions_GetOrganizationName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetOrganizationName() + r = nil + r.GetOrganizationName() +} + +func TestRepositoryRulesetConditions_GetOrganizationProperty(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetOrganizationProperty() + r = nil + r.GetOrganizationProperty() +} + +func TestRepositoryRulesetConditions_GetRefName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetRefName() + r = nil + r.GetRefName() +} + +func TestRepositoryRulesetConditions_GetRepositoryID(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetRepositoryID() + r = nil + r.GetRepositoryID() +} + +func TestRepositoryRulesetConditions_GetRepositoryName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetRepositoryName() + r = nil + r.GetRepositoryName() +} + +func TestRepositoryRulesetConditions_GetRepositoryProperty(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetConditions{} + r.GetRepositoryProperty() + r = nil + r.GetRepositoryProperty() +} + +func TestRepositoryRulesetEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRulesetEvent{Action: &zeroValue} + r.GetAction() + r = &RepositoryRulesetEvent{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestRepositoryRulesetEvent_GetChanges(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetChanges() + r = nil + r.GetChanges() +} + +func TestRepositoryRulesetEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetEnterprise() + r = nil + r.GetEnterprise() +} + +func TestRepositoryRulesetEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetInstallation() + r = nil + r.GetInstallation() +} + +func TestRepositoryRulesetEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetOrganization() + r = nil + r.GetOrganization() +} + +func TestRepositoryRulesetEvent_GetRepository(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetRepository() + r = nil + r.GetRepository() +} + +func TestRepositoryRulesetEvent_GetRepositoryRuleset(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetRepositoryRuleset() + r = nil + r.GetRepositoryRuleset() +} + +func TestRepositoryRulesetEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestRepositoryRulesetLink_GetHRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRulesetLink{HRef: &zeroValue} + r.GetHRef() + r = &RepositoryRulesetLink{} + r.GetHRef() + r = nil + r.GetHRef() +} + +func TestRepositoryRulesetLinks_GetHTML(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetLinks{} + r.GetHTML() + r = nil + r.GetHTML() +} + +func TestRepositoryRulesetLinks_GetSelf(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetLinks{} + r.GetSelf() + r = nil + r.GetSelf() +} + +func TestRepositoryRulesetOrganizationIDsConditionParameters_GetOrganizationIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + r := &RepositoryRulesetOrganizationIDsConditionParameters{OrganizationIDs: zeroValue} + r.GetOrganizationIDs() + r = &RepositoryRulesetOrganizationIDsConditionParameters{} + r.GetOrganizationIDs() + r = nil + r.GetOrganizationIDs() +} + +func TestRepositoryRulesetOrganizationNamesConditionParameters_GetExclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetOrganizationNamesConditionParameters{Exclude: zeroValue} + r.GetExclude() + r = &RepositoryRulesetOrganizationNamesConditionParameters{} + r.GetExclude() + r = nil + r.GetExclude() +} + +func TestRepositoryRulesetOrganizationNamesConditionParameters_GetInclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetOrganizationNamesConditionParameters{Include: zeroValue} + r.GetInclude() + r = &RepositoryRulesetOrganizationNamesConditionParameters{} + r.GetInclude() + r = nil + r.GetInclude() +} + +func TestRepositoryRulesetOrganizationPropertyConditionParameters_GetExclude(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetRepositoryPropertyTargetParameters{} + r := &RepositoryRulesetOrganizationPropertyConditionParameters{Exclude: zeroValue} + r.GetExclude() + r = &RepositoryRulesetOrganizationPropertyConditionParameters{} + r.GetExclude() + r = nil + r.GetExclude() +} + +func TestRepositoryRulesetOrganizationPropertyConditionParameters_GetInclude(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetRepositoryPropertyTargetParameters{} + r := &RepositoryRulesetOrganizationPropertyConditionParameters{Include: zeroValue} + r.GetInclude() + r = &RepositoryRulesetOrganizationPropertyConditionParameters{} + r.GetInclude() + r = nil + r.GetInclude() +} + +func TestRepositoryRulesetRefConditionParameters_GetExclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetRefConditionParameters{Exclude: zeroValue} + r.GetExclude() + r = &RepositoryRulesetRefConditionParameters{} + r.GetExclude() + r = nil + r.GetExclude() +} + +func TestRepositoryRulesetRefConditionParameters_GetInclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetRefConditionParameters{Include: zeroValue} + r.GetInclude() + r = &RepositoryRulesetRefConditionParameters{} + r.GetInclude() + r = nil + r.GetInclude() +} + +func TestRepositoryRulesetRepositoryIDsConditionParameters_GetRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + r := &RepositoryRulesetRepositoryIDsConditionParameters{RepositoryIDs: zeroValue} + r.GetRepositoryIDs() + r = &RepositoryRulesetRepositoryIDsConditionParameters{} + r.GetRepositoryIDs() + r = nil + r.GetRepositoryIDs() +} + +func TestRepositoryRulesetRepositoryNamesConditionParameters_GetExclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetRepositoryNamesConditionParameters{Exclude: zeroValue} + r.GetExclude() + r = &RepositoryRulesetRepositoryNamesConditionParameters{} + r.GetExclude() + r = nil + r.GetExclude() +} + +func TestRepositoryRulesetRepositoryNamesConditionParameters_GetInclude(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetRepositoryNamesConditionParameters{Include: zeroValue} + r.GetInclude() + r = &RepositoryRulesetRepositoryNamesConditionParameters{} + r.GetInclude() + r = nil + r.GetInclude() +} + +func TestRepositoryRulesetRepositoryNamesConditionParameters_GetProtected(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RepositoryRulesetRepositoryNamesConditionParameters{Protected: &zeroValue} + r.GetProtected() + r = &RepositoryRulesetRepositoryNamesConditionParameters{} + r.GetProtected() + r = nil + r.GetProtected() +} + +func TestRepositoryRulesetRepositoryPropertyConditionParameters_GetExclude(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetRepositoryPropertyTargetParameters{} + r := &RepositoryRulesetRepositoryPropertyConditionParameters{Exclude: zeroValue} + r.GetExclude() + r = &RepositoryRulesetRepositoryPropertyConditionParameters{} + r.GetExclude() + r = nil + r.GetExclude() +} + +func TestRepositoryRulesetRepositoryPropertyConditionParameters_GetInclude(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepositoryRulesetRepositoryPropertyTargetParameters{} + r := &RepositoryRulesetRepositoryPropertyConditionParameters{Include: zeroValue} + r.GetInclude() + r = &RepositoryRulesetRepositoryPropertyConditionParameters{} + r.GetInclude() + r = nil + r.GetInclude() +} + +func TestRepositoryRulesetRepositoryPropertyTargetParameters_GetName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRepositoryPropertyTargetParameters{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryRulesetRepositoryPropertyTargetParameters_GetPropertyValues(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RepositoryRulesetRepositoryPropertyTargetParameters{PropertyValues: zeroValue} + r.GetPropertyValues() + r = &RepositoryRulesetRepositoryPropertyTargetParameters{} + r.GetPropertyValues() + r = nil + r.GetPropertyValues() +} + +func TestRepositoryRulesetRepositoryPropertyTargetParameters_GetSource(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryRulesetRepositoryPropertyTargetParameters{Source: &zeroValue} + r.GetSource() + r = &RepositoryRulesetRepositoryPropertyTargetParameters{} + r.GetSource() + r = nil + r.GetSource() +} + +func TestRepositoryRulesetRules_GetBranchNamePattern(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetBranchNamePattern() + r = nil + r.GetBranchNamePattern() +} + +func TestRepositoryRulesetRules_GetCodeScanning(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCodeScanning() + r = nil + r.GetCodeScanning() +} + +func TestRepositoryRulesetRules_GetCommitAuthorEmailPattern(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCommitAuthorEmailPattern() + r = nil + r.GetCommitAuthorEmailPattern() +} + +func TestRepositoryRulesetRules_GetCommitMessagePattern(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCommitMessagePattern() + r = nil + r.GetCommitMessagePattern() +} + +func TestRepositoryRulesetRules_GetCommitterEmailPattern(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCommitterEmailPattern() + r = nil + r.GetCommitterEmailPattern() +} + +func TestRepositoryRulesetRules_GetCopilotCodeReview(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCopilotCodeReview() + r = nil + r.GetCopilotCodeReview() +} + +func TestRepositoryRulesetRules_GetCreation(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCreation() + r = nil + r.GetCreation() +} + +func TestRepositoryRulesetRules_GetDeletion(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetDeletion() + r = nil + r.GetDeletion() +} + +func TestRepositoryRulesetRules_GetFileExtensionRestriction(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetFileExtensionRestriction() + r = nil + r.GetFileExtensionRestriction() +} + +func TestRepositoryRulesetRules_GetFilePathRestriction(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetFilePathRestriction() + r = nil + r.GetFilePathRestriction() +} + +func TestRepositoryRulesetRules_GetMaxFilePathLength(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetMaxFilePathLength() + r = nil + r.GetMaxFilePathLength() +} + +func TestRepositoryRulesetRules_GetMaxFileSize(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetMaxFileSize() + r = nil + r.GetMaxFileSize() +} + +func TestRepositoryRulesetRules_GetMergeQueue(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetMergeQueue() + r = nil + r.GetMergeQueue() +} + +func TestRepositoryRulesetRules_GetNonFastForward(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetNonFastForward() + r = nil + r.GetNonFastForward() +} + +func TestRepositoryRulesetRules_GetPullRequest(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetPullRequest() + r = nil + r.GetPullRequest() +} + +func TestRepositoryRulesetRules_GetRepositoryCreate(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRepositoryCreate() + r = nil + r.GetRepositoryCreate() +} + +func TestRepositoryRulesetRules_GetRepositoryDelete(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRepositoryDelete() + r = nil + r.GetRepositoryDelete() +} + +func TestRepositoryRulesetRules_GetRepositoryName(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRepositoryName() + r = nil + r.GetRepositoryName() +} + +func TestRepositoryRulesetRules_GetRepositoryTransfer(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRepositoryTransfer() + r = nil + r.GetRepositoryTransfer() +} + +func TestRepositoryRulesetRules_GetRepositoryVisibility(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRepositoryVisibility() + r = nil + r.GetRepositoryVisibility() +} + +func TestRepositoryRulesetRules_GetRequiredDeployments(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRequiredDeployments() + r = nil + r.GetRequiredDeployments() +} + +func TestRepositoryRulesetRules_GetRequiredLinearHistory(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRequiredLinearHistory() + r = nil + r.GetRequiredLinearHistory() +} + +func TestRepositoryRulesetRules_GetRequiredSignatures(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRequiredSignatures() + r = nil + r.GetRequiredSignatures() +} + +func TestRepositoryRulesetRules_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetRequiredStatusChecks() + r = nil + r.GetRequiredStatusChecks() +} + +func TestRepositoryRulesetRules_GetTagNamePattern(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetTagNamePattern() + r = nil + r.GetTagNamePattern() +} + +func TestRepositoryRulesetRules_GetUpdate(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetUpdate() + r = nil + r.GetUpdate() +} + +func TestRepositoryRulesetRules_GetWorkflows(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetWorkflows() + r = nil + r.GetWorkflows() +} + +func TestRepositoryRulesetUpdatedCondition_GetConditionType(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedCondition{} + r.GetConditionType() + r = nil + r.GetConditionType() +} + +func TestRepositoryRulesetUpdatedCondition_GetExclude(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedCondition{} + r.GetExclude() + r = nil + r.GetExclude() +} + +func TestRepositoryRulesetUpdatedCondition_GetInclude(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedCondition{} + r.GetInclude() + r = nil + r.GetInclude() +} + +func TestRepositoryRulesetUpdatedCondition_GetTarget(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedCondition{} + r.GetTarget() + r = nil + r.GetTarget() +} + +func TestRepositoryRulesetUpdatedConditions_GetChanges(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedConditions{} + r.GetChanges() + r = nil + r.GetChanges() +} + +func TestRepositoryRulesetUpdatedConditions_GetCondition(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedConditions{} + r.GetCondition() + r = nil + r.GetCondition() +} + +func TestRepositoryRulesetUpdatedRules_GetChanges(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedRules{} + r.GetChanges() + r = nil + r.GetChanges() +} + +func TestRepositoryRulesetUpdatedRules_GetRule(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetUpdatedRules{} + r.GetRule() + r = nil + r.GetRule() +} + +func TestRepositoryTag_GetCommit(tt *testing.T) { + tt.Parallel() + r := &RepositoryTag{} + r.GetCommit() + r = nil + r.GetCommit() +} + +func TestRepositoryTag_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryTag{Name: &zeroValue} + r.GetName() + r = &RepositoryTag{} + r.GetName() + r = nil + r.GetName() +} + +func TestRepositoryTag_GetTarballURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryTag{TarballURL: &zeroValue} + r.GetTarballURL() + r = &RepositoryTag{} + r.GetTarballURL() + r = nil + r.GetTarballURL() +} + +func TestRepositoryTag_GetZipballURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryTag{ZipballURL: &zeroValue} + r.GetZipballURL() + r = &RepositoryTag{} + r.GetZipballURL() + r = nil + r.GetZipballURL() +} + +func TestRepositoryVisibilityRuleParameters_GetInternal(tt *testing.T) { + tt.Parallel() + r := &RepositoryVisibilityRuleParameters{} + r.GetInternal() + r = nil + r.GetInternal() +} + +func TestRepositoryVisibilityRuleParameters_GetPrivate(tt *testing.T) { + tt.Parallel() + r := &RepositoryVisibilityRuleParameters{} + r.GetPrivate() + r = nil + r.GetPrivate() +} + +func TestRepositoryVulnerabilityAlert_GetAffectedPackageName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{AffectedPackageName: &zeroValue} + r.GetAffectedPackageName() + r = &RepositoryVulnerabilityAlert{} + r.GetAffectedPackageName() + r = nil + r.GetAffectedPackageName() +} + +func TestRepositoryVulnerabilityAlert_GetAffectedRange(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{AffectedRange: &zeroValue} + r.GetAffectedRange() + r = &RepositoryVulnerabilityAlert{} + r.GetAffectedRange() + r = nil + r.GetAffectedRange() +} + +func TestRepositoryVulnerabilityAlert_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryVulnerabilityAlert{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &RepositoryVulnerabilityAlert{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepositoryVulnerabilityAlert_GetDismissedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepositoryVulnerabilityAlert{DismissedAt: &zeroValue} + r.GetDismissedAt() + r = &RepositoryVulnerabilityAlert{} + r.GetDismissedAt() + r = nil + r.GetDismissedAt() +} + +func TestRepositoryVulnerabilityAlert_GetDismisser(tt *testing.T) { + tt.Parallel() + r := &RepositoryVulnerabilityAlert{} + r.GetDismisser() + r = nil + r.GetDismisser() +} + +func TestRepositoryVulnerabilityAlert_GetDismissReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{DismissReason: &zeroValue} + r.GetDismissReason() + r = &RepositoryVulnerabilityAlert{} + r.GetDismissReason() + r = nil + r.GetDismissReason() +} + +func TestRepositoryVulnerabilityAlert_GetExternalIdentifier(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{ExternalIdentifier: &zeroValue} + r.GetExternalIdentifier() + r = &RepositoryVulnerabilityAlert{} + r.GetExternalIdentifier() + r = nil + r.GetExternalIdentifier() +} + +func TestRepositoryVulnerabilityAlert_GetExternalReference(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{ExternalReference: &zeroValue} + r.GetExternalReference() + r = &RepositoryVulnerabilityAlert{} + r.GetExternalReference() + r = nil + r.GetExternalReference() +} + +func TestRepositoryVulnerabilityAlert_GetFixedIn(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{FixedIn: &zeroValue} + r.GetFixedIn() + r = &RepositoryVulnerabilityAlert{} + r.GetFixedIn() + r = nil + r.GetFixedIn() +} + +func TestRepositoryVulnerabilityAlert_GetGitHubSecurityAdvisoryID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{GitHubSecurityAdvisoryID: &zeroValue} + r.GetGitHubSecurityAdvisoryID() + r = &RepositoryVulnerabilityAlert{} + r.GetGitHubSecurityAdvisoryID() + r = nil + r.GetGitHubSecurityAdvisoryID() +} + +func TestRepositoryVulnerabilityAlert_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RepositoryVulnerabilityAlert{ID: &zeroValue} + r.GetID() + r = &RepositoryVulnerabilityAlert{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepositoryVulnerabilityAlert_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlert{Severity: &zeroValue} + r.GetSeverity() + r = &RepositoryVulnerabilityAlert{} + r.GetSeverity() + r = nil + r.GetSeverity() +} + +func TestRepositoryVulnerabilityAlertEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepositoryVulnerabilityAlertEvent{Action: &zeroValue} + r.GetAction() + r = &RepositoryVulnerabilityAlertEvent{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestRepositoryVulnerabilityAlertEvent_GetAlert(tt *testing.T) { + tt.Parallel() + r := &RepositoryVulnerabilityAlertEvent{} + r.GetAlert() + r = nil + r.GetAlert() +} + +func TestRepositoryVulnerabilityAlertEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + r := &RepositoryVulnerabilityAlertEvent{} + r.GetInstallation() + r = nil + r.GetInstallation() +} + +func TestRepositoryVulnerabilityAlertEvent_GetOrg(tt *testing.T) { + tt.Parallel() + r := &RepositoryVulnerabilityAlertEvent{} + r.GetOrg() + r = nil + r.GetOrg() +} + +func TestRepositoryVulnerabilityAlertEvent_GetRepository(tt *testing.T) { + tt.Parallel() + r := &RepositoryVulnerabilityAlertEvent{} + r.GetRepository() + r = nil + r.GetRepository() +} + +func TestRepositoryVulnerabilityAlertEvent_GetSender(tt *testing.T) { + tt.Parallel() + r := &RepositoryVulnerabilityAlertEvent{} + r.GetSender() + r = nil + r.GetSender() +} + +func TestRepoStats_GetForkRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepoStats{ForkRepos: &zeroValue} + r.GetForkRepos() + r = &RepoStats{} + r.GetForkRepos() + r = nil + r.GetForkRepos() +} + +func TestRepoStats_GetOrgRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepoStats{OrgRepos: &zeroValue} + r.GetOrgRepos() + r = &RepoStats{} + r.GetOrgRepos() + r = nil + r.GetOrgRepos() +} + +func TestRepoStats_GetRootRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepoStats{RootRepos: &zeroValue} + r.GetRootRepos() + r = &RepoStats{} + r.GetRootRepos() + r = nil + r.GetRootRepos() +} + +func TestRepoStats_GetTotalPushes(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepoStats{TotalPushes: &zeroValue} + r.GetTotalPushes() + r = &RepoStats{} + r.GetTotalPushes() + r = nil + r.GetTotalPushes() +} + +func TestRepoStats_GetTotalRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepoStats{TotalRepos: &zeroValue} + r.GetTotalRepos() + r = &RepoStats{} + r.GetTotalRepos() + r = nil + r.GetTotalRepos() +} + +func TestRepoStats_GetTotalWikis(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RepoStats{TotalWikis: &zeroValue} + r.GetTotalWikis() + r = &RepoStats{} + r.GetTotalWikis() + r = nil + r.GetTotalWikis() +} + +func TestRepoStatus_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{AvatarURL: &zeroValue} + r.GetAvatarURL() + r = &RepoStatus{} + r.GetAvatarURL() + r = nil + r.GetAvatarURL() +} + +func TestRepoStatus_GetContext(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{Context: &zeroValue} + r.GetContext() + r = &RepoStatus{} + r.GetContext() + r = nil + r.GetContext() +} + +func TestRepoStatus_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepoStatus{CreatedAt: &zeroValue} + r.GetCreatedAt() + r = &RepoStatus{} + r.GetCreatedAt() + r = nil + r.GetCreatedAt() +} + +func TestRepoStatus_GetCreator(tt *testing.T) { + tt.Parallel() + r := &RepoStatus{} + r.GetCreator() + r = nil + r.GetCreator() +} + +func TestRepoStatus_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{Description: &zeroValue} + r.GetDescription() + r = &RepoStatus{} + r.GetDescription() + r = nil + r.GetDescription() +} + +func TestRepoStatus_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RepoStatus{ID: &zeroValue} + r.GetID() + r = &RepoStatus{} + r.GetID() + r = nil + r.GetID() +} + +func TestRepoStatus_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{NodeID: &zeroValue} + r.GetNodeID() + r = &RepoStatus{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRepoStatus_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{State: &zeroValue} + r.GetState() + r = &RepoStatus{} + r.GetState() + r = nil + r.GetState() +} + +func TestRepoStatus_GetTargetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{TargetURL: &zeroValue} + r.GetTargetURL() + r = &RepoStatus{} + r.GetTargetURL() + r = nil + r.GetTargetURL() +} + +func TestRepoStatus_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + r := &RepoStatus{UpdatedAt: &zeroValue} + r.GetUpdatedAt() + r = &RepoStatus{} + r.GetUpdatedAt() + r = nil + r.GetUpdatedAt() +} + +func TestRepoStatus_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RepoStatus{URL: &zeroValue} + r.GetURL() + r = &RepoStatus{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRequestedAction_GetIdentifier(tt *testing.T) { + tt.Parallel() + r := &RequestedAction{} + r.GetIdentifier() + r = nil + r.GetIdentifier() +} + +func TestRequireCodeOwnerReviewChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RequireCodeOwnerReviewChanges{From: &zeroValue} + r.GetFrom() + r = &RequireCodeOwnerReviewChanges{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRequiredConversationResolution_GetEnabled(tt *testing.T) { + tt.Parallel() + r := &RequiredConversationResolution{} + r.GetEnabled() + r = nil + r.GetEnabled() +} + +func TestRequiredConversationResolutionLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RequiredConversationResolutionLevelChanges{From: &zeroValue} + r.GetFrom() + r = &RequiredConversationResolutionLevelChanges{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRequiredDeploymentsBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + r := &RequiredDeploymentsBranchRule{} + r.GetParameters() + r = nil + r.GetParameters() +} + +func TestRequiredDeploymentsEnforcementLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RequiredDeploymentsEnforcementLevelChanges{From: &zeroValue} + r.GetFrom() + r = &RequiredDeploymentsEnforcementLevelChanges{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRequiredDeploymentsRuleParameters_GetRequiredDeploymentEnvironments(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RequiredDeploymentsRuleParameters{RequiredDeploymentEnvironments: zeroValue} + r.GetRequiredDeploymentEnvironments() + r = &RequiredDeploymentsRuleParameters{} + r.GetRequiredDeploymentEnvironments() + r = nil + r.GetRequiredDeploymentEnvironments() +} + +func TestRequiredReviewer_GetReviewer(tt *testing.T) { + tt.Parallel() + r := &RequiredReviewer{} + r.GetReviewer() + r = nil + r.GetReviewer() +} + +func TestRequiredReviewer_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RequiredReviewer{Type: &zeroValue} + r.GetType() + r = &RequiredReviewer{} + r.GetType() + r = nil + r.GetType() +} + +func TestRequiredStatusCheck_GetAppID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RequiredStatusCheck{AppID: &zeroValue} + r.GetAppID() + r = &RequiredStatusCheck{} + r.GetAppID() + r = nil + r.GetAppID() +} + +func TestRequiredStatusCheck_GetContext(tt *testing.T) { + tt.Parallel() + r := &RequiredStatusCheck{} + r.GetContext() + r = nil + r.GetContext() +} + +func TestRequiredStatusChecks_GetChecks(tt *testing.T) { + tt.Parallel() + var zeroValue []*RequiredStatusCheck + r := &RequiredStatusChecks{Checks: &zeroValue} + r.GetChecks() + r = &RequiredStatusChecks{} + r.GetChecks() + r = nil + r.GetChecks() +} + +func TestRequiredStatusChecks_GetContexts(tt *testing.T) { + tt.Parallel() + var zeroValue []string + r := &RequiredStatusChecks{Contexts: &zeroValue} + r.GetContexts() + r = &RequiredStatusChecks{} + r.GetContexts() + r = nil + r.GetContexts() +} + +func TestRequiredStatusChecks_GetContextsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RequiredStatusChecks{ContextsURL: &zeroValue} + r.GetContextsURL() + r = &RequiredStatusChecks{} + r.GetContextsURL() + r = nil + r.GetContextsURL() +} + +func TestRequiredStatusChecks_GetStrict(tt *testing.T) { + tt.Parallel() + r := &RequiredStatusChecks{} + r.GetStrict() + r = nil + r.GetStrict() +} + +func TestRequiredStatusChecks_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RequiredStatusChecks{URL: &zeroValue} + r.GetURL() + r = &RequiredStatusChecks{} + r.GetURL() + r = nil + r.GetURL() +} + +func TestRequiredStatusChecksBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + r := &RequiredStatusChecksBranchRule{} + r.GetParameters() + r = nil + r.GetParameters() +} + +func TestRequiredStatusChecksChanges_GetFrom(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RequiredStatusChecksChanges{From: zeroValue} + r.GetFrom() + r = &RequiredStatusChecksChanges{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRequiredStatusChecksEnforcementLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RequiredStatusChecksEnforcementLevelChanges{From: &zeroValue} + r.GetFrom() + r = &RequiredStatusChecksEnforcementLevelChanges{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRequiredStatusChecksRequest_GetChecks(tt *testing.T) { + tt.Parallel() + zeroValue := []*RequiredStatusCheck{} + r := &RequiredStatusChecksRequest{Checks: zeroValue} + r.GetChecks() + r = &RequiredStatusChecksRequest{} + r.GetChecks() + r = nil + r.GetChecks() +} + +func TestRequiredStatusChecksRequest_GetContexts(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RequiredStatusChecksRequest{Contexts: zeroValue} + r.GetContexts() + r = &RequiredStatusChecksRequest{} + r.GetContexts() + r = nil + r.GetContexts() +} + +func TestRequiredStatusChecksRequest_GetStrict(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RequiredStatusChecksRequest{Strict: &zeroValue} + r.GetStrict() + r = &RequiredStatusChecksRequest{} + r.GetStrict() + r = nil + r.GetStrict() +} + +func TestRequiredStatusChecksRuleParameters_GetDoNotEnforceOnCreate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RequiredStatusChecksRuleParameters{DoNotEnforceOnCreate: &zeroValue} + r.GetDoNotEnforceOnCreate() + r = &RequiredStatusChecksRuleParameters{} + r.GetDoNotEnforceOnCreate() + r = nil + r.GetDoNotEnforceOnCreate() +} + +func TestRequiredStatusChecksRuleParameters_GetRequiredStatusChecks(tt *testing.T) { + tt.Parallel() + zeroValue := []*RuleStatusCheck{} + r := &RequiredStatusChecksRuleParameters{RequiredStatusChecks: zeroValue} + r.GetRequiredStatusChecks() + r = &RequiredStatusChecksRuleParameters{} + r.GetRequiredStatusChecks() + r = nil + r.GetRequiredStatusChecks() +} + +func TestRequiredStatusChecksRuleParameters_GetStrictRequiredStatusChecksPolicy(tt *testing.T) { + tt.Parallel() + r := &RequiredStatusChecksRuleParameters{} + r.GetStrictRequiredStatusChecksPolicy() + r = nil + r.GetStrictRequiredStatusChecksPolicy() +} + +func TestRequireLastPushApprovalChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RequireLastPushApprovalChanges{From: &zeroValue} + r.GetFrom() + r = &RequireLastPushApprovalChanges{} + r.GetFrom() + r = nil + r.GetFrom() +} + +func TestRequireLinearHistory_GetEnabled(tt *testing.T) { + tt.Parallel() + r := &RequireLinearHistory{} + r.GetEnabled() + r = nil + r.GetEnabled() +} + +func TestResponse_GetAfter(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetAfter() + r = nil + r.GetAfter() +} + +func TestResponse_GetBefore(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetBefore() + r = nil + r.GetBefore() +} + +func TestResponse_GetCursor(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetCursor() + r = nil + r.GetCursor() +} + +func TestResponse_GetFirstPage(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetFirstPage() + r = nil + r.GetFirstPage() +} + +func TestResponse_GetLastPage(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetLastPage() + r = nil + r.GetLastPage() +} + +func TestResponse_GetNextPage(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetNextPage() + r = nil + r.GetNextPage() +} + +func TestResponse_GetNextPageToken(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetNextPageToken() + r = nil + r.GetNextPageToken() +} + +func TestResponse_GetPrevPage(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetPrevPage() + r = nil + r.GetPrevPage() +} + +func TestResponse_GetRate(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetRate() + r = nil + r.GetRate() +} + +func TestResponse_GetTokenExpiration(tt *testing.T) { + tt.Parallel() + r := &Response{} + r.GetTokenExpiration() + r = nil + r.GetTokenExpiration() +} + +func TestReviewCustomDeploymentProtectionRuleRequest_GetComment(tt *testing.T) { + tt.Parallel() + r := &ReviewCustomDeploymentProtectionRuleRequest{} + r.GetComment() + r = nil + r.GetComment() +} + +func TestReviewCustomDeploymentProtectionRuleRequest_GetEnvironmentName(tt *testing.T) { + tt.Parallel() + r := &ReviewCustomDeploymentProtectionRuleRequest{} + r.GetEnvironmentName() + r = nil + r.GetEnvironmentName() +} + +func TestReviewCustomDeploymentProtectionRuleRequest_GetState(tt *testing.T) { + tt.Parallel() + r := &ReviewCustomDeploymentProtectionRuleRequest{} + r.GetState() + r = nil + r.GetState() +} + +func TestReviewers_GetTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + r := &Reviewers{Teams: zeroValue} + r.GetTeams() + r = &Reviewers{} + r.GetTeams() + r = nil + r.GetTeams() +} + +func TestReviewers_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + r := &Reviewers{Users: zeroValue} + r.GetUsers() + r = &Reviewers{} + r.GetUsers() + r = nil + r.GetUsers() +} + +func TestReviewersRequest_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReviewersRequest{NodeID: &zeroValue} + r.GetNodeID() + r = &ReviewersRequest{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestReviewersRequest_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &ReviewersRequest{Reviewers: zeroValue} + r.GetReviewers() + r = &ReviewersRequest{} + r.GetReviewers() + r = nil + r.GetReviewers() +} + +func TestReviewersRequest_GetTeamReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &ReviewersRequest{TeamReviewers: zeroValue} + r.GetTeamReviewers() + r = &ReviewersRequest{} + r.GetTeamReviewers() + r = nil + r.GetTeamReviewers() +} + +func TestReviewPersonalAccessTokenRequestOptions_GetAction(tt *testing.T) { + tt.Parallel() + r := &ReviewPersonalAccessTokenRequestOptions{} + r.GetAction() + r = nil + r.GetAction() +} + +func TestReviewPersonalAccessTokenRequestOptions_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &ReviewPersonalAccessTokenRequestOptions{Reason: &zeroValue} + r.GetReason() + r = &ReviewPersonalAccessTokenRequestOptions{} + r.GetReason() + r = nil + r.GetReason() +} + +func TestRule_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{Description: &zeroValue} + r.GetDescription() + r = &Rule{} + r.GetDescription() + r = nil + r.GetDescription() +} + +func TestRule_GetFullDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{FullDescription: &zeroValue} + r.GetFullDescription() + r = &Rule{} + r.GetFullDescription() + r = nil + r.GetFullDescription() +} + +func TestRule_GetHelp(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{Help: &zeroValue} + r.GetHelp() + r = &Rule{} + r.GetHelp() + r = nil + r.GetHelp() +} + +func TestRule_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{ID: &zeroValue} + r.GetID() + r = &Rule{} + r.GetID() + r = nil + r.GetID() +} + +func TestRule_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{Name: &zeroValue} + r.GetName() + r = &Rule{} + r.GetName() + r = nil + r.GetName() +} + +func TestRule_GetSecuritySeverityLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{SecuritySeverityLevel: &zeroValue} + r.GetSecuritySeverityLevel() + r = &Rule{} + r.GetSecuritySeverityLevel() + r = nil + r.GetSecuritySeverityLevel() +} + +func TestRule_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Rule{Severity: &zeroValue} + r.GetSeverity() + r = &Rule{} + r.GetSeverity() + r = nil + r.GetSeverity() +} + +func TestRule_GetTags(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &Rule{Tags: zeroValue} + r.GetTags() + r = &Rule{} + r.GetTags() + r = nil + r.GetTags() +} + +func TestRuleCodeScanningTool_GetAlertsThreshold(tt *testing.T) { + tt.Parallel() + r := &RuleCodeScanningTool{} + r.GetAlertsThreshold() + r = nil + r.GetAlertsThreshold() +} + +func TestRuleCodeScanningTool_GetSecurityAlertsThreshold(tt *testing.T) { + tt.Parallel() + r := &RuleCodeScanningTool{} + r.GetSecurityAlertsThreshold() + r = nil + r.GetSecurityAlertsThreshold() +} + +func TestRuleCodeScanningTool_GetTool(tt *testing.T) { + tt.Parallel() + r := &RuleCodeScanningTool{} + r.GetTool() + r = nil + r.GetTool() +} + +func TestRulesetRequiredReviewer_GetFilePatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RulesetRequiredReviewer{FilePatterns: zeroValue} + r.GetFilePatterns() + r = &RulesetRequiredReviewer{} + r.GetFilePatterns() + r = nil + r.GetFilePatterns() +} + +func TestRulesetRequiredReviewer_GetMinimumApprovals(tt *testing.T) { + tt.Parallel() + var zeroValue int + r := &RulesetRequiredReviewer{MinimumApprovals: &zeroValue} + r.GetMinimumApprovals() + r = &RulesetRequiredReviewer{} + r.GetMinimumApprovals() + r = nil + r.GetMinimumApprovals() +} + +func TestRulesetRequiredReviewer_GetReviewer(tt *testing.T) { + tt.Parallel() + r := &RulesetRequiredReviewer{} + r.GetReviewer() + r = nil + r.GetReviewer() +} + +func TestRulesetReviewer_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RulesetReviewer{ID: &zeroValue} + r.GetID() + r = &RulesetReviewer{} + r.GetID() + r = nil + r.GetID() +} + +func TestRulesetReviewer_GetType(tt *testing.T) { + tt.Parallel() + r := &RulesetReviewer{} + r.GetType() + r = nil + r.GetType() +} + +func TestRuleStatusCheck_GetContext(tt *testing.T) { + tt.Parallel() + r := &RuleStatusCheck{} + r.GetContext() + r = nil + r.GetContext() +} + +func TestRuleStatusCheck_GetIntegrationID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RuleStatusCheck{IntegrationID: &zeroValue} + r.GetIntegrationID() + r = &RuleStatusCheck{} + r.GetIntegrationID() + r = nil + r.GetIntegrationID() +} + +func TestRuleWorkflow_GetPath(tt *testing.T) { + tt.Parallel() + r := &RuleWorkflow{} + r.GetPath() + r = nil + r.GetPath() +} + +func TestRuleWorkflow_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RuleWorkflow{Ref: &zeroValue} + r.GetRef() + r = &RuleWorkflow{} + r.GetRef() + r = nil + r.GetRef() +} + +func TestRuleWorkflow_GetRepositoryID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RuleWorkflow{RepositoryID: &zeroValue} + r.GetRepositoryID() + r = &RuleWorkflow{} + r.GetRepositoryID() + r = nil + r.GetRepositoryID() +} + +func TestRuleWorkflow_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RuleWorkflow{SHA: &zeroValue} + r.GetSHA() + r = &RuleWorkflow{} + r.GetSHA() + r = nil + r.GetSHA() +} + +func TestRunner_GetBusy(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Runner{Busy: &zeroValue} + r.GetBusy() + r = &Runner{} + r.GetBusy() + r = nil + r.GetBusy() +} + +func TestRunner_GetEphemeral(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &Runner{Ephemeral: &zeroValue} + r.GetEphemeral() + r = &Runner{} + r.GetEphemeral() + r = nil + r.GetEphemeral() +} + +func TestRunner_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &Runner{ID: &zeroValue} + r.GetID() + r = &Runner{} + r.GetID() + r = nil + r.GetID() +} + +func TestRunner_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []*RunnerLabels{} + r := &Runner{Labels: zeroValue} + r.GetLabels() + r = &Runner{} + r.GetLabels() + r = nil + r.GetLabels() +} + +func TestRunner_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Runner{Name: &zeroValue} + r.GetName() + r = &Runner{} + r.GetName() + r = nil + r.GetName() +} + +func TestRunner_GetOS(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Runner{OS: &zeroValue} + r.GetOS() + r = &Runner{} + r.GetOS() + r = nil + r.GetOS() +} + +func TestRunner_GetRunnerGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &Runner{RunnerGroupID: &zeroValue} + r.GetRunnerGroupID() + r = &Runner{} + r.GetRunnerGroupID() + r = nil + r.GetRunnerGroupID() +} + +func TestRunner_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Runner{Status: &zeroValue} + r.GetStatus() + r = &Runner{} + r.GetStatus() + r = nil + r.GetStatus() +} + +func TestRunner_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &Runner{Version: &zeroValue} + r.GetVersion() + r = &Runner{} + r.GetVersion() + r = nil + r.GetVersion() +} + +func TestRunnerApplicationDownload_GetArchitecture(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerApplicationDownload{Architecture: &zeroValue} + r.GetArchitecture() + r = &RunnerApplicationDownload{} + r.GetArchitecture() + r = nil + r.GetArchitecture() +} + +func TestRunnerApplicationDownload_GetDownloadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerApplicationDownload{DownloadURL: &zeroValue} + r.GetDownloadURL() + r = &RunnerApplicationDownload{} + r.GetDownloadURL() + r = nil + r.GetDownloadURL() +} + +func TestRunnerApplicationDownload_GetFilename(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerApplicationDownload{Filename: &zeroValue} + r.GetFilename() + r = &RunnerApplicationDownload{} + r.GetFilename() + r = nil + r.GetFilename() +} + +func TestRunnerApplicationDownload_GetOS(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerApplicationDownload{OS: &zeroValue} + r.GetOS() + r = &RunnerApplicationDownload{} + r.GetOS() + r = nil + r.GetOS() +} + +func TestRunnerApplicationDownload_GetSHA256Checksum(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerApplicationDownload{SHA256Checksum: &zeroValue} + r.GetSHA256Checksum() + r = &RunnerApplicationDownload{} + r.GetSHA256Checksum() + r = nil + r.GetSHA256Checksum() +} + +func TestRunnerApplicationDownload_GetTempDownloadToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerApplicationDownload{TempDownloadToken: &zeroValue} + r.GetTempDownloadToken() + r = &RunnerApplicationDownload{} + r.GetTempDownloadToken() + r = nil + r.GetTempDownloadToken() +} + +func TestRunnerGroup_GetAllowsPublicRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RunnerGroup{AllowsPublicRepositories: &zeroValue} + r.GetAllowsPublicRepositories() + r = &RunnerGroup{} + r.GetAllowsPublicRepositories() + r = nil + r.GetAllowsPublicRepositories() +} + +func TestRunnerGroup_GetDefault(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RunnerGroup{Default: &zeroValue} + r.GetDefault() + r = &RunnerGroup{} + r.GetDefault() + r = nil + r.GetDefault() +} + +func TestRunnerGroup_GetHostedRunnersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerGroup{HostedRunnersURL: &zeroValue} + r.GetHostedRunnersURL() + r = &RunnerGroup{} + r.GetHostedRunnersURL() + r = nil + r.GetHostedRunnersURL() +} + +func TestRunnerGroup_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RunnerGroup{ID: &zeroValue} + r.GetID() + r = &RunnerGroup{} + r.GetID() + r = nil + r.GetID() +} + +func TestRunnerGroup_GetInherited(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RunnerGroup{Inherited: &zeroValue} + r.GetInherited() + r = &RunnerGroup{} + r.GetInherited() + r = nil + r.GetInherited() +} + +func TestRunnerGroup_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerGroup{Name: &zeroValue} + r.GetName() + r = &RunnerGroup{} + r.GetName() + r = nil + r.GetName() +} + +func TestRunnerGroup_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerGroup{NetworkConfigurationID: &zeroValue} + r.GetNetworkConfigurationID() + r = &RunnerGroup{} + r.GetNetworkConfigurationID() + r = nil + r.GetNetworkConfigurationID() +} + +func TestRunnerGroup_GetRestrictedToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RunnerGroup{RestrictedToWorkflows: &zeroValue} + r.GetRestrictedToWorkflows() + r = &RunnerGroup{} + r.GetRestrictedToWorkflows() + r = nil + r.GetRestrictedToWorkflows() +} + +func TestRunnerGroup_GetRunnersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerGroup{RunnersURL: &zeroValue} + r.GetRunnersURL() + r = &RunnerGroup{} + r.GetRunnersURL() + r = nil + r.GetRunnersURL() +} + +func TestRunnerGroup_GetSelectedRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerGroup{SelectedRepositoriesURL: &zeroValue} + r.GetSelectedRepositoriesURL() + r = &RunnerGroup{} + r.GetSelectedRepositoriesURL() + r = nil + r.GetSelectedRepositoriesURL() +} + +func TestRunnerGroup_GetSelectedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + r := &RunnerGroup{SelectedWorkflows: zeroValue} + r.GetSelectedWorkflows() + r = &RunnerGroup{} + r.GetSelectedWorkflows() + r = nil + r.GetSelectedWorkflows() +} + +func TestRunnerGroup_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerGroup{Visibility: &zeroValue} + r.GetVisibility() + r = &RunnerGroup{} + r.GetVisibility() + r = nil + r.GetVisibility() +} + +func TestRunnerGroup_GetWorkflowRestrictionsReadOnly(tt *testing.T) { + tt.Parallel() + var zeroValue bool + r := &RunnerGroup{WorkflowRestrictionsReadOnly: &zeroValue} + r.GetWorkflowRestrictionsReadOnly() + r = &RunnerGroup{} + r.GetWorkflowRestrictionsReadOnly() + r = nil + r.GetWorkflowRestrictionsReadOnly() +} + +func TestRunnerGroups_GetRunnerGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []*RunnerGroup{} + r := &RunnerGroups{RunnerGroups: zeroValue} + r.GetRunnerGroups() + r = &RunnerGroups{} + r.GetRunnerGroups() + r = nil + r.GetRunnerGroups() +} + +func TestRunnerGroups_GetTotalCount(tt *testing.T) { + tt.Parallel() + r := &RunnerGroups{} + r.GetTotalCount() + r = nil + r.GetTotalCount() +} + +func TestRunnerLabels_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + r := &RunnerLabels{ID: &zeroValue} + r.GetID() + r = &RunnerLabels{} + r.GetID() + r = nil + r.GetID() +} + +func TestRunnerLabels_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerLabels{Name: &zeroValue} + r.GetName() + r = &RunnerLabels{} + r.GetName() + r = nil + r.GetName() +} + +func TestRunnerLabels_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + r := &RunnerLabels{Type: &zeroValue} + r.GetType() + r = &RunnerLabels{} + r.GetType() + r = nil + r.GetType() +} + +func TestRunners_GetRunners(tt *testing.T) { + tt.Parallel() + zeroValue := []*Runner{} + r := &Runners{Runners: zeroValue} + r.GetRunners() + r = &Runners{} + r.GetRunners() + r = nil + r.GetRunners() +} + +func TestRunners_GetTotalCount(tt *testing.T) { + tt.Parallel() + r := &Runners{} + r.GetTotalCount() + r = nil + r.GetTotalCount() +} + +func TestSarifAnalysis_GetCheckoutURI(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SarifAnalysis{CheckoutURI: &zeroValue} + s.GetCheckoutURI() + s = &SarifAnalysis{} + s.GetCheckoutURI() + s = nil + s.GetCheckoutURI() +} + +func TestSarifAnalysis_GetCommitSHA(tt *testing.T) { + tt.Parallel() + s := &SarifAnalysis{} + s.GetCommitSHA() + s = nil + s.GetCommitSHA() +} + +func TestSarifAnalysis_GetRef(tt *testing.T) { + tt.Parallel() + s := &SarifAnalysis{} + s.GetRef() + s = nil + s.GetRef() +} + +func TestSarifAnalysis_GetSarif(tt *testing.T) { + tt.Parallel() + s := &SarifAnalysis{} + s.GetSarif() + s = nil + s.GetSarif() +} + +func TestSarifAnalysis_GetStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SarifAnalysis{StartedAt: &zeroValue} + s.GetStartedAt() + s = &SarifAnalysis{} + s.GetStartedAt() + s = nil + s.GetStartedAt() +} + +func TestSarifAnalysis_GetToolName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SarifAnalysis{ToolName: &zeroValue} + s.GetToolName() + s = &SarifAnalysis{} + s.GetToolName() + s = nil + s.GetToolName() +} + +func TestSarifAnalysis_GetValidate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SarifAnalysis{Validate: &zeroValue} + s.GetValidate() + s = &SarifAnalysis{} + s.GetValidate() + s = nil + s.GetValidate() +} + +func TestSarifID_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SarifID{ID: &zeroValue} + s.GetID() + s = &SarifID{} + s.GetID() + s = nil + s.GetID() +} + +func TestSarifID_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SarifID{URL: &zeroValue} + s.GetURL() + s = &SarifID{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSARIFUpload_GetAnalysesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SARIFUpload{AnalysesURL: &zeroValue} + s.GetAnalysesURL() + s = &SARIFUpload{} + s.GetAnalysesURL() + s = nil + s.GetAnalysesURL() +} + +func TestSARIFUpload_GetProcessingStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SARIFUpload{ProcessingStatus: &zeroValue} + s.GetProcessingStatus() + s = &SARIFUpload{} + s.GetProcessingStatus() + s = nil + s.GetProcessingStatus() +} + +func TestSBOM_GetSBOM(tt *testing.T) { + tt.Parallel() + s := &SBOM{} + s.GetSBOM() + s = nil + s.GetSBOM() +} + +func TestSBOMInfo_GetCreationInfo(tt *testing.T) { + tt.Parallel() + s := &SBOMInfo{} + s.GetCreationInfo() + s = nil + s.GetCreationInfo() +} + +func TestSBOMInfo_GetDataLicense(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SBOMInfo{DataLicense: &zeroValue} + s.GetDataLicense() + s = &SBOMInfo{} + s.GetDataLicense() + s = nil + s.GetDataLicense() +} + +func TestSBOMInfo_GetDocumentDescribes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SBOMInfo{DocumentDescribes: zeroValue} + s.GetDocumentDescribes() + s = &SBOMInfo{} + s.GetDocumentDescribes() + s = nil + s.GetDocumentDescribes() +} + +func TestSBOMInfo_GetDocumentNamespace(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SBOMInfo{DocumentNamespace: &zeroValue} + s.GetDocumentNamespace() + s = &SBOMInfo{} + s.GetDocumentNamespace() + s = nil + s.GetDocumentNamespace() +} + +func TestSBOMInfo_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SBOMInfo{Name: &zeroValue} + s.GetName() + s = &SBOMInfo{} + s.GetName() + s = nil + s.GetName() +} + +func TestSBOMInfo_GetPackages(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepoDependencies{} + s := &SBOMInfo{Packages: zeroValue} + s.GetPackages() + s = &SBOMInfo{} + s.GetPackages() + s = nil + s.GetPackages() +} + +func TestSBOMInfo_GetRelationships(tt *testing.T) { + tt.Parallel() + zeroValue := []*SBOMRelationship{} + s := &SBOMInfo{Relationships: zeroValue} + s.GetRelationships() + s = &SBOMInfo{} + s.GetRelationships() + s = nil + s.GetRelationships() +} + +func TestSBOMInfo_GetSPDXID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SBOMInfo{SPDXID: &zeroValue} + s.GetSPDXID() + s = &SBOMInfo{} + s.GetSPDXID() + s = nil + s.GetSPDXID() +} + +func TestSBOMInfo_GetSPDXVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SBOMInfo{SPDXVersion: &zeroValue} + s.GetSPDXVersion() + s = &SBOMInfo{} + s.GetSPDXVersion() + s = nil + s.GetSPDXVersion() +} + +func TestSBOMRelationship_GetRelatedSPDXElement(tt *testing.T) { + tt.Parallel() + s := &SBOMRelationship{} + s.GetRelatedSPDXElement() + s = nil + s.GetRelatedSPDXElement() +} + +func TestSBOMRelationship_GetRelationshipType(tt *testing.T) { + tt.Parallel() + s := &SBOMRelationship{} + s.GetRelationshipType() + s = nil + s.GetRelationshipType() +} + +func TestSBOMRelationship_GetSPDXElementID(tt *testing.T) { + tt.Parallel() + s := &SBOMRelationship{} + s.GetSPDXElementID() + s = nil + s.GetSPDXElementID() +} + +func TestScanningAnalysis_GetAnalysisKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{AnalysisKey: &zeroValue} + s.GetAnalysisKey() + s = &ScanningAnalysis{} + s.GetAnalysisKey() + s = nil + s.GetAnalysisKey() +} + +func TestScanningAnalysis_GetCategory(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{Category: &zeroValue} + s.GetCategory() + s = &ScanningAnalysis{} + s.GetCategory() + s = nil + s.GetCategory() +} + +func TestScanningAnalysis_GetCommitSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{CommitSHA: &zeroValue} + s.GetCommitSHA() + s = &ScanningAnalysis{} + s.GetCommitSHA() + s = nil + s.GetCommitSHA() +} + +func TestScanningAnalysis_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &ScanningAnalysis{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &ScanningAnalysis{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestScanningAnalysis_GetDeletable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &ScanningAnalysis{Deletable: &zeroValue} + s.GetDeletable() + s = &ScanningAnalysis{} + s.GetDeletable() + s = nil + s.GetDeletable() +} + +func TestScanningAnalysis_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{Environment: &zeroValue} + s.GetEnvironment() + s = &ScanningAnalysis{} + s.GetEnvironment() + s = nil + s.GetEnvironment() +} + +func TestScanningAnalysis_GetError(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{Error: &zeroValue} + s.GetError() + s = &ScanningAnalysis{} + s.GetError() + s = nil + s.GetError() +} + +func TestScanningAnalysis_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &ScanningAnalysis{ID: &zeroValue} + s.GetID() + s = &ScanningAnalysis{} + s.GetID() + s = nil + s.GetID() +} + +func TestScanningAnalysis_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{Ref: &zeroValue} + s.GetRef() + s = &ScanningAnalysis{} + s.GetRef() + s = nil + s.GetRef() +} + +func TestScanningAnalysis_GetResultsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &ScanningAnalysis{ResultsCount: &zeroValue} + s.GetResultsCount() + s = &ScanningAnalysis{} + s.GetResultsCount() + s = nil + s.GetResultsCount() +} + +func TestScanningAnalysis_GetRulesCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &ScanningAnalysis{RulesCount: &zeroValue} + s.GetRulesCount() + s = &ScanningAnalysis{} + s.GetRulesCount() + s = nil + s.GetRulesCount() +} + +func TestScanningAnalysis_GetSarifID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{SarifID: &zeroValue} + s.GetSarifID() + s = &ScanningAnalysis{} + s.GetSarifID() + s = nil + s.GetSarifID() +} + +func TestScanningAnalysis_GetTool(tt *testing.T) { + tt.Parallel() + s := &ScanningAnalysis{} + s.GetTool() + s = nil + s.GetTool() +} + +func TestScanningAnalysis_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{URL: &zeroValue} + s.GetURL() + s = &ScanningAnalysis{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestScanningAnalysis_GetWarning(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &ScanningAnalysis{Warning: &zeroValue} + s.GetWarning() + s = &ScanningAnalysis{} + s.GetWarning() + s = nil + s.GetWarning() +} + +func TestSCIMEnterpriseAttribute_GetOperations(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseAttributeOperation{} + s := &SCIMEnterpriseAttribute{Operations: zeroValue} + s.GetOperations() + s = &SCIMEnterpriseAttribute{} + s.GetOperations() + s = nil + s.GetOperations() +} + +func TestSCIMEnterpriseAttribute_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMEnterpriseAttribute{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMEnterpriseAttribute{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMEnterpriseAttributeOperation_GetOp(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseAttributeOperation{} + s.GetOp() + s = nil + s.GetOp() +} + +func TestSCIMEnterpriseAttributeOperation_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseAttributeOperation{Path: &zeroValue} + s.GetPath() + s = &SCIMEnterpriseAttributeOperation{} + s.GetPath() + s = nil + s.GetPath() +} + +func TestSCIMEnterpriseAttributeOperation_GetValue(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseAttributeOperation{} + s.GetValue() + s = nil + s.GetValue() +} + +func TestSCIMEnterpriseDisplayReference_GetDisplay(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseDisplayReference{Display: &zeroValue} + s.GetDisplay() + s = &SCIMEnterpriseDisplayReference{} + s.GetDisplay() + s = nil + s.GetDisplay() +} + +func TestSCIMEnterpriseDisplayReference_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseDisplayReference{Ref: &zeroValue} + s.GetRef() + s = &SCIMEnterpriseDisplayReference{} + s.GetRef() + s = nil + s.GetRef() +} + +func TestSCIMEnterpriseDisplayReference_GetValue(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseDisplayReference{} + s.GetValue() + s = nil + s.GetValue() +} + +func TestSCIMEnterpriseGroupAttributes_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseGroupAttributes{DisplayName: &zeroValue} + s.GetDisplayName() + s = &SCIMEnterpriseGroupAttributes{} + s.GetDisplayName() + s = nil + s.GetDisplayName() +} + +func TestSCIMEnterpriseGroupAttributes_GetExternalID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseGroupAttributes{ExternalID: &zeroValue} + s.GetExternalID() + s = &SCIMEnterpriseGroupAttributes{} + s.GetExternalID() + s = nil + s.GetExternalID() +} + +func TestSCIMEnterpriseGroupAttributes_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseGroupAttributes{ID: &zeroValue} + s.GetID() + s = &SCIMEnterpriseGroupAttributes{} + s.GetID() + s = nil + s.GetID() +} + +func TestSCIMEnterpriseGroupAttributes_GetMembers(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseDisplayReference{} + s := &SCIMEnterpriseGroupAttributes{Members: zeroValue} + s.GetMembers() + s = &SCIMEnterpriseGroupAttributes{} + s.GetMembers() + s = nil + s.GetMembers() +} + +func TestSCIMEnterpriseGroupAttributes_GetMeta(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseGroupAttributes{} + s.GetMeta() + s = nil + s.GetMeta() +} + +func TestSCIMEnterpriseGroupAttributes_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMEnterpriseGroupAttributes{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMEnterpriseGroupAttributes{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMEnterpriseGroups_GetItemsPerPage(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMEnterpriseGroups{ItemsPerPage: &zeroValue} + s.GetItemsPerPage() + s = &SCIMEnterpriseGroups{} + s.GetItemsPerPage() + s = nil + s.GetItemsPerPage() +} + +func TestSCIMEnterpriseGroups_GetResources(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseGroupAttributes{} + s := &SCIMEnterpriseGroups{Resources: zeroValue} + s.GetResources() + s = &SCIMEnterpriseGroups{} + s.GetResources() + s = nil + s.GetResources() +} + +func TestSCIMEnterpriseGroups_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMEnterpriseGroups{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMEnterpriseGroups{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMEnterpriseGroups_GetStartIndex(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMEnterpriseGroups{StartIndex: &zeroValue} + s.GetStartIndex() + s = &SCIMEnterpriseGroups{} + s.GetStartIndex() + s = nil + s.GetStartIndex() +} + +func TestSCIMEnterpriseGroups_GetTotalResults(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMEnterpriseGroups{TotalResults: &zeroValue} + s.GetTotalResults() + s = &SCIMEnterpriseGroups{} + s.GetTotalResults() + s = nil + s.GetTotalResults() +} + +func TestSCIMEnterpriseMeta_GetCreated(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SCIMEnterpriseMeta{Created: &zeroValue} + s.GetCreated() + s = &SCIMEnterpriseMeta{} + s.GetCreated() + s = nil + s.GetCreated() +} + +func TestSCIMEnterpriseMeta_GetLastModified(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SCIMEnterpriseMeta{LastModified: &zeroValue} + s.GetLastModified() + s = &SCIMEnterpriseMeta{} + s.GetLastModified() + s = nil + s.GetLastModified() +} + +func TestSCIMEnterpriseMeta_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseMeta{Location: &zeroValue} + s.GetLocation() + s = &SCIMEnterpriseMeta{} + s.GetLocation() + s = nil + s.GetLocation() +} + +func TestSCIMEnterpriseMeta_GetResourceType(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseMeta{} + s.GetResourceType() + s = nil + s.GetResourceType() +} + +func TestSCIMEnterpriseUserAttributes_GetActive(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserAttributes{} + s.GetActive() + s = nil + s.GetActive() +} + +func TestSCIMEnterpriseUserAttributes_GetDisplayName(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserAttributes{} + s.GetDisplayName() + s = nil + s.GetDisplayName() +} + +func TestSCIMEnterpriseUserAttributes_GetEmails(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseUserEmail{} + s := &SCIMEnterpriseUserAttributes{Emails: zeroValue} + s.GetEmails() + s = &SCIMEnterpriseUserAttributes{} + s.GetEmails() + s = nil + s.GetEmails() +} + +func TestSCIMEnterpriseUserAttributes_GetExternalID(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserAttributes{} + s.GetExternalID() + s = nil + s.GetExternalID() +} + +func TestSCIMEnterpriseUserAttributes_GetGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseDisplayReference{} + s := &SCIMEnterpriseUserAttributes{Groups: zeroValue} + s.GetGroups() + s = &SCIMEnterpriseUserAttributes{} + s.GetGroups() + s = nil + s.GetGroups() +} + +func TestSCIMEnterpriseUserAttributes_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseUserAttributes{ID: &zeroValue} + s.GetID() + s = &SCIMEnterpriseUserAttributes{} + s.GetID() + s = nil + s.GetID() +} + +func TestSCIMEnterpriseUserAttributes_GetMeta(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserAttributes{} + s.GetMeta() + s = nil + s.GetMeta() +} + +func TestSCIMEnterpriseUserAttributes_GetName(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserAttributes{} + s.GetName() + s = nil + s.GetName() +} + +func TestSCIMEnterpriseUserAttributes_GetRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseUserRole{} + s := &SCIMEnterpriseUserAttributes{Roles: zeroValue} + s.GetRoles() + s = &SCIMEnterpriseUserAttributes{} + s.GetRoles() + s = nil + s.GetRoles() +} + +func TestSCIMEnterpriseUserAttributes_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMEnterpriseUserAttributes{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMEnterpriseUserAttributes{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMEnterpriseUserAttributes_GetUserName(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserAttributes{} + s.GetUserName() + s = nil + s.GetUserName() +} + +func TestSCIMEnterpriseUserEmail_GetPrimary(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserEmail{} + s.GetPrimary() + s = nil + s.GetPrimary() +} + +func TestSCIMEnterpriseUserEmail_GetType(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserEmail{} + s.GetType() + s = nil + s.GetType() +} + +func TestSCIMEnterpriseUserEmail_GetValue(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserEmail{} + s.GetValue() + s = nil + s.GetValue() +} + +func TestSCIMEnterpriseUserName_GetFamilyName(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserName{} + s.GetFamilyName() + s = nil + s.GetFamilyName() +} + +func TestSCIMEnterpriseUserName_GetFormatted(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseUserName{Formatted: &zeroValue} + s.GetFormatted() + s = &SCIMEnterpriseUserName{} + s.GetFormatted() + s = nil + s.GetFormatted() +} + +func TestSCIMEnterpriseUserName_GetGivenName(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserName{} + s.GetGivenName() + s = nil + s.GetGivenName() +} + +func TestSCIMEnterpriseUserName_GetMiddleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseUserName{MiddleName: &zeroValue} + s.GetMiddleName() + s = &SCIMEnterpriseUserName{} + s.GetMiddleName() + s = nil + s.GetMiddleName() +} + +func TestSCIMEnterpriseUserRole_GetDisplay(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseUserRole{Display: &zeroValue} + s.GetDisplay() + s = &SCIMEnterpriseUserRole{} + s.GetDisplay() + s = nil + s.GetDisplay() +} + +func TestSCIMEnterpriseUserRole_GetPrimary(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SCIMEnterpriseUserRole{Primary: &zeroValue} + s.GetPrimary() + s = &SCIMEnterpriseUserRole{} + s.GetPrimary() + s = nil + s.GetPrimary() +} + +func TestSCIMEnterpriseUserRole_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMEnterpriseUserRole{Type: &zeroValue} + s.GetType() + s = &SCIMEnterpriseUserRole{} + s.GetType() + s = nil + s.GetType() +} + +func TestSCIMEnterpriseUserRole_GetValue(tt *testing.T) { + tt.Parallel() + s := &SCIMEnterpriseUserRole{} + s.GetValue() + s = nil + s.GetValue() +} + +func TestSCIMEnterpriseUsers_GetItemsPerPage(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMEnterpriseUsers{ItemsPerPage: &zeroValue} + s.GetItemsPerPage() + s = &SCIMEnterpriseUsers{} + s.GetItemsPerPage() + s = nil + s.GetItemsPerPage() +} + +func TestSCIMEnterpriseUsers_GetResources(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMEnterpriseUserAttributes{} + s := &SCIMEnterpriseUsers{Resources: zeroValue} + s.GetResources() + s = &SCIMEnterpriseUsers{} + s.GetResources() + s = nil + s.GetResources() +} + +func TestSCIMEnterpriseUsers_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMEnterpriseUsers{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMEnterpriseUsers{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMEnterpriseUsers_GetStartIndex(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMEnterpriseUsers{StartIndex: &zeroValue} + s.GetStartIndex() + s = &SCIMEnterpriseUsers{} + s.GetStartIndex() + s = nil + s.GetStartIndex() +} + +func TestSCIMEnterpriseUsers_GetTotalResults(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMEnterpriseUsers{TotalResults: &zeroValue} + s.GetTotalResults() + s = &SCIMEnterpriseUsers{} + s.GetTotalResults() + s = nil + s.GetTotalResults() +} + +func TestSCIMMeta_GetCreated(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SCIMMeta{Created: &zeroValue} + s.GetCreated() + s = &SCIMMeta{} + s.GetCreated() + s = nil + s.GetCreated() +} + +func TestSCIMMeta_GetLastModified(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SCIMMeta{LastModified: &zeroValue} + s.GetLastModified() + s = &SCIMMeta{} + s.GetLastModified() + s = nil + s.GetLastModified() +} + +func TestSCIMMeta_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMMeta{Location: &zeroValue} + s.GetLocation() + s = &SCIMMeta{} + s.GetLocation() + s = nil + s.GetLocation() +} + +func TestSCIMMeta_GetResourceType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMMeta{ResourceType: &zeroValue} + s.GetResourceType() + s = &SCIMMeta{} + s.GetResourceType() + s = nil + s.GetResourceType() +} + +func TestSCIMProvisionedIdentities_GetItemsPerPage(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMProvisionedIdentities{ItemsPerPage: &zeroValue} + s.GetItemsPerPage() + s = &SCIMProvisionedIdentities{} + s.GetItemsPerPage() + s = nil + s.GetItemsPerPage() +} + +func TestSCIMProvisionedIdentities_GetResources(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMUserAttributes{} + s := &SCIMProvisionedIdentities{Resources: zeroValue} + s.GetResources() + s = &SCIMProvisionedIdentities{} + s.GetResources() + s = nil + s.GetResources() +} + +func TestSCIMProvisionedIdentities_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMProvisionedIdentities{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMProvisionedIdentities{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMProvisionedIdentities_GetStartIndex(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMProvisionedIdentities{StartIndex: &zeroValue} + s.GetStartIndex() + s = &SCIMProvisionedIdentities{} + s.GetStartIndex() + s = nil + s.GetStartIndex() +} + +func TestSCIMProvisionedIdentities_GetTotalResults(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SCIMProvisionedIdentities{TotalResults: &zeroValue} + s.GetTotalResults() + s = &SCIMProvisionedIdentities{} + s.GetTotalResults() + s = nil + s.GetTotalResults() +} + +func TestSCIMUserAttributes_GetActive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SCIMUserAttributes{Active: &zeroValue} + s.GetActive() + s = &SCIMUserAttributes{} + s.GetActive() + s = nil + s.GetActive() +} + +func TestSCIMUserAttributes_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserAttributes{DisplayName: &zeroValue} + s.GetDisplayName() + s = &SCIMUserAttributes{} + s.GetDisplayName() + s = nil + s.GetDisplayName() +} + +func TestSCIMUserAttributes_GetEmails(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMUserEmail{} + s := &SCIMUserAttributes{Emails: zeroValue} + s.GetEmails() + s = &SCIMUserAttributes{} + s.GetEmails() + s = nil + s.GetEmails() +} + +func TestSCIMUserAttributes_GetExternalID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserAttributes{ExternalID: &zeroValue} + s.GetExternalID() + s = &SCIMUserAttributes{} + s.GetExternalID() + s = nil + s.GetExternalID() +} + +func TestSCIMUserAttributes_GetGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMUserAttributes{Groups: zeroValue} + s.GetGroups() + s = &SCIMUserAttributes{} + s.GetGroups() + s = nil + s.GetGroups() +} + +func TestSCIMUserAttributes_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserAttributes{ID: &zeroValue} + s.GetID() + s = &SCIMUserAttributes{} + s.GetID() + s = nil + s.GetID() +} + +func TestSCIMUserAttributes_GetMeta(tt *testing.T) { + tt.Parallel() + s := &SCIMUserAttributes{} + s.GetMeta() + s = nil + s.GetMeta() +} + +func TestSCIMUserAttributes_GetName(tt *testing.T) { + tt.Parallel() + s := &SCIMUserAttributes{} + s.GetName() + s = nil + s.GetName() +} + +func TestSCIMUserAttributes_GetRoles(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMUserRole{} + s := &SCIMUserAttributes{Roles: zeroValue} + s.GetRoles() + s = &SCIMUserAttributes{} + s.GetRoles() + s = nil + s.GetRoles() +} + +func TestSCIMUserAttributes_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SCIMUserAttributes{Schemas: zeroValue} + s.GetSchemas() + s = &SCIMUserAttributes{} + s.GetSchemas() + s = nil + s.GetSchemas() +} + +func TestSCIMUserAttributes_GetUserName(tt *testing.T) { + tt.Parallel() + s := &SCIMUserAttributes{} + s.GetUserName() + s = nil + s.GetUserName() +} + +func TestSCIMUserEmail_GetPrimary(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SCIMUserEmail{Primary: &zeroValue} + s.GetPrimary() + s = &SCIMUserEmail{} + s.GetPrimary() + s = nil + s.GetPrimary() +} + +func TestSCIMUserEmail_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserEmail{Type: &zeroValue} + s.GetType() + s = &SCIMUserEmail{} + s.GetType() + s = nil + s.GetType() +} + +func TestSCIMUserEmail_GetValue(tt *testing.T) { + tt.Parallel() + s := &SCIMUserEmail{} + s.GetValue() + s = nil + s.GetValue() +} + +func TestSCIMUserName_GetFamilyName(tt *testing.T) { + tt.Parallel() + s := &SCIMUserName{} + s.GetFamilyName() + s = nil + s.GetFamilyName() +} + +func TestSCIMUserName_GetFormatted(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserName{Formatted: &zeroValue} + s.GetFormatted() + s = &SCIMUserName{} + s.GetFormatted() + s = nil + s.GetFormatted() +} + +func TestSCIMUserName_GetGivenName(tt *testing.T) { + tt.Parallel() + s := &SCIMUserName{} + s.GetGivenName() + s = nil + s.GetGivenName() +} + +func TestSCIMUserRole_GetDisplay(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserRole{Display: &zeroValue} + s.GetDisplay() + s = &SCIMUserRole{} + s.GetDisplay() + s = nil + s.GetDisplay() +} + +func TestSCIMUserRole_GetPrimary(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SCIMUserRole{Primary: &zeroValue} + s.GetPrimary() + s = &SCIMUserRole{} + s.GetPrimary() + s = nil + s.GetPrimary() +} + +func TestSCIMUserRole_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SCIMUserRole{Type: &zeroValue} + s.GetType() + s = &SCIMUserRole{} + s.GetType() + s = nil + s.GetType() +} + +func TestSCIMUserRole_GetValue(tt *testing.T) { + tt.Parallel() + s := &SCIMUserRole{} + s.GetValue() + s = nil + s.GetValue() +} + +func TestSearchOptions_GetAdvancedSearch(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SearchOptions{AdvancedSearch: &zeroValue} + s.GetAdvancedSearch() + s = &SearchOptions{} + s.GetAdvancedSearch() + s = nil + s.GetAdvancedSearch() +} + +func TestSearchOptions_GetOrder(tt *testing.T) { + tt.Parallel() + s := &SearchOptions{} + s.GetOrder() + s = nil + s.GetOrder() +} + +func TestSearchOptions_GetSearchType(tt *testing.T) { + tt.Parallel() + s := &SearchOptions{} + s.GetSearchType() + s = nil + s.GetSearchType() +} + +func TestSearchOptions_GetSort(tt *testing.T) { + tt.Parallel() + s := &SearchOptions{} + s.GetSort() + s = nil + s.GetSort() +} + +func TestSearchOptions_GetTextMatch(tt *testing.T) { + tt.Parallel() + s := &SearchOptions{} + s.GetTextMatch() + s = nil + s.GetTextMatch() +} + +func TestSeatAssignments_GetSeatsCreated(tt *testing.T) { + tt.Parallel() + s := &SeatAssignments{} + s.GetSeatsCreated() + s = nil + s.GetSeatsCreated() +} + +func TestSeatCancellations_GetSeatsCancelled(tt *testing.T) { + tt.Parallel() + s := &SeatCancellations{} + s.GetSeatsCancelled() + s = nil + s.GetSeatsCancelled() +} + +func TestSecret_GetCreatedAt(tt *testing.T) { + tt.Parallel() + s := &Secret{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSecret_GetName(tt *testing.T) { + tt.Parallel() + s := &Secret{} + s.GetName() + s = nil + s.GetName() +} + +func TestSecret_GetSelectedRepositoriesURL(tt *testing.T) { + tt.Parallel() + s := &Secret{} + s.GetSelectedRepositoriesURL() + s = nil + s.GetSelectedRepositoriesURL() +} + +func TestSecret_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + s := &Secret{} + s.GetUpdatedAt() + s = nil + s.GetUpdatedAt() +} + +func TestSecret_GetVisibility(tt *testing.T) { + tt.Parallel() + s := &Secret{} + s.GetVisibility() + s = nil + s.GetVisibility() +} + +func TestSecretOrgRequest_GetEncryptedValue(tt *testing.T) { + tt.Parallel() + s := &SecretOrgRequest{} + s.GetEncryptedValue() + s = nil + s.GetEncryptedValue() +} + +func TestSecretOrgRequest_GetKeyID(tt *testing.T) { + tt.Parallel() + s := &SecretOrgRequest{} + s.GetKeyID() + s = nil + s.GetKeyID() +} + +func TestSecretOrgRequest_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + s := &SecretOrgRequest{SelectedRepositoryIDs: zeroValue} + s.GetSelectedRepositoryIDs() + s = &SecretOrgRequest{} + s.GetSelectedRepositoryIDs() + s = nil + s.GetSelectedRepositoryIDs() +} + +func TestSecretOrgRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + s := &SecretOrgRequest{} + s.GetVisibility() + s = nil + s.GetVisibility() +} + +func TestSecretRequest_GetEncryptedValue(tt *testing.T) { + tt.Parallel() + s := &SecretRequest{} + s.GetEncryptedValue() + s = nil + s.GetEncryptedValue() +} + +func TestSecretRequest_GetKeyID(tt *testing.T) { + tt.Parallel() + s := &SecretRequest{} + s.GetKeyID() + s = nil + s.GetKeyID() +} + +func TestSecrets_GetSecrets(tt *testing.T) { + tt.Parallel() + zeroValue := []*Secret{} + s := &Secrets{Secrets: zeroValue} + s.GetSecrets() + s = &Secrets{} + s.GetSecrets() + s = nil + s.GetSecrets() +} + +func TestSecrets_GetTotalCount(tt *testing.T) { + tt.Parallel() + s := &Secrets{} + s.GetTotalCount() + s = nil + s.GetTotalCount() +} + +func TestSecretScanning_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanning{Status: &zeroValue} + s.GetStatus() + s = &SecretScanning{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSecretScanningAlert_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningAlert{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SecretScanningAlert{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSecretScanningAlert_GetFirstLocationDetected(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlert{} + s.GetFirstLocationDetected() + s = nil + s.GetFirstLocationDetected() +} + +func TestSecretScanningAlert_GetHasMoreLocations(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SecretScanningAlert{HasMoreLocations: &zeroValue} + s.GetHasMoreLocations() + s = &SecretScanningAlert{} + s.GetHasMoreLocations() + s = nil + s.GetHasMoreLocations() +} + +func TestSecretScanningAlert_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{HTMLURL: &zeroValue} + s.GetHTMLURL() + s = &SecretScanningAlert{} + s.GetHTMLURL() + s = nil + s.GetHTMLURL() +} + +func TestSecretScanningAlert_GetIsBase64Encoded(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SecretScanningAlert{IsBase64Encoded: &zeroValue} + s.GetIsBase64Encoded() + s = &SecretScanningAlert{} + s.GetIsBase64Encoded() + s = nil + s.GetIsBase64Encoded() +} + +func TestSecretScanningAlert_GetLocationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{LocationsURL: &zeroValue} + s.GetLocationsURL() + s = &SecretScanningAlert{} + s.GetLocationsURL() + s = nil + s.GetLocationsURL() +} + +func TestSecretScanningAlert_GetMultiRepo(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SecretScanningAlert{MultiRepo: &zeroValue} + s.GetMultiRepo() + s = &SecretScanningAlert{} + s.GetMultiRepo() + s = nil + s.GetMultiRepo() +} + +func TestSecretScanningAlert_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningAlert{Number: &zeroValue} + s.GetNumber() + s = &SecretScanningAlert{} + s.GetNumber() + s = nil + s.GetNumber() +} + +func TestSecretScanningAlert_GetPubliclyLeaked(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SecretScanningAlert{PubliclyLeaked: &zeroValue} + s.GetPubliclyLeaked() + s = &SecretScanningAlert{} + s.GetPubliclyLeaked() + s = nil + s.GetPubliclyLeaked() +} + +func TestSecretScanningAlert_GetPushProtectionBypassed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SecretScanningAlert{PushProtectionBypassed: &zeroValue} + s.GetPushProtectionBypassed() + s = &SecretScanningAlert{} + s.GetPushProtectionBypassed() + s = nil + s.GetPushProtectionBypassed() +} + +func TestSecretScanningAlert_GetPushProtectionBypassedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningAlert{PushProtectionBypassedAt: &zeroValue} + s.GetPushProtectionBypassedAt() + s = &SecretScanningAlert{} + s.GetPushProtectionBypassedAt() + s = nil + s.GetPushProtectionBypassedAt() +} + +func TestSecretScanningAlert_GetPushProtectionBypassedBy(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlert{} + s.GetPushProtectionBypassedBy() + s = nil + s.GetPushProtectionBypassedBy() +} + +func TestSecretScanningAlert_GetPushProtectionBypassRequestComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{PushProtectionBypassRequestComment: &zeroValue} + s.GetPushProtectionBypassRequestComment() + s = &SecretScanningAlert{} + s.GetPushProtectionBypassRequestComment() + s = nil + s.GetPushProtectionBypassRequestComment() +} + +func TestSecretScanningAlert_GetPushProtectionBypassRequestHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{PushProtectionBypassRequestHTMLURL: &zeroValue} + s.GetPushProtectionBypassRequestHTMLURL() + s = &SecretScanningAlert{} + s.GetPushProtectionBypassRequestHTMLURL() + s = nil + s.GetPushProtectionBypassRequestHTMLURL() +} + +func TestSecretScanningAlert_GetPushProtectionBypassRequestReviewer(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlert{} + s.GetPushProtectionBypassRequestReviewer() + s = nil + s.GetPushProtectionBypassRequestReviewer() +} + +func TestSecretScanningAlert_GetPushProtectionBypassRequestReviewerComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{PushProtectionBypassRequestReviewerComment: &zeroValue} + s.GetPushProtectionBypassRequestReviewerComment() + s = &SecretScanningAlert{} + s.GetPushProtectionBypassRequestReviewerComment() + s = nil + s.GetPushProtectionBypassRequestReviewerComment() +} + +func TestSecretScanningAlert_GetRepository(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlert{} + s.GetRepository() + s = nil + s.GetRepository() +} + +func TestSecretScanningAlert_GetResolution(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{Resolution: &zeroValue} + s.GetResolution() + s = &SecretScanningAlert{} + s.GetResolution() + s = nil + s.GetResolution() +} + +func TestSecretScanningAlert_GetResolutionComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{ResolutionComment: &zeroValue} + s.GetResolutionComment() + s = &SecretScanningAlert{} + s.GetResolutionComment() + s = nil + s.GetResolutionComment() +} + +func TestSecretScanningAlert_GetResolvedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningAlert{ResolvedAt: &zeroValue} + s.GetResolvedAt() + s = &SecretScanningAlert{} + s.GetResolvedAt() + s = nil + s.GetResolvedAt() +} + +func TestSecretScanningAlert_GetResolvedBy(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlert{} + s.GetResolvedBy() + s = nil + s.GetResolvedBy() +} + +func TestSecretScanningAlert_GetSecret(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{Secret: &zeroValue} + s.GetSecret() + s = &SecretScanningAlert{} + s.GetSecret() + s = nil + s.GetSecret() +} + +func TestSecretScanningAlert_GetSecretType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{SecretType: &zeroValue} + s.GetSecretType() + s = &SecretScanningAlert{} + s.GetSecretType() + s = nil + s.GetSecretType() +} + +func TestSecretScanningAlert_GetSecretTypeDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{SecretTypeDisplayName: &zeroValue} + s.GetSecretTypeDisplayName() + s = &SecretScanningAlert{} + s.GetSecretTypeDisplayName() + s = nil + s.GetSecretTypeDisplayName() +} + +func TestSecretScanningAlert_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{State: &zeroValue} + s.GetState() + s = &SecretScanningAlert{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecretScanningAlert_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningAlert{UpdatedAt: &zeroValue} + s.GetUpdatedAt() + s = &SecretScanningAlert{} + s.GetUpdatedAt() + s = nil + s.GetUpdatedAt() +} + +func TestSecretScanningAlert_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{URL: &zeroValue} + s.GetURL() + s = &SecretScanningAlert{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSecretScanningAlert_GetValidity(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlert{Validity: &zeroValue} + s.GetValidity() + s = &SecretScanningAlert{} + s.GetValidity() + s = nil + s.GetValidity() +} + +func TestSecretScanningAlertEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertEvent{Action: &zeroValue} + s.GetAction() + s = &SecretScanningAlertEvent{} + s.GetAction() + s = nil + s.GetAction() +} + +func TestSecretScanningAlertEvent_GetAlert(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertEvent{} + s.GetAlert() + s = nil + s.GetAlert() +} + +func TestSecretScanningAlertEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertEvent{} + s.GetEnterprise() + s = nil + s.GetEnterprise() +} + +func TestSecretScanningAlertEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestSecretScanningAlertEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertEvent{} + s.GetOrganization() + s = nil + s.GetOrganization() +} + +func TestSecretScanningAlertEvent_GetRepo(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertEvent{} + s.GetRepo() + s = nil + s.GetRepo() +} + +func TestSecretScanningAlertEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestSecretScanningAlertListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetDirection() + s = nil + s.GetDirection() +} + +func TestSecretScanningAlertListOptions_GetIsMultiRepo(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetIsMultiRepo() + s = nil + s.GetIsMultiRepo() +} + +func TestSecretScanningAlertListOptions_GetIsPubliclyLeaked(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetIsPubliclyLeaked() + s = nil + s.GetIsPubliclyLeaked() +} + +func TestSecretScanningAlertListOptions_GetResolution(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetResolution() + s = nil + s.GetResolution() +} + +func TestSecretScanningAlertListOptions_GetSecretType(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetSecretType() + s = nil + s.GetSecretType() +} + +func TestSecretScanningAlertListOptions_GetSort(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetSort() + s = nil + s.GetSort() +} + +func TestSecretScanningAlertListOptions_GetState(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecretScanningAlertListOptions_GetValidity(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertListOptions{} + s.GetValidity() + s = nil + s.GetValidity() +} + +func TestSecretScanningAlertLocation_GetDetails(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocation{} + s.GetDetails() + s = nil + s.GetDetails() +} + +func TestSecretScanningAlertLocation_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocation{Type: &zeroValue} + s.GetType() + s = &SecretScanningAlertLocation{} + s.GetType() + s = nil + s.GetType() +} + +func TestSecretScanningAlertLocationDetails_GetBlobSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationDetails{BlobSHA: &zeroValue} + s.GetBlobSHA() + s = &SecretScanningAlertLocationDetails{} + s.GetBlobSHA() + s = nil + s.GetBlobSHA() +} + +func TestSecretScanningAlertLocationDetails_GetBlobURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationDetails{BlobURL: &zeroValue} + s.GetBlobURL() + s = &SecretScanningAlertLocationDetails{} + s.GetBlobURL() + s = nil + s.GetBlobURL() +} + +func TestSecretScanningAlertLocationDetails_GetCommitSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationDetails{CommitSHA: &zeroValue} + s.GetCommitSHA() + s = &SecretScanningAlertLocationDetails{} + s.GetCommitSHA() + s = nil + s.GetCommitSHA() +} + +func TestSecretScanningAlertLocationDetails_GetCommitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationDetails{CommitURL: &zeroValue} + s.GetCommitURL() + s = &SecretScanningAlertLocationDetails{} + s.GetCommitURL() + s = nil + s.GetCommitURL() +} + +func TestSecretScanningAlertLocationDetails_GetEndColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningAlertLocationDetails{EndColumn: &zeroValue} + s.GetEndColumn() + s = &SecretScanningAlertLocationDetails{} + s.GetEndColumn() + s = nil + s.GetEndColumn() +} + +func TestSecretScanningAlertLocationDetails_GetEndLine(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningAlertLocationDetails{EndLine: &zeroValue} + s.GetEndLine() + s = &SecretScanningAlertLocationDetails{} + s.GetEndLine() + s = nil + s.GetEndLine() +} + +func TestSecretScanningAlertLocationDetails_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationDetails{Path: &zeroValue} + s.GetPath() + s = &SecretScanningAlertLocationDetails{} + s.GetPath() + s = nil + s.GetPath() +} + +func TestSecretScanningAlertLocationDetails_GetPullRequestCommentURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationDetails{PullRequestCommentURL: &zeroValue} + s.GetPullRequestCommentURL() + s = &SecretScanningAlertLocationDetails{} + s.GetPullRequestCommentURL() + s = nil + s.GetPullRequestCommentURL() +} + +func TestSecretScanningAlertLocationDetails_GetStartColumn(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningAlertLocationDetails{StartColumn: &zeroValue} + s.GetStartColumn() + s = &SecretScanningAlertLocationDetails{} + s.GetStartColumn() + s = nil + s.GetStartColumn() +} + +func TestSecretScanningAlertLocationDetails_GetStartline(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningAlertLocationDetails{Startline: &zeroValue} + s.GetStartline() + s = &SecretScanningAlertLocationDetails{} + s.GetStartline() + s = nil + s.GetStartline() +} + +func TestSecretScanningAlertLocationEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertLocationEvent{Action: &zeroValue} + s.GetAction() + s = &SecretScanningAlertLocationEvent{} + s.GetAction() + s = nil + s.GetAction() +} + +func TestSecretScanningAlertLocationEvent_GetAlert(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocationEvent{} + s.GetAlert() + s = nil + s.GetAlert() +} + +func TestSecretScanningAlertLocationEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocationEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestSecretScanningAlertLocationEvent_GetLocation(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocationEvent{} + s.GetLocation() + s = nil + s.GetLocation() +} + +func TestSecretScanningAlertLocationEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocationEvent{} + s.GetOrganization() + s = nil + s.GetOrganization() +} + +func TestSecretScanningAlertLocationEvent_GetRepo(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocationEvent{} + s.GetRepo() + s = nil + s.GetRepo() +} + +func TestSecretScanningAlertLocationEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertLocationEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestSecretScanningAlertUpdateOptions_GetResolution(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertUpdateOptions{Resolution: &zeroValue} + s.GetResolution() + s = &SecretScanningAlertUpdateOptions{} + s.GetResolution() + s = nil + s.GetResolution() +} + +func TestSecretScanningAlertUpdateOptions_GetResolutionComment(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningAlertUpdateOptions{ResolutionComment: &zeroValue} + s.GetResolutionComment() + s = &SecretScanningAlertUpdateOptions{} + s.GetResolutionComment() + s = nil + s.GetResolutionComment() +} + +func TestSecretScanningAlertUpdateOptions_GetState(tt *testing.T) { + tt.Parallel() + s := &SecretScanningAlertUpdateOptions{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecretScanningCreateCustomPatternsRequest_GetPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPatternRequest{} + s := &SecretScanningCreateCustomPatternsRequest{Patterns: zeroValue} + s.GetPatterns() + s = &SecretScanningCreateCustomPatternsRequest{} + s.GetPatterns() + s = nil + s.GetPatterns() +} + +func TestSecretScanningCreateCustomPatternsResponse_GetCreatedPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPattern{} + s := &SecretScanningCreateCustomPatternsResponse{CreatedPatterns: zeroValue} + s.GetCreatedPatterns() + s = &SecretScanningCreateCustomPatternsResponse{} + s.GetCreatedPatterns() + s = nil + s.GetCreatedPatterns() +} + +func TestSecretScanningCustomPattern_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningCustomPattern{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SecretScanningCustomPattern{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSecretScanningCustomPattern_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningCustomPattern{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningCustomPattern_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningCustomPattern{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningCustomPattern_GetID(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetID() + s = nil + s.GetID() +} + +func TestSecretScanningCustomPattern_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPattern{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningCustomPattern{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningCustomPattern_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPattern{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningCustomPattern{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningCustomPattern_GetName(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetName() + s = nil + s.GetName() +} + +func TestSecretScanningCustomPattern_GetPattern(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningCustomPattern_GetPushProtectionEnabled(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetPushProtectionEnabled() + s = nil + s.GetPushProtectionEnabled() +} + +func TestSecretScanningCustomPattern_GetSlug(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetSlug() + s = nil + s.GetSlug() +} + +func TestSecretScanningCustomPattern_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPattern{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningCustomPattern{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + +func TestSecretScanningCustomPattern_GetState(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPattern{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecretScanningCustomPattern_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretScanningCustomPattern{UpdatedAt: &zeroValue} + s.GetUpdatedAt() + s = &SecretScanningCustomPattern{} + s.GetUpdatedAt() + s = nil + s.GetUpdatedAt() +} + +func TestSecretScanningCustomPatternListOptions_GetDirection(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetDirection() + s = nil + s.GetDirection() +} + +func TestSecretScanningCustomPatternListOptions_GetPushProtection(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetPushProtection() + s = nil + s.GetPushProtection() +} + +func TestSecretScanningCustomPatternListOptions_GetSort(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetSort() + s = nil + s.GetSort() +} + +func TestSecretScanningCustomPatternListOptions_GetState(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternListOptions{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecretScanningCustomPatternRequest_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternRequest{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningCustomPatternRequest{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningCustomPatternRequest_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPatternRequest{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningCustomPatternRequest{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningCustomPatternRequest_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningCustomPatternRequest{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningCustomPatternRequest{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningCustomPatternRequest_GetName(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternRequest{} + s.GetName() + s = nil + s.GetName() +} + +func TestSecretScanningCustomPatternRequest_GetPattern(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternRequest{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningCustomPatternRequest_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternRequest{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningCustomPatternRequest{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + +func TestSecretScanningCustomPatternSetting_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternSetting{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningCustomPatternSetting{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningCustomPatternSetting_GetPushProtectionSetting(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternSetting{} + s.GetPushProtectionSetting() + s = nil + s.GetPushProtectionSetting() +} + +func TestSecretScanningCustomPatternSetting_GetTokenType(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternSetting{} + s.GetTokenType() + s = nil + s.GetTokenType() +} + +func TestSecretScanningCustomPatternToDelete_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningCustomPatternToDelete{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningCustomPatternToDelete{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningCustomPatternToDelete_GetPatternID(tt *testing.T) { + tt.Parallel() + s := &SecretScanningCustomPatternToDelete{} + s.GetPatternID() + s = nil + s.GetPatternID() +} + +func TestSecretScanningDelegatedBypassOptions_GetReviewers(tt *testing.T) { + tt.Parallel() + zeroValue := []*BypassReviewer{} + s := &SecretScanningDelegatedBypassOptions{Reviewers: zeroValue} + s.GetReviewers() + s = &SecretScanningDelegatedBypassOptions{} + s.GetReviewers() + s = nil + s.GetReviewers() +} + +func TestSecretScanningDeleteCustomPatternsRequest_GetPatterns(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPatternToDelete{} + s := &SecretScanningDeleteCustomPatternsRequest{Patterns: zeroValue} + s.GetPatterns() + s = &SecretScanningDeleteCustomPatternsRequest{} + s.GetPatterns() + s = nil + s.GetPatterns() +} + +func TestSecretScanningDeleteCustomPatternsRequest_GetPostDeleteAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningDeleteCustomPatternsRequest{PostDeleteAction: &zeroValue} + s.GetPostDeleteAction() + s = &SecretScanningDeleteCustomPatternsRequest{} + s.GetPostDeleteAction() + s = nil + s.GetPostDeleteAction() +} + +func TestSecretScanningPatternConfigs_GetCustomPatternOverrides(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningPatternOverride{} + s := &SecretScanningPatternConfigs{CustomPatternOverrides: zeroValue} + s.GetCustomPatternOverrides() + s = &SecretScanningPatternConfigs{} + s.GetCustomPatternOverrides() + s = nil + s.GetCustomPatternOverrides() +} + +func TestSecretScanningPatternConfigs_GetPatternConfigVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternConfigs{PatternConfigVersion: &zeroValue} + s.GetPatternConfigVersion() + s = &SecretScanningPatternConfigs{} + s.GetPatternConfigVersion() + s = nil + s.GetPatternConfigVersion() +} + +func TestSecretScanningPatternConfigs_GetProviderPatternOverrides(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningPatternOverride{} + s := &SecretScanningPatternConfigs{ProviderPatternOverrides: zeroValue} + s.GetProviderPatternOverrides() + s = &SecretScanningPatternConfigs{} + s.GetProviderPatternOverrides() + s = nil + s.GetProviderPatternOverrides() +} + +func TestSecretScanningPatternConfigsUpdate_GetPatternConfigVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternConfigsUpdate{PatternConfigVersion: &zeroValue} + s.GetPatternConfigVersion() + s = &SecretScanningPatternConfigsUpdate{} + s.GetPatternConfigVersion() + s = nil + s.GetPatternConfigVersion() +} + +func TestSecretScanningPatternConfigsUpdateOptions_GetCustomPatternSettings(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningCustomPatternSetting{} + s := &SecretScanningPatternConfigsUpdateOptions{CustomPatternSettings: zeroValue} + s.GetCustomPatternSettings() + s = &SecretScanningPatternConfigsUpdateOptions{} + s.GetCustomPatternSettings() + s = nil + s.GetCustomPatternSettings() +} + +func TestSecretScanningPatternConfigsUpdateOptions_GetPatternConfigVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternConfigsUpdateOptions{PatternConfigVersion: &zeroValue} + s.GetPatternConfigVersion() + s = &SecretScanningPatternConfigsUpdateOptions{} + s.GetPatternConfigVersion() + s = nil + s.GetPatternConfigVersion() +} + +func TestSecretScanningPatternConfigsUpdateOptions_GetProviderPatternSettings(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretScanningProviderPatternSetting{} + s := &SecretScanningPatternConfigsUpdateOptions{ProviderPatternSettings: zeroValue} + s.GetProviderPatternSettings() + s = &SecretScanningPatternConfigsUpdateOptions{} + s.GetProviderPatternSettings() + s = nil + s.GetProviderPatternSettings() +} + +func TestSecretScanningPatternOverride_GetAlertTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningPatternOverride{AlertTotal: &zeroValue} + s.GetAlertTotal() + s = &SecretScanningPatternOverride{} + s.GetAlertTotal() + s = nil + s.GetAlertTotal() +} + +func TestSecretScanningPatternOverride_GetAlertTotalPercentage(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningPatternOverride{AlertTotalPercentage: &zeroValue} + s.GetAlertTotalPercentage() + s = &SecretScanningPatternOverride{} + s.GetAlertTotalPercentage() + s = nil + s.GetAlertTotalPercentage() +} + +func TestSecretScanningPatternOverride_GetBypassrate(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningPatternOverride{Bypassrate: &zeroValue} + s.GetBypassrate() + s = &SecretScanningPatternOverride{} + s.GetBypassrate() + s = nil + s.GetBypassrate() +} + +func TestSecretScanningPatternOverride_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningPatternOverride{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningPatternOverride_GetDefaultSetting(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{DefaultSetting: &zeroValue} + s.GetDefaultSetting() + s = &SecretScanningPatternOverride{} + s.GetDefaultSetting() + s = nil + s.GetDefaultSetting() +} + +func TestSecretScanningPatternOverride_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{DisplayName: &zeroValue} + s.GetDisplayName() + s = &SecretScanningPatternOverride{} + s.GetDisplayName() + s = nil + s.GetDisplayName() +} + +func TestSecretScanningPatternOverride_GetEnterpriseSetting(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{EnterpriseSetting: &zeroValue} + s.GetEnterpriseSetting() + s = &SecretScanningPatternOverride{} + s.GetEnterpriseSetting() + s = nil + s.GetEnterpriseSetting() +} + +func TestSecretScanningPatternOverride_GetFalsePositiveRate(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningPatternOverride{FalsePositiveRate: &zeroValue} + s.GetFalsePositiveRate() + s = &SecretScanningPatternOverride{} + s.GetFalsePositiveRate() + s = nil + s.GetFalsePositiveRate() +} + +func TestSecretScanningPatternOverride_GetFalsePositives(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SecretScanningPatternOverride{FalsePositives: &zeroValue} + s.GetFalsePositives() + s = &SecretScanningPatternOverride{} + s.GetFalsePositives() + s = nil + s.GetFalsePositives() +} + +func TestSecretScanningPatternOverride_GetSetting(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{Setting: &zeroValue} + s.GetSetting() + s = &SecretScanningPatternOverride{} + s.GetSetting() + s = nil + s.GetSetting() +} + +func TestSecretScanningPatternOverride_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{Slug: &zeroValue} + s.GetSlug() + s = &SecretScanningPatternOverride{} + s.GetSlug() + s = nil + s.GetSlug() +} + +func TestSecretScanningPatternOverride_GetTokenType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPatternOverride{TokenType: &zeroValue} + s.GetTokenType() + s = &SecretScanningPatternOverride{} + s.GetTokenType() + s = nil + s.GetTokenType() +} + +func TestSecretScanningProviderPatternSetting_GetPushProtectionSetting(tt *testing.T) { + tt.Parallel() + s := &SecretScanningProviderPatternSetting{} + s.GetPushProtectionSetting() + s = nil + s.GetPushProtectionSetting() +} + +func TestSecretScanningProviderPatternSetting_GetTokenType(tt *testing.T) { + tt.Parallel() + s := &SecretScanningProviderPatternSetting{} + s.GetTokenType() + s = nil + s.GetTokenType() +} + +func TestSecretScanningPushProtection_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningPushProtection{Status: &zeroValue} + s.GetStatus() + s = &SecretScanningPushProtection{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSecretScanningScanHistory_GetBackfillScans(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretsScan{} + s := &SecretScanningScanHistory{BackfillScans: zeroValue} + s.GetBackfillScans() + s = &SecretScanningScanHistory{} + s.GetBackfillScans() + s = nil + s.GetBackfillScans() +} + +func TestSecretScanningScanHistory_GetCustomPatternBackfillScans(tt *testing.T) { + tt.Parallel() + zeroValue := []*CustomPatternBackfillScan{} + s := &SecretScanningScanHistory{CustomPatternBackfillScans: zeroValue} + s.GetCustomPatternBackfillScans() + s = &SecretScanningScanHistory{} + s.GetCustomPatternBackfillScans() + s = nil + s.GetCustomPatternBackfillScans() +} + +func TestSecretScanningScanHistory_GetIncrementalScans(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretsScan{} + s := &SecretScanningScanHistory{IncrementalScans: zeroValue} + s.GetIncrementalScans() + s = &SecretScanningScanHistory{} + s.GetIncrementalScans() + s = nil + s.GetIncrementalScans() +} + +func TestSecretScanningScanHistory_GetPatternUpdateScans(tt *testing.T) { + tt.Parallel() + zeroValue := []*SecretsScan{} + s := &SecretScanningScanHistory{PatternUpdateScans: zeroValue} + s.GetPatternUpdateScans() + s = &SecretScanningScanHistory{} + s.GetPatternUpdateScans() + s = nil + s.GetPatternUpdateScans() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetCustomPatternVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{CustomPatternVersion: &zeroValue} + s.GetCustomPatternVersion() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetCustomPatternVersion() + s = nil + s.GetCustomPatternVersion() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetEndDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{EndDelimiter: &zeroValue} + s.GetEndDelimiter() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetEndDelimiter() + s = nil + s.GetEndDelimiter() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetMustMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningUpdateCustomPatternRequest{MustMatch: zeroValue} + s.GetMustMatch() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetMustMatch() + s = nil + s.GetMustMatch() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetMustNotMatch(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecretScanningUpdateCustomPatternRequest{MustNotMatch: zeroValue} + s.GetMustNotMatch() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetMustNotMatch() + s = nil + s.GetMustNotMatch() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetPattern(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{Pattern: &zeroValue} + s.GetPattern() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSecretScanningUpdateCustomPatternRequest_GetStartDelimiter(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningUpdateCustomPatternRequest{StartDelimiter: &zeroValue} + s.GetStartDelimiter() + s = &SecretScanningUpdateCustomPatternRequest{} + s.GetStartDelimiter() + s = nil + s.GetStartDelimiter() +} + +func TestSecretScanningValidityChecks_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecretScanningValidityChecks{Status: &zeroValue} + s.GetStatus() + s = &SecretScanningValidityChecks{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSecretsScan_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretsScan{CompletedAt: &zeroValue} + s.GetCompletedAt() + s = &SecretsScan{} + s.GetCompletedAt() + s = nil + s.GetCompletedAt() +} + +func TestSecretsScan_GetStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecretsScan{StartedAt: &zeroValue} + s.GetStartedAt() + s = &SecretsScan{} + s.GetStartedAt() + s = nil + s.GetStartedAt() +} + +func TestSecretsScan_GetStatus(tt *testing.T) { + tt.Parallel() + s := &SecretsScan{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSecretsScan_GetType(tt *testing.T) { + tt.Parallel() + s := &SecretsScan{} + s.GetType() + s = nil + s.GetType() +} + +func TestSecurityAdvisory_GetAuthor(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisory{} + s.GetAuthor() + s = nil + s.GetAuthor() +} + +func TestSecurityAdvisory_GetClosedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecurityAdvisory{ClosedAt: &zeroValue} + s.GetClosedAt() + s = &SecurityAdvisory{} + s.GetClosedAt() + s = nil + s.GetClosedAt() +} + +func TestSecurityAdvisory_GetCollaboratingTeams(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + s := &SecurityAdvisory{CollaboratingTeams: zeroValue} + s.GetCollaboratingTeams() + s = &SecurityAdvisory{} + s.GetCollaboratingTeams() + s = nil + s.GetCollaboratingTeams() +} + +func TestSecurityAdvisory_GetCollaboratingUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + s := &SecurityAdvisory{CollaboratingUsers: zeroValue} + s.GetCollaboratingUsers() + s = &SecurityAdvisory{} + s.GetCollaboratingUsers() + s = nil + s.GetCollaboratingUsers() +} + +func TestSecurityAdvisory_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecurityAdvisory{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SecurityAdvisory{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSecurityAdvisory_GetCredits(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepoAdvisoryCredit{} + s := &SecurityAdvisory{Credits: zeroValue} + s.GetCredits() + s = &SecurityAdvisory{} + s.GetCredits() + s = nil + s.GetCredits() +} + +func TestSecurityAdvisory_GetCreditsDetailed(tt *testing.T) { + tt.Parallel() + zeroValue := []*RepoAdvisoryCreditDetailed{} + s := &SecurityAdvisory{CreditsDetailed: zeroValue} + s.GetCreditsDetailed() + s = &SecurityAdvisory{} + s.GetCreditsDetailed() + s = nil + s.GetCreditsDetailed() +} + +func TestSecurityAdvisory_GetCVEID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{CVEID: &zeroValue} + s.GetCVEID() + s = &SecurityAdvisory{} + s.GetCVEID() + s = nil + s.GetCVEID() +} + +func TestSecurityAdvisory_GetCVSS(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisory{} + s.GetCVSS() + s = nil + s.GetCVSS() +} + +func TestSecurityAdvisory_GetCVSSSeverities(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisory{} + s.GetCVSSSeverities() + s = nil + s.GetCVSSSeverities() +} + +func TestSecurityAdvisory_GetCWEIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + s := &SecurityAdvisory{CWEIDs: zeroValue} + s.GetCWEIDs() + s = &SecurityAdvisory{} + s.GetCWEIDs() + s = nil + s.GetCWEIDs() +} + +func TestSecurityAdvisory_GetCWEs(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryCWEs{} + s := &SecurityAdvisory{CWEs: zeroValue} + s.GetCWEs() + s = &SecurityAdvisory{} + s.GetCWEs() + s = nil + s.GetCWEs() +} + +func TestSecurityAdvisory_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{Description: &zeroValue} + s.GetDescription() + s = &SecurityAdvisory{} + s.GetDescription() + s = nil + s.GetDescription() +} + +func TestSecurityAdvisory_GetGHSAID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{GHSAID: &zeroValue} + s.GetGHSAID() + s = &SecurityAdvisory{} + s.GetGHSAID() + s = nil + s.GetGHSAID() +} + +func TestSecurityAdvisory_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{HTMLURL: &zeroValue} + s.GetHTMLURL() + s = &SecurityAdvisory{} + s.GetHTMLURL() + s = nil + s.GetHTMLURL() +} + +func TestSecurityAdvisory_GetIdentifiers(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryIdentifier{} + s := &SecurityAdvisory{Identifiers: zeroValue} + s.GetIdentifiers() + s = &SecurityAdvisory{} + s.GetIdentifiers() + s = nil + s.GetIdentifiers() +} + +func TestSecurityAdvisory_GetPrivateFork(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisory{} + s.GetPrivateFork() + s = nil + s.GetPrivateFork() +} + +func TestSecurityAdvisory_GetPublishedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecurityAdvisory{PublishedAt: &zeroValue} + s.GetPublishedAt() + s = &SecurityAdvisory{} + s.GetPublishedAt() + s = nil + s.GetPublishedAt() +} + +func TestSecurityAdvisory_GetPublisher(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisory{} + s.GetPublisher() + s = nil + s.GetPublisher() +} + +func TestSecurityAdvisory_GetReferences(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryReference{} + s := &SecurityAdvisory{References: zeroValue} + s.GetReferences() + s = &SecurityAdvisory{} + s.GetReferences() + s = nil + s.GetReferences() +} + +func TestSecurityAdvisory_GetSeverity(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{Severity: &zeroValue} + s.GetSeverity() + s = &SecurityAdvisory{} + s.GetSeverity() + s = nil + s.GetSeverity() +} + +func TestSecurityAdvisory_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{State: &zeroValue} + s.GetState() + s = &SecurityAdvisory{} + s.GetState() + s = nil + s.GetState() +} + +func TestSecurityAdvisory_GetSubmission(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisory{} + s.GetSubmission() + s = nil + s.GetSubmission() +} + +func TestSecurityAdvisory_GetSummary(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{Summary: &zeroValue} + s.GetSummary() + s = &SecurityAdvisory{} + s.GetSummary() + s = nil + s.GetSummary() +} + +func TestSecurityAdvisory_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecurityAdvisory{UpdatedAt: &zeroValue} + s.GetUpdatedAt() + s = &SecurityAdvisory{} + s.GetUpdatedAt() + s = nil + s.GetUpdatedAt() +} + +func TestSecurityAdvisory_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisory{URL: &zeroValue} + s.GetURL() + s = &SecurityAdvisory{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSecurityAdvisory_GetVulnerabilities(tt *testing.T) { + tt.Parallel() + zeroValue := []*AdvisoryVulnerability{} + s := &SecurityAdvisory{Vulnerabilities: zeroValue} + s.GetVulnerabilities() + s = &SecurityAdvisory{} + s.GetVulnerabilities() + s = nil + s.GetVulnerabilities() +} + +func TestSecurityAdvisory_GetWithdrawnAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SecurityAdvisory{WithdrawnAt: &zeroValue} + s.GetWithdrawnAt() + s = &SecurityAdvisory{} + s.GetWithdrawnAt() + s = nil + s.GetWithdrawnAt() +} + +func TestSecurityAdvisoryEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SecurityAdvisoryEvent{Action: &zeroValue} + s.GetAction() + s = &SecurityAdvisoryEvent{} + s.GetAction() + s = nil + s.GetAction() +} + +func TestSecurityAdvisoryEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisoryEvent{} + s.GetEnterprise() + s = nil + s.GetEnterprise() +} + +func TestSecurityAdvisoryEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisoryEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestSecurityAdvisoryEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisoryEvent{} + s.GetOrganization() + s = nil + s.GetOrganization() +} + +func TestSecurityAdvisoryEvent_GetRepository(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisoryEvent{} + s.GetRepository() + s = nil + s.GetRepository() +} + +func TestSecurityAdvisoryEvent_GetSecurityAdvisory(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisoryEvent{} + s.GetSecurityAdvisory() + s = nil + s.GetSecurityAdvisory() +} + +func TestSecurityAdvisoryEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &SecurityAdvisoryEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestSecurityAdvisorySubmission_GetAccepted(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SecurityAdvisorySubmission{Accepted: &zeroValue} + s.GetAccepted() + s = &SecurityAdvisorySubmission{} + s.GetAccepted() + s = nil + s.GetAccepted() +} + +func TestSecurityAndAnalysis_GetAdvancedSecurity(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysis{} + s.GetAdvancedSecurity() + s = nil + s.GetAdvancedSecurity() +} + +func TestSecurityAndAnalysis_GetCodeSecurity(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysis{} + s.GetCodeSecurity() + s = nil + s.GetCodeSecurity() +} + +func TestSecurityAndAnalysis_GetDependabotSecurityUpdates(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysis{} + s.GetDependabotSecurityUpdates() + s = nil + s.GetDependabotSecurityUpdates() +} + +func TestSecurityAndAnalysis_GetSecretScanning(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysis{} + s.GetSecretScanning() + s = nil + s.GetSecretScanning() +} + +func TestSecurityAndAnalysis_GetSecretScanningPushProtection(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysis{} + s.GetSecretScanningPushProtection() + s = nil + s.GetSecretScanningPushProtection() +} + +func TestSecurityAndAnalysis_GetSecretScanningValidityChecks(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysis{} + s.GetSecretScanningValidityChecks() + s = nil + s.GetSecretScanningValidityChecks() +} + +func TestSecurityAndAnalysisChange_GetFrom(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisChange{} + s.GetFrom() + s = nil + s.GetFrom() +} + +func TestSecurityAndAnalysisChangeFrom_GetSecurityAndAnalysis(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisChangeFrom{} + s.GetSecurityAndAnalysis() + s = nil + s.GetSecurityAndAnalysis() +} + +func TestSecurityAndAnalysisEvent_GetChanges(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisEvent{} + s.GetChanges() + s = nil + s.GetChanges() +} + +func TestSecurityAndAnalysisEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisEvent{} + s.GetEnterprise() + s = nil + s.GetEnterprise() +} + +func TestSecurityAndAnalysisEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestSecurityAndAnalysisEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisEvent{} + s.GetOrganization() + s = nil + s.GetOrganization() +} + +func TestSecurityAndAnalysisEvent_GetRepository(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisEvent{} + s.GetRepository() + s = nil + s.GetRepository() +} + +func TestSecurityAndAnalysisEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &SecurityAndAnalysisEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestSelectedReposList_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + s := &SelectedReposList{Repositories: zeroValue} + s.GetRepositories() + s = &SelectedReposList{} + s.GetRepositories() + s = nil + s.GetRepositories() +} + +func TestSelectedReposList_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SelectedReposList{TotalCount: &zeroValue} + s.GetTotalCount() + s = &SelectedReposList{} + s.GetTotalCount() + s = nil + s.GetTotalCount() +} + +func TestSelfHostedRunnersAllowedRepos_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + s := &SelfHostedRunnersAllowedRepos{Repositories: zeroValue} + s.GetRepositories() + s = &SelfHostedRunnersAllowedRepos{} + s.GetRepositories() + s = nil + s.GetRepositories() +} + +func TestSelfHostedRunnersAllowedRepos_GetTotalCount(tt *testing.T) { + tt.Parallel() + s := &SelfHostedRunnersAllowedRepos{} + s.GetTotalCount() + s = nil + s.GetTotalCount() +} + +func TestSelfHostedRunnersSettingsOrganization_GetEnabledRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SelfHostedRunnersSettingsOrganization{EnabledRepositories: &zeroValue} + s.GetEnabledRepositories() + s = &SelfHostedRunnersSettingsOrganization{} + s.GetEnabledRepositories() + s = nil + s.GetEnabledRepositories() +} + +func TestSelfHostedRunnersSettingsOrganization_GetSelectedRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SelfHostedRunnersSettingsOrganization{SelectedRepositoriesURL: &zeroValue} + s.GetSelectedRepositoriesURL() + s = &SelfHostedRunnersSettingsOrganization{} + s.GetSelectedRepositoriesURL() + s = nil + s.GetSelectedRepositoriesURL() +} + +func TestSelfHostedRunnersSettingsOrganizationOpt_GetEnabledRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SelfHostedRunnersSettingsOrganizationOpt{EnabledRepositories: &zeroValue} + s.GetEnabledRepositories() + s = &SelfHostedRunnersSettingsOrganizationOpt{} + s.GetEnabledRepositories() + s = nil + s.GetEnabledRepositories() +} + +func TestSelfHostRunnerPermissionsEnterprise_GetDisableSelfHostedRunnersForAllOrgs(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SelfHostRunnerPermissionsEnterprise{DisableSelfHostedRunnersForAllOrgs: &zeroValue} + s.GetDisableSelfHostedRunnersForAllOrgs() + s = &SelfHostRunnerPermissionsEnterprise{} + s.GetDisableSelfHostedRunnersForAllOrgs() + s = nil + s.GetDisableSelfHostedRunnersForAllOrgs() +} + +func TestServerInstanceProperties_GetServerInstances(tt *testing.T) { + tt.Parallel() + s := &ServerInstanceProperties{} + s.GetServerInstances() + s = nil + s.GetServerInstances() +} + +func TestServerInstances_GetItems(tt *testing.T) { + tt.Parallel() + s := &ServerInstances{} + s.GetItems() + s = nil + s.GetItems() +} + +func TestServerInstances_GetType(tt *testing.T) { + tt.Parallel() + s := &ServerInstances{} + s.GetType() + s = nil + s.GetType() +} + +func TestServerItemProperties_GetHostname(tt *testing.T) { + tt.Parallel() + s := &ServerItemProperties{} + s.GetHostname() + s = nil + s.GetHostname() +} + +func TestServerItemProperties_GetLastSync(tt *testing.T) { + tt.Parallel() + s := &ServerItemProperties{} + s.GetLastSync() + s = nil + s.GetLastSync() +} + +func TestServerItemProperties_GetServerID(tt *testing.T) { + tt.Parallel() + s := &ServerItemProperties{} + s.GetServerID() + s = nil + s.GetServerID() +} + +func TestServiceInstanceItems_GetProperties(tt *testing.T) { + tt.Parallel() + s := &ServiceInstanceItems{} + s.GetProperties() + s = nil + s.GetProperties() +} + +func TestServiceInstanceItems_GetType(tt *testing.T) { + tt.Parallel() + s := &ServiceInstanceItems{} + s.GetType() + s = nil + s.GetType() +} + +func TestSetOrgAccessRunnerGroupRequest_GetSelectedOrganizationIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + s := &SetOrgAccessRunnerGroupRequest{SelectedOrganizationIDs: zeroValue} + s.GetSelectedOrganizationIDs() + s = &SetOrgAccessRunnerGroupRequest{} + s.GetSelectedOrganizationIDs() + s = nil + s.GetSelectedOrganizationIDs() +} + +func TestSetRepoAccessRunnerGroupRequest_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + s := &SetRepoAccessRunnerGroupRequest{SelectedRepositoryIDs: zeroValue} + s.GetSelectedRepositoryIDs() + s = &SetRepoAccessRunnerGroupRequest{} + s.GetSelectedRepositoryIDs() + s = nil + s.GetSelectedRepositoryIDs() +} + +func TestSetRunnerGroupRunnersRequest_GetRunners(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + s := &SetRunnerGroupRunnersRequest{Runners: zeroValue} + s.GetRunners() + s = &SetRunnerGroupRunnersRequest{} + s.GetRunners() + s = nil + s.GetRunners() +} + +func TestSignatureRequirementEnforcementLevelChanges_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SignatureRequirementEnforcementLevelChanges{From: &zeroValue} + s.GetFrom() + s = &SignatureRequirementEnforcementLevelChanges{} + s.GetFrom() + s = nil + s.GetFrom() +} + +func TestSignaturesProtectedBranch_GetEnabled(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SignaturesProtectedBranch{Enabled: &zeroValue} + s.GetEnabled() + s = &SignaturesProtectedBranch{} + s.GetEnabled() + s = nil + s.GetEnabled() +} + +func TestSignaturesProtectedBranch_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SignaturesProtectedBranch{URL: &zeroValue} + s.GetURL() + s = &SignaturesProtectedBranch{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSignatureVerification_GetPayload(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SignatureVerification{Payload: &zeroValue} + s.GetPayload() + s = &SignatureVerification{} + s.GetPayload() + s = nil + s.GetPayload() +} + +func TestSignatureVerification_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SignatureVerification{Reason: &zeroValue} + s.GetReason() + s = &SignatureVerification{} + s.GetReason() + s = nil + s.GetReason() +} + +func TestSignatureVerification_GetSignature(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SignatureVerification{Signature: &zeroValue} + s.GetSignature() + s = &SignatureVerification{} + s.GetSignature() + s = nil + s.GetSignature() +} + +func TestSignatureVerification_GetVerified(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SignatureVerification{Verified: &zeroValue} + s.GetVerified() + s = &SignatureVerification{} + s.GetVerified() + s = nil + s.GetVerified() +} + +func TestSimplePatternRuleParameters_GetNegate(tt *testing.T) { + tt.Parallel() + s := &SimplePatternRuleParameters{} + s.GetNegate() + s = nil + s.GetNegate() +} + +func TestSimplePatternRuleParameters_GetPattern(tt *testing.T) { + tt.Parallel() + s := &SimplePatternRuleParameters{} + s.GetPattern() + s = nil + s.GetPattern() +} + +func TestSocialAccount_GetProvider(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SocialAccount{Provider: &zeroValue} + s.GetProvider() + s = &SocialAccount{} + s.GetProvider() + s = nil + s.GetProvider() +} + +func TestSocialAccount_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SocialAccount{URL: &zeroValue} + s.GetURL() + s = &SocialAccount{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSource_GetActor(tt *testing.T) { + tt.Parallel() + s := &Source{} + s.GetActor() + s = nil + s.GetActor() +} + +func TestSource_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &Source{ID: &zeroValue} + s.GetID() + s = &Source{} + s.GetID() + s = nil + s.GetID() +} + +func TestSource_GetIssue(tt *testing.T) { + tt.Parallel() + s := &Source{} + s.GetIssue() + s = nil + s.GetIssue() +} + +func TestSource_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &Source{Type: &zeroValue} + s.GetType() + s = &Source{} + s.GetType() + s = nil + s.GetType() +} + +func TestSource_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &Source{URL: &zeroValue} + s.GetURL() + s = &Source{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSourceImportAuthor_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SourceImportAuthor{Email: &zeroValue} + s.GetEmail() + s = &SourceImportAuthor{} + s.GetEmail() + s = nil + s.GetEmail() +} + +func TestSourceImportAuthor_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &SourceImportAuthor{ID: &zeroValue} + s.GetID() + s = &SourceImportAuthor{} + s.GetID() + s = nil + s.GetID() +} + +func TestSourceImportAuthor_GetImportURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SourceImportAuthor{ImportURL: &zeroValue} + s.GetImportURL() + s = &SourceImportAuthor{} + s.GetImportURL() + s = nil + s.GetImportURL() +} + +func TestSourceImportAuthor_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SourceImportAuthor{Name: &zeroValue} + s.GetName() + s = &SourceImportAuthor{} + s.GetName() + s = nil + s.GetName() +} + +func TestSourceImportAuthor_GetRemoteID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SourceImportAuthor{RemoteID: &zeroValue} + s.GetRemoteID() + s = &SourceImportAuthor{} + s.GetRemoteID() + s = nil + s.GetRemoteID() +} + +func TestSourceImportAuthor_GetRemoteName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SourceImportAuthor{RemoteName: &zeroValue} + s.GetRemoteName() + s = &SourceImportAuthor{} + s.GetRemoteName() + s = nil + s.GetRemoteName() +} + +func TestSourceImportAuthor_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SourceImportAuthor{URL: &zeroValue} + s.GetURL() + s = &SourceImportAuthor{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSplunkConfig_GetDomain(tt *testing.T) { + tt.Parallel() + s := &SplunkConfig{} + s.GetDomain() + s = nil + s.GetDomain() +} + +func TestSplunkConfig_GetEncryptedToken(tt *testing.T) { + tt.Parallel() + s := &SplunkConfig{} + s.GetEncryptedToken() + s = nil + s.GetEncryptedToken() +} + +func TestSplunkConfig_GetKeyID(tt *testing.T) { + tt.Parallel() + s := &SplunkConfig{} + s.GetKeyID() + s = nil + s.GetKeyID() +} + +func TestSplunkConfig_GetPort(tt *testing.T) { + tt.Parallel() + s := &SplunkConfig{} + s.GetPort() + s = nil + s.GetPort() +} + +func TestSplunkConfig_GetSSLVerify(tt *testing.T) { + tt.Parallel() + s := &SplunkConfig{} + s.GetSSLVerify() + s = nil + s.GetSSLVerify() +} + +func TestSponsorshipChanges_GetPrivacyLevel(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SponsorshipChanges{PrivacyLevel: &zeroValue} + s.GetPrivacyLevel() + s = &SponsorshipChanges{} + s.GetPrivacyLevel() + s = nil + s.GetPrivacyLevel() +} + +func TestSponsorshipChanges_GetTier(tt *testing.T) { + tt.Parallel() + s := &SponsorshipChanges{} + s.GetTier() + s = nil + s.GetTier() +} + +func TestSponsorshipEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SponsorshipEvent{Action: &zeroValue} + s.GetAction() + s = &SponsorshipEvent{} + s.GetAction() + s = nil + s.GetAction() +} + +func TestSponsorshipEvent_GetChanges(tt *testing.T) { + tt.Parallel() + s := &SponsorshipEvent{} + s.GetChanges() + s = nil + s.GetChanges() +} + +func TestSponsorshipEvent_GetEffectiveDate(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SponsorshipEvent{EffectiveDate: &zeroValue} + s.GetEffectiveDate() + s = &SponsorshipEvent{} + s.GetEffectiveDate() + s = nil + s.GetEffectiveDate() +} + +func TestSponsorshipEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &SponsorshipEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestSponsorshipEvent_GetOrganization(tt *testing.T) { + tt.Parallel() + s := &SponsorshipEvent{} + s.GetOrganization() + s = nil + s.GetOrganization() +} + +func TestSponsorshipEvent_GetRepository(tt *testing.T) { + tt.Parallel() + s := &SponsorshipEvent{} + s.GetRepository() + s = nil + s.GetRepository() +} + +func TestSponsorshipEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &SponsorshipEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestSponsorshipTier_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SponsorshipTier{From: &zeroValue} + s.GetFrom() + s = &SponsorshipTier{} + s.GetFrom() + s = nil + s.GetFrom() +} + +func TestSSHKeyOptions_GetKey(tt *testing.T) { + tt.Parallel() + s := &SSHKeyOptions{} + s.GetKey() + s = nil + s.GetKey() +} + +func TestSSHKeyStatus_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SSHKeyStatus{Hostname: &zeroValue} + s.GetHostname() + s = &SSHKeyStatus{} + s.GetHostname() + s = nil + s.GetHostname() +} + +func TestSSHKeyStatus_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SSHKeyStatus{Message: &zeroValue} + s.GetMessage() + s = &SSHKeyStatus{} + s.GetMessage() + s = nil + s.GetMessage() +} + +func TestSSHKeyStatus_GetModified(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SSHKeyStatus{Modified: &zeroValue} + s.GetModified() + s = &SSHKeyStatus{} + s.GetModified() + s = nil + s.GetModified() +} + +func TestSSHKeyStatus_GetUUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SSHKeyStatus{UUID: &zeroValue} + s.GetUUID() + s = &SSHKeyStatus{} + s.GetUUID() + s = nil + s.GetUUID() +} + +func TestSSHSigningKey_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &SSHSigningKey{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &SSHSigningKey{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSSHSigningKey_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &SSHSigningKey{ID: &zeroValue} + s.GetID() + s = &SSHSigningKey{} + s.GetID() + s = nil + s.GetID() +} + +func TestSSHSigningKey_GetKey(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SSHSigningKey{Key: &zeroValue} + s.GetKey() + s = &SSHSigningKey{} + s.GetKey() + s = nil + s.GetKey() +} + +func TestSSHSigningKey_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SSHSigningKey{Title: &zeroValue} + s.GetTitle() + s = &SSHSigningKey{} + s.GetTitle() + s = nil + s.GetTitle() +} + +func TestStarEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StarEvent{Action: &zeroValue} + s.GetAction() + s = &StarEvent{} + s.GetAction() + s = nil + s.GetAction() +} + +func TestStarEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &StarEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestStarEvent_GetOrg(tt *testing.T) { + tt.Parallel() + s := &StarEvent{} + s.GetOrg() + s = nil + s.GetOrg() +} + +func TestStarEvent_GetRepo(tt *testing.T) { + tt.Parallel() + s := &StarEvent{} + s.GetRepo() + s = nil + s.GetRepo() +} + +func TestStarEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &StarEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestStarEvent_GetStarredAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &StarEvent{StarredAt: &zeroValue} + s.GetStarredAt() + s = &StarEvent{} + s.GetStarredAt() + s = nil + s.GetStarredAt() +} + +func TestStargazer_GetStarredAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &Stargazer{StarredAt: &zeroValue} + s.GetStarredAt() + s = &Stargazer{} + s.GetStarredAt() + s = nil + s.GetStarredAt() +} + +func TestStargazer_GetUser(tt *testing.T) { + tt.Parallel() + s := &Stargazer{} + s.GetUser() + s = nil + s.GetUser() +} + +func TestStarredRepository_GetRepository(tt *testing.T) { + tt.Parallel() + s := &StarredRepository{} + s.GetRepository() + s = nil + s.GetRepository() +} + +func TestStarredRepository_GetStarredAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &StarredRepository{StarredAt: &zeroValue} + s.GetStarredAt() + s = &StarredRepository{} + s.GetStarredAt() + s = nil + s.GetStarredAt() +} + +func TestStatusEvent_GetBranches(tt *testing.T) { + tt.Parallel() + zeroValue := []*Branch{} + s := &StatusEvent{Branches: zeroValue} + s.GetBranches() + s = &StatusEvent{} + s.GetBranches() + s = nil + s.GetBranches() +} + +func TestStatusEvent_GetCommit(tt *testing.T) { + tt.Parallel() + s := &StatusEvent{} + s.GetCommit() + s = nil + s.GetCommit() +} + +func TestStatusEvent_GetContext(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StatusEvent{Context: &zeroValue} + s.GetContext() + s = &StatusEvent{} + s.GetContext() + s = nil + s.GetContext() +} + +func TestStatusEvent_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &StatusEvent{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &StatusEvent{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestStatusEvent_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StatusEvent{Description: &zeroValue} + s.GetDescription() + s = &StatusEvent{} + s.GetDescription() + s = nil + s.GetDescription() +} + +func TestStatusEvent_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &StatusEvent{ID: &zeroValue} + s.GetID() + s = &StatusEvent{} + s.GetID() + s = nil + s.GetID() +} + +func TestStatusEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + s := &StatusEvent{} + s.GetInstallation() + s = nil + s.GetInstallation() +} + +func TestStatusEvent_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StatusEvent{Name: &zeroValue} + s.GetName() + s = &StatusEvent{} + s.GetName() + s = nil + s.GetName() +} + +func TestStatusEvent_GetOrg(tt *testing.T) { + tt.Parallel() + s := &StatusEvent{} + s.GetOrg() + s = nil + s.GetOrg() +} + +func TestStatusEvent_GetRepo(tt *testing.T) { + tt.Parallel() + s := &StatusEvent{} + s.GetRepo() + s = nil + s.GetRepo() +} + +func TestStatusEvent_GetSender(tt *testing.T) { + tt.Parallel() + s := &StatusEvent{} + s.GetSender() + s = nil + s.GetSender() +} + +func TestStatusEvent_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StatusEvent{SHA: &zeroValue} + s.GetSHA() + s = &StatusEvent{} + s.GetSHA() + s = nil + s.GetSHA() +} + +func TestStatusEvent_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StatusEvent{State: &zeroValue} + s.GetState() + s = &StatusEvent{} + s.GetState() + s = nil + s.GetState() +} + +func TestStatusEvent_GetTargetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &StatusEvent{TargetURL: &zeroValue} + s.GetTargetURL() + s = &StatusEvent{} + s.GetTargetURL() + s = nil + s.GetTargetURL() +} + +func TestStatusEvent_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &StatusEvent{UpdatedAt: &zeroValue} + s.GetUpdatedAt() + s = &StatusEvent{} + s.GetUpdatedAt() + s = nil + s.GetUpdatedAt() +} + +func TestStorageBilling_GetDaysLeftInBillingCycle(tt *testing.T) { + tt.Parallel() + s := &StorageBilling{} + s.GetDaysLeftInBillingCycle() + s = nil + s.GetDaysLeftInBillingCycle() +} + +func TestStorageBilling_GetEstimatedPaidStorageForMonth(tt *testing.T) { + tt.Parallel() + s := &StorageBilling{} + s.GetEstimatedPaidStorageForMonth() + s = nil + s.GetEstimatedPaidStorageForMonth() +} + +func TestStorageBilling_GetEstimatedStorageForMonth(tt *testing.T) { + tt.Parallel() + s := &StorageBilling{} + s.GetEstimatedStorageForMonth() + s = nil + s.GetEstimatedStorageForMonth() +} + +func TestSubIssueRequest_GetAfterID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &SubIssueRequest{AfterID: &zeroValue} + s.GetAfterID() + s = &SubIssueRequest{} + s.GetAfterID() + s = nil + s.GetAfterID() +} + +func TestSubIssueRequest_GetBeforeID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + s := &SubIssueRequest{BeforeID: &zeroValue} + s.GetBeforeID() + s = &SubIssueRequest{} + s.GetBeforeID() + s = nil + s.GetBeforeID() +} + +func TestSubIssueRequest_GetReplaceParent(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &SubIssueRequest{ReplaceParent: &zeroValue} + s.GetReplaceParent() + s = &SubIssueRequest{} + s.GetReplaceParent() + s = nil + s.GetReplaceParent() +} + +func TestSubIssueRequest_GetSubIssueID(tt *testing.T) { + tt.Parallel() + s := &SubIssueRequest{} + s.GetSubIssueID() + s = nil + s.GetSubIssueID() +} + +func TestSubIssuesSummary_GetCompleted(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SubIssuesSummary{Completed: &zeroValue} + s.GetCompleted() + s = &SubIssuesSummary{} + s.GetCompleted() + s = nil + s.GetCompleted() +} + +func TestSubIssuesSummary_GetPercentCompleted(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SubIssuesSummary{PercentCompleted: &zeroValue} + s.GetPercentCompleted() + s = &SubIssuesSummary{} + s.GetPercentCompleted() + s = nil + s.GetPercentCompleted() +} + +func TestSubIssuesSummary_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + s := &SubIssuesSummary{Total: &zeroValue} + s.GetTotal() + s = &SubIssuesSummary{} + s.GetTotal() + s = nil + s.GetTotal() +} + +func TestSubscription_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + s := &Subscription{CreatedAt: &zeroValue} + s.GetCreatedAt() + s = &Subscription{} + s.GetCreatedAt() + s = nil + s.GetCreatedAt() +} + +func TestSubscription_GetIgnored(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &Subscription{Ignored: &zeroValue} + s.GetIgnored() + s = &Subscription{} + s.GetIgnored() + s = nil + s.GetIgnored() +} + +func TestSubscription_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &Subscription{Reason: &zeroValue} + s.GetReason() + s = &Subscription{} + s.GetReason() + s = nil + s.GetReason() +} + +func TestSubscription_GetRepositoryURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &Subscription{RepositoryURL: &zeroValue} + s.GetRepositoryURL() + s = &Subscription{} + s.GetRepositoryURL() + s = nil + s.GetRepositoryURL() +} + +func TestSubscription_GetSubscribed(tt *testing.T) { + tt.Parallel() + var zeroValue bool + s := &Subscription{Subscribed: &zeroValue} + s.GetSubscribed() + s = &Subscription{} + s.GetSubscribed() + s = nil + s.GetSubscribed() +} + +func TestSubscription_GetThreadURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &Subscription{ThreadURL: &zeroValue} + s.GetThreadURL() + s = &Subscription{} + s.GetThreadURL() + s = nil + s.GetThreadURL() +} + +func TestSubscription_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &Subscription{URL: &zeroValue} + s.GetURL() + s = &Subscription{} + s.GetURL() + s = nil + s.GetURL() +} + +func TestSystemRequirements_GetNodes(tt *testing.T) { + tt.Parallel() + zeroValue := []*SystemRequirementsNode{} + s := &SystemRequirements{Nodes: zeroValue} + s.GetNodes() + s = &SystemRequirements{} + s.GetNodes() + s = nil + s.GetNodes() +} + +func TestSystemRequirements_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SystemRequirements{Status: &zeroValue} + s.GetStatus() + s = &SystemRequirements{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSystemRequirementsNode_GetHostname(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SystemRequirementsNode{Hostname: &zeroValue} + s.GetHostname() + s = &SystemRequirementsNode{} + s.GetHostname() + s = nil + s.GetHostname() +} + +func TestSystemRequirementsNode_GetRolesStatus(tt *testing.T) { + tt.Parallel() + zeroValue := []*SystemRequirementsNodeRoleStatus{} + s := &SystemRequirementsNode{RolesStatus: zeroValue} + s.GetRolesStatus() + s = &SystemRequirementsNode{} + s.GetRolesStatus() + s = nil + s.GetRolesStatus() +} + +func TestSystemRequirementsNode_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SystemRequirementsNode{Status: &zeroValue} + s.GetStatus() + s = &SystemRequirementsNode{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestSystemRequirementsNodeRoleStatus_GetRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SystemRequirementsNodeRoleStatus{Role: &zeroValue} + s.GetRole() + s = &SystemRequirementsNodeRoleStatus{} + s.GetRole() + s = nil + s.GetRole() +} + +func TestSystemRequirementsNodeRoleStatus_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SystemRequirementsNodeRoleStatus{Status: &zeroValue} + s.GetStatus() + s = &SystemRequirementsNodeRoleStatus{} + s.GetStatus() + s = nil + s.GetStatus() +} + +func TestTag_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tag{Message: &zeroValue} + t.GetMessage() + t = &Tag{} + t.GetMessage() + t = nil + t.GetMessage() +} + +func TestTag_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tag{NodeID: &zeroValue} + t.GetNodeID() + t = &Tag{} + t.GetNodeID() + t = nil + t.GetNodeID() +} + +func TestTag_GetObject(tt *testing.T) { + tt.Parallel() + t := &Tag{} + t.GetObject() + t = nil + t.GetObject() +} + +func TestTag_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tag{SHA: &zeroValue} + t.GetSHA() + t = &Tag{} + t.GetSHA() + t = nil + t.GetSHA() +} + +func TestTag_GetTag(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tag{Tag: &zeroValue} + t.GetTag() + t = &Tag{} + t.GetTag() + t = nil + t.GetTag() +} + +func TestTag_GetTagger(tt *testing.T) { + tt.Parallel() + t := &Tag{} + t.GetTagger() + t = nil + t.GetTagger() +} + +func TestTag_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tag{URL: &zeroValue} + t.GetURL() + t = &Tag{} + t.GetURL() + t = nil + t.GetURL() +} + +func TestTag_GetVerification(tt *testing.T) { + tt.Parallel() + t := &Tag{} + t.GetVerification() + t = nil + t.GetVerification() +} + +func TestTagProtection_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + t := &TagProtection{ID: &zeroValue} + t.GetID() + t = &TagProtection{} + t.GetID() + t = nil + t.GetID() +} + +func TestTagProtection_GetPattern(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TagProtection{Pattern: &zeroValue} + t.GetPattern() + t = &TagProtection{} + t.GetPattern() + t = nil + t.GetPattern() +} + +func TestTaskStep_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TaskStep{CompletedAt: &zeroValue} + t.GetCompletedAt() + t = &TaskStep{} + t.GetCompletedAt() + t = nil + t.GetCompletedAt() +} + +func TestTaskStep_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TaskStep{Conclusion: &zeroValue} + t.GetConclusion() + t = &TaskStep{} + t.GetConclusion() + t = nil + t.GetConclusion() +} + +func TestTaskStep_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TaskStep{Name: &zeroValue} + t.GetName() + t = &TaskStep{} + t.GetName() + t = nil + t.GetName() +} + +func TestTaskStep_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + t := &TaskStep{Number: &zeroValue} + t.GetNumber() + t = &TaskStep{} + t.GetNumber() + t = nil + t.GetNumber() +} + +func TestTaskStep_GetStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TaskStep{StartedAt: &zeroValue} + t.GetStartedAt() + t = &TaskStep{} + t.GetStartedAt() + t = nil + t.GetStartedAt() +} + +func TestTaskStep_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TaskStep{Status: &zeroValue} + t.GetStatus() + t = &TaskStep{} + t.GetStatus() + t = nil + t.GetStatus() +} + +func TestTeam_GetAccessSource(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{AccessSource: &zeroValue} + t.GetAccessSource() + t = &Team{} + t.GetAccessSource() + t = nil + t.GetAccessSource() +} + +func TestTeam_GetAssignment(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Assignment: &zeroValue} + t.GetAssignment() + t = &Team{} + t.GetAssignment() + t = nil + t.GetAssignment() +} + +func TestTeam_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Description: &zeroValue} + t.GetDescription() + t = &Team{} + t.GetDescription() + t = nil + t.GetDescription() +} + +func TestTeam_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{HTMLURL: &zeroValue} + t.GetHTMLURL() + t = &Team{} + t.GetHTMLURL() + t = nil + t.GetHTMLURL() +} + +func TestTeam_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + t := &Team{ID: &zeroValue} + t.GetID() + t = &Team{} + t.GetID() + t = nil + t.GetID() +} + +func TestTeam_GetLDAPDN(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{LDAPDN: &zeroValue} + t.GetLDAPDN() + t = &Team{} + t.GetLDAPDN() + t = nil + t.GetLDAPDN() +} + +func TestTeam_GetMembersCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &Team{MembersCount: &zeroValue} + t.GetMembersCount() + t = &Team{} + t.GetMembersCount() + t = nil + t.GetMembersCount() +} + +func TestTeam_GetMembersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{MembersURL: &zeroValue} + t.GetMembersURL() + t = &Team{} + t.GetMembersURL() + t = nil + t.GetMembersURL() +} + +func TestTeam_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Name: &zeroValue} + t.GetName() + t = &Team{} + t.GetName() + t = nil + t.GetName() +} + +func TestTeam_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{NodeID: &zeroValue} + t.GetNodeID() + t = &Team{} + t.GetNodeID() + t = nil + t.GetNodeID() +} + +func TestTeam_GetNotificationSetting(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{NotificationSetting: &zeroValue} + t.GetNotificationSetting() + t = &Team{} + t.GetNotificationSetting() + t = nil + t.GetNotificationSetting() +} + +func TestTeam_GetOrganization(tt *testing.T) { + tt.Parallel() + t := &Team{} + t.GetOrganization() + t = nil + t.GetOrganization() +} + +func TestTeam_GetParent(tt *testing.T) { + tt.Parallel() + t := &Team{} + t.GetParent() + t = nil + t.GetParent() +} + +func TestTeam_GetPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Permission: &zeroValue} + t.GetPermission() + t = &Team{} + t.GetPermission() + t = nil + t.GetPermission() +} + +func TestTeam_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := map[string]bool{} + t := &Team{Permissions: zeroValue} + t.GetPermissions() + t = &Team{} + t.GetPermissions() + t = nil + t.GetPermissions() +} + +func TestTeam_GetPrivacy(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Privacy: &zeroValue} + t.GetPrivacy() + t = &Team{} + t.GetPrivacy() + t = nil + t.GetPrivacy() +} + +func TestTeam_GetReposCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &Team{ReposCount: &zeroValue} + t.GetReposCount() + t = &Team{} + t.GetReposCount() + t = nil + t.GetReposCount() +} + +func TestTeam_GetRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{RepositoriesURL: &zeroValue} + t.GetRepositoriesURL() + t = &Team{} + t.GetRepositoriesURL() + t = nil + t.GetRepositoriesURL() +} + +func TestTeam_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Slug: &zeroValue} + t.GetSlug() + t = &Team{} + t.GetSlug() + t = nil + t.GetSlug() +} + +func TestTeam_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{Type: &zeroValue} + t.GetType() + t = &Team{} + t.GetType() + t = nil + t.GetType() +} + +func TestTeam_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Team{URL: &zeroValue} + t.GetURL() + t = &Team{} + t.GetURL() + t = nil + t.GetURL() +} + +func TestTeamAddEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + t := &TeamAddEvent{} + t.GetInstallation() + t = nil + t.GetInstallation() +} + +func TestTeamAddEvent_GetOrg(tt *testing.T) { + tt.Parallel() + t := &TeamAddEvent{} + t.GetOrg() + t = nil + t.GetOrg() +} + +func TestTeamAddEvent_GetRepo(tt *testing.T) { + tt.Parallel() + t := &TeamAddEvent{} + t.GetRepo() + t = nil + t.GetRepo() +} + +func TestTeamAddEvent_GetSender(tt *testing.T) { + tt.Parallel() + t := &TeamAddEvent{} + t.GetSender() + t = nil + t.GetSender() +} + +func TestTeamAddEvent_GetTeam(tt *testing.T) { + tt.Parallel() + t := &TeamAddEvent{} + t.GetTeam() + t = nil + t.GetTeam() +} + +func TestTeamAddTeamMembershipOptions_GetRole(tt *testing.T) { + tt.Parallel() + t := &TeamAddTeamMembershipOptions{} + t.GetRole() + t = nil + t.GetRole() +} + +func TestTeamAddTeamRepoOptions_GetPermission(tt *testing.T) { + tt.Parallel() + t := &TeamAddTeamRepoOptions{} + t.GetPermission() + t = nil + t.GetPermission() +} + +func TestTeamChange_GetDescription(tt *testing.T) { + tt.Parallel() + t := &TeamChange{} + t.GetDescription() + t = nil + t.GetDescription() +} + +func TestTeamChange_GetName(tt *testing.T) { + tt.Parallel() + t := &TeamChange{} + t.GetName() + t = nil + t.GetName() +} + +func TestTeamChange_GetPrivacy(tt *testing.T) { + tt.Parallel() + t := &TeamChange{} + t.GetPrivacy() + t = nil + t.GetPrivacy() +} + +func TestTeamChange_GetRepository(tt *testing.T) { + tt.Parallel() + t := &TeamChange{} + t.GetRepository() + t = nil + t.GetRepository() +} + +func TestTeamDescription_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDescription{From: &zeroValue} + t.GetFrom() + t = &TeamDescription{} + t.GetFrom() + t = nil + t.GetFrom() +} + +func TestTeamDiscussion_GetAuthor(tt *testing.T) { + tt.Parallel() + t := &TeamDiscussion{} + t.GetAuthor() + t = nil + t.GetAuthor() +} + +func TestTeamDiscussion_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{Body: &zeroValue} + t.GetBody() + t = &TeamDiscussion{} + t.GetBody() + t = nil + t.GetBody() +} + +func TestTeamDiscussion_GetBodyHTML(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{BodyHTML: &zeroValue} + t.GetBodyHTML() + t = &TeamDiscussion{} + t.GetBodyHTML() + t = nil + t.GetBodyHTML() +} + +func TestTeamDiscussion_GetBodyVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{BodyVersion: &zeroValue} + t.GetBodyVersion() + t = &TeamDiscussion{} + t.GetBodyVersion() + t = nil + t.GetBodyVersion() +} + +func TestTeamDiscussion_GetCommentsCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TeamDiscussion{CommentsCount: &zeroValue} + t.GetCommentsCount() + t = &TeamDiscussion{} + t.GetCommentsCount() + t = nil + t.GetCommentsCount() +} + +func TestTeamDiscussion_GetCommentsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{CommentsURL: &zeroValue} + t.GetCommentsURL() + t = &TeamDiscussion{} + t.GetCommentsURL() + t = nil + t.GetCommentsURL() +} + +func TestTeamDiscussion_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TeamDiscussion{CreatedAt: &zeroValue} + t.GetCreatedAt() + t = &TeamDiscussion{} + t.GetCreatedAt() + t = nil + t.GetCreatedAt() +} + +func TestTeamDiscussion_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{HTMLURL: &zeroValue} + t.GetHTMLURL() + t = &TeamDiscussion{} + t.GetHTMLURL() + t = nil + t.GetHTMLURL() +} + +func TestTeamDiscussion_GetLastEditedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TeamDiscussion{LastEditedAt: &zeroValue} + t.GetLastEditedAt() + t = &TeamDiscussion{} + t.GetLastEditedAt() + t = nil + t.GetLastEditedAt() +} + +func TestTeamDiscussion_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{NodeID: &zeroValue} + t.GetNodeID() + t = &TeamDiscussion{} + t.GetNodeID() + t = nil + t.GetNodeID() +} + +func TestTeamDiscussion_GetNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TeamDiscussion{Number: &zeroValue} + t.GetNumber() + t = &TeamDiscussion{} + t.GetNumber() + t = nil + t.GetNumber() +} + +func TestTeamDiscussion_GetPinned(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TeamDiscussion{Pinned: &zeroValue} + t.GetPinned() + t = &TeamDiscussion{} + t.GetPinned() + t = nil + t.GetPinned() +} + +func TestTeamDiscussion_GetPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TeamDiscussion{Private: &zeroValue} + t.GetPrivate() + t = &TeamDiscussion{} + t.GetPrivate() + t = nil + t.GetPrivate() +} + +func TestTeamDiscussion_GetReactions(tt *testing.T) { + tt.Parallel() + t := &TeamDiscussion{} + t.GetReactions() + t = nil + t.GetReactions() +} + +func TestTeamDiscussion_GetTeamURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{TeamURL: &zeroValue} + t.GetTeamURL() + t = &TeamDiscussion{} + t.GetTeamURL() + t = nil + t.GetTeamURL() +} + +func TestTeamDiscussion_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{Title: &zeroValue} + t.GetTitle() + t = &TeamDiscussion{} + t.GetTitle() + t = nil + t.GetTitle() +} + +func TestTeamDiscussion_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TeamDiscussion{UpdatedAt: &zeroValue} + t.GetUpdatedAt() + t = &TeamDiscussion{} + t.GetUpdatedAt() + t = nil + t.GetUpdatedAt() +} + +func TestTeamDiscussion_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamDiscussion{URL: &zeroValue} + t.GetURL() + t = &TeamDiscussion{} + t.GetURL() + t = nil + t.GetURL() +} + +func TestTeamEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamEvent{Action: &zeroValue} + t.GetAction() + t = &TeamEvent{} + t.GetAction() + t = nil + t.GetAction() +} + +func TestTeamEvent_GetChanges(tt *testing.T) { + tt.Parallel() + t := &TeamEvent{} + t.GetChanges() + t = nil + t.GetChanges() +} + +func TestTeamEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + t := &TeamEvent{} + t.GetInstallation() + t = nil + t.GetInstallation() +} + +func TestTeamEvent_GetOrg(tt *testing.T) { + tt.Parallel() + t := &TeamEvent{} + t.GetOrg() + t = nil + t.GetOrg() +} + +func TestTeamEvent_GetRepo(tt *testing.T) { + tt.Parallel() + t := &TeamEvent{} + t.GetRepo() + t = nil + t.GetRepo() +} + +func TestTeamEvent_GetSender(tt *testing.T) { + tt.Parallel() + t := &TeamEvent{} + t.GetSender() + t = nil + t.GetSender() +} + +func TestTeamEvent_GetTeam(tt *testing.T) { + tt.Parallel() + t := &TeamEvent{} + t.GetTeam() + t = nil + t.GetTeam() +} + +func TestTeamLDAPMapping_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{Description: &zeroValue} + t.GetDescription() + t = &TeamLDAPMapping{} + t.GetDescription() + t = nil + t.GetDescription() +} + +func TestTeamLDAPMapping_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + t := &TeamLDAPMapping{ID: &zeroValue} + t.GetID() + t = &TeamLDAPMapping{} + t.GetID() + t = nil + t.GetID() +} + +func TestTeamLDAPMapping_GetLDAPDN(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{LDAPDN: &zeroValue} + t.GetLDAPDN() + t = &TeamLDAPMapping{} + t.GetLDAPDN() + t = nil + t.GetLDAPDN() +} + +func TestTeamLDAPMapping_GetMembersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{MembersURL: &zeroValue} + t.GetMembersURL() + t = &TeamLDAPMapping{} + t.GetMembersURL() + t = nil + t.GetMembersURL() +} + +func TestTeamLDAPMapping_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{Name: &zeroValue} + t.GetName() + t = &TeamLDAPMapping{} + t.GetName() + t = nil + t.GetName() +} + +func TestTeamLDAPMapping_GetPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{Permission: &zeroValue} + t.GetPermission() + t = &TeamLDAPMapping{} + t.GetPermission() + t = nil + t.GetPermission() +} + +func TestTeamLDAPMapping_GetPrivacy(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{Privacy: &zeroValue} + t.GetPrivacy() + t = &TeamLDAPMapping{} + t.GetPrivacy() + t = nil + t.GetPrivacy() +} + +func TestTeamLDAPMapping_GetRepositoriesURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{RepositoriesURL: &zeroValue} + t.GetRepositoriesURL() + t = &TeamLDAPMapping{} + t.GetRepositoriesURL() + t = nil + t.GetRepositoriesURL() +} + +func TestTeamLDAPMapping_GetSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{Slug: &zeroValue} + t.GetSlug() + t = &TeamLDAPMapping{} + t.GetSlug() + t = nil + t.GetSlug() +} + +func TestTeamLDAPMapping_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamLDAPMapping{URL: &zeroValue} + t.GetURL() + t = &TeamLDAPMapping{} + t.GetURL() + t = nil + t.GetURL() +} + +func TestTeamListTeamMembersOptions_GetRole(tt *testing.T) { + tt.Parallel() + t := &TeamListTeamMembersOptions{} + t.GetRole() + t = nil + t.GetRole() +} + +func TestTeamName_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamName{From: &zeroValue} + t.GetFrom() + t = &TeamName{} + t.GetFrom() + t = nil + t.GetFrom() +} + +func TestTeamPermissions_GetFrom(tt *testing.T) { + tt.Parallel() + t := &TeamPermissions{} + t.GetFrom() + t = nil + t.GetFrom() +} + +func TestTeamPermissionsFrom_GetAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TeamPermissionsFrom{Admin: &zeroValue} + t.GetAdmin() + t = &TeamPermissionsFrom{} + t.GetAdmin() + t = nil + t.GetAdmin() +} + +func TestTeamPermissionsFrom_GetPull(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TeamPermissionsFrom{Pull: &zeroValue} + t.GetPull() + t = &TeamPermissionsFrom{} + t.GetPull() + t = nil + t.GetPull() +} + +func TestTeamPermissionsFrom_GetPush(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TeamPermissionsFrom{Push: &zeroValue} + t.GetPush() + t = &TeamPermissionsFrom{} + t.GetPush() + t = nil + t.GetPush() +} + +func TestTeamPrivacy_GetFrom(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamPrivacy{From: &zeroValue} + t.GetFrom() + t = &TeamPrivacy{} + t.GetFrom() + t = nil + t.GetFrom() +} + +func TestTeamProjectOptions_GetPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TeamProjectOptions{Permission: &zeroValue} + t.GetPermission() + t = &TeamProjectOptions{} + t.GetPermission() + t = nil + t.GetPermission() +} + +func TestTeamRepository_GetPermissions(tt *testing.T) { + tt.Parallel() + t := &TeamRepository{} + t.GetPermissions() + t = nil + t.GetPermissions() +} + +func TestTemplateRepoRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TemplateRepoRequest{Description: &zeroValue} + t.GetDescription() + t = &TemplateRepoRequest{} + t.GetDescription() + t = nil + t.GetDescription() +} + +func TestTemplateRepoRequest_GetIncludeAllBranches(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TemplateRepoRequest{IncludeAllBranches: &zeroValue} + t.GetIncludeAllBranches() + t = &TemplateRepoRequest{} + t.GetIncludeAllBranches() + t = nil + t.GetIncludeAllBranches() +} + +func TestTemplateRepoRequest_GetName(tt *testing.T) { + tt.Parallel() + t := &TemplateRepoRequest{} + t.GetName() + t = nil + t.GetName() +} + +func TestTemplateRepoRequest_GetOwner(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TemplateRepoRequest{Owner: &zeroValue} + t.GetOwner() + t = &TemplateRepoRequest{} + t.GetOwner() + t = nil + t.GetOwner() +} + +func TestTemplateRepoRequest_GetPrivate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TemplateRepoRequest{Private: &zeroValue} + t.GetPrivate() + t = &TemplateRepoRequest{} + t.GetPrivate() + t = nil + t.GetPrivate() +} + +func TestTextMatch_GetFragment(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TextMatch{Fragment: &zeroValue} + t.GetFragment() + t = &TextMatch{} + t.GetFragment() + t = nil + t.GetFragment() +} + +func TestTextMatch_GetMatches(tt *testing.T) { + tt.Parallel() + zeroValue := []*Match{} + t := &TextMatch{Matches: zeroValue} + t.GetMatches() + t = &TextMatch{} + t.GetMatches() + t = nil + t.GetMatches() +} + +func TestTextMatch_GetObjectType(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TextMatch{ObjectType: &zeroValue} + t.GetObjectType() + t = &TextMatch{} + t.GetObjectType() + t = nil + t.GetObjectType() +} + +func TestTextMatch_GetObjectURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TextMatch{ObjectURL: &zeroValue} + t.GetObjectURL() + t = &TextMatch{} + t.GetObjectURL() + t = nil + t.GetObjectURL() +} + +func TestTextMatch_GetProperty(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TextMatch{Property: &zeroValue} + t.GetProperty() + t = &TextMatch{} + t.GetProperty() + t = nil + t.GetProperty() +} + +func TestTimeline_GetActor(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetActor() + t = nil + t.GetActor() +} + +func TestTimeline_GetAssignee(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetAssignee() + t = nil + t.GetAssignee() +} + +func TestTimeline_GetAssigner(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetAssigner() + t = nil + t.GetAssigner() +} + +func TestTimeline_GetAuthor(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetAuthor() + t = nil + t.GetAuthor() +} + +func TestTimeline_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{Body: &zeroValue} + t.GetBody() + t = &Timeline{} + t.GetBody() + t = nil + t.GetBody() +} + +func TestTimeline_GetCommitID(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{CommitID: &zeroValue} + t.GetCommitID() + t = &Timeline{} + t.GetCommitID() + t = nil + t.GetCommitID() +} + +func TestTimeline_GetCommitter(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetCommitter() + t = nil + t.GetCommitter() +} + +func TestTimeline_GetCommitURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{CommitURL: &zeroValue} + t.GetCommitURL() + t = &Timeline{} + t.GetCommitURL() + t = nil + t.GetCommitURL() +} + +func TestTimeline_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &Timeline{CreatedAt: &zeroValue} + t.GetCreatedAt() + t = &Timeline{} + t.GetCreatedAt() + t = nil + t.GetCreatedAt() +} + +func TestTimeline_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{Event: &zeroValue} + t.GetEvent() + t = &Timeline{} + t.GetEvent() + t = nil + t.GetEvent() +} + +func TestTimeline_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + t := &Timeline{ID: &zeroValue} + t.GetID() + t = &Timeline{} + t.GetID() + t = nil + t.GetID() +} + +func TestTimeline_GetLabel(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetLabel() + t = nil + t.GetLabel() +} + +func TestTimeline_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{Message: &zeroValue} + t.GetMessage() + t = &Timeline{} + t.GetMessage() + t = nil + t.GetMessage() +} + +func TestTimeline_GetMilestone(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetMilestone() + t = nil + t.GetMilestone() +} + +func TestTimeline_GetParents(tt *testing.T) { + tt.Parallel() + zeroValue := []*Commit{} + t := &Timeline{Parents: zeroValue} + t.GetParents() + t = &Timeline{} + t.GetParents() + t = nil + t.GetParents() +} + +func TestTimeline_GetPerformedViaGithubApp(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetPerformedViaGithubApp() + t = nil + t.GetPerformedViaGithubApp() +} + +func TestTimeline_GetRename(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetRename() + t = nil + t.GetRename() +} + +func TestTimeline_GetRequestedTeam(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetRequestedTeam() + t = nil + t.GetRequestedTeam() +} + +func TestTimeline_GetRequester(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetRequester() + t = nil + t.GetRequester() +} + +func TestTimeline_GetReviewer(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetReviewer() + t = nil + t.GetReviewer() +} + +func TestTimeline_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{SHA: &zeroValue} + t.GetSHA() + t = &Timeline{} + t.GetSHA() + t = nil + t.GetSHA() +} + +func TestTimeline_GetSource(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetSource() + t = nil + t.GetSource() +} + +func TestTimeline_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{State: &zeroValue} + t.GetState() + t = &Timeline{} + t.GetState() + t = nil + t.GetState() +} + +func TestTimeline_GetSubmittedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &Timeline{SubmittedAt: &zeroValue} + t.GetSubmittedAt() + t = &Timeline{} + t.GetSubmittedAt() + t = nil + t.GetSubmittedAt() +} + +func TestTimeline_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Timeline{URL: &zeroValue} + t.GetURL() + t = &Timeline{} + t.GetURL() + t = nil + t.GetURL() +} + +func TestTimeline_GetUser(tt *testing.T) { + tt.Parallel() + t := &Timeline{} + t.GetUser() + t = nil + t.GetUser() +} + +func TestTool_GetGUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tool{GUID: &zeroValue} + t.GetGUID() + t = &Tool{} + t.GetGUID() + t = nil + t.GetGUID() +} + +func TestTool_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tool{Name: &zeroValue} + t.GetName() + t = &Tool{} + t.GetName() + t = nil + t.GetName() +} + +func TestTool_GetVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tool{Version: &zeroValue} + t.GetVersion() + t = &Tool{} + t.GetVersion() + t = nil + t.GetVersion() +} + +func TestTopicResult_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TopicResult{CreatedAt: &zeroValue} + t.GetCreatedAt() + t = &TopicResult{} + t.GetCreatedAt() + t = nil + t.GetCreatedAt() +} + +func TestTopicResult_GetCreatedBy(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TopicResult{CreatedBy: &zeroValue} + t.GetCreatedBy() + t = &TopicResult{} + t.GetCreatedBy() + t = nil + t.GetCreatedBy() +} + +func TestTopicResult_GetCurated(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TopicResult{Curated: &zeroValue} + t.GetCurated() + t = &TopicResult{} + t.GetCurated() + t = nil + t.GetCurated() +} + +func TestTopicResult_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TopicResult{Description: &zeroValue} + t.GetDescription() + t = &TopicResult{} + t.GetDescription() + t = nil + t.GetDescription() +} + +func TestTopicResult_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TopicResult{DisplayName: &zeroValue} + t.GetDisplayName() + t = &TopicResult{} + t.GetDisplayName() + t = nil + t.GetDisplayName() +} + +func TestTopicResult_GetFeatured(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TopicResult{Featured: &zeroValue} + t.GetFeatured() + t = &TopicResult{} + t.GetFeatured() + t = nil + t.GetFeatured() +} + +func TestTopicResult_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TopicResult{Name: &zeroValue} + t.GetName() + t = &TopicResult{} + t.GetName() + t = nil + t.GetName() +} + +func TestTopicResult_GetScore(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + t := &TopicResult{Score: &zeroValue} + t.GetScore() + t = &TopicResult{} + t.GetScore() + t = nil + t.GetScore() +} + +func TestTopicResult_GetShortDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TopicResult{ShortDescription: &zeroValue} + t.GetShortDescription() + t = &TopicResult{} + t.GetShortDescription() + t = nil + t.GetShortDescription() +} + +func TestTopicResult_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TopicResult{UpdatedAt: &zeroValue} + t.GetUpdatedAt() + t = &TopicResult{} + t.GetUpdatedAt() + t = nil + t.GetUpdatedAt() +} + +func TestTopicsSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &TopicsSearchResult{IncompleteResults: &zeroValue} + t.GetIncompleteResults() + t = &TopicsSearchResult{} + t.GetIncompleteResults() + t = nil + t.GetIncompleteResults() +} + +func TestTopicsSearchResult_GetTopics(tt *testing.T) { + tt.Parallel() + zeroValue := []*TopicResult{} + t := &TopicsSearchResult{Topics: zeroValue} + t.GetTopics() + t = &TopicsSearchResult{} + t.GetTopics() + t = nil + t.GetTopics() +} + +func TestTopicsSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TopicsSearchResult{Total: &zeroValue} + t.GetTotal() + t = &TopicsSearchResult{} + t.GetTotal() + t = nil + t.GetTotal() +} + +func TestTotalCacheUsage_GetTotalActiveCachesCount(tt *testing.T) { + tt.Parallel() + t := &TotalCacheUsage{} + t.GetTotalActiveCachesCount() + t = nil + t.GetTotalActiveCachesCount() +} + +func TestTotalCacheUsage_GetTotalActiveCachesUsageSizeInBytes(tt *testing.T) { + tt.Parallel() + t := &TotalCacheUsage{} + t.GetTotalActiveCachesUsageSizeInBytes() + t = nil + t.GetTotalActiveCachesUsageSizeInBytes() +} + +func TestTrafficBreakdownOptions_GetPer(tt *testing.T) { + tt.Parallel() + t := &TrafficBreakdownOptions{} + t.GetPer() + t = nil + t.GetPer() +} + +func TestTrafficClones_GetClones(tt *testing.T) { + tt.Parallel() + zeroValue := []*TrafficData{} + t := &TrafficClones{Clones: zeroValue} + t.GetClones() + t = &TrafficClones{} + t.GetClones() + t = nil + t.GetClones() +} + +func TestTrafficClones_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficClones{Count: &zeroValue} + t.GetCount() + t = &TrafficClones{} + t.GetCount() + t = nil + t.GetCount() +} + +func TestTrafficClones_GetUniques(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficClones{Uniques: &zeroValue} + t.GetUniques() + t = &TrafficClones{} + t.GetUniques() + t = nil + t.GetUniques() +} + +func TestTrafficData_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficData{Count: &zeroValue} + t.GetCount() + t = &TrafficData{} + t.GetCount() + t = nil + t.GetCount() +} + +func TestTrafficData_GetTimestamp(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + t := &TrafficData{Timestamp: &zeroValue} + t.GetTimestamp() + t = &TrafficData{} + t.GetTimestamp() + t = nil + t.GetTimestamp() +} + +func TestTrafficData_GetUniques(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficData{Uniques: &zeroValue} + t.GetUniques() + t = &TrafficData{} + t.GetUniques() + t = nil + t.GetUniques() +} + +func TestTrafficPath_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficPath{Count: &zeroValue} + t.GetCount() + t = &TrafficPath{} + t.GetCount() + t = nil + t.GetCount() +} + +func TestTrafficPath_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TrafficPath{Path: &zeroValue} + t.GetPath() + t = &TrafficPath{} + t.GetPath() + t = nil + t.GetPath() +} + +func TestTrafficPath_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TrafficPath{Title: &zeroValue} + t.GetTitle() + t = &TrafficPath{} + t.GetTitle() + t = nil + t.GetTitle() +} + +func TestTrafficPath_GetUniques(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficPath{Uniques: &zeroValue} + t.GetUniques() + t = &TrafficPath{} + t.GetUniques() + t = nil + t.GetUniques() +} + +func TestTrafficReferrer_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficReferrer{Count: &zeroValue} + t.GetCount() + t = &TrafficReferrer{} + t.GetCount() + t = nil + t.GetCount() +} + +func TestTrafficReferrer_GetReferrer(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TrafficReferrer{Referrer: &zeroValue} + t.GetReferrer() + t = &TrafficReferrer{} + t.GetReferrer() + t = nil + t.GetReferrer() +} + +func TestTrafficReferrer_GetUniques(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficReferrer{Uniques: &zeroValue} + t.GetUniques() + t = &TrafficReferrer{} + t.GetUniques() + t = nil + t.GetUniques() +} + +func TestTrafficViews_GetCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficViews{Count: &zeroValue} + t.GetCount() + t = &TrafficViews{} + t.GetCount() + t = nil + t.GetCount() +} + +func TestTrafficViews_GetUniques(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TrafficViews{Uniques: &zeroValue} + t.GetUniques() + t = &TrafficViews{} + t.GetUniques() + t = nil + t.GetUniques() +} + +func TestTrafficViews_GetViews(tt *testing.T) { + tt.Parallel() + zeroValue := []*TrafficData{} + t := &TrafficViews{Views: zeroValue} + t.GetViews() + t = &TrafficViews{} + t.GetViews() + t = nil + t.GetViews() +} + +func TestTransferRequest_GetNewName(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TransferRequest{NewName: &zeroValue} + t.GetNewName() + t = &TransferRequest{} + t.GetNewName() + t = nil + t.GetNewName() +} + +func TestTransferRequest_GetNewOwner(tt *testing.T) { + tt.Parallel() + t := &TransferRequest{} + t.GetNewOwner() + t = nil + t.GetNewOwner() +} + +func TestTransferRequest_GetTeamID(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + t := &TransferRequest{TeamID: zeroValue} + t.GetTeamID() + t = &TransferRequest{} + t.GetTeamID() + t = nil + t.GetTeamID() +} + +func TestTree_GetEntries(tt *testing.T) { + tt.Parallel() + zeroValue := []*TreeEntry{} + t := &Tree{Entries: zeroValue} + t.GetEntries() + t = &Tree{} + t.GetEntries() + t = nil + t.GetEntries() +} + +func TestTree_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &Tree{SHA: &zeroValue} + t.GetSHA() + t = &Tree{} + t.GetSHA() + t = nil + t.GetSHA() +} + +func TestTree_GetTruncated(tt *testing.T) { + tt.Parallel() + var zeroValue bool + t := &Tree{Truncated: &zeroValue} + t.GetTruncated() + t = &Tree{} + t.GetTruncated() + t = nil + t.GetTruncated() +} + +func TestTreeEntry_GetContent(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TreeEntry{Content: &zeroValue} + t.GetContent() + t = &TreeEntry{} + t.GetContent() + t = nil + t.GetContent() +} + +func TestTreeEntry_GetMode(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TreeEntry{Mode: &zeroValue} + t.GetMode() + t = &TreeEntry{} + t.GetMode() + t = nil + t.GetMode() +} + +func TestTreeEntry_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TreeEntry{Path: &zeroValue} + t.GetPath() + t = &TreeEntry{} + t.GetPath() + t = nil + t.GetPath() +} + +func TestTreeEntry_GetSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TreeEntry{SHA: &zeroValue} + t.GetSHA() + t = &TreeEntry{} + t.GetSHA() + t = nil + t.GetSHA() +} + +func TestTreeEntry_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue int + t := &TreeEntry{Size: &zeroValue} + t.GetSize() + t = &TreeEntry{} + t.GetSize() + t = nil + t.GetSize() +} + +func TestTreeEntry_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TreeEntry{Type: &zeroValue} + t.GetType() + t = &TreeEntry{} + t.GetType() + t = nil + t.GetType() +} + +func TestTreeEntry_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + t := &TreeEntry{URL: &zeroValue} + t.GetURL() + t = &TreeEntry{} + t.GetURL() + t = nil + t.GetURL() +} + +func TestUnauthenticatedRateLimitedTransport_GetClientID(tt *testing.T) { + tt.Parallel() + u := &UnauthenticatedRateLimitedTransport{} + u.GetClientID() + u = nil + u.GetClientID() +} + +func TestUnauthenticatedRateLimitedTransport_GetClientSecret(tt *testing.T) { + tt.Parallel() + u := &UnauthenticatedRateLimitedTransport{} + u.GetClientSecret() + u = nil + u.GetClientSecret() +} + +func TestUpdateAppInstallationRepositoriesRequest_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateAppInstallationRepositoriesRequest{Repositories: zeroValue} + u.GetRepositories() + u = &UpdateAppInstallationRepositoriesRequest{} + u.GetRepositories() + u = nil + u.GetRepositories() +} + +func TestUpdateAppInstallationRepositoriesRequest_GetRepositorySelection(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateAppInstallationRepositoriesRequest{RepositorySelection: &zeroValue} + u.GetRepositorySelection() + u = &UpdateAppInstallationRepositoriesRequest{} + u.GetRepositorySelection() + u = nil + u.GetRepositorySelection() +} + +func TestUpdateAttributeForSCIMUserOperations_GetOp(tt *testing.T) { + tt.Parallel() + u := &UpdateAttributeForSCIMUserOperations{} + u.GetOp() + u = nil + u.GetOp() +} + +func TestUpdateAttributeForSCIMUserOperations_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateAttributeForSCIMUserOperations{Path: &zeroValue} + u.GetPath() + u = &UpdateAttributeForSCIMUserOperations{} + u.GetPath() + u = nil + u.GetPath() +} + +func TestUpdateAttributeForSCIMUserOperations_GetValue(tt *testing.T) { + tt.Parallel() + u := &UpdateAttributeForSCIMUserOperations{} + u.GetValue() + u = nil + u.GetValue() +} + +func TestUpdateAttributeForSCIMUserRequest_GetOperations(tt *testing.T) { + tt.Parallel() + zeroValue := []*UpdateAttributeForSCIMUserOperations{} + u := &UpdateAttributeForSCIMUserRequest{Operations: zeroValue} + u.GetOperations() + u = &UpdateAttributeForSCIMUserRequest{} + u.GetOperations() + u = nil + u.GetOperations() +} + +func TestUpdateAttributeForSCIMUserRequest_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateAttributeForSCIMUserRequest{Schemas: zeroValue} + u.GetSchemas() + u = &UpdateAttributeForSCIMUserRequest{} + u.GetSchemas() + u = nil + u.GetSchemas() +} + +func TestUpdateBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + u := &UpdateBranchRule{} + u.GetParameters() + u = nil + u.GetParameters() +} + +func TestUpdateCheckRunOptions_GetActions(tt *testing.T) { + tt.Parallel() + zeroValue := []*CheckRunAction{} + u := &UpdateCheckRunOptions{Actions: zeroValue} + u.GetActions() + u = &UpdateCheckRunOptions{} + u.GetActions() + u = nil + u.GetActions() +} + +func TestUpdateCheckRunOptions_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &UpdateCheckRunOptions{CompletedAt: &zeroValue} + u.GetCompletedAt() + u = &UpdateCheckRunOptions{} + u.GetCompletedAt() + u = nil + u.GetCompletedAt() +} + +func TestUpdateCheckRunOptions_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCheckRunOptions{Conclusion: &zeroValue} + u.GetConclusion() + u = &UpdateCheckRunOptions{} + u.GetConclusion() + u = nil + u.GetConclusion() +} + +func TestUpdateCheckRunOptions_GetDetailsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCheckRunOptions{DetailsURL: &zeroValue} + u.GetDetailsURL() + u = &UpdateCheckRunOptions{} + u.GetDetailsURL() + u = nil + u.GetDetailsURL() +} + +func TestUpdateCheckRunOptions_GetExternalID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCheckRunOptions{ExternalID: &zeroValue} + u.GetExternalID() + u = &UpdateCheckRunOptions{} + u.GetExternalID() + u = nil + u.GetExternalID() +} + +func TestUpdateCheckRunOptions_GetName(tt *testing.T) { + tt.Parallel() + u := &UpdateCheckRunOptions{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateCheckRunOptions_GetOutput(tt *testing.T) { + tt.Parallel() + u := &UpdateCheckRunOptions{} + u.GetOutput() + u = nil + u.GetOutput() +} + +func TestUpdateCheckRunOptions_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCheckRunOptions{Status: &zeroValue} + u.GetStatus() + u = &UpdateCheckRunOptions{} + u.GetStatus() + u = nil + u.GetStatus() +} + +func TestUpdateCodespaceOptions_GetMachine(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCodespaceOptions{Machine: &zeroValue} + u.GetMachine() + u = &UpdateCodespaceOptions{} + u.GetMachine() + u = nil + u.GetMachine() +} + +func TestUpdateCodespaceOptions_GetRecentFolders(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateCodespaceOptions{RecentFolders: zeroValue} + u.GetRecentFolders() + u = &UpdateCodespaceOptions{} + u.GetRecentFolders() + u = nil + u.GetRecentFolders() +} + +func TestUpdateConnectedExternalGroupRequest_GetGroupID(tt *testing.T) { + tt.Parallel() + u := &UpdateConnectedExternalGroupRequest{} + u.GetGroupID() + u = nil + u.GetGroupID() +} + +func TestUpdateCustomOrgRoleRequest_GetBaseRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCustomOrgRoleRequest{BaseRole: &zeroValue} + u.GetBaseRole() + u = &UpdateCustomOrgRoleRequest{} + u.GetBaseRole() + u = nil + u.GetBaseRole() +} + +func TestUpdateCustomOrgRoleRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCustomOrgRoleRequest{Description: &zeroValue} + u.GetDescription() + u = &UpdateCustomOrgRoleRequest{} + u.GetDescription() + u = nil + u.GetDescription() +} + +func TestUpdateCustomOrgRoleRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCustomOrgRoleRequest{Name: &zeroValue} + u.GetName() + u = &UpdateCustomOrgRoleRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateCustomOrgRoleRequest_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateCustomOrgRoleRequest{Permissions: zeroValue} + u.GetPermissions() + u = &UpdateCustomOrgRoleRequest{} + u.GetPermissions() + u = nil + u.GetPermissions() +} + +func TestUpdateCustomRepoRoleRequest_GetBaseRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCustomRepoRoleRequest{BaseRole: &zeroValue} + u.GetBaseRole() + u = &UpdateCustomRepoRoleRequest{} + u.GetBaseRole() + u = nil + u.GetBaseRole() +} + +func TestUpdateCustomRepoRoleRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCustomRepoRoleRequest{Description: &zeroValue} + u.GetDescription() + u = &UpdateCustomRepoRoleRequest{} + u.GetDescription() + u = nil + u.GetDescription() +} + +func TestUpdateCustomRepoRoleRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateCustomRepoRoleRequest{Name: &zeroValue} + u.GetName() + u = &UpdateCustomRepoRoleRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateCustomRepoRoleRequest_GetPermissions(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateCustomRepoRoleRequest{Permissions: zeroValue} + u.GetPermissions() + u = &UpdateCustomRepoRoleRequest{} + u.GetPermissions() + u = nil + u.GetPermissions() +} + +func TestUpdateDefaultSetupConfigurationOptions_GetLanguages(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateDefaultSetupConfigurationOptions{Languages: zeroValue} + u.GetLanguages() + u = &UpdateDefaultSetupConfigurationOptions{} + u.GetLanguages() + u = nil + u.GetLanguages() +} + +func TestUpdateDefaultSetupConfigurationOptions_GetQuerySuite(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateDefaultSetupConfigurationOptions{QuerySuite: &zeroValue} + u.GetQuerySuite() + u = &UpdateDefaultSetupConfigurationOptions{} + u.GetQuerySuite() + u = nil + u.GetQuerySuite() +} + +func TestUpdateDefaultSetupConfigurationOptions_GetState(tt *testing.T) { + tt.Parallel() + u := &UpdateDefaultSetupConfigurationOptions{} + u.GetState() + u = nil + u.GetState() +} + +func TestUpdateDefaultSetupConfigurationResponse_GetRunID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UpdateDefaultSetupConfigurationResponse{RunID: &zeroValue} + u.GetRunID() + u = &UpdateDefaultSetupConfigurationResponse{} + u.GetRunID() + u = nil + u.GetRunID() +} + +func TestUpdateDefaultSetupConfigurationResponse_GetRunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateDefaultSetupConfigurationResponse{RunURL: &zeroValue} + u.GetRunURL() + u = &UpdateDefaultSetupConfigurationResponse{} + u.GetRunURL() + u = nil + u.GetRunURL() +} + +func TestUpdateDeploymentBranchPolicyRequest_GetName(tt *testing.T) { + tt.Parallel() + u := &UpdateDeploymentBranchPolicyRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateEnterpriseRunnerGroupRequest_GetAllowsPublicRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateEnterpriseRunnerGroupRequest{AllowsPublicRepositories: &zeroValue} + u.GetAllowsPublicRepositories() + u = &UpdateEnterpriseRunnerGroupRequest{} + u.GetAllowsPublicRepositories() + u = nil + u.GetAllowsPublicRepositories() +} + +func TestUpdateEnterpriseRunnerGroupRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateEnterpriseRunnerGroupRequest{Name: &zeroValue} + u.GetName() + u = &UpdateEnterpriseRunnerGroupRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateEnterpriseRunnerGroupRequest_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateEnterpriseRunnerGroupRequest{NetworkConfigurationID: &zeroValue} + u.GetNetworkConfigurationID() + u = &UpdateEnterpriseRunnerGroupRequest{} + u.GetNetworkConfigurationID() + u = nil + u.GetNetworkConfigurationID() +} + +func TestUpdateEnterpriseRunnerGroupRequest_GetRestrictedToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateEnterpriseRunnerGroupRequest{RestrictedToWorkflows: &zeroValue} + u.GetRestrictedToWorkflows() + u = &UpdateEnterpriseRunnerGroupRequest{} + u.GetRestrictedToWorkflows() + u = nil + u.GetRestrictedToWorkflows() +} + +func TestUpdateEnterpriseRunnerGroupRequest_GetSelectedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateEnterpriseRunnerGroupRequest{SelectedWorkflows: zeroValue} + u.GetSelectedWorkflows() + u = &UpdateEnterpriseRunnerGroupRequest{} + u.GetSelectedWorkflows() + u = nil + u.GetSelectedWorkflows() +} + +func TestUpdateEnterpriseRunnerGroupRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateEnterpriseRunnerGroupRequest{Visibility: &zeroValue} + u.GetVisibility() + u = &UpdateEnterpriseRunnerGroupRequest{} + u.GetVisibility() + u = nil + u.GetVisibility() +} + +func TestUpdateGistCommentRequest_GetBody(tt *testing.T) { + tt.Parallel() + u := &UpdateGistCommentRequest{} + u.GetBody() + u = nil + u.GetBody() +} + +func TestUpdateGistFile_GetContent(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateGistFile{Content: &zeroValue} + u.GetContent() + u = &UpdateGistFile{} + u.GetContent() + u = nil + u.GetContent() +} + +func TestUpdateGistFile_GetFilename(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateGistFile{Filename: &zeroValue} + u.GetFilename() + u = &UpdateGistFile{} + u.GetFilename() + u = nil + u.GetFilename() +} + +func TestUpdateGistRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateGistRequest{Description: &zeroValue} + u.GetDescription() + u = &UpdateGistRequest{} + u.GetDescription() + u = nil + u.GetDescription() +} + +func TestUpdateHostedRunnerRequest_GetEnableStaticIP(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateHostedRunnerRequest{EnableStaticIP: &zeroValue} + u.GetEnableStaticIP() + u = &UpdateHostedRunnerRequest{} + u.GetEnableStaticIP() + u = nil + u.GetEnableStaticIP() +} + +func TestUpdateHostedRunnerRequest_GetImageID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateHostedRunnerRequest{ImageID: &zeroValue} + u.GetImageID() + u = &UpdateHostedRunnerRequest{} + u.GetImageID() + u = nil + u.GetImageID() +} + +func TestUpdateHostedRunnerRequest_GetImageVersion(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateHostedRunnerRequest{ImageVersion: &zeroValue} + u.GetImageVersion() + u = &UpdateHostedRunnerRequest{} + u.GetImageVersion() + u = nil + u.GetImageVersion() +} + +func TestUpdateHostedRunnerRequest_GetMaximumRunners(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UpdateHostedRunnerRequest{MaximumRunners: &zeroValue} + u.GetMaximumRunners() + u = &UpdateHostedRunnerRequest{} + u.GetMaximumRunners() + u = nil + u.GetMaximumRunners() +} + +func TestUpdateHostedRunnerRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateHostedRunnerRequest{Name: &zeroValue} + u.GetName() + u = &UpdateHostedRunnerRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateHostedRunnerRequest_GetRunnerGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UpdateHostedRunnerRequest{RunnerGroupID: &zeroValue} + u.GetRunnerGroupID() + u = &UpdateHostedRunnerRequest{} + u.GetRunnerGroupID() + u = nil + u.GetRunnerGroupID() +} + +func TestUpdateHostedRunnerRequest_GetSize(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateHostedRunnerRequest{Size: &zeroValue} + u.GetSize() + u = &UpdateHostedRunnerRequest{} + u.GetSize() + u = nil + u.GetSize() +} + +func TestUpdateIssueLabelRequest_GetColor(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueLabelRequest{Color: &zeroValue} + u.GetColor() + u = &UpdateIssueLabelRequest{} + u.GetColor() + u = nil + u.GetColor() +} + +func TestUpdateIssueLabelRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueLabelRequest{Description: &zeroValue} + u.GetDescription() + u = &UpdateIssueLabelRequest{} + u.GetDescription() + u = nil + u.GetDescription() +} + +func TestUpdateIssueLabelRequest_GetNewName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueLabelRequest{NewName: &zeroValue} + u.GetNewName() + u = &UpdateIssueLabelRequest{} + u.GetNewName() + u = nil + u.GetNewName() +} + +func TestUpdateIssueRequest_GetAssignee(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueRequest{Assignee: &zeroValue} + u.GetAssignee() + u = &UpdateIssueRequest{} + u.GetAssignee() + u = nil + u.GetAssignee() +} + +func TestUpdateIssueRequest_GetAssignees(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateIssueRequest{Assignees: zeroValue} + u.GetAssignees() + u = &UpdateIssueRequest{} + u.GetAssignees() + u = nil + u.GetAssignees() +} + +func TestUpdateIssueRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueRequest{Body: &zeroValue} + u.GetBody() + u = &UpdateIssueRequest{} + u.GetBody() + u = nil + u.GetBody() +} + +func TestUpdateIssueRequest_GetDuplicateIssueID(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UpdateIssueRequest{DuplicateIssueID: &zeroValue} + u.GetDuplicateIssueID() + u = &UpdateIssueRequest{} + u.GetDuplicateIssueID() + u = nil + u.GetDuplicateIssueID() +} + +func TestUpdateIssueRequest_GetIssueFieldValues(tt *testing.T) { + tt.Parallel() + zeroValue := []*IssueRequestFieldValue{} + u := &UpdateIssueRequest{IssueFieldValues: zeroValue} + u.GetIssueFieldValues() + u = &UpdateIssueRequest{} + u.GetIssueFieldValues() + u = nil + u.GetIssueFieldValues() +} + +func TestUpdateIssueRequest_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateIssueRequest{Labels: zeroValue} + u.GetLabels() + u = &UpdateIssueRequest{} + u.GetLabels() + u = nil + u.GetLabels() +} + +func TestUpdateIssueRequest_GetMilestone(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UpdateIssueRequest{Milestone: &zeroValue} + u.GetMilestone() + u = &UpdateIssueRequest{} + u.GetMilestone() + u = nil + u.GetMilestone() +} + +func TestUpdateIssueRequest_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueRequest{State: &zeroValue} + u.GetState() + u = &UpdateIssueRequest{} + u.GetState() + u = nil + u.GetState() +} + +func TestUpdateIssueRequest_GetStateReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueRequest{StateReason: &zeroValue} + u.GetStateReason() + u = &UpdateIssueRequest{} + u.GetStateReason() + u = nil + u.GetStateReason() +} + +func TestUpdateIssueRequest_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueRequest{Title: &zeroValue} + u.GetTitle() + u = &UpdateIssueRequest{} + u.GetTitle() + u = nil + u.GetTitle() +} + +func TestUpdateIssueRequest_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateIssueRequest{Type: &zeroValue} + u.GetType() + u = &UpdateIssueRequest{} + u.GetType() + u = nil + u.GetType() +} + +func TestUpdateMilestoneRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateMilestoneRequest{Description: &zeroValue} + u.GetDescription() + u = &UpdateMilestoneRequest{} + u.GetDescription() + u = nil + u.GetDescription() +} + +func TestUpdateMilestoneRequest_GetDueOn(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &UpdateMilestoneRequest{DueOn: &zeroValue} + u.GetDueOn() + u = &UpdateMilestoneRequest{} + u.GetDueOn() + u = nil + u.GetDueOn() +} + +func TestUpdateMilestoneRequest_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateMilestoneRequest{State: &zeroValue} + u.GetState() + u = &UpdateMilestoneRequest{} + u.GetState() + u = nil + u.GetState() +} + +func TestUpdateMilestoneRequest_GetTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateMilestoneRequest{Title: &zeroValue} + u.GetTitle() + u = &UpdateMilestoneRequest{} + u.GetTitle() + u = nil + u.GetTitle() +} + +func TestUpdateOrganizationPrivateRegistry_GetAccountID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{AccountID: &zeroValue} + u.GetAccountID() + u = &UpdateOrganizationPrivateRegistry{} + u.GetAccountID() + u = nil + u.GetAccountID() +} + +func TestUpdateOrganizationPrivateRegistry_GetAudience(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{Audience: &zeroValue} + u.GetAudience() + u = &UpdateOrganizationPrivateRegistry{} + u.GetAudience() + u = nil + u.GetAudience() +} + +func TestUpdateOrganizationPrivateRegistry_GetAuthType(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{AuthType: &zeroValue} + u.GetAuthType() + u = &UpdateOrganizationPrivateRegistry{} + u.GetAuthType() + u = nil + u.GetAuthType() +} + +func TestUpdateOrganizationPrivateRegistry_GetAWSRegion(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{AWSRegion: &zeroValue} + u.GetAWSRegion() + u = &UpdateOrganizationPrivateRegistry{} + u.GetAWSRegion() + u = nil + u.GetAWSRegion() +} + +func TestUpdateOrganizationPrivateRegistry_GetClientID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{ClientID: &zeroValue} + u.GetClientID() + u = &UpdateOrganizationPrivateRegistry{} + u.GetClientID() + u = nil + u.GetClientID() +} + +func TestUpdateOrganizationPrivateRegistry_GetDomain(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{Domain: &zeroValue} + u.GetDomain() + u = &UpdateOrganizationPrivateRegistry{} + u.GetDomain() + u = nil + u.GetDomain() +} + +func TestUpdateOrganizationPrivateRegistry_GetDomainOwner(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{DomainOwner: &zeroValue} + u.GetDomainOwner() + u = &UpdateOrganizationPrivateRegistry{} + u.GetDomainOwner() + u = nil + u.GetDomainOwner() +} + +func TestUpdateOrganizationPrivateRegistry_GetEncryptedValue(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{EncryptedValue: &zeroValue} + u.GetEncryptedValue() + u = &UpdateOrganizationPrivateRegistry{} + u.GetEncryptedValue() + u = nil + u.GetEncryptedValue() +} + +func TestUpdateOrganizationPrivateRegistry_GetIdentityMappingName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{IdentityMappingName: &zeroValue} + u.GetIdentityMappingName() + u = &UpdateOrganizationPrivateRegistry{} + u.GetIdentityMappingName() + u = nil + u.GetIdentityMappingName() +} + +func TestUpdateOrganizationPrivateRegistry_GetJFrogOIDCProviderName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{JFrogOIDCProviderName: &zeroValue} + u.GetJFrogOIDCProviderName() + u = &UpdateOrganizationPrivateRegistry{} + u.GetJFrogOIDCProviderName() + u = nil + u.GetJFrogOIDCProviderName() +} + +func TestUpdateOrganizationPrivateRegistry_GetKeyID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{KeyID: &zeroValue} + u.GetKeyID() + u = &UpdateOrganizationPrivateRegistry{} + u.GetKeyID() + u = nil + u.GetKeyID() +} + +func TestUpdateOrganizationPrivateRegistry_GetRegistryType(tt *testing.T) { + tt.Parallel() + u := &UpdateOrganizationPrivateRegistry{} + u.GetRegistryType() + u = nil + u.GetRegistryType() +} + +func TestUpdateOrganizationPrivateRegistry_GetReplacesBase(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateOrganizationPrivateRegistry{ReplacesBase: &zeroValue} + u.GetReplacesBase() + u = &UpdateOrganizationPrivateRegistry{} + u.GetReplacesBase() + u = nil + u.GetReplacesBase() +} + +func TestUpdateOrganizationPrivateRegistry_GetRoleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{RoleName: &zeroValue} + u.GetRoleName() + u = &UpdateOrganizationPrivateRegistry{} + u.GetRoleName() + u = nil + u.GetRoleName() +} + +func TestUpdateOrganizationPrivateRegistry_GetSelectedRepositoryIDs(tt *testing.T) { + tt.Parallel() + zeroValue := []int64{} + u := &UpdateOrganizationPrivateRegistry{SelectedRepositoryIDs: zeroValue} + u.GetSelectedRepositoryIDs() + u = &UpdateOrganizationPrivateRegistry{} + u.GetSelectedRepositoryIDs() + u = nil + u.GetSelectedRepositoryIDs() +} + +func TestUpdateOrganizationPrivateRegistry_GetTenantID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{TenantID: &zeroValue} + u.GetTenantID() + u = &UpdateOrganizationPrivateRegistry{} + u.GetTenantID() + u = nil + u.GetTenantID() +} + +func TestUpdateOrganizationPrivateRegistry_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{URL: &zeroValue} + u.GetURL() + u = &UpdateOrganizationPrivateRegistry{} + u.GetURL() + u = nil + u.GetURL() +} + +func TestUpdateOrganizationPrivateRegistry_GetUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateOrganizationPrivateRegistry{Username: &zeroValue} + u.GetUsername() + u = &UpdateOrganizationPrivateRegistry{} + u.GetUsername() + u = nil + u.GetUsername() +} + +func TestUpdateOrganizationPrivateRegistry_GetVisibility(tt *testing.T) { + tt.Parallel() + u := &UpdateOrganizationPrivateRegistry{} + u.GetVisibility() + u = nil + u.GetVisibility() +} + +func TestUpdatePreReceiveHookRequest_GetEnforcement(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdatePreReceiveHookRequest{Enforcement: &zeroValue} + u.GetEnforcement() + u = &UpdatePreReceiveHookRequest{} + u.GetEnforcement() + u = nil + u.GetEnforcement() +} + +func TestUpdateProjectItemOptions_GetArchived(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateProjectItemOptions{Archived: &zeroValue} + u.GetArchived() + u = &UpdateProjectItemOptions{} + u.GetArchived() + u = nil + u.GetArchived() +} + +func TestUpdateProjectItemOptions_GetFields(tt *testing.T) { + tt.Parallel() + zeroValue := []*UpdateProjectV2Field{} + u := &UpdateProjectItemOptions{Fields: zeroValue} + u.GetFields() + u = &UpdateProjectItemOptions{} + u.GetFields() + u = nil + u.GetFields() +} + +func TestUpdateProjectV2Field_GetID(tt *testing.T) { + tt.Parallel() + u := &UpdateProjectV2Field{} + u.GetID() + u = nil + u.GetID() +} + +func TestUpdateProjectV2Field_GetValue(tt *testing.T) { + tt.Parallel() + u := &UpdateProjectV2Field{} + u.GetValue() + u = nil + u.GetValue() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetActive(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateProvisionedOrgMembershipRequest{Active: &zeroValue} + u.GetActive() + u = &UpdateProvisionedOrgMembershipRequest{} + u.GetActive() + u = nil + u.GetActive() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetDisplayName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateProvisionedOrgMembershipRequest{DisplayName: &zeroValue} + u.GetDisplayName() + u = &UpdateProvisionedOrgMembershipRequest{} + u.GetDisplayName() + u = nil + u.GetDisplayName() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetEmails(tt *testing.T) { + tt.Parallel() + zeroValue := []*SCIMUserEmail{} + u := &UpdateProvisionedOrgMembershipRequest{Emails: zeroValue} + u.GetEmails() + u = &UpdateProvisionedOrgMembershipRequest{} + u.GetEmails() + u = nil + u.GetEmails() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetExternalID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateProvisionedOrgMembershipRequest{ExternalID: &zeroValue} + u.GetExternalID() + u = &UpdateProvisionedOrgMembershipRequest{} + u.GetExternalID() + u = nil + u.GetExternalID() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetGroups(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateProvisionedOrgMembershipRequest{Groups: zeroValue} + u.GetGroups() + u = &UpdateProvisionedOrgMembershipRequest{} + u.GetGroups() + u = nil + u.GetGroups() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetName(tt *testing.T) { + tt.Parallel() + u := &UpdateProvisionedOrgMembershipRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetSchemas(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateProvisionedOrgMembershipRequest{Schemas: zeroValue} + u.GetSchemas() + u = &UpdateProvisionedOrgMembershipRequest{} + u.GetSchemas() + u = nil + u.GetSchemas() +} + +func TestUpdateProvisionedOrgMembershipRequest_GetUserName(tt *testing.T) { + tt.Parallel() + u := &UpdateProvisionedOrgMembershipRequest{} + u.GetUserName() + u = nil + u.GetUserName() +} + +func TestUpdateRef_GetForce(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateRef{Force: &zeroValue} + u.GetForce() + u = &UpdateRef{} + u.GetForce() + u = nil + u.GetForce() +} + +func TestUpdateRef_GetSHA(tt *testing.T) { + tt.Parallel() + u := &UpdateRef{} + u.GetSHA() + u = nil + u.GetSHA() +} + +func TestUpdateReleaseAssetRequest_GetLabel(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseAssetRequest{Label: &zeroValue} + u.GetLabel() + u = &UpdateReleaseAssetRequest{} + u.GetLabel() + u = nil + u.GetLabel() +} + +func TestUpdateReleaseAssetRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseAssetRequest{Name: &zeroValue} + u.GetName() + u = &UpdateReleaseAssetRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateReleaseAssetRequest_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseAssetRequest{State: &zeroValue} + u.GetState() + u = &UpdateReleaseAssetRequest{} + u.GetState() + u = nil + u.GetState() +} + +func TestUpdateReleaseRequest_GetBody(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseRequest{Body: &zeroValue} + u.GetBody() + u = &UpdateReleaseRequest{} + u.GetBody() + u = nil + u.GetBody() +} + +func TestUpdateReleaseRequest_GetDiscussionCategoryName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseRequest{DiscussionCategoryName: &zeroValue} + u.GetDiscussionCategoryName() + u = &UpdateReleaseRequest{} + u.GetDiscussionCategoryName() + u = nil + u.GetDiscussionCategoryName() +} + +func TestUpdateReleaseRequest_GetDraft(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateReleaseRequest{Draft: &zeroValue} + u.GetDraft() + u = &UpdateReleaseRequest{} + u.GetDraft() + u = nil + u.GetDraft() +} + +func TestUpdateReleaseRequest_GetMakeLatest(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseRequest{MakeLatest: &zeroValue} + u.GetMakeLatest() + u = &UpdateReleaseRequest{} + u.GetMakeLatest() + u = nil + u.GetMakeLatest() +} + +func TestUpdateReleaseRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseRequest{Name: &zeroValue} + u.GetName() + u = &UpdateReleaseRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateReleaseRequest_GetPrerelease(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateReleaseRequest{Prerelease: &zeroValue} + u.GetPrerelease() + u = &UpdateReleaseRequest{} + u.GetPrerelease() + u = nil + u.GetPrerelease() +} + +func TestUpdateReleaseRequest_GetTagName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseRequest{TagName: &zeroValue} + u.GetTagName() + u = &UpdateReleaseRequest{} + u.GetTagName() + u = nil + u.GetTagName() +} + +func TestUpdateReleaseRequest_GetTargetCommitish(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateReleaseRequest{TargetCommitish: &zeroValue} + u.GetTargetCommitish() + u = &UpdateReleaseRequest{} + u.GetTargetCommitish() + u = nil + u.GetTargetCommitish() +} + +func TestUpdateRuleParameters_GetUpdateAllowsFetchAndMerge(tt *testing.T) { + tt.Parallel() + u := &UpdateRuleParameters{} + u.GetUpdateAllowsFetchAndMerge() + u = nil + u.GetUpdateAllowsFetchAndMerge() +} + +func TestUpdateRunnerGroupRequest_GetAllowsPublicRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateRunnerGroupRequest{AllowsPublicRepositories: &zeroValue} + u.GetAllowsPublicRepositories() + u = &UpdateRunnerGroupRequest{} + u.GetAllowsPublicRepositories() + u = nil + u.GetAllowsPublicRepositories() +} + +func TestUpdateRunnerGroupRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateRunnerGroupRequest{Name: &zeroValue} + u.GetName() + u = &UpdateRunnerGroupRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateRunnerGroupRequest_GetNetworkConfigurationID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateRunnerGroupRequest{NetworkConfigurationID: &zeroValue} + u.GetNetworkConfigurationID() + u = &UpdateRunnerGroupRequest{} + u.GetNetworkConfigurationID() + u = nil + u.GetNetworkConfigurationID() +} + +func TestUpdateRunnerGroupRequest_GetRestrictedToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UpdateRunnerGroupRequest{RestrictedToWorkflows: &zeroValue} + u.GetRestrictedToWorkflows() + u = &UpdateRunnerGroupRequest{} + u.GetRestrictedToWorkflows() + u = nil + u.GetRestrictedToWorkflows() +} + +func TestUpdateRunnerGroupRequest_GetSelectedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UpdateRunnerGroupRequest{SelectedWorkflows: zeroValue} + u.GetSelectedWorkflows() + u = &UpdateRunnerGroupRequest{} + u.GetSelectedWorkflows() + u = nil + u.GetSelectedWorkflows() +} + +func TestUpdateRunnerGroupRequest_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateRunnerGroupRequest{Visibility: &zeroValue} + u.GetVisibility() + u = &UpdateRunnerGroupRequest{} + u.GetVisibility() + u = nil + u.GetVisibility() +} + +func TestUpdateTeamLDAPMappingRequest_GetLDAPDN(tt *testing.T) { + tt.Parallel() + u := &UpdateTeamLDAPMappingRequest{} + u.GetLDAPDN() + u = nil + u.GetLDAPDN() +} + +func TestUpdateTeamRequest_GetDescription(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateTeamRequest{Description: &zeroValue} + u.GetDescription() + u = &UpdateTeamRequest{} + u.GetDescription() + u = nil + u.GetDescription() +} + +func TestUpdateTeamRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateTeamRequest{Name: &zeroValue} + u.GetName() + u = &UpdateTeamRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUpdateTeamRequest_GetNotificationSetting(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateTeamRequest{NotificationSetting: &zeroValue} + u.GetNotificationSetting() + u = &UpdateTeamRequest{} + u.GetNotificationSetting() + u = nil + u.GetNotificationSetting() +} + +func TestUpdateTeamRequest_GetParentTeamID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UpdateTeamRequest{ParentTeamID: &zeroValue} + u.GetParentTeamID() + u = &UpdateTeamRequest{} + u.GetParentTeamID() + u = nil + u.GetParentTeamID() +} + +func TestUpdateTeamRequest_GetParentTeamSlug(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateTeamRequest{ParentTeamSlug: &zeroValue} + u.GetParentTeamSlug() + u = &UpdateTeamRequest{} + u.GetParentTeamSlug() + u = nil + u.GetParentTeamSlug() +} + +func TestUpdateTeamRequest_GetPermission(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateTeamRequest{Permission: &zeroValue} + u.GetPermission() + u = &UpdateTeamRequest{} + u.GetPermission() + u = nil + u.GetPermission() +} + +func TestUpdateTeamRequest_GetPrivacy(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UpdateTeamRequest{Privacy: &zeroValue} + u.GetPrivacy() + u = &UpdateTeamRequest{} + u.GetPrivacy() + u = nil + u.GetPrivacy() +} + +func TestUpdateTeamRequest_GetRemoveParentTeam(tt *testing.T) { + tt.Parallel() + u := &UpdateTeamRequest{} + u.GetRemoveParentTeam() + u = nil + u.GetRemoveParentTeam() +} + +func TestUpdateUserLDAPMappingRequest_GetLDAPDN(tt *testing.T) { + tt.Parallel() + u := &UpdateUserLDAPMappingRequest{} + u.GetLDAPDN() + u = nil + u.GetLDAPDN() +} + +func TestUploadLicenseOptions_GetLicense(tt *testing.T) { + tt.Parallel() + u := &UploadLicenseOptions{} + u.GetLicense() + u = nil + u.GetLicense() +} + +func TestUploadOptions_GetLabel(tt *testing.T) { + tt.Parallel() + u := &UploadOptions{} + u.GetLabel() + u = nil + u.GetLabel() +} + +func TestUploadOptions_GetMediaType(tt *testing.T) { + tt.Parallel() + u := &UploadOptions{} + u.GetMediaType() + u = nil + u.GetMediaType() +} + +func TestUploadOptions_GetName(tt *testing.T) { + tt.Parallel() + u := &UploadOptions{} + u.GetName() + u = nil + u.GetName() +} + +func TestUsageItem_GetDate(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetDate() + u = nil + u.GetDate() +} + +func TestUsageItem_GetDiscountAmount(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetDiscountAmount() + u = nil + u.GetDiscountAmount() +} + +func TestUsageItem_GetGrossAmount(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetGrossAmount() + u = nil + u.GetGrossAmount() +} + +func TestUsageItem_GetNetAmount(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetNetAmount() + u = nil + u.GetNetAmount() +} + +func TestUsageItem_GetOrganizationName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UsageItem{OrganizationName: &zeroValue} + u.GetOrganizationName() + u = &UsageItem{} + u.GetOrganizationName() + u = nil + u.GetOrganizationName() +} + +func TestUsageItem_GetPricePerUnit(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetPricePerUnit() + u = nil + u.GetPricePerUnit() +} + +func TestUsageItem_GetProduct(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetProduct() + u = nil + u.GetProduct() +} + +func TestUsageItem_GetQuantity(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetQuantity() + u = nil + u.GetQuantity() +} + +func TestUsageItem_GetRepositoryName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UsageItem{RepositoryName: &zeroValue} + u.GetRepositoryName() + u = &UsageItem{} + u.GetRepositoryName() + u = nil + u.GetRepositoryName() +} + +func TestUsageItem_GetSKU(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetSKU() + u = nil + u.GetSKU() +} + +func TestUsageItem_GetUnitType(tt *testing.T) { + tt.Parallel() + u := &UsageItem{} + u.GetUnitType() + u = nil + u.GetUnitType() +} + +func TestUsageReport_GetUsageItems(tt *testing.T) { + tt.Parallel() + zeroValue := []*UsageItem{} + u := &UsageReport{UsageItems: zeroValue} + u.GetUsageItems() + u = &UsageReport{} + u.GetUsageItems() + u = nil + u.GetUsageItems() +} + +func TestUsageReportOptions_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UsageReportOptions{Day: &zeroValue} + u.GetDay() + u = &UsageReportOptions{} + u.GetDay() + u = nil + u.GetDay() +} + +func TestUsageReportOptions_GetHour(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UsageReportOptions{Hour: &zeroValue} + u.GetHour() + u = &UsageReportOptions{} + u.GetHour() + u = nil + u.GetHour() +} + +func TestUsageReportOptions_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UsageReportOptions{Month: &zeroValue} + u.GetMonth() + u = &UsageReportOptions{} + u.GetMonth() + u = nil + u.GetMonth() +} + +func TestUsageReportOptions_GetYear(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UsageReportOptions{Year: &zeroValue} + u.GetYear() + u = &UsageReportOptions{} + u.GetYear() + u = nil + u.GetYear() +} + +func TestUser_GetAssignment(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Assignment: &zeroValue} + u.GetAssignment() + u = &User{} + u.GetAssignment() + u = nil + u.GetAssignment() +} + +func TestUser_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{AvatarURL: &zeroValue} + u.GetAvatarURL() + u = &User{} + u.GetAvatarURL() + u = nil + u.GetAvatarURL() +} + +func TestUser_GetBio(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Bio: &zeroValue} + u.GetBio() + u = &User{} + u.GetBio() + u = nil + u.GetBio() +} + +func TestUser_GetBlog(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Blog: &zeroValue} + u.GetBlog() + u = &User{} + u.GetBlog() + u = nil + u.GetBlog() +} + +func TestUser_GetBusinessPlus(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &User{BusinessPlus: &zeroValue} + u.GetBusinessPlus() + u = &User{} + u.GetBusinessPlus() + u = nil + u.GetBusinessPlus() +} + +func TestUser_GetCollaborators(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{Collaborators: &zeroValue} + u.GetCollaborators() + u = &User{} + u.GetCollaborators() + u = nil + u.GetCollaborators() +} + +func TestUser_GetCompany(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Company: &zeroValue} + u.GetCompany() + u = &User{} + u.GetCompany() + u = nil + u.GetCompany() +} + +func TestUser_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &User{CreatedAt: &zeroValue} + u.GetCreatedAt() + u = &User{} + u.GetCreatedAt() + u = nil + u.GetCreatedAt() +} + +func TestUser_GetDiskUsage(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{DiskUsage: &zeroValue} + u.GetDiskUsage() + u = &User{} + u.GetDiskUsage() + u = nil + u.GetDiskUsage() +} + +func TestUser_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Email: &zeroValue} + u.GetEmail() + u = &User{} + u.GetEmail() + u = nil + u.GetEmail() +} + +func TestUser_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{EventsURL: &zeroValue} + u.GetEventsURL() + u = &User{} + u.GetEventsURL() + u = nil + u.GetEventsURL() +} + +func TestUser_GetFollowers(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{Followers: &zeroValue} + u.GetFollowers() + u = &User{} + u.GetFollowers() + u = nil + u.GetFollowers() +} + +func TestUser_GetFollowersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{FollowersURL: &zeroValue} + u.GetFollowersURL() + u = &User{} + u.GetFollowersURL() + u = nil + u.GetFollowersURL() +} + +func TestUser_GetFollowing(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{Following: &zeroValue} + u.GetFollowing() + u = &User{} + u.GetFollowing() + u = nil + u.GetFollowing() +} + +func TestUser_GetFollowingURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{FollowingURL: &zeroValue} + u.GetFollowingURL() + u = &User{} + u.GetFollowingURL() + u = nil + u.GetFollowingURL() +} + +func TestUser_GetGistsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{GistsURL: &zeroValue} + u.GetGistsURL() + u = &User{} + u.GetGistsURL() + u = nil + u.GetGistsURL() +} + +func TestUser_GetGravatarID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{GravatarID: &zeroValue} + u.GetGravatarID() + u = &User{} + u.GetGravatarID() + u = nil + u.GetGravatarID() +} + +func TestUser_GetHireable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &User{Hireable: &zeroValue} + u.GetHireable() + u = &User{} + u.GetHireable() + u = nil + u.GetHireable() +} + +func TestUser_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{HTMLURL: &zeroValue} + u.GetHTMLURL() + u = &User{} + u.GetHTMLURL() + u = nil + u.GetHTMLURL() +} + +func TestUser_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &User{ID: &zeroValue} + u.GetID() + u = &User{} + u.GetID() + u = nil + u.GetID() +} + +func TestUser_GetInherited(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &User{Inherited: &zeroValue} + u.GetInherited() + u = &User{} + u.GetInherited() + u = nil + u.GetInherited() +} + +func TestUser_GetInheritedFrom(tt *testing.T) { + tt.Parallel() + zeroValue := []*Team{} + u := &User{InheritedFrom: zeroValue} + u.GetInheritedFrom() + u = &User{} + u.GetInheritedFrom() + u = nil + u.GetInheritedFrom() +} + +func TestUser_GetLdapDn(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{LdapDn: &zeroValue} + u.GetLdapDn() + u = &User{} + u.GetLdapDn() + u = nil + u.GetLdapDn() +} + +func TestUser_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Location: &zeroValue} + u.GetLocation() + u = &User{} + u.GetLocation() + u = nil + u.GetLocation() +} + +func TestUser_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Login: &zeroValue} + u.GetLogin() + u = &User{} + u.GetLogin() + u = nil + u.GetLogin() +} + +func TestUser_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Name: &zeroValue} + u.GetName() + u = &User{} + u.GetName() + u = nil + u.GetName() +} + +func TestUser_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{NodeID: &zeroValue} + u.GetNodeID() + u = &User{} + u.GetNodeID() + u = nil + u.GetNodeID() +} + +func TestUser_GetNotificationEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{NotificationEmail: &zeroValue} + u.GetNotificationEmail() + u = &User{} + u.GetNotificationEmail() + u = nil + u.GetNotificationEmail() +} + +func TestUser_GetOrganizationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{OrganizationsURL: &zeroValue} + u.GetOrganizationsURL() + u = &User{} + u.GetOrganizationsURL() + u = nil + u.GetOrganizationsURL() +} + +func TestUser_GetOwnedPrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &User{OwnedPrivateRepos: &zeroValue} + u.GetOwnedPrivateRepos() + u = &User{} + u.GetOwnedPrivateRepos() + u = nil + u.GetOwnedPrivateRepos() +} + +func TestUser_GetPermissions(tt *testing.T) { + tt.Parallel() + u := &User{} + u.GetPermissions() + u = nil + u.GetPermissions() +} + +func TestUser_GetPlan(tt *testing.T) { + tt.Parallel() + u := &User{} + u.GetPlan() + u = nil + u.GetPlan() +} + +func TestUser_GetPrivateGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{PrivateGists: &zeroValue} + u.GetPrivateGists() + u = &User{} + u.GetPrivateGists() + u = nil + u.GetPrivateGists() +} + +func TestUser_GetPublicGists(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{PublicGists: &zeroValue} + u.GetPublicGists() + u = &User{} + u.GetPublicGists() + u = nil + u.GetPublicGists() +} + +func TestUser_GetPublicRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &User{PublicRepos: &zeroValue} + u.GetPublicRepos() + u = &User{} + u.GetPublicRepos() + u = nil + u.GetPublicRepos() +} + +func TestUser_GetReceivedEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{ReceivedEventsURL: &zeroValue} + u.GetReceivedEventsURL() + u = &User{} + u.GetReceivedEventsURL() + u = nil + u.GetReceivedEventsURL() +} + +func TestUser_GetReposURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{ReposURL: &zeroValue} + u.GetReposURL() + u = &User{} + u.GetReposURL() + u = nil + u.GetReposURL() +} + +func TestUser_GetRole(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Role: &zeroValue} + u.GetRole() + u = &User{} + u.GetRole() + u = nil + u.GetRole() +} + +func TestUser_GetRoleName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{RoleName: &zeroValue} + u.GetRoleName() + u = &User{} + u.GetRoleName() + u = nil + u.GetRoleName() +} + +func TestUser_GetSiteAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &User{SiteAdmin: &zeroValue} + u.GetSiteAdmin() + u = &User{} + u.GetSiteAdmin() + u = nil + u.GetSiteAdmin() +} + +func TestUser_GetStarredURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{StarredURL: &zeroValue} + u.GetStarredURL() + u = &User{} + u.GetStarredURL() + u = nil + u.GetStarredURL() +} + +func TestUser_GetSubscriptionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{SubscriptionsURL: &zeroValue} + u.GetSubscriptionsURL() + u = &User{} + u.GetSubscriptionsURL() + u = nil + u.GetSubscriptionsURL() +} + +func TestUser_GetSuspendedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &User{SuspendedAt: &zeroValue} + u.GetSuspendedAt() + u = &User{} + u.GetSuspendedAt() + u = nil + u.GetSuspendedAt() +} + +func TestUser_GetTextMatches(tt *testing.T) { + tt.Parallel() + zeroValue := []*TextMatch{} + u := &User{TextMatches: zeroValue} + u.GetTextMatches() + u = &User{} + u.GetTextMatches() + u = nil + u.GetTextMatches() +} + +func TestUser_GetTotalPrivateRepos(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &User{TotalPrivateRepos: &zeroValue} + u.GetTotalPrivateRepos() + u = &User{} + u.GetTotalPrivateRepos() + u = nil + u.GetTotalPrivateRepos() +} + +func TestUser_GetTwitterUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{TwitterUsername: &zeroValue} + u.GetTwitterUsername() + u = &User{} + u.GetTwitterUsername() + u = nil + u.GetTwitterUsername() +} + +func TestUser_GetTwoFactorAuthentication(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &User{TwoFactorAuthentication: &zeroValue} + u.GetTwoFactorAuthentication() + u = &User{} + u.GetTwoFactorAuthentication() + u = nil + u.GetTwoFactorAuthentication() +} + +func TestUser_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{Type: &zeroValue} + u.GetType() + u = &User{} + u.GetType() + u = nil + u.GetType() +} + +func TestUser_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &User{UpdatedAt: &zeroValue} + u.GetUpdatedAt() + u = &User{} + u.GetUpdatedAt() + u = nil + u.GetUpdatedAt() +} + +func TestUser_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{URL: &zeroValue} + u.GetURL() + u = &User{} + u.GetURL() + u = nil + u.GetURL() +} + +func TestUser_GetUserViewType(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &User{UserViewType: &zeroValue} + u.GetUserViewType() + u = &User{} + u.GetUserViewType() + u = nil + u.GetUserViewType() +} + +func TestUserAuthorization_GetApp(tt *testing.T) { + tt.Parallel() + u := &UserAuthorization{} + u.GetApp() + u = nil + u.GetApp() +} + +func TestUserAuthorization_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &UserAuthorization{CreatedAt: &zeroValue} + u.GetCreatedAt() + u = &UserAuthorization{} + u.GetCreatedAt() + u = nil + u.GetCreatedAt() +} + +func TestUserAuthorization_GetFingerprint(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{Fingerprint: &zeroValue} + u.GetFingerprint() + u = &UserAuthorization{} + u.GetFingerprint() + u = nil + u.GetFingerprint() +} + +func TestUserAuthorization_GetHashedToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{HashedToken: &zeroValue} + u.GetHashedToken() + u = &UserAuthorization{} + u.GetHashedToken() + u = nil + u.GetHashedToken() +} + +func TestUserAuthorization_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UserAuthorization{ID: &zeroValue} + u.GetID() + u = &UserAuthorization{} + u.GetID() + u = nil + u.GetID() +} + +func TestUserAuthorization_GetNote(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{Note: &zeroValue} + u.GetNote() + u = &UserAuthorization{} + u.GetNote() + u = nil + u.GetNote() +} + +func TestUserAuthorization_GetNoteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{NoteURL: &zeroValue} + u.GetNoteURL() + u = &UserAuthorization{} + u.GetNoteURL() + u = nil + u.GetNoteURL() +} + +func TestUserAuthorization_GetScopes(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + u := &UserAuthorization{Scopes: zeroValue} + u.GetScopes() + u = &UserAuthorization{} + u.GetScopes() + u = nil + u.GetScopes() +} + +func TestUserAuthorization_GetToken(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{Token: &zeroValue} + u.GetToken() + u = &UserAuthorization{} + u.GetToken() + u = nil + u.GetToken() +} + +func TestUserAuthorization_GetTokenLastEight(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{TokenLastEight: &zeroValue} + u.GetTokenLastEight() + u = &UserAuthorization{} + u.GetTokenLastEight() + u = nil + u.GetTokenLastEight() +} + +func TestUserAuthorization_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + u := &UserAuthorization{UpdatedAt: &zeroValue} + u.GetUpdatedAt() + u = &UserAuthorization{} + u.GetUpdatedAt() + u = nil + u.GetUpdatedAt() +} + +func TestUserAuthorization_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserAuthorization{URL: &zeroValue} + u.GetURL() + u = &UserAuthorization{} + u.GetURL() + u = nil + u.GetURL() +} + +func TestUserContext_GetMessage(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserContext{Message: &zeroValue} + u.GetMessage() + u = &UserContext{} + u.GetMessage() + u = nil + u.GetMessage() +} + +func TestUserContext_GetOcticon(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserContext{Octicon: &zeroValue} + u.GetOcticon() + u = &UserContext{} + u.GetOcticon() + u = nil + u.GetOcticon() +} + +func TestUserEmail_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserEmail{Email: &zeroValue} + u.GetEmail() + u = &UserEmail{} + u.GetEmail() + u = nil + u.GetEmail() +} + +func TestUserEmail_GetPrimary(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UserEmail{Primary: &zeroValue} + u.GetPrimary() + u = &UserEmail{} + u.GetPrimary() + u = nil + u.GetPrimary() +} + +func TestUserEmail_GetVerified(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UserEmail{Verified: &zeroValue} + u.GetVerified() + u = &UserEmail{} + u.GetVerified() + u = nil + u.GetVerified() +} + +func TestUserEmail_GetVisibility(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserEmail{Visibility: &zeroValue} + u.GetVisibility() + u = &UserEmail{} + u.GetVisibility() + u = nil + u.GetVisibility() +} + +func TestUserEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserEvent{Action: &zeroValue} + u.GetAction() + u = &UserEvent{} + u.GetAction() + u = nil + u.GetAction() +} + +func TestUserEvent_GetEnterprise(tt *testing.T) { + tt.Parallel() + u := &UserEvent{} + u.GetEnterprise() + u = nil + u.GetEnterprise() +} + +func TestUserEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + u := &UserEvent{} + u.GetInstallation() + u = nil + u.GetInstallation() +} + +func TestUserEvent_GetSender(tt *testing.T) { + tt.Parallel() + u := &UserEvent{} + u.GetSender() + u = nil + u.GetSender() +} + +func TestUserEvent_GetUser(tt *testing.T) { + tt.Parallel() + u := &UserEvent{} + u.GetUser() + u = nil + u.GetUser() +} + +func TestUserLDAPMapping_GetAvatarURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{AvatarURL: &zeroValue} + u.GetAvatarURL() + u = &UserLDAPMapping{} + u.GetAvatarURL() + u = nil + u.GetAvatarURL() +} + +func TestUserLDAPMapping_GetEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{EventsURL: &zeroValue} + u.GetEventsURL() + u = &UserLDAPMapping{} + u.GetEventsURL() + u = nil + u.GetEventsURL() +} + +func TestUserLDAPMapping_GetFollowersURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{FollowersURL: &zeroValue} + u.GetFollowersURL() + u = &UserLDAPMapping{} + u.GetFollowersURL() + u = nil + u.GetFollowersURL() +} + +func TestUserLDAPMapping_GetFollowingURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{FollowingURL: &zeroValue} + u.GetFollowingURL() + u = &UserLDAPMapping{} + u.GetFollowingURL() + u = nil + u.GetFollowingURL() +} + +func TestUserLDAPMapping_GetGistsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{GistsURL: &zeroValue} + u.GetGistsURL() + u = &UserLDAPMapping{} + u.GetGistsURL() + u = nil + u.GetGistsURL() +} + +func TestUserLDAPMapping_GetGravatarID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{GravatarID: &zeroValue} + u.GetGravatarID() + u = &UserLDAPMapping{} + u.GetGravatarID() + u = nil + u.GetGravatarID() +} + +func TestUserLDAPMapping_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UserLDAPMapping{ID: &zeroValue} + u.GetID() + u = &UserLDAPMapping{} + u.GetID() + u = nil + u.GetID() +} + +func TestUserLDAPMapping_GetLDAPDN(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{LDAPDN: &zeroValue} + u.GetLDAPDN() + u = &UserLDAPMapping{} + u.GetLDAPDN() + u = nil + u.GetLDAPDN() +} + +func TestUserLDAPMapping_GetLogin(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{Login: &zeroValue} + u.GetLogin() + u = &UserLDAPMapping{} + u.GetLogin() + u = nil + u.GetLogin() +} + +func TestUserLDAPMapping_GetOrganizationsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{OrganizationsURL: &zeroValue} + u.GetOrganizationsURL() + u = &UserLDAPMapping{} + u.GetOrganizationsURL() + u = nil + u.GetOrganizationsURL() +} + +func TestUserLDAPMapping_GetReceivedEventsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{ReceivedEventsURL: &zeroValue} + u.GetReceivedEventsURL() + u = &UserLDAPMapping{} + u.GetReceivedEventsURL() + u = nil + u.GetReceivedEventsURL() +} + +func TestUserLDAPMapping_GetReposURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{ReposURL: &zeroValue} + u.GetReposURL() + u = &UserLDAPMapping{} + u.GetReposURL() + u = nil + u.GetReposURL() +} + +func TestUserLDAPMapping_GetSiteAdmin(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UserLDAPMapping{SiteAdmin: &zeroValue} + u.GetSiteAdmin() + u = &UserLDAPMapping{} + u.GetSiteAdmin() + u = nil + u.GetSiteAdmin() +} + +func TestUserLDAPMapping_GetStarredURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{StarredURL: &zeroValue} + u.GetStarredURL() + u = &UserLDAPMapping{} + u.GetStarredURL() + u = nil + u.GetStarredURL() +} + +func TestUserLDAPMapping_GetSubscriptionsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{SubscriptionsURL: &zeroValue} + u.GetSubscriptionsURL() + u = &UserLDAPMapping{} + u.GetSubscriptionsURL() + u = nil + u.GetSubscriptionsURL() +} + +func TestUserLDAPMapping_GetType(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{Type: &zeroValue} + u.GetType() + u = &UserLDAPMapping{} + u.GetType() + u = nil + u.GetType() +} + +func TestUserLDAPMapping_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserLDAPMapping{URL: &zeroValue} + u.GetURL() + u = &UserLDAPMapping{} + u.GetURL() + u = nil + u.GetURL() +} + +func TestUserListOptions_GetPerPage(tt *testing.T) { + tt.Parallel() + u := &UserListOptions{} + u.GetPerPage() + u = nil + u.GetPerPage() +} + +func TestUserListOptions_GetSince(tt *testing.T) { + tt.Parallel() + u := &UserListOptions{} + u.GetSince() + u = nil + u.GetSince() +} + +func TestUserMigration_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserMigration{CreatedAt: &zeroValue} + u.GetCreatedAt() + u = &UserMigration{} + u.GetCreatedAt() + u = nil + u.GetCreatedAt() +} + +func TestUserMigration_GetExcludeAttachments(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UserMigration{ExcludeAttachments: &zeroValue} + u.GetExcludeAttachments() + u = &UserMigration{} + u.GetExcludeAttachments() + u = nil + u.GetExcludeAttachments() +} + +func TestUserMigration_GetGUID(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserMigration{GUID: &zeroValue} + u.GetGUID() + u = &UserMigration{} + u.GetGUID() + u = nil + u.GetGUID() +} + +func TestUserMigration_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + u := &UserMigration{ID: &zeroValue} + u.GetID() + u = &UserMigration{} + u.GetID() + u = nil + u.GetID() +} + +func TestUserMigration_GetLockRepositories(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UserMigration{LockRepositories: &zeroValue} + u.GetLockRepositories() + u = &UserMigration{} + u.GetLockRepositories() + u = nil + u.GetLockRepositories() +} + +func TestUserMigration_GetRepositories(tt *testing.T) { + tt.Parallel() + zeroValue := []*Repository{} + u := &UserMigration{Repositories: zeroValue} + u.GetRepositories() + u = &UserMigration{} + u.GetRepositories() + u = nil + u.GetRepositories() +} + +func TestUserMigration_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserMigration{State: &zeroValue} + u.GetState() + u = &UserMigration{} + u.GetState() + u = nil + u.GetState() +} + +func TestUserMigration_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserMigration{UpdatedAt: &zeroValue} + u.GetUpdatedAt() + u = &UserMigration{} + u.GetUpdatedAt() + u = nil + u.GetUpdatedAt() +} + +func TestUserMigration_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserMigration{URL: &zeroValue} + u.GetURL() + u = &UserMigration{} + u.GetURL() + u = nil + u.GetURL() +} + +func TestUserMigrationOptions_GetExcludeAttachments(tt *testing.T) { + tt.Parallel() + u := &UserMigrationOptions{} + u.GetExcludeAttachments() + u = nil + u.GetExcludeAttachments() +} + +func TestUserMigrationOptions_GetLockRepositories(tt *testing.T) { + tt.Parallel() + u := &UserMigrationOptions{} + u.GetLockRepositories() + u = nil + u.GetLockRepositories() +} + +func TestUsersSearchResult_GetIncompleteResults(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UsersSearchResult{IncompleteResults: &zeroValue} + u.GetIncompleteResults() + u = &UsersSearchResult{} + u.GetIncompleteResults() + u = nil + u.GetIncompleteResults() +} + +func TestUsersSearchResult_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UsersSearchResult{Total: &zeroValue} + u.GetTotal() + u = &UsersSearchResult{} + u.GetTotal() + u = nil + u.GetTotal() +} + +func TestUsersSearchResult_GetUsers(tt *testing.T) { + tt.Parallel() + zeroValue := []*User{} + u := &UsersSearchResult{Users: zeroValue} + u.GetUsers() + u = &UsersSearchResult{} + u.GetUsers() + u = nil + u.GetUsers() +} + +func TestUserStats_GetAdminUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UserStats{AdminUsers: &zeroValue} + u.GetAdminUsers() + u = &UserStats{} + u.GetAdminUsers() + u = nil + u.GetAdminUsers() +} + +func TestUserStats_GetSuspendedUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UserStats{SuspendedUsers: &zeroValue} + u.GetSuspendedUsers() + u = &UserStats{} + u.GetSuspendedUsers() + u = nil + u.GetSuspendedUsers() +} + +func TestUserStats_GetTotalUsers(tt *testing.T) { + tt.Parallel() + var zeroValue int + u := &UserStats{TotalUsers: &zeroValue} + u.GetTotalUsers() + u = &UserStats{} + u.GetTotalUsers() + u = nil + u.GetTotalUsers() +} + +func TestUserSuspendOptions_GetReason(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserSuspendOptions{Reason: &zeroValue} + u.GetReason() + u = &UserSuspendOptions{} + u.GetReason() + u = nil + u.GetReason() +} + +func TestUserUpdateRequest_GetBio(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{Bio: &zeroValue} + u.GetBio() + u = &UserUpdateRequest{} + u.GetBio() + u = nil + u.GetBio() +} + +func TestUserUpdateRequest_GetBlog(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{Blog: &zeroValue} + u.GetBlog() + u = &UserUpdateRequest{} + u.GetBlog() + u = nil + u.GetBlog() +} + +func TestUserUpdateRequest_GetCompany(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{Company: &zeroValue} + u.GetCompany() + u = &UserUpdateRequest{} + u.GetCompany() + u = nil + u.GetCompany() +} + +func TestUserUpdateRequest_GetEmail(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{Email: &zeroValue} + u.GetEmail() + u = &UserUpdateRequest{} + u.GetEmail() + u = nil + u.GetEmail() +} + +func TestUserUpdateRequest_GetHireable(tt *testing.T) { + tt.Parallel() + var zeroValue bool + u := &UserUpdateRequest{Hireable: &zeroValue} + u.GetHireable() + u = &UserUpdateRequest{} + u.GetHireable() + u = nil + u.GetHireable() +} + +func TestUserUpdateRequest_GetLocation(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{Location: &zeroValue} + u.GetLocation() + u = &UserUpdateRequest{} + u.GetLocation() + u = nil + u.GetLocation() +} + +func TestUserUpdateRequest_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{Name: &zeroValue} + u.GetName() + u = &UserUpdateRequest{} + u.GetName() + u = nil + u.GetName() +} + +func TestUserUpdateRequest_GetTwitterUsername(tt *testing.T) { + tt.Parallel() + var zeroValue string + u := &UserUpdateRequest{TwitterUsername: &zeroValue} + u.GetTwitterUsername() + u = &UserUpdateRequest{} + u.GetTwitterUsername() + u = nil + u.GetTwitterUsername() +} + +func TestVulnerabilityPackage_GetEcosystem(tt *testing.T) { + tt.Parallel() + var zeroValue string + v := &VulnerabilityPackage{Ecosystem: &zeroValue} + v.GetEcosystem() + v = &VulnerabilityPackage{} + v.GetEcosystem() + v = nil + v.GetEcosystem() +} + +func TestVulnerabilityPackage_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + v := &VulnerabilityPackage{Name: &zeroValue} + v.GetName() + v = &VulnerabilityPackage{} + v.GetName() + v = nil + v.GetName() +} + +func TestWatchEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WatchEvent{Action: &zeroValue} + w.GetAction() + w = &WatchEvent{} + w.GetAction() + w = nil + w.GetAction() +} + +func TestWatchEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + w := &WatchEvent{} + w.GetInstallation() + w = nil + w.GetInstallation() +} + +func TestWatchEvent_GetOrg(tt *testing.T) { + tt.Parallel() + w := &WatchEvent{} + w.GetOrg() + w = nil + w.GetOrg() +} + +func TestWatchEvent_GetRepo(tt *testing.T) { + tt.Parallel() + w := &WatchEvent{} + w.GetRepo() + w = nil + w.GetRepo() +} + +func TestWatchEvent_GetSender(tt *testing.T) { + tt.Parallel() + w := &WatchEvent{} + w.GetSender() + w = nil + w.GetSender() +} + +func TestWeeklyCommitActivity_GetDays(tt *testing.T) { + tt.Parallel() + zeroValue := []int{} + w := &WeeklyCommitActivity{Days: zeroValue} + w.GetDays() + w = &WeeklyCommitActivity{} + w.GetDays() + w = nil + w.GetDays() +} + +func TestWeeklyCommitActivity_GetTotal(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WeeklyCommitActivity{Total: &zeroValue} + w.GetTotal() + w = &WeeklyCommitActivity{} + w.GetTotal() + w = nil + w.GetTotal() +} + +func TestWeeklyCommitActivity_GetWeek(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WeeklyCommitActivity{Week: &zeroValue} + w.GetWeek() + w = &WeeklyCommitActivity{} + w.GetWeek() + w = nil + w.GetWeek() +} + +func TestWeeklyStats_GetAdditions(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WeeklyStats{Additions: &zeroValue} + w.GetAdditions() + w = &WeeklyStats{} + w.GetAdditions() + w = nil + w.GetAdditions() +} + +func TestWeeklyStats_GetCommits(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WeeklyStats{Commits: &zeroValue} + w.GetCommits() + w = &WeeklyStats{} + w.GetCommits() + w = nil + w.GetCommits() +} + +func TestWeeklyStats_GetDeletions(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WeeklyStats{Deletions: &zeroValue} + w.GetDeletions() + w = &WeeklyStats{} + w.GetDeletions() + w = nil + w.GetDeletions() +} + +func TestWeeklyStats_GetWeek(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WeeklyStats{Week: &zeroValue} + w.GetWeek() + w = &WeeklyStats{} + w.GetWeek() + w = nil + w.GetWeek() +} + +func TestWorkflow_GetBadgeURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{BadgeURL: &zeroValue} + w.GetBadgeURL() + w = &Workflow{} + w.GetBadgeURL() + w = nil + w.GetBadgeURL() +} + +func TestWorkflow_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &Workflow{CreatedAt: &zeroValue} + w.GetCreatedAt() + w = &Workflow{} + w.GetCreatedAt() + w = nil + w.GetCreatedAt() +} + +func TestWorkflow_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{HTMLURL: &zeroValue} + w.GetHTMLURL() + w = &Workflow{} + w.GetHTMLURL() + w = nil + w.GetHTMLURL() +} + +func TestWorkflow_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &Workflow{ID: &zeroValue} + w.GetID() + w = &Workflow{} + w.GetID() + w = nil + w.GetID() +} + +func TestWorkflow_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{Name: &zeroValue} + w.GetName() + w = &Workflow{} + w.GetName() + w = nil + w.GetName() +} + +func TestWorkflow_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{NodeID: &zeroValue} + w.GetNodeID() + w = &Workflow{} + w.GetNodeID() + w = nil + w.GetNodeID() +} + +func TestWorkflow_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{Path: &zeroValue} + w.GetPath() + w = &Workflow{} + w.GetPath() + w = nil + w.GetPath() +} + +func TestWorkflow_GetState(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{State: &zeroValue} + w.GetState() + w = &Workflow{} + w.GetState() + w = nil + w.GetState() +} + +func TestWorkflow_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &Workflow{UpdatedAt: &zeroValue} + w.GetUpdatedAt() + w = &Workflow{} + w.GetUpdatedAt() + w = nil + w.GetUpdatedAt() +} + +func TestWorkflow_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &Workflow{URL: &zeroValue} + w.GetURL() + w = &Workflow{} + w.GetURL() + w = nil + w.GetURL() +} + +func TestWorkflowBill_GetTotalMS(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowBill{TotalMS: &zeroValue} + w.GetTotalMS() + w = &WorkflowBill{} + w.GetTotalMS() + w = nil + w.GetTotalMS() +} + +func TestWorkflowDispatchEvent_GetInputs(tt *testing.T) { + tt.Parallel() + w := &WorkflowDispatchEvent{} + w.GetInputs() + w = nil + w.GetInputs() +} + +func TestWorkflowDispatchEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + w := &WorkflowDispatchEvent{} + w.GetInstallation() + w = nil + w.GetInstallation() +} + +func TestWorkflowDispatchEvent_GetOrg(tt *testing.T) { + tt.Parallel() + w := &WorkflowDispatchEvent{} + w.GetOrg() + w = nil + w.GetOrg() +} + +func TestWorkflowDispatchEvent_GetRef(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowDispatchEvent{Ref: &zeroValue} + w.GetRef() + w = &WorkflowDispatchEvent{} + w.GetRef() + w = nil + w.GetRef() +} + +func TestWorkflowDispatchEvent_GetRepo(tt *testing.T) { + tt.Parallel() + w := &WorkflowDispatchEvent{} + w.GetRepo() + w = nil + w.GetRepo() +} + +func TestWorkflowDispatchEvent_GetSender(tt *testing.T) { + tt.Parallel() + w := &WorkflowDispatchEvent{} + w.GetSender() + w = nil + w.GetSender() +} + +func TestWorkflowDispatchEvent_GetWorkflow(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowDispatchEvent{Workflow: &zeroValue} + w.GetWorkflow() + w = &WorkflowDispatchEvent{} + w.GetWorkflow() + w = nil + w.GetWorkflow() +} + +func TestWorkflowDispatchRunDetails_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowDispatchRunDetails{HTMLURL: &zeroValue} + w.GetHTMLURL() + w = &WorkflowDispatchRunDetails{} + w.GetHTMLURL() + w = nil + w.GetHTMLURL() +} + +func TestWorkflowDispatchRunDetails_GetRunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowDispatchRunDetails{RunURL: &zeroValue} + w.GetRunURL() + w = &WorkflowDispatchRunDetails{} + w.GetRunURL() + w = nil + w.GetRunURL() +} + +func TestWorkflowDispatchRunDetails_GetWorkflowRunID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowDispatchRunDetails{WorkflowRunID: &zeroValue} + w.GetWorkflowRunID() + w = &WorkflowDispatchRunDetails{} + w.GetWorkflowRunID() + w = nil + w.GetWorkflowRunID() +} + +func TestWorkflowJob_GetCheckRunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{CheckRunURL: &zeroValue} + w.GetCheckRunURL() + w = &WorkflowJob{} + w.GetCheckRunURL() + w = nil + w.GetCheckRunURL() +} + +func TestWorkflowJob_GetCompletedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowJob{CompletedAt: &zeroValue} + w.GetCompletedAt() + w = &WorkflowJob{} + w.GetCompletedAt() + w = nil + w.GetCompletedAt() +} + +func TestWorkflowJob_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{Conclusion: &zeroValue} + w.GetConclusion() + w = &WorkflowJob{} + w.GetConclusion() + w = nil + w.GetConclusion() +} + +func TestWorkflowJob_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowJob{CreatedAt: &zeroValue} + w.GetCreatedAt() + w = &WorkflowJob{} + w.GetCreatedAt() + w = nil + w.GetCreatedAt() +} + +func TestWorkflowJob_GetHeadBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{HeadBranch: &zeroValue} + w.GetHeadBranch() + w = &WorkflowJob{} + w.GetHeadBranch() + w = nil + w.GetHeadBranch() +} + +func TestWorkflowJob_GetHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{HeadSHA: &zeroValue} + w.GetHeadSHA() + w = &WorkflowJob{} + w.GetHeadSHA() + w = nil + w.GetHeadSHA() +} + +func TestWorkflowJob_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{HTMLURL: &zeroValue} + w.GetHTMLURL() + w = &WorkflowJob{} + w.GetHTMLURL() + w = nil + w.GetHTMLURL() +} + +func TestWorkflowJob_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowJob{ID: &zeroValue} + w.GetID() + w = &WorkflowJob{} + w.GetID() + w = nil + w.GetID() +} + +func TestWorkflowJob_GetLabels(tt *testing.T) { + tt.Parallel() + zeroValue := []string{} + w := &WorkflowJob{Labels: zeroValue} + w.GetLabels() + w = &WorkflowJob{} + w.GetLabels() + w = nil + w.GetLabels() +} + +func TestWorkflowJob_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{Name: &zeroValue} + w.GetName() + w = &WorkflowJob{} + w.GetName() + w = nil + w.GetName() +} + +func TestWorkflowJob_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{NodeID: &zeroValue} + w.GetNodeID() + w = &WorkflowJob{} + w.GetNodeID() + w = nil + w.GetNodeID() +} + +func TestWorkflowJob_GetRunAttempt(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowJob{RunAttempt: &zeroValue} + w.GetRunAttempt() + w = &WorkflowJob{} + w.GetRunAttempt() + w = nil + w.GetRunAttempt() +} + +func TestWorkflowJob_GetRunID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowJob{RunID: &zeroValue} + w.GetRunID() + w = &WorkflowJob{} + w.GetRunID() + w = nil + w.GetRunID() +} + +func TestWorkflowJob_GetRunnerGroupID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowJob{RunnerGroupID: &zeroValue} + w.GetRunnerGroupID() + w = &WorkflowJob{} + w.GetRunnerGroupID() + w = nil + w.GetRunnerGroupID() +} + +func TestWorkflowJob_GetRunnerGroupName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{RunnerGroupName: &zeroValue} + w.GetRunnerGroupName() + w = &WorkflowJob{} + w.GetRunnerGroupName() + w = nil + w.GetRunnerGroupName() +} + +func TestWorkflowJob_GetRunnerID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowJob{RunnerID: &zeroValue} + w.GetRunnerID() + w = &WorkflowJob{} + w.GetRunnerID() + w = nil + w.GetRunnerID() +} + +func TestWorkflowJob_GetRunnerName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{RunnerName: &zeroValue} + w.GetRunnerName() + w = &WorkflowJob{} + w.GetRunnerName() + w = nil + w.GetRunnerName() +} + +func TestWorkflowJob_GetRunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{RunURL: &zeroValue} + w.GetRunURL() + w = &WorkflowJob{} + w.GetRunURL() + w = nil + w.GetRunURL() +} + +func TestWorkflowJob_GetStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowJob{StartedAt: &zeroValue} + w.GetStartedAt() + w = &WorkflowJob{} + w.GetStartedAt() + w = nil + w.GetStartedAt() +} + +func TestWorkflowJob_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{Status: &zeroValue} + w.GetStatus() + w = &WorkflowJob{} + w.GetStatus() + w = nil + w.GetStatus() +} + +func TestWorkflowJob_GetSteps(tt *testing.T) { + tt.Parallel() + zeroValue := []*TaskStep{} + w := &WorkflowJob{Steps: zeroValue} + w.GetSteps() + w = &WorkflowJob{} + w.GetSteps() + w = nil + w.GetSteps() +} + +func TestWorkflowJob_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{URL: &zeroValue} + w.GetURL() + w = &WorkflowJob{} + w.GetURL() + w = nil + w.GetURL() +} + +func TestWorkflowJob_GetWorkflowName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJob{WorkflowName: &zeroValue} + w.GetWorkflowName() + w = &WorkflowJob{} + w.GetWorkflowName() + w = nil + w.GetWorkflowName() +} + +func TestWorkflowJobEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJobEvent{Action: &zeroValue} + w.GetAction() + w = &WorkflowJobEvent{} + w.GetAction() + w = nil + w.GetAction() +} + +func TestWorkflowJobEvent_GetDeployment(tt *testing.T) { + tt.Parallel() + w := &WorkflowJobEvent{} + w.GetDeployment() + w = nil + w.GetDeployment() +} + +func TestWorkflowJobEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + w := &WorkflowJobEvent{} + w.GetInstallation() + w = nil + w.GetInstallation() +} + +func TestWorkflowJobEvent_GetOrg(tt *testing.T) { + tt.Parallel() + w := &WorkflowJobEvent{} + w.GetOrg() + w = nil + w.GetOrg() +} + +func TestWorkflowJobEvent_GetRepo(tt *testing.T) { + tt.Parallel() + w := &WorkflowJobEvent{} + w.GetRepo() + w = nil + w.GetRepo() +} + +func TestWorkflowJobEvent_GetSender(tt *testing.T) { + tt.Parallel() + w := &WorkflowJobEvent{} + w.GetSender() + w = nil + w.GetSender() +} + +func TestWorkflowJobEvent_GetWorkflowJob(tt *testing.T) { + tt.Parallel() + w := &WorkflowJobEvent{} + w.GetWorkflowJob() + w = nil + w.GetWorkflowJob() +} + +func TestWorkflowJobRun_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJobRun{Conclusion: &zeroValue} + w.GetConclusion() + w = &WorkflowJobRun{} + w.GetConclusion() + w = nil + w.GetConclusion() +} + +func TestWorkflowJobRun_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowJobRun{CreatedAt: &zeroValue} + w.GetCreatedAt() + w = &WorkflowJobRun{} + w.GetCreatedAt() + w = nil + w.GetCreatedAt() +} + +func TestWorkflowJobRun_GetEnvironment(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJobRun{Environment: &zeroValue} + w.GetEnvironment() + w = &WorkflowJobRun{} + w.GetEnvironment() + w = nil + w.GetEnvironment() +} + +func TestWorkflowJobRun_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJobRun{HTMLURL: &zeroValue} + w.GetHTMLURL() + w = &WorkflowJobRun{} + w.GetHTMLURL() + w = nil + w.GetHTMLURL() +} + +func TestWorkflowJobRun_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowJobRun{ID: &zeroValue} + w.GetID() + w = &WorkflowJobRun{} + w.GetID() + w = nil + w.GetID() +} + +func TestWorkflowJobRun_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJobRun{Name: &zeroValue} + w.GetName() + w = &WorkflowJobRun{} + w.GetName() + w = nil + w.GetName() +} + +func TestWorkflowJobRun_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowJobRun{Status: &zeroValue} + w.GetStatus() + w = &WorkflowJobRun{} + w.GetStatus() + w = nil + w.GetStatus() +} + +func TestWorkflowJobRun_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowJobRun{UpdatedAt: &zeroValue} + w.GetUpdatedAt() + w = &WorkflowJobRun{} + w.GetUpdatedAt() + w = nil + w.GetUpdatedAt() +} + +func TestWorkflowRun_GetActor(tt *testing.T) { + tt.Parallel() + w := &WorkflowRun{} + w.GetActor() + w = nil + w.GetActor() +} + +func TestWorkflowRun_GetArtifactsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{ArtifactsURL: &zeroValue} + w.GetArtifactsURL() + w = &WorkflowRun{} + w.GetArtifactsURL() + w = nil + w.GetArtifactsURL() +} + +func TestWorkflowRun_GetCancelURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{CancelURL: &zeroValue} + w.GetCancelURL() + w = &WorkflowRun{} + w.GetCancelURL() + w = nil + w.GetCancelURL() +} + +func TestWorkflowRun_GetCheckSuiteID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowRun{CheckSuiteID: &zeroValue} + w.GetCheckSuiteID() + w = &WorkflowRun{} + w.GetCheckSuiteID() + w = nil + w.GetCheckSuiteID() +} + +func TestWorkflowRun_GetCheckSuiteNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{CheckSuiteNodeID: &zeroValue} + w.GetCheckSuiteNodeID() + w = &WorkflowRun{} + w.GetCheckSuiteNodeID() + w = nil + w.GetCheckSuiteNodeID() +} + +func TestWorkflowRun_GetCheckSuiteURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{CheckSuiteURL: &zeroValue} + w.GetCheckSuiteURL() + w = &WorkflowRun{} + w.GetCheckSuiteURL() + w = nil + w.GetCheckSuiteURL() +} + +func TestWorkflowRun_GetConclusion(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{Conclusion: &zeroValue} + w.GetConclusion() + w = &WorkflowRun{} + w.GetConclusion() + w = nil + w.GetConclusion() +} + +func TestWorkflowRun_GetCreatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowRun{CreatedAt: &zeroValue} + w.GetCreatedAt() + w = &WorkflowRun{} + w.GetCreatedAt() + w = nil + w.GetCreatedAt() +} + +func TestWorkflowRun_GetDisplayTitle(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{DisplayTitle: &zeroValue} + w.GetDisplayTitle() + w = &WorkflowRun{} + w.GetDisplayTitle() + w = nil + w.GetDisplayTitle() +} + +func TestWorkflowRun_GetEvent(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{Event: &zeroValue} + w.GetEvent() + w = &WorkflowRun{} + w.GetEvent() + w = nil + w.GetEvent() +} + +func TestWorkflowRun_GetHeadBranch(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{HeadBranch: &zeroValue} + w.GetHeadBranch() + w = &WorkflowRun{} + w.GetHeadBranch() + w = nil + w.GetHeadBranch() +} + +func TestWorkflowRun_GetHeadCommit(tt *testing.T) { + tt.Parallel() + w := &WorkflowRun{} + w.GetHeadCommit() + w = nil + w.GetHeadCommit() +} + +func TestWorkflowRun_GetHeadRepository(tt *testing.T) { + tt.Parallel() + w := &WorkflowRun{} + w.GetHeadRepository() + w = nil + w.GetHeadRepository() +} + +func TestWorkflowRun_GetHeadSHA(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{HeadSHA: &zeroValue} + w.GetHeadSHA() + w = &WorkflowRun{} + w.GetHeadSHA() + w = nil + w.GetHeadSHA() +} + +func TestWorkflowRun_GetHTMLURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{HTMLURL: &zeroValue} + w.GetHTMLURL() + w = &WorkflowRun{} + w.GetHTMLURL() + w = nil + w.GetHTMLURL() +} + +func TestWorkflowRun_GetID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowRun{ID: &zeroValue} + w.GetID() + w = &WorkflowRun{} + w.GetID() + w = nil + w.GetID() +} + +func TestWorkflowRun_GetJobsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{JobsURL: &zeroValue} + w.GetJobsURL() + w = &WorkflowRun{} + w.GetJobsURL() + w = nil + w.GetJobsURL() +} + +func TestWorkflowRun_GetLogsURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{LogsURL: &zeroValue} + w.GetLogsURL() + w = &WorkflowRun{} + w.GetLogsURL() + w = nil + w.GetLogsURL() +} + +func TestWorkflowRun_GetName(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{Name: &zeroValue} + w.GetName() + w = &WorkflowRun{} + w.GetName() + w = nil + w.GetName() +} + +func TestWorkflowRun_GetNodeID(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{NodeID: &zeroValue} + w.GetNodeID() + w = &WorkflowRun{} + w.GetNodeID() + w = nil + w.GetNodeID() +} + +func TestWorkflowRun_GetPath(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{Path: &zeroValue} + w.GetPath() + w = &WorkflowRun{} + w.GetPath() + w = nil + w.GetPath() +} + +func TestWorkflowRun_GetPreviousAttemptURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{PreviousAttemptURL: &zeroValue} + w.GetPreviousAttemptURL() + w = &WorkflowRun{} + w.GetPreviousAttemptURL() + w = nil + w.GetPreviousAttemptURL() +} + +func TestWorkflowRun_GetPullRequests(tt *testing.T) { + tt.Parallel() + zeroValue := []*PullRequest{} + w := &WorkflowRun{PullRequests: zeroValue} + w.GetPullRequests() + w = &WorkflowRun{} + w.GetPullRequests() + w = nil + w.GetPullRequests() +} + +func TestWorkflowRun_GetReferencedWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []*ReferencedWorkflow{} + w := &WorkflowRun{ReferencedWorkflows: zeroValue} + w.GetReferencedWorkflows() + w = &WorkflowRun{} + w.GetReferencedWorkflows() + w = nil + w.GetReferencedWorkflows() +} + +func TestWorkflowRun_GetRepository(tt *testing.T) { + tt.Parallel() + w := &WorkflowRun{} + w.GetRepository() + w = nil + w.GetRepository() +} + +func TestWorkflowRun_GetRerunURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{RerunURL: &zeroValue} + w.GetRerunURL() + w = &WorkflowRun{} + w.GetRerunURL() + w = nil + w.GetRerunURL() +} + +func TestWorkflowRun_GetRunAttempt(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WorkflowRun{RunAttempt: &zeroValue} + w.GetRunAttempt() + w = &WorkflowRun{} + w.GetRunAttempt() + w = nil + w.GetRunAttempt() +} + +func TestWorkflowRun_GetRunNumber(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WorkflowRun{RunNumber: &zeroValue} + w.GetRunNumber() + w = &WorkflowRun{} + w.GetRunNumber() + w = nil + w.GetRunNumber() +} + +func TestWorkflowRun_GetRunStartedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowRun{RunStartedAt: &zeroValue} + w.GetRunStartedAt() + w = &WorkflowRun{} + w.GetRunStartedAt() + w = nil + w.GetRunStartedAt() +} + +func TestWorkflowRun_GetStatus(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{Status: &zeroValue} + w.GetStatus() + w = &WorkflowRun{} + w.GetStatus() + w = nil + w.GetStatus() +} + +func TestWorkflowRun_GetTriggeringActor(tt *testing.T) { + tt.Parallel() + w := &WorkflowRun{} + w.GetTriggeringActor() + w = nil + w.GetTriggeringActor() +} + +func TestWorkflowRun_GetUpdatedAt(tt *testing.T) { + tt.Parallel() + var zeroValue Timestamp + w := &WorkflowRun{UpdatedAt: &zeroValue} + w.GetUpdatedAt() + w = &WorkflowRun{} + w.GetUpdatedAt() + w = nil + w.GetUpdatedAt() +} + +func TestWorkflowRun_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{URL: &zeroValue} + w.GetURL() + w = &WorkflowRun{} + w.GetURL() + w = nil + w.GetURL() +} + +func TestWorkflowRun_GetWorkflowID(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowRun{WorkflowID: &zeroValue} + w.GetWorkflowID() + w = &WorkflowRun{} + w.GetWorkflowID() + w = nil + w.GetWorkflowID() +} + +func TestWorkflowRun_GetWorkflowURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRun{WorkflowURL: &zeroValue} + w.GetWorkflowURL() + w = &WorkflowRun{} + w.GetWorkflowURL() + w = nil + w.GetWorkflowURL() +} + +func TestWorkflowRunAttemptOptions_GetExcludePullRequests(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowRunAttemptOptions{ExcludePullRequests: &zeroValue} + w.GetExcludePullRequests() + w = &WorkflowRunAttemptOptions{} + w.GetExcludePullRequests() + w = nil + w.GetExcludePullRequests() +} + +func TestWorkflowRunBill_GetJobRuns(tt *testing.T) { + tt.Parallel() + zeroValue := []*WorkflowRunJobRun{} + w := &WorkflowRunBill{JobRuns: zeroValue} + w.GetJobRuns() + w = &WorkflowRunBill{} + w.GetJobRuns() + w = nil + w.GetJobRuns() +} + +func TestWorkflowRunBill_GetJobs(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WorkflowRunBill{Jobs: &zeroValue} + w.GetJobs() + w = &WorkflowRunBill{} + w.GetJobs() + w = nil + w.GetJobs() +} + +func TestWorkflowRunBill_GetTotalMS(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowRunBill{TotalMS: &zeroValue} + w.GetTotalMS() + w = &WorkflowRunBill{} + w.GetTotalMS() + w = nil + w.GetTotalMS() +} + +func TestWorkflowRunEvent_GetAction(tt *testing.T) { + tt.Parallel() + var zeroValue string + w := &WorkflowRunEvent{Action: &zeroValue} + w.GetAction() + w = &WorkflowRunEvent{} + w.GetAction() + w = nil + w.GetAction() +} + +func TestWorkflowRunEvent_GetInstallation(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunEvent{} + w.GetInstallation() + w = nil + w.GetInstallation() +} + +func TestWorkflowRunEvent_GetOrg(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunEvent{} + w.GetOrg() + w = nil + w.GetOrg() +} + +func TestWorkflowRunEvent_GetRepo(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunEvent{} + w.GetRepo() + w = nil + w.GetRepo() +} + +func TestWorkflowRunEvent_GetSender(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunEvent{} + w.GetSender() + w = nil + w.GetSender() +} + +func TestWorkflowRunEvent_GetWorkflow(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunEvent{} + w.GetWorkflow() + w = nil + w.GetWorkflow() +} + +func TestWorkflowRunEvent_GetWorkflowRun(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunEvent{} + w.GetWorkflowRun() + w = nil + w.GetWorkflowRun() +} + +func TestWorkflowRunJobRun_GetDurationMS(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowRunJobRun{DurationMS: &zeroValue} + w.GetDurationMS() + w = &WorkflowRunJobRun{} + w.GetDurationMS() + w = nil + w.GetDurationMS() +} + +func TestWorkflowRunJobRun_GetJobID(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WorkflowRunJobRun{JobID: &zeroValue} + w.GetJobID() + w = &WorkflowRunJobRun{} + w.GetJobID() + w = nil + w.GetJobID() +} + +func TestWorkflowRuns_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &WorkflowRuns{TotalCount: &zeroValue} + w.GetTotalCount() + w = &WorkflowRuns{} + w.GetTotalCount() + w = nil + w.GetTotalCount() +} + +func TestWorkflowRuns_GetWorkflowRuns(tt *testing.T) { + tt.Parallel() + zeroValue := []*WorkflowRun{} + w := &WorkflowRuns{WorkflowRuns: zeroValue} + w.GetWorkflowRuns() + w = &WorkflowRuns{} + w.GetWorkflowRuns() + w = nil + w.GetWorkflowRuns() +} + +func TestWorkflowRunUsage_GetBillable(tt *testing.T) { + tt.Parallel() + w := &WorkflowRunUsage{} + w.GetBillable() + w = nil + w.GetBillable() +} + +func TestWorkflowRunUsage_GetRunDurationMS(tt *testing.T) { + tt.Parallel() + var zeroValue int64 + w := &WorkflowRunUsage{RunDurationMS: &zeroValue} + w.GetRunDurationMS() + w = &WorkflowRunUsage{} + w.GetRunDurationMS() + w = nil + w.GetRunDurationMS() +} + +func TestWorkflows_GetTotalCount(tt *testing.T) { + tt.Parallel() + var zeroValue int + w := &Workflows{TotalCount: &zeroValue} + w.GetTotalCount() + w = &Workflows{} + w.GetTotalCount() + w = nil + w.GetTotalCount() +} + +func TestWorkflows_GetWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []*Workflow{} + w := &Workflows{Workflows: zeroValue} + w.GetWorkflows() + w = &Workflows{} + w.GetWorkflows() + w = nil + w.GetWorkflows() +} + +func TestWorkflowsBranchRule_GetParameters(tt *testing.T) { + tt.Parallel() + w := &WorkflowsBranchRule{} + w.GetParameters() + w = nil + w.GetParameters() +} + +func TestWorkflowsPermissions_GetRequireApprovalForForkPRWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissions{RequireApprovalForForkPRWorkflows: &zeroValue} + w.GetRequireApprovalForForkPRWorkflows() + w = &WorkflowsPermissions{} + w.GetRequireApprovalForForkPRWorkflows() + w = nil + w.GetRequireApprovalForForkPRWorkflows() +} + +func TestWorkflowsPermissions_GetRunWorkflowsFromForkPullRequests(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissions{RunWorkflowsFromForkPullRequests: &zeroValue} + w.GetRunWorkflowsFromForkPullRequests() + w = &WorkflowsPermissions{} + w.GetRunWorkflowsFromForkPullRequests() + w = nil + w.GetRunWorkflowsFromForkPullRequests() +} + +func TestWorkflowsPermissions_GetSendSecretsAndVariables(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissions{SendSecretsAndVariables: &zeroValue} + w.GetSendSecretsAndVariables() + w = &WorkflowsPermissions{} + w.GetSendSecretsAndVariables() + w = nil + w.GetSendSecretsAndVariables() +} + +func TestWorkflowsPermissions_GetSendWriteTokensToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissions{SendWriteTokensToWorkflows: &zeroValue} + w.GetSendWriteTokensToWorkflows() + w = &WorkflowsPermissions{} + w.GetSendWriteTokensToWorkflows() + w = nil + w.GetSendWriteTokensToWorkflows() +} + +func TestWorkflowsPermissionsOpt_GetRequireApprovalForForkPRWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissionsOpt{RequireApprovalForForkPRWorkflows: &zeroValue} + w.GetRequireApprovalForForkPRWorkflows() + w = &WorkflowsPermissionsOpt{} + w.GetRequireApprovalForForkPRWorkflows() + w = nil + w.GetRequireApprovalForForkPRWorkflows() +} + +func TestWorkflowsPermissionsOpt_GetRunWorkflowsFromForkPullRequests(tt *testing.T) { + tt.Parallel() + w := &WorkflowsPermissionsOpt{} + w.GetRunWorkflowsFromForkPullRequests() + w = nil + w.GetRunWorkflowsFromForkPullRequests() +} + +func TestWorkflowsPermissionsOpt_GetSendSecretsAndVariables(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissionsOpt{SendSecretsAndVariables: &zeroValue} + w.GetSendSecretsAndVariables() + w = &WorkflowsPermissionsOpt{} + w.GetSendSecretsAndVariables() + w = nil + w.GetSendSecretsAndVariables() +} + +func TestWorkflowsPermissionsOpt_GetSendWriteTokensToWorkflows(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsPermissionsOpt{SendWriteTokensToWorkflows: &zeroValue} + w.GetSendWriteTokensToWorkflows() + w = &WorkflowsPermissionsOpt{} + w.GetSendWriteTokensToWorkflows() + w = nil + w.GetSendWriteTokensToWorkflows() +} + +func TestWorkflowsRuleParameters_GetDoNotEnforceOnCreate(tt *testing.T) { + tt.Parallel() + var zeroValue bool + w := &WorkflowsRuleParameters{DoNotEnforceOnCreate: &zeroValue} + w.GetDoNotEnforceOnCreate() + w = &WorkflowsRuleParameters{} + w.GetDoNotEnforceOnCreate() + w = nil + w.GetDoNotEnforceOnCreate() +} + +func TestWorkflowsRuleParameters_GetWorkflows(tt *testing.T) { + tt.Parallel() + zeroValue := []*RuleWorkflow{} + w := &WorkflowsRuleParameters{Workflows: zeroValue} + w.GetWorkflows() + w = &WorkflowsRuleParameters{} + w.GetWorkflows() + w = nil + w.GetWorkflows() +} + +func TestWorkflowUsage_GetBillable(tt *testing.T) { + tt.Parallel() + w := &WorkflowUsage{} + w.GetBillable() + w = nil + w.GetBillable() +} diff --git a/github/github-iterators.go b/github/github-iterators.go new file mode 100644 index 00000000000..05120877ca5 --- /dev/null +++ b/github/github-iterators.go @@ -0,0 +1,7879 @@ +// Code generated by gen-iterators; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "iter" +) + +// ListArtifactsIter returns an iterator that paginates through all results of ListArtifacts. +func (s *ActionsService) ListArtifactsIter(ctx context.Context, owner string, repo string, opts *ListArtifactsOptions) iter.Seq2[*Artifact, error] { + return func(yield func(*Artifact, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListArtifactsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListArtifacts(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Artifact + if results != nil { + iterItems = results.Artifacts + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCacheUsageByRepoForOrgIter returns an iterator that paginates through all results of ListCacheUsageByRepoForOrg. +func (s *ActionsService) ListCacheUsageByRepoForOrgIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*ActionsCacheUsage, error] { + return func(yield func(*ActionsCacheUsage, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCacheUsageByRepoForOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ActionsCacheUsage + if results != nil { + iterItems = results.RepoCacheUsage + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCachesIter returns an iterator that paginates through all results of ListCaches. +func (s *ActionsService) ListCachesIter(ctx context.Context, owner string, repo string, opts *ActionsCacheListOptions) iter.Seq2[*ActionsCache, error] { + return func(yield func(*ActionsCache, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ActionsCacheListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCaches(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ActionsCache + if results != nil { + iterItems = results.ActionsCaches + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListEnabledOrgsInEnterpriseIter returns an iterator that paginates through all results of ListEnabledOrgsInEnterprise. +func (s *ActionsService) ListEnabledOrgsInEnterpriseIter(ctx context.Context, owner string, opts *ListOptions) iter.Seq2[*Organization, error] { + return func(yield func(*Organization, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEnabledOrgsInEnterprise(ctx, owner, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Organization + if results != nil { + iterItems = results.Organizations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEnabledReposInOrgIter returns an iterator that paginates through all results of ListEnabledReposInOrg. +func (s *ActionsService) ListEnabledReposInOrgIter(ctx context.Context, owner string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEnabledReposInOrg(ctx, owner, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEnvSecretsIter returns an iterator that paginates through all results of ListEnvSecrets. +func (s *ActionsService) ListEnvSecretsIter(ctx context.Context, owner string, repo string, env string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEnvSecrets(ctx, owner, repo, env, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEnvVariablesIter returns an iterator that paginates through all results of ListEnvVariables. +func (s *ActionsService) ListEnvVariablesIter(ctx context.Context, owner string, repo string, env string, opts *ListOptions) iter.Seq2[*ActionsVariable, error] { + return func(yield func(*ActionsVariable, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEnvVariables(ctx, owner, repo, env, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ActionsVariable + if results != nil { + iterItems = results.Variables + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListHostedRunnersIter returns an iterator that paginates through all results of ListHostedRunners. +func (s *ActionsService) ListHostedRunnersIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*HostedRunner, error] { + return func(yield func(*HostedRunner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHostedRunners(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*HostedRunner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrgSecretsIter returns an iterator that paginates through all results of ListOrgSecrets. +func (s *ActionsService) ListOrgSecretsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgSecrets(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrgVariablesIter returns an iterator that paginates through all results of ListOrgVariables. +func (s *ActionsService) ListOrgVariablesIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*ActionsVariable, error] { + return func(yield func(*ActionsVariable, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgVariables(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ActionsVariable + if results != nil { + iterItems = results.Variables + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrganizationRunnerGroupsIter returns an iterator that paginates through all results of ListOrganizationRunnerGroups. +func (s *ActionsService) ListOrganizationRunnerGroupsIter(ctx context.Context, org string, opts *ListOrgRunnerGroupOptions) iter.Seq2[*RunnerGroup, error] { + return func(yield func(*RunnerGroup, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOrgRunnerGroupOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationRunnerGroups(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*RunnerGroup + if results != nil { + iterItems = results.RunnerGroups + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListOrganizationRunnersIter returns an iterator that paginates through all results of ListOrganizationRunners. +func (s *ActionsService) ListOrganizationRunnersIter(ctx context.Context, org string, opts *ListRunnersOptions) iter.Seq2[*Runner, error] { + return func(yield func(*Runner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListRunnersOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationRunners(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Runner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListRepoOrgSecretsIter returns an iterator that paginates through all results of ListRepoOrgSecrets. +func (s *ActionsService) ListRepoOrgSecretsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoOrgSecrets(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoOrgVariablesIter returns an iterator that paginates through all results of ListRepoOrgVariables. +func (s *ActionsService) ListRepoOrgVariablesIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*ActionsVariable, error] { + return func(yield func(*ActionsVariable, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoOrgVariables(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ActionsVariable + if results != nil { + iterItems = results.Variables + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoSecretsIter returns an iterator that paginates through all results of ListRepoSecrets. +func (s *ActionsService) ListRepoSecretsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoSecrets(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoVariablesIter returns an iterator that paginates through all results of ListRepoVariables. +func (s *ActionsService) ListRepoVariablesIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*ActionsVariable, error] { + return func(yield func(*ActionsVariable, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoVariables(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ActionsVariable + if results != nil { + iterItems = results.Variables + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter returns an iterator that paginates through all results of ListRepositoriesSelfHostedRunnersAllowedInOrganization. +func (s *ActionsService) ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepositoryAccessRunnerGroupIter returns an iterator that paginates through all results of ListRepositoryAccessRunnerGroup. +func (s *ActionsService) ListRepositoryAccessRunnerGroupIter(ctx context.Context, org string, groupID int64, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoryAccessRunnerGroup(ctx, org, groupID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepositoryWorkflowRunsIter returns an iterator that paginates through all results of ListRepositoryWorkflowRuns. +func (s *ActionsService) ListRepositoryWorkflowRunsIter(ctx context.Context, owner string, repo string, opts *ListWorkflowRunsOptions) iter.Seq2[*WorkflowRun, error] { + return func(yield func(*WorkflowRun, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListWorkflowRunsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoryWorkflowRuns(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*WorkflowRun + if results != nil { + iterItems = results.WorkflowRuns + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListRunnerGroupHostedRunnersIter returns an iterator that paginates through all results of ListRunnerGroupHostedRunners. +func (s *ActionsService) ListRunnerGroupHostedRunnersIter(ctx context.Context, org string, groupID int64, opts *ListOptions) iter.Seq2[*HostedRunner, error] { + return func(yield func(*HostedRunner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRunnerGroupHostedRunners(ctx, org, groupID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*HostedRunner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRunnerGroupRunnersIter returns an iterator that paginates through all results of ListRunnerGroupRunners. +func (s *ActionsService) ListRunnerGroupRunnersIter(ctx context.Context, org string, groupID int64, opts *ListOptions) iter.Seq2[*Runner, error] { + return func(yield func(*Runner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRunnerGroupRunners(ctx, org, groupID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Runner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRunnersIter returns an iterator that paginates through all results of ListRunners. +func (s *ActionsService) ListRunnersIter(ctx context.Context, owner string, repo string, opts *ListRunnersOptions) iter.Seq2[*Runner, error] { + return func(yield func(*Runner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListRunnersOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRunners(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Runner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListSelectedReposForOrgSecretIter returns an iterator that paginates through all results of ListSelectedReposForOrgSecret. +func (s *ActionsService) ListSelectedReposForOrgSecretIter(ctx context.Context, org string, name string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSelectedReposForOrgSecret(ctx, org, name, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListSelectedReposForOrgVariableIter returns an iterator that paginates through all results of ListSelectedReposForOrgVariable. +func (s *ActionsService) ListSelectedReposForOrgVariableIter(ctx context.Context, org string, name string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSelectedReposForOrgVariable(ctx, org, name, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListWorkflowJobsIter returns an iterator that paginates through all results of ListWorkflowJobs. +func (s *ActionsService) ListWorkflowJobsIter(ctx context.Context, owner string, repo string, runID int64, opts *ListWorkflowJobsOptions) iter.Seq2[*WorkflowJob, error] { + return func(yield func(*WorkflowJob, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListWorkflowJobsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWorkflowJobs(ctx, owner, repo, runID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*WorkflowJob + if results != nil { + iterItems = results.Jobs + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListWorkflowJobsAttemptIter returns an iterator that paginates through all results of ListWorkflowJobsAttempt. +func (s *ActionsService) ListWorkflowJobsAttemptIter(ctx context.Context, owner string, repo string, runID int64, attemptNumber int64, opts *ListOptions) iter.Seq2[*WorkflowJob, error] { + return func(yield func(*WorkflowJob, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWorkflowJobsAttempt(ctx, owner, repo, runID, attemptNumber, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*WorkflowJob + if results != nil { + iterItems = results.Jobs + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListWorkflowRunArtifactsIter returns an iterator that paginates through all results of ListWorkflowRunArtifacts. +func (s *ActionsService) ListWorkflowRunArtifactsIter(ctx context.Context, owner string, repo string, runID int64, opts *ListOptions) iter.Seq2[*Artifact, error] { + return func(yield func(*Artifact, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWorkflowRunArtifacts(ctx, owner, repo, runID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Artifact + if results != nil { + iterItems = results.Artifacts + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListWorkflowRunsByFileNameIter returns an iterator that paginates through all results of ListWorkflowRunsByFileName. +func (s *ActionsService) ListWorkflowRunsByFileNameIter(ctx context.Context, owner string, repo string, workflowFileName string, opts *ListWorkflowRunsOptions) iter.Seq2[*WorkflowRun, error] { + return func(yield func(*WorkflowRun, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListWorkflowRunsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWorkflowRunsByFileName(ctx, owner, repo, workflowFileName, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*WorkflowRun + if results != nil { + iterItems = results.WorkflowRuns + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListWorkflowRunsByIDIter returns an iterator that paginates through all results of ListWorkflowRunsByID. +func (s *ActionsService) ListWorkflowRunsByIDIter(ctx context.Context, owner string, repo string, workflowID int64, opts *ListWorkflowRunsOptions) iter.Seq2[*WorkflowRun, error] { + return func(yield func(*WorkflowRun, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListWorkflowRunsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWorkflowRunsByID(ctx, owner, repo, workflowID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*WorkflowRun + if results != nil { + iterItems = results.WorkflowRuns + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListWorkflowsIter returns an iterator that paginates through all results of ListWorkflows. +func (s *ActionsService) ListWorkflowsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Workflow, error] { + return func(yield func(*Workflow, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWorkflows(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Workflow + if results != nil { + iterItems = results.Workflows + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEventsIter returns an iterator that paginates through all results of ListEvents. +func (s *ActivityService) ListEventsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEvents(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEventsForOrganizationIter returns an iterator that paginates through all results of ListEventsForOrganization. +func (s *ActivityService) ListEventsForOrganizationIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEventsForOrganization(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEventsForRepoNetworkIter returns an iterator that paginates through all results of ListEventsForRepoNetwork. +func (s *ActivityService) ListEventsForRepoNetworkIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEventsForRepoNetwork(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEventsPerformedByUserIter returns an iterator that paginates through all results of ListEventsPerformedByUser. +func (s *ActivityService) ListEventsPerformedByUserIter(ctx context.Context, user string, publicOnly bool, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEventsPerformedByUser(ctx, user, publicOnly, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEventsReceivedByUserIter returns an iterator that paginates through all results of ListEventsReceivedByUser. +func (s *ActivityService) ListEventsReceivedByUserIter(ctx context.Context, user string, publicOnly bool, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEventsReceivedByUser(ctx, user, publicOnly, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListIssueEventsForRepositoryIter returns an iterator that paginates through all results of ListIssueEventsForRepository. +func (s *ActivityService) ListIssueEventsForRepositoryIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*IssueEvent, error] { + return func(yield func(*IssueEvent, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListIssueEventsForRepository(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListNotificationsIter returns an iterator that paginates through all results of ListNotifications. +func (s *ActivityService) ListNotificationsIter(ctx context.Context, opts *NotificationListOptions) iter.Seq2[*Notification, error] { + return func(yield func(*Notification, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &NotificationListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListNotifications(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListRepositoryEventsIter returns an iterator that paginates through all results of ListRepositoryEvents. +func (s *ActivityService) ListRepositoryEventsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoryEvents(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepositoryNotificationsIter returns an iterator that paginates through all results of ListRepositoryNotifications. +func (s *ActivityService) ListRepositoryNotificationsIter(ctx context.Context, owner string, repo string, opts *NotificationListOptions) iter.Seq2[*Notification, error] { + return func(yield func(*Notification, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &NotificationListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoryNotifications(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListStargazersIter returns an iterator that paginates through all results of ListStargazers. +func (s *ActivityService) ListStargazersIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Stargazer, error] { + return func(yield func(*Stargazer, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListStargazers(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListStarredIter returns an iterator that paginates through all results of ListStarred. +func (s *ActivityService) ListStarredIter(ctx context.Context, user string, opts *ActivityListStarredOptions) iter.Seq2[*StarredRepository, error] { + return func(yield func(*StarredRepository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ActivityListStarredOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListStarred(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListUserEventsForOrganizationIter returns an iterator that paginates through all results of ListUserEventsForOrganization. +func (s *ActivityService) ListUserEventsForOrganizationIter(ctx context.Context, org string, user string, opts *ListOptions) iter.Seq2[*Event, error] { + return func(yield func(*Event, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserEventsForOrganization(ctx, org, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListWatchedIter returns an iterator that paginates through all results of ListWatched. +func (s *ActivityService) ListWatchedIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWatched(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListWatchersIter returns an iterator that paginates through all results of ListWatchers. +func (s *ActivityService) ListWatchersIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListWatchers(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListHookDeliveriesIter returns an iterator that paginates through all results of ListHookDeliveries. +func (s *AppsService) ListHookDeliveriesIter(ctx context.Context, opts *ListCursorOptions) iter.Seq2[*HookDelivery, error] { + return func(yield func(*HookDelivery, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCursorOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHookDeliveries(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.Cursor == "" { + break + } + opts.Cursor = resp.Cursor + } + } +} + +// ListInstallationRequestsIter returns an iterator that paginates through all results of ListInstallationRequests. +func (s *AppsService) ListInstallationRequestsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*InstallationRequest, error] { + return func(yield func(*InstallationRequest, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInstallationRequests(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListInstallationsIter returns an iterator that paginates through all results of ListInstallations. +func (s *AppsService) ListInstallationsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Installation, error] { + return func(yield func(*Installation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInstallations(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListReposIter returns an iterator that paginates through all results of ListRepos. +func (s *AppsService) ListReposIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepos(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserInstallationsIter returns an iterator that paginates through all results of ListUserInstallations. +func (s *AppsService) ListUserInstallationsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Installation, error] { + return func(yield func(*Installation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserInstallations(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserReposIter returns an iterator that paginates through all results of ListUserRepos. +func (s *AppsService) ListUserReposIter(ctx context.Context, id int64, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserRepos(ctx, id, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCheckRunAnnotationsIter returns an iterator that paginates through all results of ListCheckRunAnnotations. +func (s *ChecksService) ListCheckRunAnnotationsIter(ctx context.Context, owner string, repo string, checkRunID int64, opts *ListOptions) iter.Seq2[*CheckRunAnnotation, error] { + return func(yield func(*CheckRunAnnotation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCheckRunAnnotations(ctx, owner, repo, checkRunID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCheckRunsCheckSuiteIter returns an iterator that paginates through all results of ListCheckRunsCheckSuite. +func (s *ChecksService) ListCheckRunsCheckSuiteIter(ctx context.Context, owner string, repo string, checkSuiteID int64, opts *ListCheckRunsOptions) iter.Seq2[*CheckRun, error] { + return func(yield func(*CheckRun, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCheckRunsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCheckRunsCheckSuite(ctx, owner, repo, checkSuiteID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CheckRun + if results != nil { + iterItems = results.CheckRuns + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCheckRunsForRefIter returns an iterator that paginates through all results of ListCheckRunsForRef. +func (s *ChecksService) ListCheckRunsForRefIter(ctx context.Context, owner string, repo string, ref string, opts *ListCheckRunsOptions) iter.Seq2[*CheckRun, error] { + return func(yield func(*CheckRun, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCheckRunsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCheckRunsForRef(ctx, owner, repo, ref, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CheckRun + if results != nil { + iterItems = results.CheckRuns + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCheckSuitesForRefIter returns an iterator that paginates through all results of ListCheckSuitesForRef. +func (s *ChecksService) ListCheckSuitesForRefIter(ctx context.Context, owner string, repo string, ref string, opts *ListCheckSuiteOptions) iter.Seq2[*CheckSuite, error] { + return func(yield func(*CheckSuite, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCheckSuiteOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCheckSuitesForRef(ctx, owner, repo, ref, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CheckSuite + if results != nil { + iterItems = results.CheckSuites + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAcceptedAssignmentsIter returns an iterator that paginates through all results of ListAcceptedAssignments. +func (s *ClassroomService) ListAcceptedAssignmentsIter(ctx context.Context, assignmentID int64, opts *ListOptions) iter.Seq2[*AcceptedAssignment, error] { + return func(yield func(*AcceptedAssignment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAcceptedAssignments(ctx, assignmentID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListClassroomAssignmentsIter returns an iterator that paginates through all results of ListClassroomAssignments. +func (s *ClassroomService) ListClassroomAssignmentsIter(ctx context.Context, classroomID int64, opts *ListOptions) iter.Seq2[*ClassroomAssignment, error] { + return func(yield func(*ClassroomAssignment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListClassroomAssignments(ctx, classroomID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListClassroomsIter returns an iterator that paginates through all results of ListClassrooms. +func (s *ClassroomService) ListClassroomsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Classroom, error] { + return func(yield func(*Classroom, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListClassrooms(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListFindingsIter returns an iterator that paginates through all results of ListFindings. +func (s *CodeQualityService) ListFindingsIter(ctx context.Context, owner string, repo string, opts *ListCodeQualityFindingsOptions) iter.Seq2[*CodeQualityFinding, error] { + return func(yield func(*CodeQualityFinding, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCodeQualityFindingsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFindings(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + +// ListAlertInstancesIter returns an iterator that paginates through all results of ListAlertInstances. +func (s *CodeScanningService) ListAlertInstancesIter(ctx context.Context, owner string, repo string, id int64, opts *AlertInstancesListOptions) iter.Seq2[*MostRecentInstance, error] { + return func(yield func(*MostRecentInstance, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &AlertInstancesListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAlertInstances(ctx, owner, repo, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAlertsForOrgIter returns an iterator that paginates through all results of ListAlertsForOrg. +func (s *CodeScanningService) ListAlertsForOrgIter(ctx context.Context, org string, opts *AlertListOptions) iter.Seq2[*Alert, error] { + return func(yield func(*Alert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &AlertListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAlertsForOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAlertsForRepoIter returns an iterator that paginates through all results of ListAlertsForRepo. +func (s *CodeScanningService) ListAlertsForRepoIter(ctx context.Context, owner string, repo string, opts *AlertListOptions) iter.Seq2[*Alert, error] { + return func(yield func(*Alert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &AlertListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAlertsForRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAnalysesForRepoIter returns an iterator that paginates through all results of ListAnalysesForRepo. +func (s *CodeScanningService) ListAnalysesForRepoIter(ctx context.Context, owner string, repo string, opts *AnalysesListOptions) iter.Seq2[*ScanningAnalysis, error] { + return func(yield func(*ScanningAnalysis, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &AnalysesListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAnalysesForRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListIter returns an iterator that paginates through all results of List. +func (s *CodespacesService) ListIter(ctx context.Context, opts *ListCodespacesOptions) iter.Seq2[*Codespace, error] { + return func(yield func(*Codespace, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCodespacesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.List(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Codespace + if results != nil { + iterItems = results.Codespaces + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListDevContainerConfigurationsIter returns an iterator that paginates through all results of ListDevContainerConfigurations. +func (s *CodespacesService) ListDevContainerConfigurationsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*DevContainer, error] { + return func(yield func(*DevContainer, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListDevContainerConfigurations(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*DevContainer + if results != nil { + iterItems = results.Devcontainers + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListInOrgIter returns an iterator that paginates through all results of ListInOrg. +func (s *CodespacesService) ListInOrgIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Codespace, error] { + return func(yield func(*Codespace, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Codespace + if results != nil { + iterItems = results.Codespaces + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListInRepoIter returns an iterator that paginates through all results of ListInRepo. +func (s *CodespacesService) ListInRepoIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Codespace, error] { + return func(yield func(*Codespace, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Codespace + if results != nil { + iterItems = results.Codespaces + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrgSecretsIter returns an iterator that paginates through all results of ListOrgSecrets. +func (s *CodespacesService) ListOrgSecretsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgSecrets(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoSecretsIter returns an iterator that paginates through all results of ListRepoSecrets. +func (s *CodespacesService) ListRepoSecretsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoSecrets(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListSelectedReposForOrgSecretIter returns an iterator that paginates through all results of ListSelectedReposForOrgSecret. +func (s *CodespacesService) ListSelectedReposForOrgSecretIter(ctx context.Context, org string, name string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSelectedReposForOrgSecret(ctx, org, name, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListSelectedReposForUserSecretIter returns an iterator that paginates through all results of ListSelectedReposForUserSecret. +func (s *CodespacesService) ListSelectedReposForUserSecretIter(ctx context.Context, name string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSelectedReposForUserSecret(ctx, name, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserCodespacesInOrgIter returns an iterator that paginates through all results of ListUserCodespacesInOrg. +func (s *CodespacesService) ListUserCodespacesInOrgIter(ctx context.Context, org string, username string, opts *ListOptions) iter.Seq2[*Codespace, error] { + return func(yield func(*Codespace, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserCodespacesInOrg(ctx, org, username, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Codespace + if results != nil { + iterItems = results.Codespaces + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserSecretsIter returns an iterator that paginates through all results of ListUserSecrets. +func (s *CodespacesService) ListUserSecretsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserSecrets(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCopilotEnterpriseSeatsIter returns an iterator that paginates through all results of ListCopilotEnterpriseSeats. +func (s *CopilotService) ListCopilotEnterpriseSeatsIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*CopilotSeatDetails, error] { + return func(yield func(*CopilotSeatDetails, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCopilotEnterpriseSeats(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CopilotSeatDetails + if results != nil { + iterItems = results.Seats + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCopilotSeatsIter returns an iterator that paginates through all results of ListCopilotSeats. +func (s *CopilotService) ListCopilotSeatsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*CopilotSeatDetails, error] { + return func(yield func(*CopilotSeatDetails, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCopilotSeats(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CopilotSeatDetails + if results != nil { + iterItems = results.Seats + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrganizationCodingAgentRepositoriesIter returns an iterator that paginates through all results of ListOrganizationCodingAgentRepositories. +func (s *CopilotService) ListOrganizationCodingAgentRepositoriesIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationCodingAgentRepositories(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrgAlertsIter returns an iterator that paginates through all results of ListOrgAlerts. +func (s *DependabotService) ListOrgAlertsIter(ctx context.Context, org string, opts *ListAlertsOptions) iter.Seq2[*DependabotAlert, error] { + return func(yield func(*DependabotAlert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListAlertsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgAlerts(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListOrgSecretsIter returns an iterator that paginates through all results of ListOrgSecrets. +func (s *DependabotService) ListOrgSecretsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgSecrets(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepoAlertsIter returns an iterator that paginates through all results of ListRepoAlerts. +func (s *DependabotService) ListRepoAlertsIter(ctx context.Context, owner string, repo string, opts *ListAlertsOptions) iter.Seq2[*DependabotAlert, error] { + return func(yield func(*DependabotAlert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListAlertsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoAlerts(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListRepoSecretsIter returns an iterator that paginates through all results of ListRepoSecrets. +func (s *DependabotService) ListRepoSecretsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Secret, error] { + return func(yield func(*Secret, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepoSecrets(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Secret + if results != nil { + iterItems = results.Secrets + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListSelectedReposForOrgSecretIter returns an iterator that paginates through all results of ListSelectedReposForOrgSecret. +func (s *DependabotService) ListSelectedReposForOrgSecretIter(ctx context.Context, org string, name string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSelectedReposForOrgSecret(ctx, org, name, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAppAccessibleOrganizationRepositoriesIter returns an iterator that paginates through all results of ListAppAccessibleOrganizationRepositories. +func (s *EnterpriseService) ListAppAccessibleOrganizationRepositoriesIter(ctx context.Context, enterprise string, org string, opts *ListOptions) iter.Seq2[*AccessibleRepository, error] { + return func(yield func(*AccessibleRepository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAppAccessibleOrganizationRepositories(ctx, enterprise, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAppInstallableOrganizationsIter returns an iterator that paginates through all results of ListAppInstallableOrganizations. +func (s *EnterpriseService) ListAppInstallableOrganizationsIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*InstallableOrganization, error] { + return func(yield func(*InstallableOrganization, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAppInstallableOrganizations(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAppInstallationsIter returns an iterator that paginates through all results of ListAppInstallations. +func (s *EnterpriseService) ListAppInstallationsIter(ctx context.Context, enterprise string, org string, opts *ListOptions) iter.Seq2[*Installation, error] { + return func(yield func(*Installation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAppInstallations(ctx, enterprise, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAssignmentsIter returns an iterator that paginates through all results of ListAssignments. +func (s *EnterpriseService) ListAssignmentsIter(ctx context.Context, enterprise string, enterpriseTeam string, opts *ListOptions) iter.Seq2[*Organization, error] { + return func(yield func(*Organization, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAssignments(ctx, enterprise, enterpriseTeam, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCodeSecurityConfigurationRepositoriesIter returns an iterator that paginates through all results of ListCodeSecurityConfigurationRepositories. +func (s *EnterpriseService) ListCodeSecurityConfigurationRepositoriesIter(ctx context.Context, enterprise string, configurationID int64, opts *ListCodeSecurityConfigurationRepositoriesOptions) iter.Seq2[*RepositoryAttachment, error] { + return func(yield func(*RepositoryAttachment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCodeSecurityConfigurationRepositoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCodeSecurityConfigurationRepositories(ctx, enterprise, configurationID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListCodeSecurityConfigurationsIter returns an iterator that paginates through all results of ListCodeSecurityConfigurations. +func (s *EnterpriseService) ListCodeSecurityConfigurationsIter(ctx context.Context, enterprise string, opts *ListEnterpriseCodeSecurityConfigurationOptions) iter.Seq2[*CodeSecurityConfiguration, error] { + return func(yield func(*CodeSecurityConfiguration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListEnterpriseCodeSecurityConfigurationOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCodeSecurityConfigurations(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListConsumedLicensesIter returns an iterator that paginates through all results of ListConsumedLicenses. +func (s *EnterpriseService) ListConsumedLicensesIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*EnterpriseLicensedUsers, error] { + return func(yield func(*EnterpriseLicensedUsers, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListConsumedLicenses(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*EnterpriseLicensedUsers + if results != nil { + iterItems = results.Users + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEnterpriseNetworkConfigurationsIter returns an iterator that paginates through all results of ListEnterpriseNetworkConfigurations. +func (s *EnterpriseService) ListEnterpriseNetworkConfigurationsIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*NetworkConfiguration, error] { + return func(yield func(*NetworkConfiguration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEnterpriseNetworkConfigurations(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*NetworkConfiguration + if results != nil { + iterItems = results.NetworkConfigurations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListHostedRunnersIter returns an iterator that paginates through all results of ListHostedRunners. +func (s *EnterpriseService) ListHostedRunnersIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*HostedRunner, error] { + return func(yield func(*HostedRunner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHostedRunners(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*HostedRunner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrganizationAccessRunnerGroupIter returns an iterator that paginates through all results of ListOrganizationAccessRunnerGroup. +func (s *EnterpriseService) ListOrganizationAccessRunnerGroupIter(ctx context.Context, enterprise string, groupID int64, opts *ListOptions) iter.Seq2[*Organization, error] { + return func(yield func(*Organization, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationAccessRunnerGroup(ctx, enterprise, groupID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Organization + if results != nil { + iterItems = results.Organizations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrganizationCustomPropertyValuesIter returns an iterator that paginates through all results of ListOrganizationCustomPropertyValues. +func (s *EnterpriseService) ListOrganizationCustomPropertyValuesIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*EnterpriseCustomPropertiesValues, error] { + return func(yield func(*EnterpriseCustomPropertiesValues, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationCustomPropertyValues(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepositoriesForOrgAppInstallationIter returns an iterator that paginates through all results of ListRepositoriesForOrgAppInstallation. +func (s *EnterpriseService) ListRepositoriesForOrgAppInstallationIter(ctx context.Context, enterprise string, org string, installationID int64, opts *ListOptions) iter.Seq2[*AccessibleRepository, error] { + return func(yield func(*AccessibleRepository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoriesForOrgAppInstallation(ctx, enterprise, org, installationID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRunnerGroupRunnersIter returns an iterator that paginates through all results of ListRunnerGroupRunners. +func (s *EnterpriseService) ListRunnerGroupRunnersIter(ctx context.Context, enterprise string, groupID int64, opts *ListOptions) iter.Seq2[*Runner, error] { + return func(yield func(*Runner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRunnerGroupRunners(ctx, enterprise, groupID, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Runner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRunnerGroupsIter returns an iterator that paginates through all results of ListRunnerGroups. +func (s *EnterpriseService) ListRunnerGroupsIter(ctx context.Context, enterprise string, opts *ListEnterpriseRunnerGroupOptions) iter.Seq2[*EnterpriseRunnerGroup, error] { + return func(yield func(*EnterpriseRunnerGroup, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListEnterpriseRunnerGroupOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRunnerGroups(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*EnterpriseRunnerGroup + if results != nil { + iterItems = results.RunnerGroups + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListRunnersIter returns an iterator that paginates through all results of ListRunners. +func (s *EnterpriseService) ListRunnersIter(ctx context.Context, enterprise string, opts *ListRunnersOptions) iter.Seq2[*Runner, error] { + return func(yield func(*Runner, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListRunnersOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRunners(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Runner + if results != nil { + iterItems = results.Runners + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListTeamMembersIter returns an iterator that paginates through all results of ListTeamMembers. +func (s *EnterpriseService) ListTeamMembersIter(ctx context.Context, enterprise string, enterpriseTeam string, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamMembers(ctx, enterprise, enterpriseTeam, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTeamsIter returns an iterator that paginates through all results of ListTeams. +func (s *EnterpriseService) ListTeamsIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*EnterpriseTeam, error] { + return func(yield func(*EnterpriseTeam, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeams(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListIter returns an iterator that paginates through all results of List. +func (s *GistsService) ListIter(ctx context.Context, user string, opts *GistListOptions) iter.Seq2[*Gist, error] { + return func(yield func(*Gist, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &GistListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.List(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAllIter returns an iterator that paginates through all results of ListAll. +func (s *GistsService) ListAllIter(ctx context.Context, opts *GistListOptions) iter.Seq2[*Gist, error] { + return func(yield func(*Gist, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &GistListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAll(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommentsIter returns an iterator that paginates through all results of ListComments. +func (s *GistsService) ListCommentsIter(ctx context.Context, gistID string, opts *ListOptions) iter.Seq2[*GistComment, error] { + return func(yield func(*GistComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListComments(ctx, gistID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCommitsIter returns an iterator that paginates through all results of ListCommits. +func (s *GistsService) ListCommitsIter(ctx context.Context, id string, opts *ListOptions) iter.Seq2[*GistCommit, error] { + return func(yield func(*GistCommit, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommits(ctx, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListForksIter returns an iterator that paginates through all results of ListForks. +func (s *GistsService) ListForksIter(ctx context.Context, id string, opts *ListOptions) iter.Seq2[*GistFork, error] { + return func(yield func(*GistFork, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListForks(ctx, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListStarredIter returns an iterator that paginates through all results of ListStarred. +func (s *GistsService) ListStarredIter(ctx context.Context, opts *GistListOptions) iter.Seq2[*Gist, error] { + return func(yield func(*Gist, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &GistListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListStarred(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAllIssuesIter returns an iterator that paginates through all results of ListAllIssues. +func (s *IssuesService) ListAllIssuesIter(ctx context.Context, opts *ListAllIssuesOptions) iter.Seq2[*Issue, error] { + return func(yield func(*Issue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListAllIssuesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAllIssues(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAssigneesIter returns an iterator that paginates through all results of ListAssignees. +func (s *IssuesService) ListAssigneesIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAssignees(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListBlockedByIter returns an iterator that paginates through all results of ListBlockedBy. +func (s *IssuesService) ListBlockedByIter(ctx context.Context, owner string, repo string, issueNumber int64, opts *ListOptions) iter.Seq2[*Issue, error] { + return func(yield func(*Issue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListBlockedBy(ctx, owner, repo, issueNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListBlockingIter returns an iterator that paginates through all results of ListBlocking. +func (s *IssuesService) ListBlockingIter(ctx context.Context, owner string, repo string, issueNumber int64, opts *ListOptions) iter.Seq2[*Issue, error] { + return func(yield func(*Issue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListBlocking(ctx, owner, repo, issueNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListByOrgIter returns an iterator that paginates through all results of ListByOrg. +func (s *IssuesService) ListByOrgIter(ctx context.Context, org string, opts *IssueListByOrgOptions) iter.Seq2[*Issue, error] { + return func(yield func(*Issue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &IssueListByOrgOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListByOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListByRepoIter returns an iterator that paginates through all results of ListByRepo. +func (s *IssuesService) ListByRepoIter(ctx context.Context, owner string, repo string, opts *IssueListByRepoOptions) iter.Seq2[*Issue, error] { + return func(yield func(*Issue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &IssueListByRepoOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListByRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommentsIter returns an iterator that paginates through all results of ListComments. +func (s *IssuesService) ListCommentsIter(ctx context.Context, owner string, repo string, number int, opts *IssueListCommentsOptions) iter.Seq2[*IssueComment, error] { + return func(yield func(*IssueComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &IssueListCommentsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListComments(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListIssueEventsIter returns an iterator that paginates through all results of ListIssueEvents. +func (s *IssuesService) ListIssueEventsIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*IssueEvent, error] { + return func(yield func(*IssueEvent, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListIssueEvents(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListIssueTimelineIter returns an iterator that paginates through all results of ListIssueTimeline. +func (s *IssuesService) ListIssueTimelineIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*Timeline, error] { + return func(yield func(*Timeline, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListIssueTimeline(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListLabelsIter returns an iterator that paginates through all results of ListLabels. +func (s *IssuesService) ListLabelsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Label, error] { + return func(yield func(*Label, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListLabels(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListLabelsByIssueIter returns an iterator that paginates through all results of ListLabelsByIssue. +func (s *IssuesService) ListLabelsByIssueIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*Label, error] { + return func(yield func(*Label, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListLabelsByIssue(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListLabelsForMilestoneIter returns an iterator that paginates through all results of ListLabelsForMilestone. +func (s *IssuesService) ListLabelsForMilestoneIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*Label, error] { + return func(yield func(*Label, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListLabelsForMilestone(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListMilestonesIter returns an iterator that paginates through all results of ListMilestones. +func (s *IssuesService) ListMilestonesIter(ctx context.Context, owner string, repo string, opts *MilestoneListOptions) iter.Seq2[*Milestone, error] { + return func(yield func(*Milestone, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &MilestoneListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListMilestones(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListRepositoryEventsIter returns an iterator that paginates through all results of ListRepositoryEvents. +func (s *IssuesService) ListRepositoryEventsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*IssueEvent, error] { + return func(yield func(*IssueEvent, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoryEvents(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserIssuesIter returns an iterator that paginates through all results of ListUserIssues. +func (s *IssuesService) ListUserIssuesIter(ctx context.Context, opts *ListUserIssuesOptions) iter.Seq2[*Issue, error] { + return func(yield func(*Issue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListUserIssuesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserIssues(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListIter returns an iterator that paginates through all results of List. +func (s *LicensesService) ListIter(ctx context.Context, opts *ListLicensesOptions) iter.Seq2[*License, error] { + return func(yield func(*License, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListLicensesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.List(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListMarketplacePurchasesForUserIter returns an iterator that paginates through all results of ListMarketplacePurchasesForUser. +func (s *MarketplaceService) ListMarketplacePurchasesForUserIter(ctx context.Context, opts *ListOptions) iter.Seq2[*MarketplacePurchase, error] { + return func(yield func(*MarketplacePurchase, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListMarketplacePurchasesForUser(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPlanAccountsForPlanIter returns an iterator that paginates through all results of ListPlanAccountsForPlan. +func (s *MarketplaceService) ListPlanAccountsForPlanIter(ctx context.Context, planID int64, opts *ListOptions) iter.Seq2[*MarketplacePlanAccount, error] { + return func(yield func(*MarketplacePlanAccount, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPlanAccountsForPlan(ctx, planID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPlansIter returns an iterator that paginates through all results of ListPlans. +func (s *MarketplaceService) ListPlansIter(ctx context.Context, opts *ListOptions) iter.Seq2[*MarketplacePlan, error] { + return func(yield func(*MarketplacePlan, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPlans(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListMigrationsIter returns an iterator that paginates through all results of ListMigrations. +func (s *MigrationService) ListMigrationsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Migration, error] { + return func(yield func(*Migration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListMigrations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserMigrationsIter returns an iterator that paginates through all results of ListUserMigrations. +func (s *MigrationService) ListUserMigrationsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*UserMigration, error] { + return func(yield func(*UserMigration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserMigrations(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListIter returns an iterator that paginates through all results of List. +func (s *OrganizationsService) ListIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*Organization, error] { + return func(yield func(*Organization, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.List(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAllRepositoryRulesetsIter returns an iterator that paginates through all results of ListAllRepositoryRulesets. +func (s *OrganizationsService) ListAllRepositoryRulesetsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*RepositoryRuleset, error] { + return func(yield func(*RepositoryRuleset, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAllRepositoryRulesets(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAttestationsIter returns an iterator that paginates through all results of ListAttestations. +func (s *OrganizationsService) ListAttestationsIter(ctx context.Context, org string, subjectDigest string, opts *ListOptions) iter.Seq2[*Attestation, error] { + return func(yield func(*Attestation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAttestations(ctx, org, subjectDigest, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Attestation + if results != nil { + iterItems = results.Attestations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListBlockedUsersIter returns an iterator that paginates through all results of ListBlockedUsers. +func (s *OrganizationsService) ListBlockedUsersIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListBlockedUsers(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCodeSecurityConfigurationRepositoriesIter returns an iterator that paginates through all results of ListCodeSecurityConfigurationRepositories. +func (s *OrganizationsService) ListCodeSecurityConfigurationRepositoriesIter(ctx context.Context, org string, configurationID int64, opts *ListCodeSecurityConfigurationRepositoriesOptions) iter.Seq2[*RepositoryAttachment, error] { + return func(yield func(*RepositoryAttachment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCodeSecurityConfigurationRepositoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCodeSecurityConfigurationRepositories(ctx, org, configurationID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListCodeSecurityConfigurationsIter returns an iterator that paginates through all results of ListCodeSecurityConfigurations. +func (s *OrganizationsService) ListCodeSecurityConfigurationsIter(ctx context.Context, org string, opts *ListOrgCodeSecurityConfigurationOptions) iter.Seq2[*CodeSecurityConfiguration, error] { + return func(yield func(*CodeSecurityConfiguration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOrgCodeSecurityConfigurationOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCodeSecurityConfigurations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListCredentialAuthorizationsIter returns an iterator that paginates through all results of ListCredentialAuthorizations. +func (s *OrganizationsService) ListCredentialAuthorizationsIter(ctx context.Context, org string, opts *CredentialAuthorizationsListOptions) iter.Seq2[*CredentialAuthorization, error] { + return func(yield func(*CredentialAuthorization, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &CredentialAuthorizationsListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCredentialAuthorizations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCustomPropertyValuesIter returns an iterator that paginates through all results of ListCustomPropertyValues. +func (s *OrganizationsService) ListCustomPropertyValuesIter(ctx context.Context, org string, opts *ListCustomPropertyValuesOptions) iter.Seq2[*RepoCustomPropertyValue, error] { + return func(yield func(*RepoCustomPropertyValue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCustomPropertyValuesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCustomPropertyValues(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListFailedOrgInvitationsIter returns an iterator that paginates through all results of ListFailedOrgInvitations. +func (s *OrganizationsService) ListFailedOrgInvitationsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Invitation, error] { + return func(yield func(*Invitation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFailedOrgInvitations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListFineGrainedPersonalAccessTokenRequestsIter returns an iterator that paginates through all results of ListFineGrainedPersonalAccessTokenRequests. +func (s *OrganizationsService) ListFineGrainedPersonalAccessTokenRequestsIter(ctx context.Context, org string, opts *ListFineGrainedPATOptions) iter.Seq2[*FineGrainedPersonalAccessTokenRequest, error] { + return func(yield func(*FineGrainedPersonalAccessTokenRequest, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListFineGrainedPATOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFineGrainedPersonalAccessTokenRequests(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListFineGrainedPersonalAccessTokensIter returns an iterator that paginates through all results of ListFineGrainedPersonalAccessTokens. +func (s *OrganizationsService) ListFineGrainedPersonalAccessTokensIter(ctx context.Context, org string, opts *ListFineGrainedPATOptions) iter.Seq2[*PersonalAccessToken, error] { + return func(yield func(*PersonalAccessToken, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListFineGrainedPATOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFineGrainedPersonalAccessTokens(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListHookDeliveriesIter returns an iterator that paginates through all results of ListHookDeliveries. +func (s *OrganizationsService) ListHookDeliveriesIter(ctx context.Context, org string, id int64, opts *ListCursorOptions) iter.Seq2[*HookDelivery, error] { + return func(yield func(*HookDelivery, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCursorOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHookDeliveries(ctx, org, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.Cursor == "" { + break + } + opts.Cursor = resp.Cursor + } + } +} + +// ListHooksIter returns an iterator that paginates through all results of ListHooks. +func (s *OrganizationsService) ListHooksIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Hook, error] { + return func(yield func(*Hook, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHooks(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListImmutableReleaseRepositoriesIter returns an iterator that paginates through all results of ListImmutableReleaseRepositories. +func (s *OrganizationsService) ListImmutableReleaseRepositoriesIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListImmutableReleaseRepositories(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Repository + if results != nil { + iterItems = results.Repositories + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListInstallationsIter returns an iterator that paginates through all results of ListInstallations. +func (s *OrganizationsService) ListInstallationsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Installation, error] { + return func(yield func(*Installation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInstallations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Installation + if results != nil { + iterItems = results.Installations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListMembersIter returns an iterator that paginates through all results of ListMembers. +func (s *OrganizationsService) ListMembersIter(ctx context.Context, org string, opts *ListMembersOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListMembersOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListMembers(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListNetworkConfigurationsIter returns an iterator that paginates through all results of ListNetworkConfigurations. +func (s *OrganizationsService) ListNetworkConfigurationsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*NetworkConfiguration, error] { + return func(yield func(*NetworkConfiguration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListNetworkConfigurations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*NetworkConfiguration + if results != nil { + iterItems = results.NetworkConfigurations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrgInvitationTeamsIter returns an iterator that paginates through all results of ListOrgInvitationTeams. +func (s *OrganizationsService) ListOrgInvitationTeamsIter(ctx context.Context, org string, invitationID string, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgInvitationTeams(ctx, org, invitationID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrgMembershipsIter returns an iterator that paginates through all results of ListOrgMemberships. +func (s *OrganizationsService) ListOrgMembershipsIter(ctx context.Context, opts *ListOrgMembershipsOptions) iter.Seq2[*Membership, error] { + return func(yield func(*Membership, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOrgMembershipsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrgMemberships(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListOutsideCollaboratorsIter returns an iterator that paginates through all results of ListOutsideCollaborators. +func (s *OrganizationsService) ListOutsideCollaboratorsIter(ctx context.Context, org string, opts *ListOutsideCollaboratorsOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOutsideCollaboratorsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOutsideCollaborators(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListPackagesIter returns an iterator that paginates through all results of ListPackages. +func (s *OrganizationsService) ListPackagesIter(ctx context.Context, org string, opts *PackageListOptions) iter.Seq2[*Package, error] { + return func(yield func(*Package, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &PackageListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPackages(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListPendingOrgInvitationsIter returns an iterator that paginates through all results of ListPendingOrgInvitations. +func (s *OrganizationsService) ListPendingOrgInvitationsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Invitation, error] { + return func(yield func(*Invitation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPendingOrgInvitations(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTeamsAssignedToOrgRoleIter returns an iterator that paginates through all results of ListTeamsAssignedToOrgRole. +func (s *OrganizationsService) ListTeamsAssignedToOrgRoleIter(ctx context.Context, org string, roleID int64, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamsAssignedToOrgRole(ctx, org, roleID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUsersAssignedToOrgRoleIter returns an iterator that paginates through all results of ListUsersAssignedToOrgRole. +func (s *OrganizationsService) ListUsersAssignedToOrgRoleIter(ctx context.Context, org string, roleID int64, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUsersAssignedToOrgRole(ctx, org, roleID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrganizationPrivateRegistriesIter returns an iterator that paginates through all results of ListOrganizationPrivateRegistries. +func (s *PrivateRegistriesService) ListOrganizationPrivateRegistriesIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*PrivateRegistry, error] { + return func(yield func(*PrivateRegistry, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationPrivateRegistries(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*PrivateRegistry + if results != nil { + iterItems = results.Configurations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListOrganizationProjectFieldsIter returns an iterator that paginates through all results of ListOrganizationProjectFields. +func (s *ProjectsService) ListOrganizationProjectFieldsIter(ctx context.Context, org string, projectNumber int, opts *ListProjectsOptions) iter.Seq2[*ProjectV2Field, error] { + return func(yield func(*ProjectV2Field, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationProjectFields(ctx, org, projectNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListOrganizationProjectItemsIter returns an iterator that paginates through all results of ListOrganizationProjectItems. +func (s *ProjectsService) ListOrganizationProjectItemsIter(ctx context.Context, org string, projectNumber int, opts *ListProjectItemsOptions) iter.Seq2[*ProjectV2Item, error] { + return func(yield func(*ProjectV2Item, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectItemsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationProjectItems(ctx, org, projectNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListOrganizationProjectViewItemsIter returns an iterator that paginates through all results of ListOrganizationProjectViewItems. +func (s *ProjectsService) ListOrganizationProjectViewItemsIter(ctx context.Context, org string, projectNumber int, viewNumber int, opts *ListProjectItemsOptions) iter.Seq2[*ProjectV2Item, error] { + return func(yield func(*ProjectV2Item, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectItemsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationProjectViewItems(ctx, org, projectNumber, viewNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListOrganizationProjectsIter returns an iterator that paginates through all results of ListOrganizationProjects. +func (s *ProjectsService) ListOrganizationProjectsIter(ctx context.Context, org string, opts *ListProjectsOptions) iter.Seq2[*ProjectV2, error] { + return func(yield func(*ProjectV2, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListOrganizationProjects(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListUserProjectFieldsIter returns an iterator that paginates through all results of ListUserProjectFields. +func (s *ProjectsService) ListUserProjectFieldsIter(ctx context.Context, user string, projectNumber int, opts *ListProjectsOptions) iter.Seq2[*ProjectV2Field, error] { + return func(yield func(*ProjectV2Field, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserProjectFields(ctx, user, projectNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListUserProjectItemsIter returns an iterator that paginates through all results of ListUserProjectItems. +func (s *ProjectsService) ListUserProjectItemsIter(ctx context.Context, username string, projectNumber int, opts *ListProjectItemsOptions) iter.Seq2[*ProjectV2Item, error] { + return func(yield func(*ProjectV2Item, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectItemsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserProjectItems(ctx, username, projectNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListUserProjectViewItemsIter returns an iterator that paginates through all results of ListUserProjectViewItems. +func (s *ProjectsService) ListUserProjectViewItemsIter(ctx context.Context, username string, projectNumber int, viewNumber int, opts *ListProjectItemsOptions) iter.Seq2[*ProjectV2Item, error] { + return func(yield func(*ProjectV2Item, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectItemsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserProjectViewItems(ctx, username, projectNumber, viewNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListUserProjectsIter returns an iterator that paginates through all results of ListUserProjects. +func (s *ProjectsService) ListUserProjectsIter(ctx context.Context, username string, opts *ListProjectsOptions) iter.Seq2[*ProjectV2, error] { + return func(yield func(*ProjectV2, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListProjectsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserProjects(ctx, username, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListIter returns an iterator that paginates through all results of List. +func (s *PullRequestsService) ListIter(ctx context.Context, owner string, repo string, opts *PullRequestListOptions) iter.Seq2[*PullRequest, error] { + return func(yield func(*PullRequest, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &PullRequestListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.List(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommentsIter returns an iterator that paginates through all results of ListComments. +func (s *PullRequestsService) ListCommentsIter(ctx context.Context, owner string, repo string, number int, opts *PullRequestListCommentsOptions) iter.Seq2[*PullRequestComment, error] { + return func(yield func(*PullRequestComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &PullRequestListCommentsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListComments(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommitsIter returns an iterator that paginates through all results of ListCommits. +func (s *PullRequestsService) ListCommitsIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*RepositoryCommit, error] { + return func(yield func(*RepositoryCommit, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommits(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListFilesIter returns an iterator that paginates through all results of ListFiles. +func (s *PullRequestsService) ListFilesIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*CommitFile, error] { + return func(yield func(*CommitFile, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFiles(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPullRequestsWithCommitIter returns an iterator that paginates through all results of ListPullRequestsWithCommit. +func (s *PullRequestsService) ListPullRequestsWithCommitIter(ctx context.Context, owner string, repo string, sha string, opts *ListOptions) iter.Seq2[*PullRequest, error] { + return func(yield func(*PullRequest, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPullRequestsWithCommit(ctx, owner, repo, sha, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListReviewCommentsIter returns an iterator that paginates through all results of ListReviewComments. +func (s *PullRequestsService) ListReviewCommentsIter(ctx context.Context, owner string, repo string, number int, reviewID int64, opts *ListOptions) iter.Seq2[*PullRequestComment, error] { + return func(yield func(*PullRequestComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListReviewComments(ctx, owner, repo, number, reviewID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListReviewsIter returns an iterator that paginates through all results of ListReviews. +func (s *PullRequestsService) ListReviewsIter(ctx context.Context, owner string, repo string, number int, opts *ListOptions) iter.Seq2[*PullRequestReview, error] { + return func(yield func(*PullRequestReview, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListReviews(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCommentReactionsIter returns an iterator that paginates through all results of ListCommentReactions. +func (s *ReactionsService) ListCommentReactionsIter(ctx context.Context, owner string, repo string, id int64, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommentReactions(ctx, owner, repo, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListIssueCommentReactionsIter returns an iterator that paginates through all results of ListIssueCommentReactions. +func (s *ReactionsService) ListIssueCommentReactionsIter(ctx context.Context, owner string, repo string, id int64, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListIssueCommentReactions(ctx, owner, repo, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListIssueReactionsIter returns an iterator that paginates through all results of ListIssueReactions. +func (s *ReactionsService) ListIssueReactionsIter(ctx context.Context, owner string, repo string, number int, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListIssueReactions(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListPullRequestCommentReactionsIter returns an iterator that paginates through all results of ListPullRequestCommentReactions. +func (s *ReactionsService) ListPullRequestCommentReactionsIter(ctx context.Context, owner string, repo string, id int64, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPullRequestCommentReactions(ctx, owner, repo, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListReleaseReactionsIter returns an iterator that paginates through all results of ListReleaseReactions. +func (s *ReactionsService) ListReleaseReactionsIter(ctx context.Context, owner string, repo string, releaseID int64, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListReleaseReactions(ctx, owner, repo, releaseID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListTeamDiscussionCommentReactionsIter returns an iterator that paginates through all results of ListTeamDiscussionCommentReactions. +func (s *ReactionsService) ListTeamDiscussionCommentReactionsIter(ctx context.Context, teamID int64, discussionNumber int, commentNumber int, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamDiscussionCommentReactions(ctx, teamID, discussionNumber, commentNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListTeamDiscussionReactionsIter returns an iterator that paginates through all results of ListTeamDiscussionReactions. +func (s *ReactionsService) ListTeamDiscussionReactionsIter(ctx context.Context, teamID int64, discussionNumber int, opts *ListReactionOptions) iter.Seq2[*Reaction, error] { + return func(yield func(*Reaction, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListReactionOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamDiscussionReactions(ctx, teamID, discussionNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommitComparisonFilesIter returns an iterator that paginates through all results of CompareCommits. +func (s *RepositoriesService) ListCommitComparisonFilesIter(ctx context.Context, owner string, repo string, base string, head string, opts *ListOptions) iter.Seq2[*CommitFile, error] { + return func(yield func(*CommitFile, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.CompareCommits(ctx, owner, repo, base, head, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CommitFile + if results != nil { + iterItems = results.Files + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCombinedStatusIter returns an iterator that paginates through all results of GetCombinedStatus. +func (s *RepositoriesService) ListCombinedStatusIter(ctx context.Context, owner string, repo string, ref string, opts *ListOptions) iter.Seq2[*RepoStatus, error] { + return func(yield func(*RepoStatus, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.GetCombinedStatus(ctx, owner, repo, ref, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*RepoStatus + if results != nil { + iterItems = results.Statuses + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCommitFilesIter returns an iterator that paginates through all results of GetCommit. +func (s *RepositoriesService) ListCommitFilesIter(ctx context.Context, owner string, repo string, sha string, opts *ListOptions) iter.Seq2[*CommitFile, error] { + return func(yield func(*CommitFile, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.GetCommit(ctx, owner, repo, sha, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CommitFile + if results != nil { + iterItems = results.Files + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListIter returns an iterator that paginates through all results of List. +func (s *RepositoriesService) ListIter(ctx context.Context, user string, opts *RepositoryListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &RepositoryListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.List(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAllTopicsIter returns an iterator that paginates through all results of ListAllTopics. +func (s *RepositoriesService) ListAllTopicsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[string, error] { + return func(yield func(string, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAllTopics(ctx, owner, repo, opts) + if err != nil { + yield(*new(string), err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAttestationsIter returns an iterator that paginates through all results of ListAttestations. +func (s *RepositoriesService) ListAttestationsIter(ctx context.Context, owner string, repo string, subjectDigest string, opts *ListOptions) iter.Seq2[*Attestation, error] { + return func(yield func(*Attestation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAttestations(ctx, owner, repo, subjectDigest, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Attestation + if results != nil { + iterItems = results.Attestations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListBranchesIter returns an iterator that paginates through all results of ListBranches. +func (s *RepositoriesService) ListBranchesIter(ctx context.Context, owner string, repo string, opts *BranchListOptions) iter.Seq2[*Branch, error] { + return func(yield func(*Branch, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &BranchListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListBranches(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListByAuthenticatedUserIter returns an iterator that paginates through all results of ListByAuthenticatedUser. +func (s *RepositoriesService) ListByAuthenticatedUserIter(ctx context.Context, opts *RepositoryListByAuthenticatedUserOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &RepositoryListByAuthenticatedUserOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListByAuthenticatedUser(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListByOrgIter returns an iterator that paginates through all results of ListByOrg. +func (s *RepositoriesService) ListByOrgIter(ctx context.Context, org string, opts *RepositoryListByOrgOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &RepositoryListByOrgOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListByOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListByUserIter returns an iterator that paginates through all results of ListByUser. +func (s *RepositoriesService) ListByUserIter(ctx context.Context, user string, opts *RepositoryListByUserOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &RepositoryListByUserOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListByUser(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCollaboratorsIter returns an iterator that paginates through all results of ListCollaborators. +func (s *RepositoriesService) ListCollaboratorsIter(ctx context.Context, owner string, repo string, opts *ListCollaboratorsOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCollaboratorsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCollaborators(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommentsIter returns an iterator that paginates through all results of ListComments. +func (s *RepositoriesService) ListCommentsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*RepositoryComment, error] { + return func(yield func(*RepositoryComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListComments(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCommitCommentsIter returns an iterator that paginates through all results of ListCommitComments. +func (s *RepositoriesService) ListCommitCommentsIter(ctx context.Context, owner string, repo string, sha string, opts *ListOptions) iter.Seq2[*RepositoryComment, error] { + return func(yield func(*RepositoryComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommitComments(ctx, owner, repo, sha, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCommitsIter returns an iterator that paginates through all results of ListCommits. +func (s *RepositoriesService) ListCommitsIter(ctx context.Context, owner string, repo string, opts *CommitsListOptions) iter.Seq2[*RepositoryCommit, error] { + return func(yield func(*RepositoryCommit, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &CommitsListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommits(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListContributorsIter returns an iterator that paginates through all results of ListContributors. +func (s *RepositoriesService) ListContributorsIter(ctx context.Context, owner string, repository string, opts *ListContributorsOptions) iter.Seq2[*Contributor, error] { + return func(yield func(*Contributor, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListContributorsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListContributors(ctx, owner, repository, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCustomDeploymentRuleIntegrationsIter returns an iterator that paginates through all results of ListCustomDeploymentRuleIntegrations. +func (s *RepositoriesService) ListCustomDeploymentRuleIntegrationsIter(ctx context.Context, owner string, repo string, environment string, opts *ListOptions) iter.Seq2[*CustomDeploymentProtectionRuleApp, error] { + return func(yield func(*CustomDeploymentProtectionRuleApp, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCustomDeploymentRuleIntegrations(ctx, owner, repo, environment, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*CustomDeploymentProtectionRuleApp + if results != nil { + iterItems = results.AvailableIntegrations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListDeploymentBranchPoliciesIter returns an iterator that paginates through all results of ListDeploymentBranchPolicies. +func (s *RepositoriesService) ListDeploymentBranchPoliciesIter(ctx context.Context, owner string, repo string, environment string, opts *ListOptions) iter.Seq2[*DeploymentBranchPolicy, error] { + return func(yield func(*DeploymentBranchPolicy, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListDeploymentBranchPolicies(ctx, owner, repo, environment, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*DeploymentBranchPolicy + if results != nil { + iterItems = results.BranchPolicies + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListDeploymentStatusesIter returns an iterator that paginates through all results of ListDeploymentStatuses. +func (s *RepositoriesService) ListDeploymentStatusesIter(ctx context.Context, owner string, repo string, deployment int64, opts *ListOptions) iter.Seq2[*DeploymentStatus, error] { + return func(yield func(*DeploymentStatus, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListDeploymentStatuses(ctx, owner, repo, deployment, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListDeploymentsIter returns an iterator that paginates through all results of ListDeployments. +func (s *RepositoriesService) ListDeploymentsIter(ctx context.Context, owner string, repo string, opts *DeploymentsListOptions) iter.Seq2[*Deployment, error] { + return func(yield func(*Deployment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &DeploymentsListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListDeployments(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListEnvironmentsIter returns an iterator that paginates through all results of ListEnvironments. +func (s *RepositoriesService) ListEnvironmentsIter(ctx context.Context, owner string, repo string, opts *EnvironmentListOptions) iter.Seq2[*Environment, error] { + return func(yield func(*Environment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &EnvironmentListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEnvironments(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Environment + if results != nil { + iterItems = results.Environments + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListForksIter returns an iterator that paginates through all results of ListForks. +func (s *RepositoriesService) ListForksIter(ctx context.Context, owner string, repo string, opts *RepositoryListForksOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &RepositoryListForksOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListForks(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListHookDeliveriesIter returns an iterator that paginates through all results of ListHookDeliveries. +func (s *RepositoriesService) ListHookDeliveriesIter(ctx context.Context, owner string, repo string, id int64, opts *ListCursorOptions) iter.Seq2[*HookDelivery, error] { + return func(yield func(*HookDelivery, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCursorOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHookDeliveries(ctx, owner, repo, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.Cursor == "" { + break + } + opts.Cursor = resp.Cursor + } + } +} + +// ListHooksIter returns an iterator that paginates through all results of ListHooks. +func (s *RepositoriesService) ListHooksIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Hook, error] { + return func(yield func(*Hook, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListHooks(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListInvitationsIter returns an iterator that paginates through all results of ListInvitations. +func (s *RepositoriesService) ListInvitationsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*RepositoryInvitation, error] { + return func(yield func(*RepositoryInvitation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInvitations(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListKeysIter returns an iterator that paginates through all results of ListKeys. +func (s *RepositoriesService) ListKeysIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Key, error] { + return func(yield func(*Key, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListKeys(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPagesBuildsIter returns an iterator that paginates through all results of ListPagesBuilds. +func (s *RepositoriesService) ListPagesBuildsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*PagesBuild, error] { + return func(yield func(*PagesBuild, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPagesBuilds(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPreReceiveHooksIter returns an iterator that paginates through all results of ListPreReceiveHooks. +func (s *RepositoriesService) ListPreReceiveHooksIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*PreReceiveHook, error] { + return func(yield func(*PreReceiveHook, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPreReceiveHooks(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListReleaseAssetsIter returns an iterator that paginates through all results of ListReleaseAssets. +func (s *RepositoriesService) ListReleaseAssetsIter(ctx context.Context, owner string, repo string, id int64, opts *ListOptions) iter.Seq2[*ReleaseAsset, error] { + return func(yield func(*ReleaseAsset, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListReleaseAssets(ctx, owner, repo, id, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListReleasesIter returns an iterator that paginates through all results of ListReleases. +func (s *RepositoriesService) ListReleasesIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*RepositoryRelease, error] { + return func(yield func(*RepositoryRelease, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListReleases(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListRepositoryActivitiesIter returns an iterator that paginates through all results of ListRepositoryActivities. +func (s *RepositoriesService) ListRepositoryActivitiesIter(ctx context.Context, owner string, repo string, opts *ListRepositoryActivityOptions) iter.Seq2[*RepositoryActivity, error] { + return func(yield func(*RepositoryActivity, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListRepositoryActivityOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositoryActivities(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.After = resp.After + } + } +} + +// ListStatusesIter returns an iterator that paginates through all results of ListStatuses. +func (s *RepositoriesService) ListStatusesIter(ctx context.Context, owner string, repo string, ref string, opts *ListOptions) iter.Seq2[*RepoStatus, error] { + return func(yield func(*RepoStatus, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListStatuses(ctx, owner, repo, ref, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTagsIter returns an iterator that paginates through all results of ListTags. +func (s *RepositoriesService) ListTagsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*RepositoryTag, error] { + return func(yield func(*RepositoryTag, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTags(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTeamsIter returns an iterator that paginates through all results of ListTeams. +func (s *RepositoriesService) ListTeamsIter(ctx context.Context, owner string, repo string, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeams(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAlertsForEnterpriseIter returns an iterator that paginates through all results of ListAlertsForEnterprise. +func (s *SecretScanningService) ListAlertsForEnterpriseIter(ctx context.Context, enterprise string, opts *SecretScanningAlertListOptions) iter.Seq2[*SecretScanningAlert, error] { + return func(yield func(*SecretScanningAlert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningAlertListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAlertsForEnterprise(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAlertsForOrgIter returns an iterator that paginates through all results of ListAlertsForOrg. +func (s *SecretScanningService) ListAlertsForOrgIter(ctx context.Context, org string, opts *SecretScanningAlertListOptions) iter.Seq2[*SecretScanningAlert, error] { + return func(yield func(*SecretScanningAlert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningAlertListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAlertsForOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListAlertsForRepoIter returns an iterator that paginates through all results of ListAlertsForRepo. +func (s *SecretScanningService) ListAlertsForRepoIter(ctx context.Context, owner string, repo string, opts *SecretScanningAlertListOptions) iter.Seq2[*SecretScanningAlert, error] { + return func(yield func(*SecretScanningAlert, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningAlertListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAlertsForRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" && resp.NextPage == 0 { + break + } + opts.ListCursorOptions.After = resp.After + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCustomPatternsForEnterpriseIter returns an iterator that paginates through all results of ListCustomPatternsForEnterprise. +func (s *SecretScanningService) ListCustomPatternsForEnterpriseIter(ctx context.Context, enterprise string, opts *SecretScanningCustomPatternListOptions) iter.Seq2[*SecretScanningCustomPattern, error] { + return func(yield func(*SecretScanningCustomPattern, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningCustomPatternListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCustomPatternsForEnterprise(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCustomPatternsForOrgIter returns an iterator that paginates through all results of ListCustomPatternsForOrg. +func (s *SecretScanningService) ListCustomPatternsForOrgIter(ctx context.Context, org string, opts *SecretScanningCustomPatternListOptions) iter.Seq2[*SecretScanningCustomPattern, error] { + return func(yield func(*SecretScanningCustomPattern, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningCustomPatternListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCustomPatternsForOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCustomPatternsForRepoIter returns an iterator that paginates through all results of ListCustomPatternsForRepo. +func (s *SecretScanningService) ListCustomPatternsForRepoIter(ctx context.Context, owner string, repo string, opts *SecretScanningCustomPatternListOptions) iter.Seq2[*SecretScanningCustomPattern, error] { + return func(yield func(*SecretScanningCustomPattern, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &SecretScanningCustomPatternListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCustomPatternsForRepo(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListLocationsForAlertIter returns an iterator that paginates through all results of ListLocationsForAlert. +func (s *SecretScanningService) ListLocationsForAlertIter(ctx context.Context, owner string, repo string, number int64, opts *ListOptions) iter.Seq2[*SecretScanningAlertLocation, error] { + return func(yield func(*SecretScanningAlertLocation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListLocationsForAlert(ctx, owner, repo, number, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListGlobalSecurityAdvisoriesIter returns an iterator that paginates through all results of ListGlobalSecurityAdvisories. +func (s *SecurityAdvisoriesService) ListGlobalSecurityAdvisoriesIter(ctx context.Context, opts *ListGlobalSecurityAdvisoriesOptions) iter.Seq2[*GlobalSecurityAdvisory, error] { + return func(yield func(*GlobalSecurityAdvisory, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListGlobalSecurityAdvisoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListGlobalSecurityAdvisories(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + +// ListRepositorySecurityAdvisoriesIter returns an iterator that paginates through all results of ListRepositorySecurityAdvisories. +func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisoriesIter(ctx context.Context, owner string, repo string, opts *ListRepositorySecurityAdvisoriesOptions) iter.Seq2[*SecurityAdvisory, error] { + return func(yield func(*SecurityAdvisory, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListRepositorySecurityAdvisoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositorySecurityAdvisories(ctx, owner, repo, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + +// ListRepositorySecurityAdvisoriesForOrgIter returns an iterator that paginates through all results of ListRepositorySecurityAdvisoriesForOrg. +func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisoriesForOrgIter(ctx context.Context, org string, opts *ListRepositorySecurityAdvisoriesOptions) iter.Seq2[*SecurityAdvisory, error] { + return func(yield func(*SecurityAdvisory, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListRepositorySecurityAdvisoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRepositorySecurityAdvisoriesForOrg(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + +// ListByIssueIter returns an iterator that paginates through all results of ListByIssue. +func (s *SubIssueService) ListByIssueIter(ctx context.Context, owner string, repo string, issueNumber int64, opts *ListOptions) iter.Seq2[*SubIssue, error] { + return func(yield func(*SubIssue, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListByIssue(ctx, owner, repo, issueNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListChildTeamsByParentIDIter returns an iterator that paginates through all results of ListChildTeamsByParentID. +func (s *TeamsService) ListChildTeamsByParentIDIter(ctx context.Context, orgID int64, teamID int64, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListChildTeamsByParentID(ctx, orgID, teamID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListChildTeamsByParentSlugIter returns an iterator that paginates through all results of ListChildTeamsByParentSlug. +func (s *TeamsService) ListChildTeamsByParentSlugIter(ctx context.Context, org string, slug string, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListChildTeamsByParentSlug(ctx, org, slug, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListCommentsByIDIter returns an iterator that paginates through all results of ListCommentsByID. +func (s *TeamsService) ListCommentsByIDIter(ctx context.Context, orgID int64, teamID int64, discussionNumber int, opts *DiscussionCommentListOptions) iter.Seq2[*DiscussionComment, error] { + return func(yield func(*DiscussionComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &DiscussionCommentListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommentsByID(ctx, orgID, teamID, discussionNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListCommentsBySlugIter returns an iterator that paginates through all results of ListCommentsBySlug. +func (s *TeamsService) ListCommentsBySlugIter(ctx context.Context, org string, slug string, discussionNumber int, opts *DiscussionCommentListOptions) iter.Seq2[*DiscussionComment, error] { + return func(yield func(*DiscussionComment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &DiscussionCommentListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListCommentsBySlug(ctx, org, slug, discussionNumber, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListDiscussionsByIDIter returns an iterator that paginates through all results of ListDiscussionsByID. +func (s *TeamsService) ListDiscussionsByIDIter(ctx context.Context, orgID int64, teamID int64, opts *DiscussionListOptions) iter.Seq2[*TeamDiscussion, error] { + return func(yield func(*TeamDiscussion, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &DiscussionListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListDiscussionsByID(ctx, orgID, teamID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListDiscussionsBySlugIter returns an iterator that paginates through all results of ListDiscussionsBySlug. +func (s *TeamsService) ListDiscussionsBySlugIter(ctx context.Context, org string, slug string, opts *DiscussionListOptions) iter.Seq2[*TeamDiscussion, error] { + return func(yield func(*TeamDiscussion, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &DiscussionListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListDiscussionsBySlug(ctx, org, slug, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListExternalGroupsIter returns an iterator that paginates through all results of ListExternalGroups. +func (s *TeamsService) ListExternalGroupsIter(ctx context.Context, org string, opts *ListExternalGroupsOptions) iter.Seq2[*ExternalGroup, error] { + return func(yield func(*ExternalGroup, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListExternalGroupsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListExternalGroups(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*ExternalGroup + if results != nil { + iterItems = results.Groups + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListIDPGroupsInOrganizationIter returns an iterator that paginates through all results of ListIDPGroupsInOrganization. +func (s *TeamsService) ListIDPGroupsInOrganizationIter(ctx context.Context, org string, opts *ListIDPGroupsOptions) iter.Seq2[*IDPGroup, error] { + return func(yield func(*IDPGroup, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListIDPGroupsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListIDPGroupsInOrganization(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*IDPGroup + if results != nil { + iterItems = results.Groups + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + +// ListPendingTeamInvitationsByIDIter returns an iterator that paginates through all results of ListPendingTeamInvitationsByID. +func (s *TeamsService) ListPendingTeamInvitationsByIDIter(ctx context.Context, orgID int64, teamID int64, opts *ListOptions) iter.Seq2[*Invitation, error] { + return func(yield func(*Invitation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPendingTeamInvitationsByID(ctx, orgID, teamID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPendingTeamInvitationsBySlugIter returns an iterator that paginates through all results of ListPendingTeamInvitationsBySlug. +func (s *TeamsService) ListPendingTeamInvitationsBySlugIter(ctx context.Context, org string, slug string, opts *ListOptions) iter.Seq2[*Invitation, error] { + return func(yield func(*Invitation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPendingTeamInvitationsBySlug(ctx, org, slug, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTeamMembersByIDIter returns an iterator that paginates through all results of ListTeamMembersByID. +func (s *TeamsService) ListTeamMembersByIDIter(ctx context.Context, orgID int64, teamID int64, opts *TeamListTeamMembersOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &TeamListTeamMembersOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamMembersByID(ctx, orgID, teamID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListTeamMembersBySlugIter returns an iterator that paginates through all results of ListTeamMembersBySlug. +func (s *TeamsService) ListTeamMembersBySlugIter(ctx context.Context, org string, slug string, opts *TeamListTeamMembersOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &TeamListTeamMembersOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamMembersBySlug(ctx, org, slug, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListTeamReposByIDIter returns an iterator that paginates through all results of ListTeamReposByID. +func (s *TeamsService) ListTeamReposByIDIter(ctx context.Context, orgID int64, teamID int64, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamReposByID(ctx, orgID, teamID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTeamReposBySlugIter returns an iterator that paginates through all results of ListTeamReposBySlug. +func (s *TeamsService) ListTeamReposBySlugIter(ctx context.Context, org string, slug string, opts *ListOptions) iter.Seq2[*Repository, error] { + return func(yield func(*Repository, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeamReposBySlug(ctx, org, slug, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListTeamsIter returns an iterator that paginates through all results of ListTeams. +func (s *TeamsService) ListTeamsIter(ctx context.Context, org string, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListTeams(ctx, org, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserTeamsIter returns an iterator that paginates through all results of ListUserTeams. +func (s *TeamsService) ListUserTeamsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*Team, error] { + return func(yield func(*Team, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserTeams(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListAttestationsIter returns an iterator that paginates through all results of ListAttestations. +func (s *UsersService) ListAttestationsIter(ctx context.Context, user string, subjectDigest string, opts *ListOptions) iter.Seq2[*Attestation, error] { + return func(yield func(*Attestation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListAttestations(ctx, user, subjectDigest, opts) + if err != nil { + yield(nil, err) + return + } + + var iterItems []*Attestation + if results != nil { + iterItems = results.Attestations + } + for _, item := range iterItems { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListBlockedUsersIter returns an iterator that paginates through all results of ListBlockedUsers. +func (s *UsersService) ListBlockedUsersIter(ctx context.Context, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListBlockedUsers(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListEmailsIter returns an iterator that paginates through all results of ListEmails. +func (s *UsersService) ListEmailsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*UserEmail, error] { + return func(yield func(*UserEmail, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListEmails(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListFollowersIter returns an iterator that paginates through all results of ListFollowers. +func (s *UsersService) ListFollowersIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFollowers(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListFollowingIter returns an iterator that paginates through all results of ListFollowing. +func (s *UsersService) ListFollowingIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*User, error] { + return func(yield func(*User, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListFollowing(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListGPGKeysIter returns an iterator that paginates through all results of ListGPGKeys. +func (s *UsersService) ListGPGKeysIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*GPGKey, error] { + return func(yield func(*GPGKey, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListGPGKeys(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListInvitationsIter returns an iterator that paginates through all results of ListInvitations. +func (s *UsersService) ListInvitationsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*RepositoryInvitation, error] { + return func(yield func(*RepositoryInvitation, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListInvitations(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListKeysIter returns an iterator that paginates through all results of ListKeys. +func (s *UsersService) ListKeysIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*Key, error] { + return func(yield func(*Key, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListKeys(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListPackageVersionsIter returns an iterator that paginates through all results of ListPackageVersions. +func (s *UsersService) ListPackageVersionsIter(ctx context.Context, packageType string, packageName string, opts *ListPackageVersionsOptions) iter.Seq2[*PackageVersion, error] { + return func(yield func(*PackageVersion, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListPackageVersionsOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPackageVersions(ctx, packageType, packageName, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListPackagesIter returns an iterator that paginates through all results of ListPackages. +func (s *UsersService) ListPackagesIter(ctx context.Context, user string, opts *PackageListOptions) iter.Seq2[*Package, error] { + return func(yield func(*Package, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &PackageListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListPackages(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + +// ListSSHSigningKeysIter returns an iterator that paginates through all results of ListSSHSigningKeys. +func (s *UsersService) ListSSHSigningKeysIter(ctx context.Context, user string, opts *ListOptions) iter.Seq2[*SSHSigningKey, error] { + return func(yield func(*SSHSigningKey, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSSHSigningKeys(ctx, user, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListSocialAccountsIter returns an iterator that paginates through all results of ListSocialAccounts. +func (s *UsersService) ListSocialAccountsIter(ctx context.Context, opts *ListOptions) iter.Seq2[*SocialAccount, error] { + return func(yield func(*SocialAccount, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListSocialAccounts(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// ListUserSocialAccountsIter returns an iterator that paginates through all results of ListUserSocialAccounts. +func (s *UsersService) ListUserSocialAccountsIter(ctx context.Context, username string, opts *ListOptions) iter.Seq2[*SocialAccount, error] { + return func(yield func(*SocialAccount, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListUserSocialAccounts(ctx, username, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range results { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} diff --git a/github/github-iterators_test.go b/github/github-iterators_test.go new file mode 100644 index 00000000000..75bc9f90c2b --- /dev/null +++ b/github/github-iterators_test.go @@ -0,0 +1,17584 @@ +// Code generated by gen-iterators; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" +) + +func TestActionsService_ListArtifactsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"artifacts": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"artifacts": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"artifacts": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"artifacts": [{},{}]}`) + } + }) + + iter := client.Actions.ListArtifactsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListArtifactsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListArtifactsOptions{} + iter = client.Actions.ListArtifactsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListArtifactsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListArtifactsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListArtifactsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListArtifactsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Artifact, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListArtifactsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListCacheUsageByRepoForOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repository_cache_usages": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repository_cache_usages": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repository_cache_usages": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repository_cache_usages": [{},{}]}`) + } + }) + + iter := client.Actions.ListCacheUsageByRepoForOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListCacheUsageByRepoForOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListCacheUsageByRepoForOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListCacheUsageByRepoForOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListCacheUsageByRepoForOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListCacheUsageByRepoForOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListCacheUsageByRepoForOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *ActionsCacheUsage, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListCacheUsageByRepoForOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListCachesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"actions_caches": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"actions_caches": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"actions_caches": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"actions_caches": [{},{}]}`) + } + }) + + iter := client.Actions.ListCachesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListCachesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ActionsCacheListOptions{} + iter = client.Actions.ListCachesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListCachesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListCachesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListCachesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListCachesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *ActionsCache, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListCachesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListEnabledOrgsInEnterpriseIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"organizations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"organizations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"organizations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"organizations": [{},{}]}`) + } + }) + + iter := client.Actions.ListEnabledOrgsInEnterpriseIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListEnabledOrgsInEnterpriseIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListEnabledOrgsInEnterpriseIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListEnabledOrgsInEnterpriseIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListEnabledOrgsInEnterpriseIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListEnabledOrgsInEnterpriseIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListEnabledOrgsInEnterpriseIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Organization, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListEnabledOrgsInEnterpriseIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListEnabledReposInOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Actions.ListEnabledReposInOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListEnabledReposInOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListEnabledReposInOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListEnabledReposInOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListEnabledReposInOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListEnabledReposInOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListEnabledReposInOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListEnabledReposInOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListEnvSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Actions.ListEnvSecretsIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListEnvSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListEnvSecretsIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListEnvSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListEnvSecretsIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListEnvSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListEnvSecretsIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListEnvSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListEnvVariablesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"variables": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"variables": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"variables": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"variables": [{},{}]}`) + } + }) + + iter := client.Actions.ListEnvVariablesIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListEnvVariablesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListEnvVariablesIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListEnvVariablesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListEnvVariablesIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListEnvVariablesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListEnvVariablesIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *ActionsVariable, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListEnvVariablesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListHostedRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Actions.ListHostedRunnersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListHostedRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListHostedRunnersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListHostedRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListHostedRunnersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListHostedRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListHostedRunnersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *HostedRunner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListHostedRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListOrgSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Actions.ListOrgSecretsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListOrgSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListOrgSecretsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListOrgSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListOrgSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListOrgSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListOrgVariablesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"variables": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"variables": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"variables": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"variables": [{},{}]}`) + } + }) + + iter := client.Actions.ListOrgVariablesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListOrgVariablesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListOrgVariablesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListOrgVariablesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListOrgVariablesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListOrgVariablesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListOrgVariablesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *ActionsVariable, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListOrgVariablesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListOrganizationRunnerGroupsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runner_groups": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runner_groups": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runner_groups": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runner_groups": [{},{}]}`) + } + }) + + iter := client.Actions.ListOrganizationRunnerGroupsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListOrganizationRunnerGroupsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOrgRunnerGroupOptions{} + iter = client.Actions.ListOrganizationRunnerGroupsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListOrganizationRunnerGroupsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListOrganizationRunnerGroupsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListOrganizationRunnerGroupsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListOrganizationRunnerGroupsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *RunnerGroup, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListOrganizationRunnerGroupsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListOrganizationRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Actions.ListOrganizationRunnersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListOrganizationRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListRunnersOptions{} + iter = client.Actions.ListOrganizationRunnersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListOrganizationRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListOrganizationRunnersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListOrganizationRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListOrganizationRunnersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Runner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListOrganizationRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepoOrgSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepoOrgSecretsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepoOrgSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRepoOrgSecretsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepoOrgSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepoOrgSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoOrgSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepoOrgSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoOrgSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepoOrgVariablesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"variables": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"variables": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"variables": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"variables": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepoOrgVariablesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepoOrgVariablesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRepoOrgVariablesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepoOrgVariablesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepoOrgVariablesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoOrgVariablesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepoOrgVariablesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *ActionsVariable, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoOrgVariablesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepoSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepoSecretsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepoSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRepoSecretsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepoSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepoVariablesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"variables": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"variables": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"variables": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"variables": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepoVariablesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepoVariablesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRepoVariablesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepoVariablesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepoVariablesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoVariablesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepoVariablesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *ActionsVariable, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepoVariablesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganizationIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepositoryAccessRunnerGroupIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepositoryAccessRunnerGroupIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepositoryAccessRunnerGroupIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRepositoryAccessRunnerGroupIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepositoryAccessRunnerGroupIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepositoryAccessRunnerGroupIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepositoryAccessRunnerGroupIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepositoryAccessRunnerGroupIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepositoryAccessRunnerGroupIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRepositoryWorkflowRunsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"workflow_runs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"workflow_runs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"workflow_runs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"workflow_runs": [{},{}]}`) + } + }) + + iter := client.Actions.ListRepositoryWorkflowRunsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRepositoryWorkflowRunsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListWorkflowRunsOptions{} + iter = client.Actions.ListRepositoryWorkflowRunsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRepositoryWorkflowRunsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRepositoryWorkflowRunsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRepositoryWorkflowRunsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRepositoryWorkflowRunsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *WorkflowRun, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRepositoryWorkflowRunsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRunnerGroupHostedRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Actions.ListRunnerGroupHostedRunnersIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRunnerGroupHostedRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRunnerGroupHostedRunnersIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRunnerGroupHostedRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRunnerGroupHostedRunnersIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRunnerGroupHostedRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRunnerGroupHostedRunnersIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *HostedRunner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRunnerGroupHostedRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRunnerGroupRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Actions.ListRunnerGroupRunnersIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRunnerGroupRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListRunnerGroupRunnersIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRunnerGroupRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRunnerGroupRunnersIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRunnerGroupRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRunnerGroupRunnersIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *Runner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRunnerGroupRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Actions.ListRunnersIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListRunnersOptions{} + iter = client.Actions.ListRunnersIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListRunnersIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListRunnersIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Runner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListSelectedReposForOrgSecretIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Actions.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListSelectedReposForOrgSecretIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListSelectedReposForOrgSecretIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListSelectedReposForOrgSecretIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListSelectedReposForOrgSecretIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListSelectedReposForOrgSecretIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListSelectedReposForOrgVariableIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Actions.ListSelectedReposForOrgVariableIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListSelectedReposForOrgVariableIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListSelectedReposForOrgVariableIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListSelectedReposForOrgVariableIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListSelectedReposForOrgVariableIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListSelectedReposForOrgVariableIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListSelectedReposForOrgVariableIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListSelectedReposForOrgVariableIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListWorkflowJobsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"jobs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"jobs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"jobs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"jobs": [{},{}]}`) + } + }) + + iter := client.Actions.ListWorkflowJobsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListWorkflowJobsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListWorkflowJobsOptions{} + iter = client.Actions.ListWorkflowJobsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListWorkflowJobsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListWorkflowJobsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowJobsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListWorkflowJobsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *WorkflowJob, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowJobsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListWorkflowJobsAttemptIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"jobs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"jobs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"jobs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"jobs": [{},{}]}`) + } + }) + + iter := client.Actions.ListWorkflowJobsAttemptIter(t.Context(), "", "", 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListWorkflowJobsAttemptIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListWorkflowJobsAttemptIter(t.Context(), "", "", 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListWorkflowJobsAttemptIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListWorkflowJobsAttemptIter(t.Context(), "", "", 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowJobsAttemptIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListWorkflowJobsAttemptIter(t.Context(), "", "", 0, 0, nil) + gotItems = 0 + iter(func(item *WorkflowJob, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowJobsAttemptIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListWorkflowRunArtifactsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"artifacts": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"artifacts": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"artifacts": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"artifacts": [{},{}]}`) + } + }) + + iter := client.Actions.ListWorkflowRunArtifactsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListWorkflowRunArtifactsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListWorkflowRunArtifactsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListWorkflowRunArtifactsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListWorkflowRunArtifactsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowRunArtifactsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListWorkflowRunArtifactsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Artifact, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowRunArtifactsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListWorkflowRunsByFileNameIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"workflow_runs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"workflow_runs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"workflow_runs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"workflow_runs": [{},{}]}`) + } + }) + + iter := client.Actions.ListWorkflowRunsByFileNameIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListWorkflowRunsByFileNameIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListWorkflowRunsOptions{} + iter = client.Actions.ListWorkflowRunsByFileNameIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListWorkflowRunsByFileNameIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListWorkflowRunsByFileNameIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowRunsByFileNameIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListWorkflowRunsByFileNameIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *WorkflowRun, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowRunsByFileNameIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListWorkflowRunsByIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"workflow_runs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"workflow_runs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"workflow_runs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"workflow_runs": [{},{}]}`) + } + }) + + iter := client.Actions.ListWorkflowRunsByIDIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListWorkflowRunsByIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListWorkflowRunsOptions{} + iter = client.Actions.ListWorkflowRunsByIDIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListWorkflowRunsByIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListWorkflowRunsByIDIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowRunsByIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListWorkflowRunsByIDIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *WorkflowRun, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowRunsByIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActionsService_ListWorkflowsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"workflows": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"workflows": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"workflows": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"workflows": [{},{}]}`) + } + }) + + iter := client.Actions.ListWorkflowsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Actions.ListWorkflowsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Actions.ListWorkflowsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Actions.ListWorkflowsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Actions.ListWorkflowsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Actions.ListWorkflowsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Workflow, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Actions.ListWorkflowsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListEventsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListEventsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListEventsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListEventsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListEventsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListEventsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListEventsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListEventsForOrganizationIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListEventsForOrganizationIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListEventsForOrganizationIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListEventsForOrganizationIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListEventsForOrganizationIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListEventsForOrganizationIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsForOrganizationIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListEventsForOrganizationIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsForOrganizationIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListEventsForRepoNetworkIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListEventsForRepoNetworkIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListEventsForRepoNetworkIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListEventsForRepoNetworkIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListEventsForRepoNetworkIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListEventsForRepoNetworkIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsForRepoNetworkIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListEventsForRepoNetworkIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsForRepoNetworkIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListEventsPerformedByUserIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListEventsPerformedByUserIter(t.Context(), "", false, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListEventsPerformedByUserIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListEventsPerformedByUserIter(t.Context(), "", false, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListEventsPerformedByUserIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListEventsPerformedByUserIter(t.Context(), "", false, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsPerformedByUserIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListEventsPerformedByUserIter(t.Context(), "", false, nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsPerformedByUserIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListEventsReceivedByUserIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListEventsReceivedByUserIter(t.Context(), "", false, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListEventsReceivedByUserIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListEventsReceivedByUserIter(t.Context(), "", false, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListEventsReceivedByUserIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListEventsReceivedByUserIter(t.Context(), "", false, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsReceivedByUserIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListEventsReceivedByUserIter(t.Context(), "", false, nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListEventsReceivedByUserIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListIssueEventsForRepositoryIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListIssueEventsForRepositoryIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListIssueEventsForRepositoryIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListIssueEventsForRepositoryIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListIssueEventsForRepositoryIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListIssueEventsForRepositoryIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListIssueEventsForRepositoryIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListIssueEventsForRepositoryIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *IssueEvent, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListIssueEventsForRepositoryIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListNotificationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListNotificationsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListNotificationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &NotificationListOptions{} + iter = client.Activity.ListNotificationsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListNotificationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListNotificationsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListNotificationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListNotificationsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Notification, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListNotificationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListRepositoryEventsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListRepositoryEventsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListRepositoryEventsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListRepositoryEventsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListRepositoryEventsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListRepositoryEventsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListRepositoryEventsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListRepositoryEventsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListRepositoryEventsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListRepositoryNotificationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListRepositoryNotificationsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListRepositoryNotificationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &NotificationListOptions{} + iter = client.Activity.ListRepositoryNotificationsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListRepositoryNotificationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListRepositoryNotificationsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListRepositoryNotificationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListRepositoryNotificationsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Notification, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListRepositoryNotificationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListStargazersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListStargazersIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListStargazersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListStargazersIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListStargazersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListStargazersIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListStargazersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListStargazersIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Stargazer, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListStargazersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListStarredIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListStarredIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListStarredIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ActivityListStarredOptions{} + iter = client.Activity.ListStarredIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListStarredIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListStarredIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListStarredIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListStarredIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *StarredRepository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListStarredIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListUserEventsForOrganizationIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListUserEventsForOrganizationIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListUserEventsForOrganizationIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListUserEventsForOrganizationIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListUserEventsForOrganizationIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListUserEventsForOrganizationIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListUserEventsForOrganizationIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListUserEventsForOrganizationIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Event, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListUserEventsForOrganizationIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListWatchedIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListWatchedIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListWatchedIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListWatchedIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListWatchedIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListWatchedIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListWatchedIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListWatchedIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListWatchedIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestActivityService_ListWatchersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Activity.ListWatchersIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Activity.ListWatchersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Activity.ListWatchersIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Activity.ListWatchersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Activity.ListWatchersIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Activity.ListWatchersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Activity.ListWatchersIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Activity.ListWatchersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAppsService_ListHookDeliveriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Apps.ListHookDeliveriesIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Apps.ListHookDeliveriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCursorOptions{} + iter = client.Apps.ListHookDeliveriesIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Apps.ListHookDeliveriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Apps.ListHookDeliveriesIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Apps.ListHookDeliveriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Apps.ListHookDeliveriesIter(t.Context(), nil) + gotItems = 0 + iter(func(item *HookDelivery, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Apps.ListHookDeliveriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAppsService_ListInstallationRequestsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Apps.ListInstallationRequestsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Apps.ListInstallationRequestsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Apps.ListInstallationRequestsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Apps.ListInstallationRequestsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Apps.ListInstallationRequestsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Apps.ListInstallationRequestsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Apps.ListInstallationRequestsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *InstallationRequest, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Apps.ListInstallationRequestsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAppsService_ListInstallationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Apps.ListInstallationsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Apps.ListInstallationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Apps.ListInstallationsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Apps.ListInstallationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Apps.ListInstallationsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Apps.ListInstallationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Apps.ListInstallationsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Installation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Apps.ListInstallationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAppsService_ListReposIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Apps.ListReposIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Apps.ListReposIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Apps.ListReposIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Apps.ListReposIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Apps.ListReposIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Apps.ListReposIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Apps.ListReposIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Apps.ListReposIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAppsService_ListUserInstallationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"installations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"installations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"installations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"installations": [{},{}]}`) + } + }) + + iter := client.Apps.ListUserInstallationsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Apps.ListUserInstallationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Apps.ListUserInstallationsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Apps.ListUserInstallationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Apps.ListUserInstallationsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Apps.ListUserInstallationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Apps.ListUserInstallationsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Installation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Apps.ListUserInstallationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestAppsService_ListUserReposIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Apps.ListUserReposIter(t.Context(), 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Apps.ListUserReposIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Apps.ListUserReposIter(t.Context(), 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Apps.ListUserReposIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Apps.ListUserReposIter(t.Context(), 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Apps.ListUserReposIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Apps.ListUserReposIter(t.Context(), 0, nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Apps.ListUserReposIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestChecksService_ListCheckRunAnnotationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Checks.ListCheckRunAnnotationsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Checks.ListCheckRunAnnotationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Checks.ListCheckRunAnnotationsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Checks.ListCheckRunAnnotationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Checks.ListCheckRunAnnotationsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckRunAnnotationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Checks.ListCheckRunAnnotationsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *CheckRunAnnotation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckRunAnnotationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestChecksService_ListCheckRunsCheckSuiteIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"check_runs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"check_runs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"check_runs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"check_runs": [{},{}]}`) + } + }) + + iter := client.Checks.ListCheckRunsCheckSuiteIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Checks.ListCheckRunsCheckSuiteIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCheckRunsOptions{} + iter = client.Checks.ListCheckRunsCheckSuiteIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Checks.ListCheckRunsCheckSuiteIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Checks.ListCheckRunsCheckSuiteIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckRunsCheckSuiteIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Checks.ListCheckRunsCheckSuiteIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *CheckRun, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckRunsCheckSuiteIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestChecksService_ListCheckRunsForRefIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"check_runs": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"check_runs": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"check_runs": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"check_runs": [{},{}]}`) + } + }) + + iter := client.Checks.ListCheckRunsForRefIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Checks.ListCheckRunsForRefIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCheckRunsOptions{} + iter = client.Checks.ListCheckRunsForRefIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Checks.ListCheckRunsForRefIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Checks.ListCheckRunsForRefIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckRunsForRefIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Checks.ListCheckRunsForRefIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *CheckRun, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckRunsForRefIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestChecksService_ListCheckSuitesForRefIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"check_suites": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"check_suites": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"check_suites": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"check_suites": [{},{}]}`) + } + }) + + iter := client.Checks.ListCheckSuitesForRefIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Checks.ListCheckSuitesForRefIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCheckSuiteOptions{} + iter = client.Checks.ListCheckSuitesForRefIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Checks.ListCheckSuitesForRefIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Checks.ListCheckSuitesForRefIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckSuitesForRefIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Checks.ListCheckSuitesForRefIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *CheckSuite, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Checks.ListCheckSuitesForRefIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestClassroomService_ListAcceptedAssignmentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Classroom.ListAcceptedAssignmentsIter(t.Context(), 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Classroom.ListAcceptedAssignmentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Classroom.ListAcceptedAssignmentsIter(t.Context(), 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Classroom.ListAcceptedAssignmentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Classroom.ListAcceptedAssignmentsIter(t.Context(), 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Classroom.ListAcceptedAssignmentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Classroom.ListAcceptedAssignmentsIter(t.Context(), 0, nil) + gotItems = 0 + iter(func(item *AcceptedAssignment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Classroom.ListAcceptedAssignmentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestClassroomService_ListClassroomAssignmentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Classroom.ListClassroomAssignmentsIter(t.Context(), 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Classroom.ListClassroomAssignmentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Classroom.ListClassroomAssignmentsIter(t.Context(), 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Classroom.ListClassroomAssignmentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Classroom.ListClassroomAssignmentsIter(t.Context(), 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Classroom.ListClassroomAssignmentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Classroom.ListClassroomAssignmentsIter(t.Context(), 0, nil) + gotItems = 0 + iter(func(item *ClassroomAssignment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Classroom.ListClassroomAssignmentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestClassroomService_ListClassroomsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Classroom.ListClassroomsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Classroom.ListClassroomsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Classroom.ListClassroomsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Classroom.ListClassroomsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Classroom.ListClassroomsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Classroom.ListClassroomsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Classroom.ListClassroomsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Classroom, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Classroom.ListClassroomsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodeQualityService_ListFindingsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.CodeQuality.ListFindingsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.CodeQuality.ListFindingsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCodeQualityFindingsOptions{} + iter = client.CodeQuality.ListFindingsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.CodeQuality.ListFindingsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.CodeQuality.ListFindingsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.CodeQuality.ListFindingsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.CodeQuality.ListFindingsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *CodeQualityFinding, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.CodeQuality.ListFindingsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodeScanningService_ListAlertInstancesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.CodeScanning.ListAlertInstancesIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.CodeScanning.ListAlertInstancesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &AlertInstancesListOptions{} + iter = client.CodeScanning.ListAlertInstancesIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.CodeScanning.ListAlertInstancesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.CodeScanning.ListAlertInstancesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAlertInstancesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.CodeScanning.ListAlertInstancesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *MostRecentInstance, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAlertInstancesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodeScanningService_ListAlertsForOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.CodeScanning.ListAlertsForOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.CodeScanning.ListAlertsForOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &AlertListOptions{} + iter = client.CodeScanning.ListAlertsForOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.CodeScanning.ListAlertsForOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.CodeScanning.ListAlertsForOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAlertsForOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.CodeScanning.ListAlertsForOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Alert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAlertsForOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodeScanningService_ListAlertsForRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.CodeScanning.ListAlertsForRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.CodeScanning.ListAlertsForRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &AlertListOptions{} + iter = client.CodeScanning.ListAlertsForRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.CodeScanning.ListAlertsForRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.CodeScanning.ListAlertsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAlertsForRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.CodeScanning.ListAlertsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Alert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAlertsForRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodeScanningService_ListAnalysesForRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.CodeScanning.ListAnalysesForRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.CodeScanning.ListAnalysesForRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &AnalysesListOptions{} + iter = client.CodeScanning.ListAnalysesForRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.CodeScanning.ListAnalysesForRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.CodeScanning.ListAnalysesForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAnalysesForRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.CodeScanning.ListAnalysesForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *ScanningAnalysis, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.CodeScanning.ListAnalysesForRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"codespaces": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"codespaces": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCodespacesOptions{} + iter = client.Codespaces.ListIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Codespace, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListDevContainerConfigurationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"devcontainers": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"devcontainers": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"devcontainers": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"devcontainers": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListDevContainerConfigurationsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListDevContainerConfigurationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListDevContainerConfigurationsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListDevContainerConfigurationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListDevContainerConfigurationsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListDevContainerConfigurationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListDevContainerConfigurationsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *DevContainer, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListDevContainerConfigurationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListInOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"codespaces": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"codespaces": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListInOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListInOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListInOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListInOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListInOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListInOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListInOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Codespace, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListInOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListInRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"codespaces": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"codespaces": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListInRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListInRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListInRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListInRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListInRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListInRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListInRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Codespace, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListInRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListOrgSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListOrgSecretsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListOrgSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListOrgSecretsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListOrgSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListOrgSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListOrgSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListRepoSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListRepoSecretsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListRepoSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListRepoSecretsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListRepoSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListRepoSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListRepoSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListSelectedReposForOrgSecretIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListSelectedReposForOrgSecretIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListSelectedReposForOrgSecretIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListSelectedReposForOrgSecretIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListSelectedReposForOrgSecretIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListSelectedReposForOrgSecretIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListSelectedReposForUserSecretIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListSelectedReposForUserSecretIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListSelectedReposForUserSecretIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListSelectedReposForUserSecretIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListSelectedReposForUserSecretIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListSelectedReposForUserSecretIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListSelectedReposForUserSecretIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListSelectedReposForUserSecretIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListSelectedReposForUserSecretIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListUserCodespacesInOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"codespaces": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"codespaces": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"codespaces": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListUserCodespacesInOrgIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListUserCodespacesInOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListUserCodespacesInOrgIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListUserCodespacesInOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListUserCodespacesInOrgIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListUserCodespacesInOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListUserCodespacesInOrgIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Codespace, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListUserCodespacesInOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCodespacesService_ListUserSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Codespaces.ListUserSecretsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Codespaces.ListUserSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Codespaces.ListUserSecretsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Codespaces.ListUserSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Codespaces.ListUserSecretsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Codespaces.ListUserSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Codespaces.ListUserSecretsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Codespaces.ListUserSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCopilotService_ListCopilotEnterpriseSeatsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"seats": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"seats": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"seats": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"seats": [{},{}]}`) + } + }) + + iter := client.Copilot.ListCopilotEnterpriseSeatsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Copilot.ListCopilotEnterpriseSeatsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Copilot.ListCopilotEnterpriseSeatsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Copilot.ListCopilotEnterpriseSeatsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Copilot.ListCopilotEnterpriseSeatsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Copilot.ListCopilotEnterpriseSeatsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Copilot.ListCopilotEnterpriseSeatsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *CopilotSeatDetails, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Copilot.ListCopilotEnterpriseSeatsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCopilotService_ListCopilotSeatsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"seats": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"seats": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"seats": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"seats": [{},{}]}`) + } + }) + + iter := client.Copilot.ListCopilotSeatsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Copilot.ListCopilotSeatsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Copilot.ListCopilotSeatsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Copilot.ListCopilotSeatsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Copilot.ListCopilotSeatsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Copilot.ListCopilotSeatsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Copilot.ListCopilotSeatsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *CopilotSeatDetails, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Copilot.ListCopilotSeatsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestCopilotService_ListOrganizationCodingAgentRepositoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Copilot.ListOrganizationCodingAgentRepositoriesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Copilot.ListOrganizationCodingAgentRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Copilot.ListOrganizationCodingAgentRepositoriesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Copilot.ListOrganizationCodingAgentRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Copilot.ListOrganizationCodingAgentRepositoriesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Copilot.ListOrganizationCodingAgentRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Copilot.ListOrganizationCodingAgentRepositoriesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Copilot.ListOrganizationCodingAgentRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestDependabotService_ListOrgAlertsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Dependabot.ListOrgAlertsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Dependabot.ListOrgAlertsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListAlertsOptions{} + iter = client.Dependabot.ListOrgAlertsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Dependabot.ListOrgAlertsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Dependabot.ListOrgAlertsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Dependabot.ListOrgAlertsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Dependabot.ListOrgAlertsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *DependabotAlert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Dependabot.ListOrgAlertsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestDependabotService_ListOrgSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Dependabot.ListOrgSecretsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Dependabot.ListOrgSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Dependabot.ListOrgSecretsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Dependabot.ListOrgSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Dependabot.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Dependabot.ListOrgSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Dependabot.ListOrgSecretsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Dependabot.ListOrgSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestDependabotService_ListRepoAlertsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Dependabot.ListRepoAlertsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Dependabot.ListRepoAlertsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListAlertsOptions{} + iter = client.Dependabot.ListRepoAlertsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Dependabot.ListRepoAlertsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Dependabot.ListRepoAlertsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Dependabot.ListRepoAlertsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Dependabot.ListRepoAlertsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *DependabotAlert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Dependabot.ListRepoAlertsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestDependabotService_ListRepoSecretsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"secrets": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"secrets": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"secrets": [{},{}]}`) + } + }) + + iter := client.Dependabot.ListRepoSecretsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Dependabot.ListRepoSecretsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Dependabot.ListRepoSecretsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Dependabot.ListRepoSecretsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Dependabot.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Dependabot.ListRepoSecretsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Dependabot.ListRepoSecretsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Secret, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Dependabot.ListRepoSecretsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestDependabotService_ListSelectedReposForOrgSecretIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Dependabot.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Dependabot.ListSelectedReposForOrgSecretIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Dependabot.ListSelectedReposForOrgSecretIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Dependabot.ListSelectedReposForOrgSecretIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Dependabot.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Dependabot.ListSelectedReposForOrgSecretIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Dependabot.ListSelectedReposForOrgSecretIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Dependabot.ListSelectedReposForOrgSecretIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListAppAccessibleOrganizationRepositoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *AccessibleRepository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAppAccessibleOrganizationRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListAppInstallableOrganizationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListAppInstallableOrganizationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListAppInstallableOrganizationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListAppInstallableOrganizationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListAppInstallableOrganizationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListAppInstallableOrganizationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAppInstallableOrganizationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListAppInstallableOrganizationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *InstallableOrganization, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAppInstallableOrganizationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListAppInstallationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListAppInstallationsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListAppInstallationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListAppInstallationsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListAppInstallationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListAppInstallationsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAppInstallationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListAppInstallationsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Installation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAppInstallationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListAssignmentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListAssignmentsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListAssignmentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListAssignmentsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListAssignmentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListAssignmentsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAssignmentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListAssignmentsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Organization, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListAssignmentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListCodeSecurityConfigurationRepositoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCodeSecurityConfigurationRepositoriesOptions{} + iter = client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *RepositoryAttachment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListCodeSecurityConfigurationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListEnterpriseCodeSecurityConfigurationOptions{} + iter = client.Enterprise.ListCodeSecurityConfigurationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *CodeSecurityConfiguration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListConsumedLicensesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"users": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"users": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"users": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"users": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListConsumedLicensesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListConsumedLicensesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListConsumedLicensesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListConsumedLicensesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListConsumedLicensesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListConsumedLicensesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListConsumedLicensesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *EnterpriseLicensedUsers, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListConsumedLicensesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListEnterpriseNetworkConfigurationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"network_configurations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"network_configurations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"network_configurations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"network_configurations": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListEnterpriseNetworkConfigurationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListEnterpriseNetworkConfigurationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListEnterpriseNetworkConfigurationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListEnterpriseNetworkConfigurationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListEnterpriseNetworkConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListEnterpriseNetworkConfigurationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListEnterpriseNetworkConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *NetworkConfiguration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListEnterpriseNetworkConfigurationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListHostedRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListHostedRunnersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListHostedRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListHostedRunnersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListHostedRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListHostedRunnersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListHostedRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListHostedRunnersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *HostedRunner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListHostedRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListOrganizationAccessRunnerGroupIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"organizations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"organizations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"organizations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"organizations": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListOrganizationAccessRunnerGroupIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListOrganizationAccessRunnerGroupIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListOrganizationAccessRunnerGroupIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListOrganizationAccessRunnerGroupIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListOrganizationAccessRunnerGroupIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListOrganizationAccessRunnerGroupIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListOrganizationAccessRunnerGroupIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *Organization, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListOrganizationAccessRunnerGroupIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListOrganizationCustomPropertyValuesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListOrganizationCustomPropertyValuesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListOrganizationCustomPropertyValuesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListOrganizationCustomPropertyValuesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListOrganizationCustomPropertyValuesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListOrganizationCustomPropertyValuesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListOrganizationCustomPropertyValuesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListOrganizationCustomPropertyValuesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *EnterpriseCustomPropertiesValues, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListOrganizationCustomPropertyValuesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListRepositoriesForOrgAppInstallationIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListRepositoriesForOrgAppInstallationIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListRepositoriesForOrgAppInstallationIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListRepositoriesForOrgAppInstallationIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListRepositoriesForOrgAppInstallationIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListRepositoriesForOrgAppInstallationIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRepositoriesForOrgAppInstallationIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListRepositoriesForOrgAppInstallationIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *AccessibleRepository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRepositoriesForOrgAppInstallationIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListRunnerGroupRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListRunnerGroupRunnersIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListRunnerGroupRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListRunnerGroupRunnersIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListRunnerGroupRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListRunnerGroupRunnersIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRunnerGroupRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListRunnerGroupRunnersIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *Runner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRunnerGroupRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListRunnerGroupsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runner_groups": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runner_groups": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runner_groups": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runner_groups": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListRunnerGroupsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListRunnerGroupsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListEnterpriseRunnerGroupOptions{} + iter = client.Enterprise.ListRunnerGroupsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListRunnerGroupsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListRunnerGroupsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRunnerGroupsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListRunnerGroupsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *EnterpriseRunnerGroup, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRunnerGroupsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListRunnersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"runners": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"runners": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"runners": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"runners": [{},{}]}`) + } + }) + + iter := client.Enterprise.ListRunnersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListRunnersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListRunnersOptions{} + iter = client.Enterprise.ListRunnersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListRunnersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListRunnersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRunnersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListRunnersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Runner, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListRunnersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListTeamMembersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListTeamMembersIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListTeamMembersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListTeamMembersIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListTeamMembersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListTeamMembersIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListTeamMembersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListTeamMembersIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListTeamMembersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListTeamsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Enterprise.ListTeamsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Enterprise.ListTeamsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Enterprise.ListTeamsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Enterprise.ListTeamsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListTeamsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListTeamsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListTeamsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *EnterpriseTeam, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListTeamsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestGistsService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Gists.ListIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Gists.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &GistListOptions{} + iter = client.Gists.ListIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Gists.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Gists.ListIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Gists.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Gists.ListIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Gist, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Gists.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestGistsService_ListAllIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Gists.ListAllIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Gists.ListAllIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &GistListOptions{} + iter = client.Gists.ListAllIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Gists.ListAllIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Gists.ListAllIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Gists.ListAllIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Gists.ListAllIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Gist, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Gists.ListAllIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestGistsService_ListCommentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Gists.ListCommentsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Gists.ListCommentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Gists.ListCommentsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Gists.ListCommentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Gists.ListCommentsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Gists.ListCommentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Gists.ListCommentsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *GistComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Gists.ListCommentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestGistsService_ListCommitsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Gists.ListCommitsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Gists.ListCommitsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Gists.ListCommitsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Gists.ListCommitsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Gists.ListCommitsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Gists.ListCommitsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Gists.ListCommitsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *GistCommit, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Gists.ListCommitsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestGistsService_ListForksIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Gists.ListForksIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Gists.ListForksIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Gists.ListForksIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Gists.ListForksIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Gists.ListForksIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Gists.ListForksIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Gists.ListForksIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *GistFork, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Gists.ListForksIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestGistsService_ListStarredIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Gists.ListStarredIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Gists.ListStarredIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &GistListOptions{} + iter = client.Gists.ListStarredIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Gists.ListStarredIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Gists.ListStarredIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Gists.ListStarredIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Gists.ListStarredIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Gist, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Gists.ListStarredIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListAllIssuesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListAllIssuesIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListAllIssuesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListAllIssuesOptions{} + iter = client.Issues.ListAllIssuesIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListAllIssuesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListAllIssuesIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListAllIssuesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListAllIssuesIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Issue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListAllIssuesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListAssigneesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListAssigneesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListAssigneesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListAssigneesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListAssigneesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListAssigneesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListAssigneesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListAssigneesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListAssigneesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListBlockedByIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListBlockedByIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListBlockedByIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListBlockedByIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListBlockedByIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListBlockedByIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListBlockedByIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListBlockedByIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Issue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListBlockedByIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListBlockingIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListBlockingIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListBlockingIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListBlockingIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListBlockingIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListBlockingIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListBlockingIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListBlockingIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Issue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListBlockingIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListByOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListByOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListByOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &IssueListByOrgOptions{} + iter = client.Issues.ListByOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListByOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListByOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListByOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListByOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Issue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListByOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListByRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListByRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListByRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &IssueListByRepoOptions{} + iter = client.Issues.ListByRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListByRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListByRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListByRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListByRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Issue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListByRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListCommentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListCommentsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListCommentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &IssueListCommentsOptions{} + iter = client.Issues.ListCommentsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListCommentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListCommentsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListCommentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListCommentsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *IssueComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListCommentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListIssueEventsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListIssueEventsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListIssueEventsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListIssueEventsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListIssueEventsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListIssueEventsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListIssueEventsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListIssueEventsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *IssueEvent, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListIssueEventsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListIssueTimelineIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListIssueTimelineIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListIssueTimelineIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListIssueTimelineIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListIssueTimelineIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListIssueTimelineIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListIssueTimelineIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListIssueTimelineIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Timeline, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListIssueTimelineIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListLabelsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListLabelsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListLabelsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListLabelsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListLabelsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListLabelsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListLabelsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListLabelsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Label, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListLabelsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListLabelsByIssueIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListLabelsByIssueIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListLabelsByIssueIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListLabelsByIssueIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListLabelsByIssueIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListLabelsByIssueIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListLabelsByIssueIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListLabelsByIssueIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Label, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListLabelsByIssueIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListLabelsForMilestoneIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListLabelsForMilestoneIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListLabelsForMilestoneIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListLabelsForMilestoneIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListLabelsForMilestoneIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListLabelsForMilestoneIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListLabelsForMilestoneIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListLabelsForMilestoneIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Label, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListLabelsForMilestoneIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListMilestonesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListMilestonesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListMilestonesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &MilestoneListOptions{} + iter = client.Issues.ListMilestonesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListMilestonesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListMilestonesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListMilestonesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListMilestonesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Milestone, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListMilestonesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListRepositoryEventsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListRepositoryEventsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListRepositoryEventsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Issues.ListRepositoryEventsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListRepositoryEventsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListRepositoryEventsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListRepositoryEventsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListRepositoryEventsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *IssueEvent, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListRepositoryEventsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestIssuesService_ListUserIssuesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Issues.ListUserIssuesIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Issues.ListUserIssuesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListUserIssuesOptions{} + iter = client.Issues.ListUserIssuesIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Issues.ListUserIssuesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Issues.ListUserIssuesIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Issues.ListUserIssuesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Issues.ListUserIssuesIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Issue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Issues.ListUserIssuesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestLicensesService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Licenses.ListIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Licenses.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListLicensesOptions{} + iter = client.Licenses.ListIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Licenses.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Licenses.ListIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Licenses.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Licenses.ListIter(t.Context(), nil) + gotItems = 0 + iter(func(item *License, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Licenses.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestMarketplaceService_ListMarketplacePurchasesForUserIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Marketplace.ListMarketplacePurchasesForUserIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Marketplace.ListMarketplacePurchasesForUserIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Marketplace.ListMarketplacePurchasesForUserIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Marketplace.ListMarketplacePurchasesForUserIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Marketplace.ListMarketplacePurchasesForUserIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Marketplace.ListMarketplacePurchasesForUserIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Marketplace.ListMarketplacePurchasesForUserIter(t.Context(), nil) + gotItems = 0 + iter(func(item *MarketplacePurchase, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Marketplace.ListMarketplacePurchasesForUserIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestMarketplaceService_ListPlanAccountsForPlanIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Marketplace.ListPlanAccountsForPlanIter(t.Context(), 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Marketplace.ListPlanAccountsForPlanIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Marketplace.ListPlanAccountsForPlanIter(t.Context(), 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Marketplace.ListPlanAccountsForPlanIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Marketplace.ListPlanAccountsForPlanIter(t.Context(), 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Marketplace.ListPlanAccountsForPlanIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Marketplace.ListPlanAccountsForPlanIter(t.Context(), 0, nil) + gotItems = 0 + iter(func(item *MarketplacePlanAccount, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Marketplace.ListPlanAccountsForPlanIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestMarketplaceService_ListPlansIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Marketplace.ListPlansIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Marketplace.ListPlansIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Marketplace.ListPlansIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Marketplace.ListPlansIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Marketplace.ListPlansIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Marketplace.ListPlansIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Marketplace.ListPlansIter(t.Context(), nil) + gotItems = 0 + iter(func(item *MarketplacePlan, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Marketplace.ListPlansIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestMigrationService_ListMigrationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Migrations.ListMigrationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Migrations.ListMigrationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Migrations.ListMigrationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Migrations.ListMigrationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Migrations.ListMigrationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Migrations.ListMigrationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Migrations.ListMigrationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Migration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Migrations.ListMigrationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestMigrationService_ListUserMigrationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Migrations.ListUserMigrationsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Migrations.ListUserMigrationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Migrations.ListUserMigrationsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Migrations.ListUserMigrationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Migrations.ListUserMigrationsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Migrations.ListUserMigrationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Migrations.ListUserMigrationsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *UserMigration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Migrations.ListUserMigrationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Organization, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListAllRepositoryRulesetsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListAllRepositoryRulesetsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListAllRepositoryRulesetsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListAllRepositoryRulesetsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListAllRepositoryRulesetsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListAllRepositoryRulesetsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListAllRepositoryRulesetsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListAllRepositoryRulesetsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *RepositoryRuleset, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListAllRepositoryRulesetsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListAttestationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"attestations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"attestations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"attestations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"attestations": [{},{}]}`) + } + }) + + iter := client.Organizations.ListAttestationsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListAttestationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListAttestationsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListAttestationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListAttestationsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListAttestationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListAttestationsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Attestation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListAttestationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListBlockedUsersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListBlockedUsersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListBlockedUsersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListBlockedUsersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListBlockedUsersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListBlockedUsersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListBlockedUsersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListBlockedUsersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListBlockedUsersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListCodeSecurityConfigurationRepositoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCodeSecurityConfigurationRepositoriesOptions{} + iter = client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *RepositoryAttachment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListCodeSecurityConfigurationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOrgCodeSecurityConfigurationOptions{} + iter = client.Organizations.ListCodeSecurityConfigurationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *CodeSecurityConfiguration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListCredentialAuthorizationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListCredentialAuthorizationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListCredentialAuthorizationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &CredentialAuthorizationsListOptions{} + iter = client.Organizations.ListCredentialAuthorizationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListCredentialAuthorizationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListCredentialAuthorizationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListCredentialAuthorizationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListCredentialAuthorizationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *CredentialAuthorization, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListCredentialAuthorizationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListCustomPropertyValuesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListCustomPropertyValuesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListCustomPropertyValuesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCustomPropertyValuesOptions{} + iter = client.Organizations.ListCustomPropertyValuesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListCustomPropertyValuesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListCustomPropertyValuesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListCustomPropertyValuesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListCustomPropertyValuesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *RepoCustomPropertyValue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListCustomPropertyValuesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListFailedOrgInvitationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListFailedOrgInvitationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListFailedOrgInvitationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListFailedOrgInvitationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListFailedOrgInvitationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListFailedOrgInvitationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListFailedOrgInvitationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListFailedOrgInvitationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Invitation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListFailedOrgInvitationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokenRequestsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListFineGrainedPATOptions{} + iter = client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *FineGrainedPersonalAccessTokenRequest, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokenRequestsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokensIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListFineGrainedPersonalAccessTokensIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokensIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListFineGrainedPATOptions{} + iter = client.Organizations.ListFineGrainedPersonalAccessTokensIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokensIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListFineGrainedPersonalAccessTokensIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokensIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListFineGrainedPersonalAccessTokensIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *PersonalAccessToken, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListFineGrainedPersonalAccessTokensIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListHookDeliveriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListHookDeliveriesIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListHookDeliveriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCursorOptions{} + iter = client.Organizations.ListHookDeliveriesIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListHookDeliveriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListHookDeliveriesIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListHookDeliveriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListHookDeliveriesIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *HookDelivery, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListHookDeliveriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListHooksIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListHooksIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListHooksIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListHooksIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListHooksIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListHooksIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListHooksIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListHooksIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Hook, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListHooksIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListImmutableReleaseRepositoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"repositories": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"repositories": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"repositories": [{},{}]}`) + } + }) + + iter := client.Organizations.ListImmutableReleaseRepositoriesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListImmutableReleaseRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListImmutableReleaseRepositoriesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListImmutableReleaseRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListImmutableReleaseRepositoriesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListImmutableReleaseRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListImmutableReleaseRepositoriesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListImmutableReleaseRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListInstallationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"installations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"installations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"installations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"installations": [{},{}]}`) + } + }) + + iter := client.Organizations.ListInstallationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListInstallationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListInstallationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListInstallationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListInstallationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListInstallationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListInstallationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Installation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListInstallationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListMembersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListMembersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListMembersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListMembersOptions{} + iter = client.Organizations.ListMembersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListMembersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListMembersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListMembersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListMembersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListMembersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListNetworkConfigurationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"network_configurations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"network_configurations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"network_configurations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"network_configurations": [{},{}]}`) + } + }) + + iter := client.Organizations.ListNetworkConfigurationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListNetworkConfigurationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListNetworkConfigurationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListNetworkConfigurationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListNetworkConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListNetworkConfigurationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListNetworkConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *NetworkConfiguration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListNetworkConfigurationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListOrgInvitationTeamsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListOrgInvitationTeamsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListOrgInvitationTeamsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListOrgInvitationTeamsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListOrgInvitationTeamsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListOrgInvitationTeamsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListOrgInvitationTeamsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListOrgInvitationTeamsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListOrgInvitationTeamsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListOrgMembershipsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListOrgMembershipsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListOrgMembershipsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOrgMembershipsOptions{} + iter = client.Organizations.ListOrgMembershipsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListOrgMembershipsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListOrgMembershipsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListOrgMembershipsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListOrgMembershipsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Membership, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListOrgMembershipsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListOutsideCollaboratorsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListOutsideCollaboratorsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListOutsideCollaboratorsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOutsideCollaboratorsOptions{} + iter = client.Organizations.ListOutsideCollaboratorsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListOutsideCollaboratorsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListOutsideCollaboratorsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListOutsideCollaboratorsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListOutsideCollaboratorsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListOutsideCollaboratorsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListPackagesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListPackagesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListPackagesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &PackageListOptions{} + iter = client.Organizations.ListPackagesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListPackagesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListPackagesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListPackagesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListPackagesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Package, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListPackagesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListPendingOrgInvitationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListPendingOrgInvitationsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListPendingOrgInvitationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListPendingOrgInvitationsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListPendingOrgInvitationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListPendingOrgInvitationsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListPendingOrgInvitationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListPendingOrgInvitationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Invitation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListPendingOrgInvitationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListTeamsAssignedToOrgRoleIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListTeamsAssignedToOrgRoleIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListTeamsAssignedToOrgRoleIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListTeamsAssignedToOrgRoleIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListTeamsAssignedToOrgRoleIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListTeamsAssignedToOrgRoleIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListTeamsAssignedToOrgRoleIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListTeamsAssignedToOrgRoleIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListTeamsAssignedToOrgRoleIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestOrganizationsService_ListUsersAssignedToOrgRoleIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Organizations.ListUsersAssignedToOrgRoleIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Organizations.ListUsersAssignedToOrgRoleIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Organizations.ListUsersAssignedToOrgRoleIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Organizations.ListUsersAssignedToOrgRoleIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListUsersAssignedToOrgRoleIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListUsersAssignedToOrgRoleIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListUsersAssignedToOrgRoleIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListUsersAssignedToOrgRoleIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPrivateRegistriesService_ListOrganizationPrivateRegistriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"configurations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"configurations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"configurations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"configurations": [{},{}]}`) + } + }) + + iter := client.PrivateRegistries.ListOrganizationPrivateRegistriesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PrivateRegistries.ListOrganizationPrivateRegistriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.PrivateRegistries.ListOrganizationPrivateRegistriesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PrivateRegistries.ListOrganizationPrivateRegistriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PrivateRegistries.ListOrganizationPrivateRegistriesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PrivateRegistries.ListOrganizationPrivateRegistriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PrivateRegistries.ListOrganizationPrivateRegistriesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *PrivateRegistry, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PrivateRegistries.ListOrganizationPrivateRegistriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListOrganizationProjectFieldsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListOrganizationProjectFieldsIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectFieldsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectsOptions{} + iter = client.Projects.ListOrganizationProjectFieldsIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectFieldsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListOrganizationProjectFieldsIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectFieldsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListOrganizationProjectFieldsIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *ProjectV2Field, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectFieldsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListOrganizationProjectItemsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListOrganizationProjectItemsIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectItemsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectItemsOptions{} + iter = client.Projects.ListOrganizationProjectItemsIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectItemsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListOrganizationProjectItemsIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectItemsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListOrganizationProjectItemsIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *ProjectV2Item, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectItemsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListOrganizationProjectViewItemsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListOrganizationProjectViewItemsIter(t.Context(), "", 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectViewItemsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectItemsOptions{} + iter = client.Projects.ListOrganizationProjectViewItemsIter(t.Context(), "", 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectViewItemsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListOrganizationProjectViewItemsIter(t.Context(), "", 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectViewItemsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListOrganizationProjectViewItemsIter(t.Context(), "", 0, 0, nil) + gotItems = 0 + iter(func(item *ProjectV2Item, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectViewItemsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListOrganizationProjectsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListOrganizationProjectsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectsOptions{} + iter = client.Projects.ListOrganizationProjectsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListOrganizationProjectsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListOrganizationProjectsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListOrganizationProjectsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *ProjectV2, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListOrganizationProjectsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListUserProjectFieldsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListUserProjectFieldsIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListUserProjectFieldsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectsOptions{} + iter = client.Projects.ListUserProjectFieldsIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListUserProjectFieldsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListUserProjectFieldsIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectFieldsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListUserProjectFieldsIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *ProjectV2Field, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectFieldsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListUserProjectItemsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListUserProjectItemsIter(t.Context(), "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListUserProjectItemsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectItemsOptions{} + iter = client.Projects.ListUserProjectItemsIter(t.Context(), "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListUserProjectItemsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListUserProjectItemsIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectItemsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListUserProjectItemsIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *ProjectV2Item, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectItemsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListUserProjectViewItemsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListUserProjectViewItemsIter(t.Context(), "", 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListUserProjectViewItemsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectItemsOptions{} + iter = client.Projects.ListUserProjectViewItemsIter(t.Context(), "", 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListUserProjectViewItemsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListUserProjectViewItemsIter(t.Context(), "", 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectViewItemsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListUserProjectViewItemsIter(t.Context(), "", 0, 0, nil) + gotItems = 0 + iter(func(item *ProjectV2Item, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectViewItemsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestProjectsService_ListUserProjectsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Projects.ListUserProjectsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Projects.ListUserProjectsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListProjectsOptions{} + iter = client.Projects.ListUserProjectsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Projects.ListUserProjectsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Projects.ListUserProjectsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Projects.ListUserProjectsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *ProjectV2, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Projects.ListUserProjectsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &PullRequestListOptions{} + iter = client.PullRequests.ListIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *PullRequest, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListCommentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListCommentsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListCommentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &PullRequestListCommentsOptions{} + iter = client.PullRequests.ListCommentsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListCommentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListCommentsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListCommentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListCommentsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *PullRequestComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListCommentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListCommitsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListCommitsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListCommitsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.PullRequests.ListCommitsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListCommitsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListCommitsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListCommitsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListCommitsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *RepositoryCommit, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListCommitsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListFilesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListFilesIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListFilesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.PullRequests.ListFilesIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListFilesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListFilesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListFilesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListFilesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *CommitFile, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListFilesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListPullRequestsWithCommitIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListPullRequestsWithCommitIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListPullRequestsWithCommitIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.PullRequests.ListPullRequestsWithCommitIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListPullRequestsWithCommitIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListPullRequestsWithCommitIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListPullRequestsWithCommitIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListPullRequestsWithCommitIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *PullRequest, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListPullRequestsWithCommitIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListReviewCommentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListReviewCommentsIter(t.Context(), "", "", 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListReviewCommentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.PullRequests.ListReviewCommentsIter(t.Context(), "", "", 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListReviewCommentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListReviewCommentsIter(t.Context(), "", "", 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListReviewCommentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListReviewCommentsIter(t.Context(), "", "", 0, 0, nil) + gotItems = 0 + iter(func(item *PullRequestComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListReviewCommentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestPullRequestsService_ListReviewsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.PullRequests.ListReviewsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.PullRequests.ListReviewsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.PullRequests.ListReviewsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.PullRequests.ListReviewsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.PullRequests.ListReviewsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.PullRequests.ListReviewsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.PullRequests.ListReviewsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *PullRequestReview, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.PullRequests.ListReviewsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListCommentReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListCommentReactionsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListCommentReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListCommentReactionsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListCommentReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListCommentReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListCommentReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListCommentReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListCommentReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListIssueCommentReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListIssueCommentReactionsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListIssueCommentReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListIssueCommentReactionsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListIssueCommentReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListIssueCommentReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListIssueCommentReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListIssueCommentReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListIssueCommentReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListIssueReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListIssueReactionsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListIssueReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListIssueReactionsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListIssueReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListIssueReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListIssueReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListIssueReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListIssueReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListPullRequestCommentReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListPullRequestCommentReactionsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListPullRequestCommentReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListPullRequestCommentReactionsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListPullRequestCommentReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListPullRequestCommentReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListPullRequestCommentReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListPullRequestCommentReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListPullRequestCommentReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListReleaseReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListReleaseReactionsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListReleaseReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListReleaseReactionsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListReleaseReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListReleaseReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListReleaseReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListReleaseReactionsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListReleaseReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListTeamDiscussionCommentReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListTeamDiscussionCommentReactionsIter(t.Context(), 0, 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListTeamDiscussionCommentReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListTeamDiscussionCommentReactionsIter(t.Context(), 0, 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListTeamDiscussionCommentReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListTeamDiscussionCommentReactionsIter(t.Context(), 0, 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListTeamDiscussionCommentReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListTeamDiscussionCommentReactionsIter(t.Context(), 0, 0, 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListTeamDiscussionCommentReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestReactionsService_ListTeamDiscussionReactionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Reactions.ListTeamDiscussionReactionsIter(t.Context(), 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Reactions.ListTeamDiscussionReactionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListReactionOptions{} + iter = client.Reactions.ListTeamDiscussionReactionsIter(t.Context(), 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Reactions.ListTeamDiscussionReactionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Reactions.ListTeamDiscussionReactionsIter(t.Context(), 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Reactions.ListTeamDiscussionReactionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Reactions.ListTeamDiscussionReactionsIter(t.Context(), 0, 0, nil) + gotItems = 0 + iter(func(item *Reaction, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Reactions.ListTeamDiscussionReactionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCommitComparisonFilesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"files": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"files": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"files": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"files": [{},{}]}`) + } + }) + + iter := client.Repositories.ListCommitComparisonFilesIter(t.Context(), "", "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCommitComparisonFilesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListCommitComparisonFilesIter(t.Context(), "", "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCommitComparisonFilesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCommitComparisonFilesIter(t.Context(), "", "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitComparisonFilesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCommitComparisonFilesIter(t.Context(), "", "", "", "", nil) + gotItems = 0 + iter(func(item *CommitFile, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitComparisonFilesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCombinedStatusIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"statuses": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"statuses": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"statuses": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"statuses": [{},{}]}`) + } + }) + + iter := client.Repositories.ListCombinedStatusIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCombinedStatusIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListCombinedStatusIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCombinedStatusIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCombinedStatusIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCombinedStatusIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCombinedStatusIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *RepoStatus, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCombinedStatusIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCommitFilesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"files": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"files": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"files": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"files": [{},{}]}`) + } + }) + + iter := client.Repositories.ListCommitFilesIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCommitFilesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListCommitFilesIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCommitFilesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCommitFilesIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitFilesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCommitFilesIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *CommitFile, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitFilesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &RepositoryListOptions{} + iter = client.Repositories.ListIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListAllTopicsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"names": ["","",""]}`) + case 2: + fmt.Fprint(w, `{"names": ["","","",""]}`) + case 3: + fmt.Fprint(w, `{"names": ["",""]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"names": ["",""]}`) + } + }) + + iter := client.Repositories.ListAllTopicsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListAllTopicsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListAllTopicsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListAllTopicsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListAllTopicsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListAllTopicsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListAllTopicsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item string, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListAllTopicsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListAttestationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"attestations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"attestations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"attestations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"attestations": [{},{}]}`) + } + }) + + iter := client.Repositories.ListAttestationsIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListAttestationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListAttestationsIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListAttestationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListAttestationsIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListAttestationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListAttestationsIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *Attestation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListAttestationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListBranchesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListBranchesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListBranchesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &BranchListOptions{} + iter = client.Repositories.ListBranchesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListBranchesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListBranchesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListBranchesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListBranchesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Branch, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListBranchesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListByAuthenticatedUserIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListByAuthenticatedUserIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListByAuthenticatedUserIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &RepositoryListByAuthenticatedUserOptions{} + iter = client.Repositories.ListByAuthenticatedUserIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListByAuthenticatedUserIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListByAuthenticatedUserIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListByAuthenticatedUserIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListByAuthenticatedUserIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListByAuthenticatedUserIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListByOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListByOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListByOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &RepositoryListByOrgOptions{} + iter = client.Repositories.ListByOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListByOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListByOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListByOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListByOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListByOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListByUserIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListByUserIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListByUserIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &RepositoryListByUserOptions{} + iter = client.Repositories.ListByUserIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListByUserIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListByUserIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListByUserIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListByUserIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListByUserIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCollaboratorsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListCollaboratorsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCollaboratorsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCollaboratorsOptions{} + iter = client.Repositories.ListCollaboratorsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCollaboratorsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCollaboratorsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCollaboratorsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCollaboratorsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCollaboratorsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCommentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListCommentsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCommentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListCommentsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCommentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCommentsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCommentsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *RepositoryComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCommitCommentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListCommitCommentsIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCommitCommentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListCommitCommentsIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCommitCommentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCommitCommentsIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitCommentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCommitCommentsIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *RepositoryComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitCommentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCommitsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListCommitsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCommitsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &CommitsListOptions{} + iter = client.Repositories.ListCommitsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCommitsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCommitsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCommitsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *RepositoryCommit, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCommitsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListContributorsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListContributorsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListContributorsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListContributorsOptions{} + iter = client.Repositories.ListContributorsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListContributorsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListContributorsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListContributorsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListContributorsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Contributor, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListContributorsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListCustomDeploymentRuleIntegrationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"available_custom_deployment_protection_rule_integrations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"available_custom_deployment_protection_rule_integrations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"available_custom_deployment_protection_rule_integrations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"available_custom_deployment_protection_rule_integrations": [{},{}]}`) + } + }) + + iter := client.Repositories.ListCustomDeploymentRuleIntegrationsIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListCustomDeploymentRuleIntegrationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListCustomDeploymentRuleIntegrationsIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListCustomDeploymentRuleIntegrationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListCustomDeploymentRuleIntegrationsIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListCustomDeploymentRuleIntegrationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListCustomDeploymentRuleIntegrationsIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *CustomDeploymentProtectionRuleApp, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListCustomDeploymentRuleIntegrationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListDeploymentBranchPoliciesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"branch_policies": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"branch_policies": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"branch_policies": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"branch_policies": [{},{}]}`) + } + }) + + iter := client.Repositories.ListDeploymentBranchPoliciesIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListDeploymentBranchPoliciesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListDeploymentBranchPoliciesIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListDeploymentBranchPoliciesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListDeploymentBranchPoliciesIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListDeploymentBranchPoliciesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListDeploymentBranchPoliciesIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *DeploymentBranchPolicy, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListDeploymentBranchPoliciesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListDeploymentStatusesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListDeploymentStatusesIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListDeploymentStatusesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListDeploymentStatusesIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListDeploymentStatusesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListDeploymentStatusesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListDeploymentStatusesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListDeploymentStatusesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *DeploymentStatus, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListDeploymentStatusesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListDeploymentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListDeploymentsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListDeploymentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &DeploymentsListOptions{} + iter = client.Repositories.ListDeploymentsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListDeploymentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListDeploymentsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListDeploymentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListDeploymentsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Deployment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListDeploymentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListEnvironmentsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"environments": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"environments": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"environments": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"environments": [{},{}]}`) + } + }) + + iter := client.Repositories.ListEnvironmentsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListEnvironmentsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &EnvironmentListOptions{} + iter = client.Repositories.ListEnvironmentsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListEnvironmentsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListEnvironmentsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListEnvironmentsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListEnvironmentsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Environment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListEnvironmentsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListForksIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListForksIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListForksIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &RepositoryListForksOptions{} + iter = client.Repositories.ListForksIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListForksIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListForksIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListForksIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListForksIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListForksIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListHookDeliveriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListHookDeliveriesIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListHookDeliveriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCursorOptions{} + iter = client.Repositories.ListHookDeliveriesIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListHookDeliveriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListHookDeliveriesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListHookDeliveriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListHookDeliveriesIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *HookDelivery, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListHookDeliveriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListHooksIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListHooksIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListHooksIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListHooksIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListHooksIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListHooksIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListHooksIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListHooksIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Hook, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListHooksIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListInvitationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListInvitationsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListInvitationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListInvitationsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListInvitationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListInvitationsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListInvitationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListInvitationsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *RepositoryInvitation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListInvitationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListKeysIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListKeysIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListKeysIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListKeysIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListKeysIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListKeysIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListKeysIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListKeysIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Key, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListKeysIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListPagesBuildsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListPagesBuildsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListPagesBuildsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListPagesBuildsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListPagesBuildsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListPagesBuildsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListPagesBuildsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListPagesBuildsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *PagesBuild, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListPagesBuildsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListPreReceiveHooksIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListPreReceiveHooksIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListPreReceiveHooksIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListPreReceiveHooksIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListPreReceiveHooksIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListPreReceiveHooksIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListPreReceiveHooksIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListPreReceiveHooksIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *PreReceiveHook, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListPreReceiveHooksIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListReleaseAssetsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListReleaseAssetsIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListReleaseAssetsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListReleaseAssetsIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListReleaseAssetsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListReleaseAssetsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListReleaseAssetsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListReleaseAssetsIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *ReleaseAsset, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListReleaseAssetsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListReleasesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListReleasesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListReleasesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListReleasesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListReleasesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListReleasesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListReleasesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListReleasesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *RepositoryRelease, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListReleasesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListRepositoryActivitiesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListRepositoryActivitiesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListRepositoryActivitiesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListRepositoryActivityOptions{} + iter = client.Repositories.ListRepositoryActivitiesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListRepositoryActivitiesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListRepositoryActivitiesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListRepositoryActivitiesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListRepositoryActivitiesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *RepositoryActivity, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListRepositoryActivitiesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListStatusesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListStatusesIter(t.Context(), "", "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListStatusesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListStatusesIter(t.Context(), "", "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListStatusesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListStatusesIter(t.Context(), "", "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListStatusesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListStatusesIter(t.Context(), "", "", "", nil) + gotItems = 0 + iter(func(item *RepoStatus, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListStatusesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListTagsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListTagsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListTagsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListTagsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListTagsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListTagsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListTagsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListTagsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *RepositoryTag, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListTagsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_ListTeamsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Repositories.ListTeamsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListTeamsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListTeamsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListTeamsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListTeamsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListTeamsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListTeamsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListTeamsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListAlertsForEnterpriseIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListAlertsForEnterpriseIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListAlertsForEnterpriseIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningAlertListOptions{} + iter = client.SecretScanning.ListAlertsForEnterpriseIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListAlertsForEnterpriseIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListAlertsForEnterpriseIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListAlertsForEnterpriseIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListAlertsForEnterpriseIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SecretScanningAlert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListAlertsForEnterpriseIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListAlertsForOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListAlertsForOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListAlertsForOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningAlertListOptions{} + iter = client.SecretScanning.ListAlertsForOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListAlertsForOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListAlertsForOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListAlertsForOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListAlertsForOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SecretScanningAlert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListAlertsForOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListAlertsForRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListAlertsForRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListAlertsForRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningAlertListOptions{} + iter = client.SecretScanning.ListAlertsForRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListAlertsForRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListAlertsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListAlertsForRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListAlertsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *SecretScanningAlert, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListAlertsForRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListCustomPatternsForEnterpriseIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListCustomPatternsForEnterpriseIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForEnterpriseIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningCustomPatternListOptions{} + iter = client.SecretScanning.ListCustomPatternsForEnterpriseIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForEnterpriseIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListCustomPatternsForEnterpriseIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForEnterpriseIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListCustomPatternsForEnterpriseIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SecretScanningCustomPattern, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForEnterpriseIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListCustomPatternsForOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListCustomPatternsForOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningCustomPatternListOptions{} + iter = client.SecretScanning.ListCustomPatternsForOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListCustomPatternsForOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListCustomPatternsForOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SecretScanningCustomPattern, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListCustomPatternsForRepoIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &SecretScanningCustomPatternListOptions{} + iter = client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListCustomPatternsForRepoIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *SecretScanningCustomPattern, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListCustomPatternsForRepoIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecretScanningService_ListLocationsForAlertIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecretScanning.ListLocationsForAlertIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecretScanning.ListLocationsForAlertIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.SecretScanning.ListLocationsForAlertIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecretScanning.ListLocationsForAlertIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecretScanning.ListLocationsForAlertIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListLocationsForAlertIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecretScanning.ListLocationsForAlertIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *SecretScanningAlertLocation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecretScanning.ListLocationsForAlertIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecurityAdvisoriesService_ListGlobalSecurityAdvisoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListGlobalSecurityAdvisoriesOptions{} + iter = client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter(t.Context(), nil) + gotItems = 0 + iter(func(item *GlobalSecurityAdvisory, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecurityAdvisories.ListGlobalSecurityAdvisoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListRepositorySecurityAdvisoriesOptions{} + iter = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *SecurityAdvisory, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrgIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListRepositorySecurityAdvisoriesOptions{} + iter = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SecurityAdvisory, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrgIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestSubIssueService_ListByIssueIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.SubIssue.ListByIssueIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.SubIssue.ListByIssueIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.SubIssue.ListByIssueIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.SubIssue.ListByIssueIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.SubIssue.ListByIssueIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.SubIssue.ListByIssueIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.SubIssue.ListByIssueIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *SubIssue, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.SubIssue.ListByIssueIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListChildTeamsByParentIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListChildTeamsByParentIDIter(t.Context(), 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListChildTeamsByParentIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListChildTeamsByParentIDIter(t.Context(), 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListChildTeamsByParentIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListChildTeamsByParentIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListChildTeamsByParentIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListChildTeamsByParentIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListChildTeamsByParentIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListChildTeamsByParentSlugIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListChildTeamsByParentSlugIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListChildTeamsByParentSlugIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListChildTeamsByParentSlugIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListChildTeamsByParentSlugIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListChildTeamsByParentSlugIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListChildTeamsByParentSlugIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListChildTeamsByParentSlugIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListChildTeamsByParentSlugIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListCommentsByIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListCommentsByIDIter(t.Context(), 0, 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListCommentsByIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &DiscussionCommentListOptions{} + iter = client.Teams.ListCommentsByIDIter(t.Context(), 0, 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListCommentsByIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListCommentsByIDIter(t.Context(), 0, 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListCommentsByIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListCommentsByIDIter(t.Context(), 0, 0, 0, nil) + gotItems = 0 + iter(func(item *DiscussionComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListCommentsByIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListCommentsBySlugIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListCommentsBySlugIter(t.Context(), "", "", 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListCommentsBySlugIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &DiscussionCommentListOptions{} + iter = client.Teams.ListCommentsBySlugIter(t.Context(), "", "", 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListCommentsBySlugIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListCommentsBySlugIter(t.Context(), "", "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListCommentsBySlugIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListCommentsBySlugIter(t.Context(), "", "", 0, nil) + gotItems = 0 + iter(func(item *DiscussionComment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListCommentsBySlugIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListDiscussionsByIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListDiscussionsByIDIter(t.Context(), 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListDiscussionsByIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &DiscussionListOptions{} + iter = client.Teams.ListDiscussionsByIDIter(t.Context(), 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListDiscussionsByIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListDiscussionsByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListDiscussionsByIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListDiscussionsByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + iter(func(item *TeamDiscussion, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListDiscussionsByIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListDiscussionsBySlugIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListDiscussionsBySlugIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListDiscussionsBySlugIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &DiscussionListOptions{} + iter = client.Teams.ListDiscussionsBySlugIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListDiscussionsBySlugIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListDiscussionsBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListDiscussionsBySlugIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListDiscussionsBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *TeamDiscussion, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListDiscussionsBySlugIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListExternalGroupsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"groups": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"groups": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"groups": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"groups": [{},{}]}`) + } + }) + + iter := client.Teams.ListExternalGroupsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListExternalGroupsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListExternalGroupsOptions{} + iter = client.Teams.ListExternalGroupsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListExternalGroupsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListExternalGroupsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListExternalGroupsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListExternalGroupsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *ExternalGroup, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListExternalGroupsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListIDPGroupsInOrganizationIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"groups": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"groups": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"groups": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"groups": [{},{}]}`) + } + }) + + iter := client.Teams.ListIDPGroupsInOrganizationIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListIDPGroupsInOrganizationIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListIDPGroupsOptions{} + iter = client.Teams.ListIDPGroupsInOrganizationIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListIDPGroupsInOrganizationIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListIDPGroupsInOrganizationIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListIDPGroupsInOrganizationIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListIDPGroupsInOrganizationIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *IDPGroup, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListIDPGroupsInOrganizationIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListPendingTeamInvitationsByIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListPendingTeamInvitationsByIDIter(t.Context(), 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListPendingTeamInvitationsByIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListPendingTeamInvitationsByIDIter(t.Context(), 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListPendingTeamInvitationsByIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListPendingTeamInvitationsByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListPendingTeamInvitationsByIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListPendingTeamInvitationsByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + iter(func(item *Invitation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListPendingTeamInvitationsByIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListPendingTeamInvitationsBySlugIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListPendingTeamInvitationsBySlugIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListPendingTeamInvitationsBySlugIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListPendingTeamInvitationsBySlugIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListPendingTeamInvitationsBySlugIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListPendingTeamInvitationsBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListPendingTeamInvitationsBySlugIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListPendingTeamInvitationsBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Invitation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListPendingTeamInvitationsBySlugIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListTeamMembersByIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListTeamMembersByIDIter(t.Context(), 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListTeamMembersByIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &TeamListTeamMembersOptions{} + iter = client.Teams.ListTeamMembersByIDIter(t.Context(), 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListTeamMembersByIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListTeamMembersByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamMembersByIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListTeamMembersByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamMembersByIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListTeamMembersBySlugIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListTeamMembersBySlugIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListTeamMembersBySlugIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &TeamListTeamMembersOptions{} + iter = client.Teams.ListTeamMembersBySlugIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListTeamMembersBySlugIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListTeamMembersBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamMembersBySlugIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListTeamMembersBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamMembersBySlugIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListTeamReposByIDIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListTeamReposByIDIter(t.Context(), 0, 0, nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListTeamReposByIDIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListTeamReposByIDIter(t.Context(), 0, 0, opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListTeamReposByIDIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListTeamReposByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamReposByIDIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListTeamReposByIDIter(t.Context(), 0, 0, nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamReposByIDIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListTeamReposBySlugIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListTeamReposBySlugIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListTeamReposBySlugIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListTeamReposBySlugIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListTeamReposBySlugIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListTeamReposBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamReposBySlugIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListTeamReposBySlugIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Repository, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamReposBySlugIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListTeamsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListTeamsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListTeamsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListTeamsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListTeamsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListTeamsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListTeamsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListTeamsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestTeamsService_ListUserTeamsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Teams.ListUserTeamsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Teams.ListUserTeamsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Teams.ListUserTeamsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Teams.ListUserTeamsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Teams.ListUserTeamsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Teams.ListUserTeamsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Teams.ListUserTeamsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *Team, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Teams.ListUserTeamsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListAttestationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `{"attestations": [{},{},{}]}`) + case 2: + fmt.Fprint(w, `{"attestations": [{},{},{},{}]}`) + case 3: + fmt.Fprint(w, `{"attestations": [{},{}]}`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `{"attestations": [{},{}]}`) + } + }) + + iter := client.Users.ListAttestationsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListAttestationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListAttestationsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListAttestationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListAttestationsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListAttestationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListAttestationsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *Attestation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListAttestationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListBlockedUsersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListBlockedUsersIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListBlockedUsersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListBlockedUsersIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListBlockedUsersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListBlockedUsersIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListBlockedUsersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListBlockedUsersIter(t.Context(), nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListBlockedUsersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListEmailsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListEmailsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListEmailsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListEmailsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListEmailsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListEmailsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListEmailsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListEmailsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *UserEmail, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListEmailsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListFollowersIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListFollowersIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListFollowersIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListFollowersIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListFollowersIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListFollowersIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListFollowersIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListFollowersIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListFollowersIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListFollowingIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListFollowingIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListFollowingIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListFollowingIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListFollowingIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListFollowingIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListFollowingIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListFollowingIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *User, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListFollowingIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListGPGKeysIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListGPGKeysIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListGPGKeysIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListGPGKeysIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListGPGKeysIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListGPGKeysIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListGPGKeysIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListGPGKeysIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *GPGKey, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListGPGKeysIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListInvitationsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListInvitationsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListInvitationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListInvitationsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListInvitationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListInvitationsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListInvitationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListInvitationsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *RepositoryInvitation, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListInvitationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListKeysIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListKeysIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListKeysIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListKeysIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListKeysIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListKeysIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListKeysIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListKeysIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Key, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListKeysIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListPackageVersionsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListPackageVersionsIter(t.Context(), "", "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListPackageVersionsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListPackageVersionsOptions{} + iter = client.Users.ListPackageVersionsIter(t.Context(), "", "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListPackageVersionsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListPackageVersionsIter(t.Context(), "", "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListPackageVersionsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListPackageVersionsIter(t.Context(), "", "", nil) + gotItems = 0 + iter(func(item *PackageVersion, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListPackageVersionsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListPackagesIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListPackagesIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListPackagesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &PackageListOptions{} + iter = client.Users.ListPackagesIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListPackagesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListPackagesIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListPackagesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListPackagesIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *Package, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListPackagesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListSSHSigningKeysIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListSSHSigningKeysIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListSSHSigningKeysIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListSSHSigningKeysIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListSSHSigningKeysIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListSSHSigningKeysIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListSSHSigningKeysIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListSSHSigningKeysIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SSHSigningKey, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListSSHSigningKeysIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListSocialAccountsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListSocialAccountsIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListSocialAccountsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListSocialAccountsIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListSocialAccountsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListSocialAccountsIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListSocialAccountsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListSocialAccountsIter(t.Context(), nil) + gotItems = 0 + iter(func(item *SocialAccount, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListSocialAccountsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestUsersService_ListUserSocialAccountsIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Users.ListUserSocialAccountsIter(t.Context(), "", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Users.ListUserSocialAccountsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Users.ListUserSocialAccountsIter(t.Context(), "", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Users.ListUserSocialAccountsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Users.ListUserSocialAccountsIter(t.Context(), "", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Users.ListUserSocialAccountsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Users.ListUserSocialAccountsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *SocialAccount, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Users.ListUserSocialAccountsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go index 22e1aafb906..858d7698338 100644 --- a/github/github-stringify_test.go +++ b/github/github-stringify_test.go @@ -1,19 +1,91 @@ +// Code generated by gen-stringify-tests; DO NOT EDIT. +// Instead, please run "go generate ./..." as described here: +// https://github.com/google/go-github/blob/master/CONTRIBUTING.md#submitting-a-patch + // Copyright 2019 The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Code generated by gen-stringify-tests; DO NOT EDIT. - package github import ( "testing" ) -func Float64(v float64) *float64 { return &v } +func TestAcceptedAssignment_String(t *testing.T) { + t.Parallel() + v := AcceptedAssignment{ + ID: Ptr(int64(0)), + Submitted: Ptr(false), + Passing: Ptr(false), + CommitCount: Ptr(0), + Grade: Ptr(""), + Repository: &Repository{}, + Assignment: &ClassroomAssignment{}, + } + want := `github.AcceptedAssignment{ID:0, Submitted:false, Passing:false, CommitCount:0, Grade:"", Repository:github.Repository{}, Assignment:github.ClassroomAssignment{}}` + if got := v.String(); got != want { + t.Errorf("AcceptedAssignment.String = %v, want %v", got, want) + } +} + +func TestActionsAllowed_String(t *testing.T) { + t.Parallel() + v := ActionsAllowed{ + GithubOwnedAllowed: Ptr(false), + VerifiedAllowed: Ptr(false), + PatternsAllowed: []string{""}, + } + want := `github.ActionsAllowed{GithubOwnedAllowed:false, VerifiedAllowed:false, PatternsAllowed:[""]}` + if got := v.String(); got != want { + t.Errorf("ActionsAllowed.String = %v, want %v", got, want) + } +} + +func TestActionsPermissions_String(t *testing.T) { + t.Parallel() + v := ActionsPermissions{ + EnabledRepositories: Ptr(""), + AllowedActions: Ptr(""), + SelectedActionsURL: Ptr(""), + SHAPinningRequired: Ptr(false), + } + want := `github.ActionsPermissions{EnabledRepositories:"", AllowedActions:"", SelectedActionsURL:"", SHAPinningRequired:false}` + if got := v.String(); got != want { + t.Errorf("ActionsPermissions.String = %v, want %v", got, want) + } +} + +func TestActionsPermissionsEnterprise_String(t *testing.T) { + t.Parallel() + v := ActionsPermissionsEnterprise{ + EnabledOrganizations: Ptr(""), + AllowedActions: Ptr(""), + SelectedActionsURL: Ptr(""), + } + want := `github.ActionsPermissionsEnterprise{EnabledOrganizations:"", AllowedActions:"", SelectedActionsURL:""}` + if got := v.String(); got != want { + t.Errorf("ActionsPermissionsEnterprise.String = %v, want %v", got, want) + } +} + +func TestActionsPermissionsRepository_String(t *testing.T) { + t.Parallel() + v := ActionsPermissionsRepository{ + Enabled: Ptr(false), + AllowedActions: Ptr(""), + SelectedActionsURL: Ptr(""), + SHAPinningRequired: Ptr(false), + } + want := `github.ActionsPermissionsRepository{Enabled:false, AllowedActions:"", SelectedActionsURL:"", SHAPinningRequired:false}` + if got := v.String(); got != want { + t.Errorf("ActionsPermissionsRepository.String = %v, want %v", got, want) + } +} func TestAdminStats_String(t *testing.T) { + t.Parallel() v := AdminStats{ Issues: &IssueStats{}, Hooks: &HookStats{}, @@ -32,32 +104,79 @@ func TestAdminStats_String(t *testing.T) { } } +func TestAdvancedSecurity_String(t *testing.T) { + t.Parallel() + v := AdvancedSecurity{ + Status: Ptr(""), + } + want := `github.AdvancedSecurity{Status:""}` + if got := v.String(); got != want { + t.Errorf("AdvancedSecurity.String = %v, want %v", got, want) + } +} + +func TestArtifactPeriod_String(t *testing.T) { + t.Parallel() + v := ArtifactPeriod{ + Days: Ptr(0), + MaximumAllowedDays: Ptr(0), + } + want := `github.ArtifactPeriod{Days:0, MaximumAllowedDays:0}` + if got := v.String(); got != want { + t.Errorf("ArtifactPeriod.String = %v, want %v", got, want) + } +} + +func TestAssignmentGrade_String(t *testing.T) { + t.Parallel() + v := AssignmentGrade{ + AssignmentName: Ptr(""), + AssignmentURL: Ptr(""), + StarterCodeURL: Ptr(""), + GithubUsername: Ptr(""), + RosterIdentifier: Ptr(""), + StudentRepositoryName: Ptr(""), + StudentRepositoryURL: Ptr(""), + SubmissionTimestamp: &Timestamp{}, + PointsAwarded: Ptr(0), + PointsAvailable: Ptr(0), + GroupName: Ptr(""), + } + want := `github.AssignmentGrade{AssignmentName:"", AssignmentURL:"", StarterCodeURL:"", GithubUsername:"", RosterIdentifier:"", StudentRepositoryName:"", StudentRepositoryURL:"", SubmissionTimestamp:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PointsAwarded:0, PointsAvailable:0, GroupName:""}` + if got := v.String(); got != want { + t.Errorf("AssignmentGrade.String = %v, want %v", got, want) + } +} + func TestAuthorization_String(t *testing.T) { + t.Parallel() v := Authorization{ - ID: Int64(0), - URL: String(""), - Token: String(""), - TokenLastEight: String(""), - HashedToken: String(""), + ID: Ptr(int64(0)), + URL: Ptr(""), + Scopes: []Scope{ScopeNone}, + Token: Ptr(""), + TokenLastEight: Ptr(""), + HashedToken: Ptr(""), App: &AuthorizationApp{}, - Note: String(""), - NoteURL: String(""), + Note: Ptr(""), + NoteURL: Ptr(""), UpdatedAt: &Timestamp{}, CreatedAt: &Timestamp{}, - Fingerprint: String(""), + Fingerprint: Ptr(""), User: &User{}, } - want := `github.Authorization{ID:0, URL:"", Token:"", TokenLastEight:"", HashedToken:"", App:github.AuthorizationApp{}, Note:"", NoteURL:"", UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Fingerprint:"", User:github.User{}}` + want := `github.Authorization{ID:0, URL:"", Scopes:["(no scope)"], Token:"", TokenLastEight:"", HashedToken:"", App:github.AuthorizationApp{}, Note:"", NoteURL:"", UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Fingerprint:"", User:github.User{}}` if got := v.String(); got != want { t.Errorf("Authorization.String = %v, want %v", got, want) } } func TestAuthorizationApp_String(t *testing.T) { + t.Parallel() v := AuthorizationApp{ - URL: String(""), - Name: String(""), - ClientID: String(""), + URL: Ptr(""), + Name: Ptr(""), + ClientID: Ptr(""), } want := `github.AuthorizationApp{URL:"", Name:"", ClientID:""}` if got := v.String(); got != want { @@ -66,46 +185,53 @@ func TestAuthorizationApp_String(t *testing.T) { } func TestAuthorizationRequest_String(t *testing.T) { + t.Parallel() v := AuthorizationRequest{ - Note: String(""), - NoteURL: String(""), - ClientID: String(""), - ClientSecret: String(""), - Fingerprint: String(""), + Scopes: []Scope{ScopeNone}, + Note: Ptr(""), + NoteURL: Ptr(""), + ClientID: Ptr(""), + ClientSecret: Ptr(""), + Fingerprint: Ptr(""), } - want := `github.AuthorizationRequest{Note:"", NoteURL:"", ClientID:"", ClientSecret:"", Fingerprint:""}` + want := `github.AuthorizationRequest{Scopes:["(no scope)"], Note:"", NoteURL:"", ClientID:"", ClientSecret:"", Fingerprint:""}` if got := v.String(); got != want { t.Errorf("AuthorizationRequest.String = %v, want %v", got, want) } } func TestAuthorizationUpdateRequest_String(t *testing.T) { + t.Parallel() v := AuthorizationUpdateRequest{ - Note: String(""), - NoteURL: String(""), - Fingerprint: String(""), + Scopes: []string{""}, + AddScopes: []string{""}, + RemoveScopes: []string{""}, + Note: Ptr(""), + NoteURL: Ptr(""), + Fingerprint: Ptr(""), } - want := `github.AuthorizationUpdateRequest{Note:"", NoteURL:"", Fingerprint:""}` + want := `github.AuthorizationUpdateRequest{Scopes:[""], AddScopes:[""], RemoveScopes:[""], Note:"", NoteURL:"", Fingerprint:""}` if got := v.String(); got != want { t.Errorf("AuthorizationUpdateRequest.String = %v, want %v", got, want) } } func TestCheckRun_String(t *testing.T) { + t.Parallel() v := CheckRun{ - ID: Int64(0), - NodeID: String(""), - HeadSHA: String(""), - ExternalID: String(""), - URL: String(""), - HTMLURL: String(""), - DetailsURL: String(""), - Status: String(""), - Conclusion: String(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + HeadSHA: Ptr(""), + ExternalID: Ptr(""), + URL: Ptr(""), + HTMLURL: Ptr(""), + DetailsURL: Ptr(""), + Status: Ptr(""), + Conclusion: Ptr(""), StartedAt: &Timestamp{}, CompletedAt: &Timestamp{}, Output: &CheckRunOutput{}, - Name: String(""), + Name: Ptr(""), CheckSuite: &CheckSuite{}, App: &App{}, } @@ -116,32 +242,111 @@ func TestCheckRun_String(t *testing.T) { } func TestCheckSuite_String(t *testing.T) { + t.Parallel() v := CheckSuite{ - ID: Int64(0), - NodeID: String(""), - HeadBranch: String(""), - HeadSHA: String(""), - URL: String(""), - BeforeSHA: String(""), - AfterSHA: String(""), - Status: String(""), - Conclusion: String(""), - App: &App{}, - Repository: &Repository{}, - HeadCommit: &Commit{}, - } - want := `github.CheckSuite{ID:0, NodeID:"", HeadBranch:"", HeadSHA:"", URL:"", BeforeSHA:"", AfterSHA:"", Status:"", Conclusion:"", App:github.App{}, Repository:github.Repository{}, HeadCommit:github.Commit{}}` + ID: Ptr(int64(0)), + NodeID: Ptr(""), + HeadBranch: Ptr(""), + HeadSHA: Ptr(""), + URL: Ptr(""), + BeforeSHA: Ptr(""), + AfterSHA: Ptr(""), + Status: Ptr(""), + Conclusion: Ptr(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + App: &App{}, + Repository: &Repository{}, + HeadCommit: &Commit{}, + LatestCheckRunsCount: Ptr(int64(0)), + Rerequestable: Ptr(false), + RunsRerequestable: Ptr(false), + } + want := `github.CheckSuite{ID:0, NodeID:"", HeadBranch:"", HeadSHA:"", URL:"", BeforeSHA:"", AfterSHA:"", Status:"", Conclusion:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, App:github.App{}, Repository:github.Repository{}, HeadCommit:github.Commit{}, LatestCheckRunsCount:0, Rerequestable:false, RunsRerequestable:false}` if got := v.String(); got != want { t.Errorf("CheckSuite.String = %v, want %v", got, want) } } +func TestClassroom_String(t *testing.T) { + t.Parallel() + v := Classroom{ + ID: Ptr(int64(0)), + Name: Ptr(""), + Archived: Ptr(false), + Organization: &Organization{}, + URL: Ptr(""), + } + want := `github.Classroom{ID:0, Name:"", Archived:false, Organization:github.Organization{}, URL:""}` + if got := v.String(); got != want { + t.Errorf("Classroom.String = %v, want %v", got, want) + } +} + +func TestClassroomAssignment_String(t *testing.T) { + t.Parallel() + v := ClassroomAssignment{ + ID: Ptr(int64(0)), + PublicRepo: Ptr(false), + Title: Ptr(""), + Type: Ptr(""), + InviteLink: Ptr(""), + InvitationsEnabled: Ptr(false), + Slug: Ptr(""), + StudentsAreRepoAdmins: Ptr(false), + FeedbackPullRequestsEnabled: Ptr(false), + MaxTeams: Ptr(0), + MaxMembers: Ptr(0), + Editor: Ptr(""), + Accepted: Ptr(0), + Submitted: Ptr(0), + Passing: Ptr(0), + Language: Ptr(""), + Deadline: &Timestamp{}, + StarterCodeRepository: &Repository{}, + Classroom: &Classroom{}, + } + want := `github.ClassroomAssignment{ID:0, PublicRepo:false, Title:"", Type:"", InviteLink:"", InvitationsEnabled:false, Slug:"", StudentsAreRepoAdmins:false, FeedbackPullRequestsEnabled:false, MaxTeams:0, MaxMembers:0, Editor:"", Accepted:0, Submitted:0, Passing:0, Language:"", Deadline:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, StarterCodeRepository:github.Repository{}, Classroom:github.Classroom{}}` + if got := v.String(); got != want { + t.Errorf("ClassroomAssignment.String = %v, want %v", got, want) + } +} + +func TestClassroomUser_String(t *testing.T) { + t.Parallel() + v := ClassroomUser{ + ID: Ptr(int64(0)), + Login: Ptr(""), + AvatarURL: Ptr(""), + HTMLURL: Ptr(""), + } + want := `github.ClassroomUser{ID:0, Login:"", AvatarURL:"", HTMLURL:""}` + if got := v.String(); got != want { + t.Errorf("ClassroomUser.String = %v, want %v", got, want) + } +} + +func TestCodeOfConduct_String(t *testing.T) { + t.Parallel() + v := CodeOfConduct{ + Name: Ptr(""), + Key: Ptr(""), + URL: Ptr(""), + Body: Ptr(""), + } + want := `github.CodeOfConduct{Name:"", Key:"", URL:"", Body:""}` + if got := v.String(); got != want { + t.Errorf("CodeOfConduct.String = %v, want %v", got, want) + } +} + func TestCodeResult_String(t *testing.T) { + t.Parallel() v := CodeResult{ - Name: String(""), - Path: String(""), - SHA: String(""), - HTMLURL: String(""), + Name: Ptr(""), + Path: Ptr(""), + SHA: Ptr(""), + HTMLURL: Ptr(""), Repository: &Repository{}, } want := `github.CodeResult{Name:"", Path:"", SHA:"", HTMLURL:"", Repository:github.Repository{}}` @@ -150,14 +355,26 @@ func TestCodeResult_String(t *testing.T) { } } +func TestCodeSecurity_String(t *testing.T) { + t.Parallel() + v := CodeSecurity{ + Status: Ptr(""), + } + want := `github.CodeSecurity{Status:""}` + if got := v.String(); got != want { + t.Errorf("CodeSecurity.String = %v, want %v", got, want) + } +} + func TestCombinedStatus_String(t *testing.T) { + t.Parallel() v := CombinedStatus{ - State: String(""), - Name: String(""), - SHA: String(""), - TotalCount: Int(0), - CommitURL: String(""), - RepositoryURL: String(""), + State: Ptr(""), + Name: Ptr(""), + SHA: Ptr(""), + TotalCount: Ptr(0), + CommitURL: Ptr(""), + RepositoryURL: Ptr(""), } want := `github.CombinedStatus{State:"", Name:"", SHA:"", TotalCount:0, CommitURL:"", RepositoryURL:""}` if got := v.String(); got != want { @@ -166,11 +383,12 @@ func TestCombinedStatus_String(t *testing.T) { } func TestCommentStats_String(t *testing.T) { + t.Parallel() v := CommentStats{ - TotalCommitComments: Int(0), - TotalGistComments: Int(0), - TotalIssueComments: Int(0), - TotalPullRequestComments: Int(0), + TotalCommitComments: Ptr(0), + TotalGistComments: Ptr(0), + TotalIssueComments: Ptr(0), + TotalPullRequestComments: Ptr(0), } want := `github.CommentStats{TotalCommitComments:0, TotalGistComments:0, TotalIssueComments:0, TotalPullRequestComments:0}` if got := v.String(); got != want { @@ -179,50 +397,53 @@ func TestCommentStats_String(t *testing.T) { } func TestCommit_String(t *testing.T) { + t.Parallel() v := Commit{ - SHA: String(""), + SHA: Ptr(""), Author: &CommitAuthor{}, Committer: &CommitAuthor{}, - Message: String(""), + Message: Ptr(""), Tree: &Tree{}, - Stats: &CommitStats{}, - HTMLURL: String(""), - URL: String(""), + HTMLURL: Ptr(""), + URL: Ptr(""), Verification: &SignatureVerification{}, - NodeID: String(""), - CommentCount: Int(0), + NodeID: Ptr(""), + CommentCount: Ptr(0), } - want := `github.Commit{SHA:"", Author:github.CommitAuthor{}, Committer:github.CommitAuthor{}, Message:"", Tree:github.Tree{}, Stats:github.CommitStats{}, HTMLURL:"", URL:"", Verification:github.SignatureVerification{}, NodeID:"", CommentCount:0}` + want := `github.Commit{SHA:"", Author:github.CommitAuthor{}, Committer:github.CommitAuthor{}, Message:"", Tree:github.Tree{}, HTMLURL:"", URL:"", Verification:github.SignatureVerification{}, NodeID:"", CommentCount:0}` if got := v.String(); got != want { t.Errorf("Commit.String = %v, want %v", got, want) } } func TestCommitAuthor_String(t *testing.T) { + t.Parallel() v := CommitAuthor{ - Name: String(""), - Email: String(""), - Login: String(""), + Date: &Timestamp{}, + Name: Ptr(""), + Email: Ptr(""), + Login: Ptr(""), } - want := `github.CommitAuthor{Name:"", Email:"", Login:""}` + want := `github.CommitAuthor{Date:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Name:"", Email:"", Login:""}` if got := v.String(); got != want { t.Errorf("CommitAuthor.String = %v, want %v", got, want) } } func TestCommitFile_String(t *testing.T) { + t.Parallel() v := CommitFile{ - SHA: String(""), - Filename: String(""), - Additions: Int(0), - Deletions: Int(0), - Changes: Int(0), - Status: String(""), - Patch: String(""), - BlobURL: String(""), - RawURL: String(""), - ContentsURL: String(""), - PreviousFilename: String(""), + SHA: Ptr(""), + Filename: Ptr(""), + Additions: Ptr(0), + Deletions: Ptr(0), + Changes: Ptr(0), + Status: Ptr(""), + Patch: Ptr(""), + BlobURL: Ptr(""), + RawURL: Ptr(""), + ContentsURL: Ptr(""), + PreviousFilename: Ptr(""), } want := `github.CommitFile{SHA:"", Filename:"", Additions:0, Deletions:0, Changes:0, Status:"", Patch:"", BlobURL:"", RawURL:"", ContentsURL:"", PreviousFilename:""}` if got := v.String(); got != want { @@ -231,10 +452,11 @@ func TestCommitFile_String(t *testing.T) { } func TestCommitStats_String(t *testing.T) { + t.Parallel() v := CommitStats{ - Additions: Int(0), - Deletions: Int(0), - Total: Int(0), + Additions: Ptr(0), + Deletions: Ptr(0), + Total: Ptr(0), } want := `github.CommitStats{Additions:0, Deletions:0, Total:0}` if got := v.String(); got != want { @@ -243,18 +465,19 @@ func TestCommitStats_String(t *testing.T) { } func TestCommitsComparison_String(t *testing.T) { + t.Parallel() v := CommitsComparison{ BaseCommit: &RepositoryCommit{}, MergeBaseCommit: &RepositoryCommit{}, - Status: String(""), - AheadBy: Int(0), - BehindBy: Int(0), - TotalCommits: Int(0), - HTMLURL: String(""), - PermalinkURL: String(""), - DiffURL: String(""), - PatchURL: String(""), - URL: String(""), + Status: Ptr(""), + AheadBy: Ptr(0), + BehindBy: Ptr(0), + TotalCommits: Ptr(0), + HTMLURL: Ptr(""), + PermalinkURL: Ptr(""), + DiffURL: Ptr(""), + PatchURL: Ptr(""), + URL: Ptr(""), } want := `github.CommitsComparison{BaseCommit:github.RepositoryCommit{}, MergeBaseCommit:github.RepositoryCommit{}, Status:"", AheadBy:0, BehindBy:0, TotalCommits:0, HTMLURL:"", PermalinkURL:"", DiffURL:"", PatchURL:"", URL:""}` if got := v.String(); got != want { @@ -262,10 +485,22 @@ func TestCommitsComparison_String(t *testing.T) { } } +func TestContributorApprovalPermissions_String(t *testing.T) { + t.Parallel() + v := ContributorApprovalPermissions{ + ApprovalPolicy: "", + } + want := `github.ContributorApprovalPermissions{ApprovalPolicy:""}` + if got := v.String(); got != want { + t.Errorf("ContributorApprovalPermissions.String = %v, want %v", got, want) + } +} + func TestContributorStats_String(t *testing.T) { + t.Parallel() v := ContributorStats{ Author: &Contributor{}, - Total: Int(0), + Total: Ptr(0), } want := `github.ContributorStats{Author:github.Contributor{}, Total:0}` if got := v.String(); got != want { @@ -273,20 +508,51 @@ func TestContributorStats_String(t *testing.T) { } } +func TestCreateTeamRequest_String(t *testing.T) { + t.Parallel() + v := CreateTeamRequest{ + Name: "", + Description: Ptr(""), + Maintainers: []string{""}, + RepoNames: []string{""}, + Privacy: Ptr(""), + NotificationSetting: Ptr(""), + Permission: Ptr(""), + ParentTeamID: Ptr(int64(0)), + ParentTeamSlug: Ptr(""), + } + want := `github.CreateTeamRequest{Name:"", Description:"", Maintainers:[""], RepoNames:[""], Privacy:"", NotificationSetting:"", Permission:"", ParentTeamID:0, ParentTeamSlug:""}` + if got := v.String(); got != want { + t.Errorf("CreateTeamRequest.String = %v, want %v", got, want) + } +} + +func TestDependabotSecurityUpdates_String(t *testing.T) { + t.Parallel() + v := DependabotSecurityUpdates{ + Status: Ptr(""), + } + want := `github.DependabotSecurityUpdates{Status:""}` + if got := v.String(); got != want { + t.Errorf("DependabotSecurityUpdates.String = %v, want %v", got, want) + } +} + func TestDiscussionComment_String(t *testing.T) { + t.Parallel() v := DiscussionComment{ Author: &User{}, - Body: String(""), - BodyHTML: String(""), - BodyVersion: String(""), + Body: Ptr(""), + BodyHTML: Ptr(""), + BodyVersion: Ptr(""), CreatedAt: &Timestamp{}, LastEditedAt: &Timestamp{}, - DiscussionURL: String(""), - HTMLURL: String(""), - NodeID: String(""), - Number: Int(0), + DiscussionURL: Ptr(""), + HTMLURL: Ptr(""), + NodeID: Ptr(""), + Number: Ptr(0), UpdatedAt: &Timestamp{}, - URL: String(""), + URL: Ptr(""), Reactions: &Reactions{}, } want := `github.DiscussionComment{Author:github.User{}, Body:"", BodyHTML:"", BodyVersion:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, LastEditedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, DiscussionURL:"", HTMLURL:"", NodeID:"", Number:0, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", Reactions:github.Reactions{}}` @@ -296,89 +562,143 @@ func TestDiscussionComment_String(t *testing.T) { } func TestDraftReviewComment_String(t *testing.T) { + t.Parallel() v := DraftReviewComment{ - Path: String(""), - Position: Int(0), - Body: String(""), + Path: Ptr(""), + Position: Ptr(0), + Body: Ptr(""), + StartSide: Ptr(""), + Side: Ptr(""), + StartLine: Ptr(0), + Line: Ptr(0), } - want := `github.DraftReviewComment{Path:"", Position:0, Body:""}` + want := `github.DraftReviewComment{Path:"", Position:0, Body:"", StartSide:"", Side:"", StartLine:0, Line:0}` if got := v.String(); got != want { t.Errorf("DraftReviewComment.String = %v, want %v", got, want) } } +func TestEnterprise_String(t *testing.T) { + t.Parallel() + v := Enterprise{ + ID: Ptr(0), + Slug: Ptr(""), + Name: Ptr(""), + NodeID: Ptr(""), + AvatarURL: Ptr(""), + Description: Ptr(""), + WebsiteURL: Ptr(""), + HTMLURL: Ptr(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + } + want := `github.Enterprise{ID:0, Slug:"", Name:"", NodeID:"", AvatarURL:"", Description:"", WebsiteURL:"", HTMLURL:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("Enterprise.String = %v, want %v", got, want) + } +} + +func TestEnterpriseBudget_String(t *testing.T) { + t.Parallel() + v := EnterpriseBudget{ + ID: Ptr(""), + BudgetType: Ptr(""), + BudgetProductSKU: Ptr(""), + BudgetScope: Ptr(""), + BudgetEntityName: Ptr(""), + BudgetAmount: Ptr(0), + PreventFurtherUsage: Ptr(false), + BudgetAlerting: &EnterpriseBudgetAlerting{}, + } + want := `github.EnterpriseBudget{ID:"", BudgetType:"", BudgetProductSKU:"", BudgetScope:"", BudgetEntityName:"", BudgetAmount:0, PreventFurtherUsage:false, BudgetAlerting:github.EnterpriseBudgetAlerting{}}` + if got := v.String(); got != want { + t.Errorf("EnterpriseBudget.String = %v, want %v", got, want) + } +} + func TestEvent_String(t *testing.T) { + t.Parallel() v := Event{ - Type: String(""), - Public: Bool(false), - Repo: &Repository{}, - Actor: &User{}, - Org: &Organization{}, - ID: String(""), + Type: Ptr(""), + Public: Ptr(false), + Repo: &Repository{}, + Actor: &User{}, + Org: &Organization{}, + CreatedAt: &Timestamp{}, + ID: Ptr(""), } - want := `github.Event{Type:"", Public:false, Repo:github.Repository{}, Actor:github.User{}, Org:github.Organization{}, ID:""}` + want := `github.Event{Type:"", Public:false, Repo:github.Repository{}, Actor:github.User{}, Org:github.Organization{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ID:""}` if got := v.String(); got != want { t.Errorf("Event.String = %v, want %v", got, want) } } func TestGPGKey_String(t *testing.T) { + t.Parallel() v := GPGKey{ - ID: Int64(0), - PrimaryKeyID: Int64(0), - KeyID: String(""), - PublicKey: String(""), - CanSign: Bool(false), - CanEncryptComms: Bool(false), - CanEncryptStorage: Bool(false), - CanCertify: Bool(false), - } - want := `github.GPGKey{ID:0, PrimaryKeyID:0, KeyID:"", PublicKey:"", CanSign:false, CanEncryptComms:false, CanEncryptStorage:false, CanCertify:false}` + ID: Ptr(int64(0)), + PrimaryKeyID: Ptr(int64(0)), + KeyID: Ptr(""), + RawKey: Ptr(""), + PublicKey: Ptr(""), + CanSign: Ptr(false), + CanEncryptComms: Ptr(false), + CanEncryptStorage: Ptr(false), + CanCertify: Ptr(false), + CreatedAt: &Timestamp{}, + ExpiresAt: &Timestamp{}, + } + want := `github.GPGKey{ID:0, PrimaryKeyID:0, KeyID:"", RawKey:"", PublicKey:"", CanSign:false, CanEncryptComms:false, CanEncryptStorage:false, CanCertify:false, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ExpiresAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("GPGKey.String = %v, want %v", got, want) } } func TestGist_String(t *testing.T) { + t.Parallel() v := Gist{ - ID: String(""), - Description: String(""), - Public: Bool(false), + ID: Ptr(""), + Description: Ptr(""), + Public: Ptr(false), Owner: &User{}, - Files: nil, - Comments: Int(0), - HTMLURL: String(""), - GitPullURL: String(""), - GitPushURL: String(""), - NodeID: String(""), + Comments: Ptr(0), + HTMLURL: Ptr(""), + GitPullURL: Ptr(""), + GitPushURL: Ptr(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + NodeID: Ptr(""), } - want := `github.Gist{ID:"", Description:"", Public:false, Owner:github.User{}, Files:map[], Comments:0, HTMLURL:"", GitPullURL:"", GitPushURL:"", NodeID:""}` + want := `github.Gist{ID:"", Description:"", Public:false, Owner:github.User{}, Comments:0, HTMLURL:"", GitPullURL:"", GitPushURL:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:""}` if got := v.String(); got != want { t.Errorf("Gist.String = %v, want %v", got, want) } } func TestGistComment_String(t *testing.T) { + t.Parallel() v := GistComment{ - ID: Int64(0), - URL: String(""), - Body: String(""), - User: &User{}, + ID: Ptr(int64(0)), + URL: Ptr(""), + Body: Ptr(""), + User: &User{}, + CreatedAt: &Timestamp{}, } - want := `github.GistComment{ID:0, URL:"", Body:"", User:github.User{}}` + want := `github.GistComment{ID:0, URL:"", Body:"", User:github.User{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("GistComment.String = %v, want %v", got, want) } } func TestGistCommit_String(t *testing.T) { + t.Parallel() v := GistCommit{ - URL: String(""), - Version: String(""), + URL: Ptr(""), + Version: Ptr(""), User: &User{}, ChangeStatus: &CommitStats{}, CommittedAt: &Timestamp{}, - NodeID: String(""), + NodeID: Ptr(""), } want := `github.GistCommit{URL:"", Version:"", User:github.User{}, ChangeStatus:github.CommitStats{}, CommittedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:""}` if got := v.String(); got != want { @@ -387,13 +707,14 @@ func TestGistCommit_String(t *testing.T) { } func TestGistFile_String(t *testing.T) { + t.Parallel() v := GistFile{ - Size: Int(0), - Filename: String(""), - Language: String(""), - Type: String(""), - RawURL: String(""), - Content: String(""), + Size: Ptr(0), + Filename: Ptr(""), + Language: Ptr(""), + Type: Ptr(""), + RawURL: Ptr(""), + Content: Ptr(""), } want := `github.GistFile{Size:0, Filename:"", Language:"", Type:"", RawURL:"", Content:""}` if got := v.String(); got != want { @@ -402,13 +723,14 @@ func TestGistFile_String(t *testing.T) { } func TestGistFork_String(t *testing.T) { + t.Parallel() v := GistFork{ - URL: String(""), + URL: Ptr(""), User: &User{}, - ID: String(""), + ID: Ptr(""), CreatedAt: &Timestamp{}, UpdatedAt: &Timestamp{}, - NodeID: String(""), + NodeID: Ptr(""), } want := `github.GistFork{URL:"", User:github.User{}, ID:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:""}` if got := v.String(); got != want { @@ -417,10 +739,11 @@ func TestGistFork_String(t *testing.T) { } func TestGistStats_String(t *testing.T) { + t.Parallel() v := GistStats{ - TotalGists: Int(0), - PrivateGists: Int(0), - PublicGists: Int(0), + TotalGists: Ptr(0), + PrivateGists: Ptr(0), + PublicGists: Ptr(0), } want := `github.GistStats{TotalGists:0, PrivateGists:0, PublicGists:0}` if got := v.String(); got != want { @@ -429,10 +752,11 @@ func TestGistStats_String(t *testing.T) { } func TestGitObject_String(t *testing.T) { + t.Parallel() v := GitObject{ - Type: String(""), - SHA: String(""), - URL: String(""), + Type: Ptr(""), + SHA: Ptr(""), + URL: Ptr(""), } want := `github.GitObject{Type:"", SHA:"", URL:""}` if got := v.String(); got != want { @@ -441,9 +765,10 @@ func TestGitObject_String(t *testing.T) { } func TestGitignore_String(t *testing.T) { + t.Parallel() v := Gitignore{ - Name: String(""), - Source: String(""), + Name: Ptr(""), + Source: Ptr(""), } want := `github.Gitignore{Name:"", Source:""}` if got := v.String(); got != want { @@ -452,37 +777,93 @@ func TestGitignore_String(t *testing.T) { } func TestGrant_String(t *testing.T) { + t.Parallel() v := Grant{ - ID: Int64(0), - URL: String(""), + ID: Ptr(int64(0)), + URL: Ptr(""), App: &AuthorizationApp{}, CreatedAt: &Timestamp{}, UpdatedAt: &Timestamp{}, + Scopes: []string{""}, } - want := `github.Grant{ID:0, URL:"", App:github.AuthorizationApp{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + want := `github.Grant{ID:0, URL:"", App:github.AuthorizationApp{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Scopes:[""]}` if got := v.String(); got != want { t.Errorf("Grant.String = %v, want %v", got, want) } } +func TestHeadCommit_String(t *testing.T) { + t.Parallel() + v := HeadCommit{ + Message: Ptr(""), + Author: &CommitAuthor{}, + URL: Ptr(""), + Distinct: Ptr(false), + SHA: Ptr(""), + ID: Ptr(""), + TreeID: Ptr(""), + Timestamp: &Timestamp{}, + Committer: &CommitAuthor{}, + Added: []string{""}, + Removed: []string{""}, + Modified: []string{""}, + } + want := `github.HeadCommit{Message:"", Author:github.CommitAuthor{}, URL:"", Distinct:false, SHA:"", ID:"", TreeID:"", Timestamp:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Committer:github.CommitAuthor{}, Added:[""], Removed:[""], Modified:[""]}` + if got := v.String(); got != want { + t.Errorf("HeadCommit.String = %v, want %v", got, want) + } +} + func TestHook_String(t *testing.T) { + t.Parallel() v := Hook{ - URL: String(""), - ID: Int64(0), - Config: nil, - Active: Bool(false), - } - want := `github.Hook{URL:"", ID:0, Config:map[], Active:false}` + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + URL: Ptr(""), + ID: Ptr(int64(0)), + Type: Ptr(""), + Name: Ptr(""), + TestURL: Ptr(""), + PingURL: Ptr(""), + Config: &HookConfig{}, + Events: []string{""}, + Active: Ptr(false), + } + want := `github.Hook{CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", ID:0, Type:"", Name:"", TestURL:"", PingURL:"", Config:github.HookConfig{}, Events:[""], Active:false}` if got := v.String(); got != want { t.Errorf("Hook.String = %v, want %v", got, want) } } +func TestHookDelivery_String(t *testing.T) { + t.Parallel() + v := HookDelivery{ + ID: Ptr(int64(0)), + GUID: Ptr(""), + DeliveredAt: &Timestamp{}, + Redelivery: Ptr(false), + Duration: Ptr(0.0), + Status: Ptr(""), + StatusCode: Ptr(0), + Event: Ptr(""), + Action: Ptr(""), + InstallationID: Ptr(int64(0)), + RepositoryID: Ptr(int64(0)), + Request: &HookRequest{}, + Response: &HookResponse{}, + } + want := `github.HookDelivery{ID:0, GUID:"", DeliveredAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Redelivery:false, Duration:0, Status:"", StatusCode:0, Event:"", Action:"", InstallationID:0, RepositoryID:0, Request:github.HookRequest{}, Response:github.HookResponse{}}` + if got := v.String(); got != want { + t.Errorf("HookDelivery.String = %v, want %v", got, want) + } +} + func TestHookStats_String(t *testing.T) { + t.Parallel() v := HookStats{ - TotalHooks: Int(0), - ActiveHooks: Int(0), - InactiveHooks: Int(0), + TotalHooks: Ptr(0), + ActiveHooks: Ptr(0), + InactiveHooks: Ptr(0), } want := `github.HookStats{TotalHooks:0, ActiveHooks:0, InactiveHooks:0}` if got := v.String(); got != want { @@ -491,29 +872,30 @@ func TestHookStats_String(t *testing.T) { } func TestImport_String(t *testing.T) { + t.Parallel() v := Import{ - VCSURL: String(""), - VCS: String(""), - VCSUsername: String(""), - VCSPassword: String(""), - TFVCProject: String(""), - UseLFS: String(""), - HasLargeFiles: Bool(false), - LargeFilesSize: Int(0), - LargeFilesCount: Int(0), - Status: String(""), - CommitCount: Int(0), - StatusText: String(""), - AuthorsCount: Int(0), - Percent: Int(0), - PushPercent: Int(0), - URL: String(""), - HTMLURL: String(""), - AuthorsURL: String(""), - RepositoryURL: String(""), - Message: String(""), - FailedStep: String(""), - HumanName: String(""), + VCSURL: Ptr(""), + VCS: Ptr(""), + VCSUsername: Ptr(""), + VCSPassword: Ptr(""), + TFVCProject: Ptr(""), + UseLFS: Ptr(""), + HasLargeFiles: Ptr(false), + LargeFilesSize: Ptr(0), + LargeFilesCount: Ptr(0), + Status: Ptr(""), + CommitCount: Ptr(0), + StatusText: Ptr(""), + AuthorsCount: Ptr(0), + Percent: Ptr(0), + PushPercent: Ptr(0), + URL: Ptr(""), + HTMLURL: Ptr(""), + AuthorsURL: Ptr(""), + RepositoryURL: Ptr(""), + Message: Ptr(""), + FailedStep: Ptr(""), + HumanName: Ptr(""), } want := `github.Import{VCSURL:"", VCS:"", VCSUsername:"", VCSPassword:"", TFVCProject:"", UseLFS:"", HasLargeFiles:false, LargeFilesSize:0, LargeFilesCount:0, Status:"", CommitCount:0, StatusText:"", AuthorsCount:0, Percent:0, PushPercent:0, URL:"", HTMLURL:"", AuthorsURL:"", RepositoryURL:"", Message:"", FailedStep:"", HumanName:""}` if got := v.String(); got != want { @@ -522,98 +904,128 @@ func TestImport_String(t *testing.T) { } func TestInstallation_String(t *testing.T) { + t.Parallel() v := Installation{ - ID: Int64(0), - AppID: Int64(0), - TargetID: Int64(0), - Account: &User{}, - AccessTokensURL: String(""), - RepositoriesURL: String(""), - HTMLURL: String(""), - TargetType: String(""), - SingleFileName: String(""), - RepositorySelection: String(""), - Permissions: &InstallationPermissions{}, - CreatedAt: &Timestamp{}, - UpdatedAt: &Timestamp{}, - } - want := `github.Installation{ID:0, AppID:0, TargetID:0, Account:github.User{}, AccessTokensURL:"", RepositoriesURL:"", HTMLURL:"", TargetType:"", SingleFileName:"", RepositorySelection:"", Permissions:github.InstallationPermissions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + ID: Ptr(int64(0)), + NodeID: Ptr(""), + ClientID: Ptr(""), + AppID: Ptr(int64(0)), + AppSlug: Ptr(""), + TargetID: Ptr(int64(0)), + Account: &User{}, + AccessTokensURL: Ptr(""), + RepositoriesURL: Ptr(""), + HTMLURL: Ptr(""), + TargetType: Ptr(""), + SingleFileName: Ptr(""), + RepositorySelection: Ptr(""), + Events: []string{""}, + SingleFilePaths: []string{""}, + Permissions: &InstallationPermissions{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + HasMultipleSingleFiles: Ptr(false), + SuspendedBy: &User{}, + SuspendedAt: &Timestamp{}, + } + want := `github.Installation{ID:0, NodeID:"", ClientID:"", AppID:0, AppSlug:"", TargetID:0, Account:github.User{}, AccessTokensURL:"", RepositoriesURL:"", HTMLURL:"", TargetType:"", SingleFileName:"", RepositorySelection:"", Events:[""], SingleFilePaths:[""], Permissions:github.InstallationPermissions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HasMultipleSingleFiles:false, SuspendedBy:github.User{}, SuspendedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("Installation.String = %v, want %v", got, want) } } func TestInvitation_String(t *testing.T) { + t.Parallel() v := Invitation{ - ID: Int64(0), - NodeID: String(""), - Login: String(""), - Email: String(""), - Role: String(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + Login: Ptr(""), + Email: Ptr(""), + Role: Ptr(""), + CreatedAt: &Timestamp{}, Inviter: &User{}, - TeamCount: Int(0), - InvitationTeamURL: String(""), + TeamCount: Ptr(0), + InvitationTeamURL: Ptr(""), + FailedAt: &Timestamp{}, + FailedReason: Ptr(""), } - want := `github.Invitation{ID:0, NodeID:"", Login:"", Email:"", Role:"", Inviter:github.User{}, TeamCount:0, InvitationTeamURL:""}` + want := `github.Invitation{ID:0, NodeID:"", Login:"", Email:"", Role:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Inviter:github.User{}, TeamCount:0, InvitationTeamURL:"", FailedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, FailedReason:""}` if got := v.String(); got != want { t.Errorf("Invitation.String = %v, want %v", got, want) } } func TestIssue_String(t *testing.T) { + t.Parallel() v := Issue{ - ID: Int64(0), - Number: Int(0), - State: String(""), - Locked: Bool(false), - Title: String(""), - Body: String(""), - User: &User{}, - Assignee: &User{}, - Comments: Int(0), - ClosedBy: &User{}, - URL: String(""), - HTMLURL: String(""), - CommentsURL: String(""), - EventsURL: String(""), - LabelsURL: String(""), - RepositoryURL: String(""), - Milestone: &Milestone{}, - PullRequestLinks: &PullRequestLinks{}, - Repository: &Repository{}, - Reactions: &Reactions{}, - NodeID: String(""), - ActiveLockReason: String(""), - } - want := `github.Issue{ID:0, Number:0, State:"", Locked:false, Title:"", Body:"", User:github.User{}, Assignee:github.User{}, Comments:0, ClosedBy:github.User{}, URL:"", HTMLURL:"", CommentsURL:"", EventsURL:"", LabelsURL:"", RepositoryURL:"", Milestone:github.Milestone{}, PullRequestLinks:github.PullRequestLinks{}, Repository:github.Repository{}, Reactions:github.Reactions{}, NodeID:"", ActiveLockReason:""}` + ID: Ptr(int64(0)), + Number: Ptr(0), + State: Ptr(""), + StateReason: Ptr(""), + Locked: Ptr(false), + Title: Ptr(""), + Body: Ptr(""), + AuthorAssociation: Ptr(""), + User: &User{}, + Assignee: &User{}, + Comments: Ptr(0), + ClosedAt: &Timestamp{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + ClosedBy: &User{}, + URL: Ptr(""), + HTMLURL: Ptr(""), + CommentsURL: Ptr(""), + EventsURL: Ptr(""), + LabelsURL: Ptr(""), + RepositoryURL: Ptr(""), + ParentIssueURL: Ptr(""), + Milestone: &Milestone{}, + PullRequestLinks: &PullRequestLinks{}, + Repository: &Repository{}, + Reactions: &Reactions{}, + NodeID: Ptr(""), + Draft: Ptr(false), + Type: &IssueType{}, + PinnedComment: &IssueComment{}, + PerformedViaGithubApp: &App{}, + IssueDependenciesSummary: &IssueDependenciesSummary{}, + SubIssuesSummary: &SubIssuesSummary{}, + ActiveLockReason: Ptr(""), + } + want := `github.Issue{ID:0, Number:0, State:"", StateReason:"", Locked:false, Title:"", Body:"", AuthorAssociation:"", User:github.User{}, Assignee:github.User{}, Comments:0, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ClosedBy:github.User{}, URL:"", HTMLURL:"", CommentsURL:"", EventsURL:"", LabelsURL:"", RepositoryURL:"", ParentIssueURL:"", Milestone:github.Milestone{}, PullRequestLinks:github.PullRequestLinks{}, Repository:github.Repository{}, Reactions:github.Reactions{}, NodeID:"", Draft:false, Type:github.IssueType{}, PinnedComment:github.IssueComment{}, PerformedViaGithubApp:github.App{}, IssueDependenciesSummary:github.IssueDependenciesSummary{}, SubIssuesSummary:github.SubIssuesSummary{}, ActiveLockReason:""}` if got := v.String(); got != want { t.Errorf("Issue.String = %v, want %v", got, want) } } func TestIssueComment_String(t *testing.T) { + t.Parallel() v := IssueComment{ - ID: Int64(0), - NodeID: String(""), - Body: String(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + Body: Ptr(""), User: &User{}, Reactions: &Reactions{}, - AuthorAssociation: String(""), - URL: String(""), - HTMLURL: String(""), - IssueURL: String(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + AuthorAssociation: Ptr(""), + URL: Ptr(""), + HTMLURL: Ptr(""), + IssueURL: Ptr(""), } - want := `github.IssueComment{ID:0, NodeID:"", Body:"", User:github.User{}, Reactions:github.Reactions{}, AuthorAssociation:"", URL:"", HTMLURL:"", IssueURL:""}` + want := `github.IssueComment{ID:0, NodeID:"", Body:"", User:github.User{}, Reactions:github.Reactions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, AuthorAssociation:"", URL:"", HTMLURL:"", IssueURL:""}` if got := v.String(); got != want { t.Errorf("IssueComment.String = %v, want %v", got, want) } } func TestIssueStats_String(t *testing.T) { + t.Parallel() v := IssueStats{ - TotalIssues: Int(0), - OpenIssues: Int(0), - ClosedIssues: Int(0), + TotalIssues: Ptr(0), + OpenIssues: Ptr(0), + ClosedIssues: Ptr(0), } want := `github.IssueStats{TotalIssues:0, OpenIssues:0, ClosedIssues:0}` if got := v.String(); got != want { @@ -622,29 +1034,34 @@ func TestIssueStats_String(t *testing.T) { } func TestKey_String(t *testing.T) { + t.Parallel() v := Key{ - ID: Int64(0), - Key: String(""), - URL: String(""), - Title: String(""), - ReadOnly: Bool(false), + ID: Ptr(int64(0)), + Key: Ptr(""), + URL: Ptr(""), + Title: Ptr(""), + ReadOnly: Ptr(false), + Verified: Ptr(false), CreatedAt: &Timestamp{}, + AddedBy: Ptr(""), + LastUsed: &Timestamp{}, } - want := `github.Key{ID:0, Key:"", URL:"", Title:"", ReadOnly:false, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + want := `github.Key{ID:0, Key:"", URL:"", Title:"", ReadOnly:false, Verified:false, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, AddedBy:"", LastUsed:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("Key.String = %v, want %v", got, want) } } func TestLabel_String(t *testing.T) { + t.Parallel() v := Label{ - ID: Int64(0), - URL: String(""), - Name: String(""), - Color: String(""), - Description: String(""), - Default: Bool(false), - NodeID: String(""), + ID: 0, + URL: "", + Name: "", + Color: "", + Description: Ptr(""), + Default: false, + NodeID: "", } want := `github.Label{ID:0, URL:"", Name:"", Color:"", Description:"", Default:false, NodeID:""}` if got := v.String(); got != want { @@ -653,14 +1070,15 @@ func TestLabel_String(t *testing.T) { } func TestLabelResult_String(t *testing.T) { + t.Parallel() v := LabelResult{ - ID: Int64(0), - URL: String(""), - Name: String(""), - Color: String(""), - Default: Bool(false), - Description: String(""), - Score: Float64(0.0), + ID: Ptr(int64(0)), + URL: Ptr(""), + Name: Ptr(""), + Color: Ptr(""), + Default: Ptr(false), + Description: Ptr(""), + Score: Ptr(0.0), } want := `github.LabelResult{ID:0, URL:"", Name:"", Color:"", Default:false, Description:"", Score:0}` if got := v.String(); got != want { @@ -669,11 +1087,12 @@ func TestLabelResult_String(t *testing.T) { } func TestLargeFile_String(t *testing.T) { + t.Parallel() v := LargeFile{ - RefName: String(""), - Path: String(""), - OID: String(""), - Size: Int(0), + RefName: Ptr(""), + Path: Ptr(""), + OID: Ptr(""), + Size: Ptr(0), } want := `github.LargeFile{RefName:"", Path:"", OID:"", Size:0}` if got := v.String(); got != want { @@ -682,16 +1101,17 @@ func TestLargeFile_String(t *testing.T) { } func TestLicense_String(t *testing.T) { + t.Parallel() v := License{ - Key: String(""), - Name: String(""), - URL: String(""), - SPDXID: String(""), - HTMLURL: String(""), - Featured: Bool(false), - Description: String(""), - Implementation: String(""), - Body: String(""), + Key: Ptr(""), + Name: Ptr(""), + URL: Ptr(""), + SPDXID: Ptr(""), + HTMLURL: Ptr(""), + Featured: Ptr(false), + Description: Ptr(""), + Implementation: Ptr(""), + Body: Ptr(""), } want := `github.License{Key:"", Name:"", URL:"", SPDXID:"", HTMLURL:"", Featured:false, Description:"", Implementation:"", Body:""}` if got := v.String(); got != want { @@ -700,11 +1120,12 @@ func TestLicense_String(t *testing.T) { } func TestMembership_String(t *testing.T) { + t.Parallel() v := Membership{ - URL: String(""), - State: String(""), - Role: String(""), - OrganizationURL: String(""), + URL: Ptr(""), + State: Ptr(""), + Role: Ptr(""), + OrganizationURL: Ptr(""), Organization: &Organization{}, User: &User{}, } @@ -715,15 +1136,16 @@ func TestMembership_String(t *testing.T) { } func TestMigration_String(t *testing.T) { + t.Parallel() v := Migration{ - ID: Int64(0), - GUID: String(""), - State: String(""), - LockRepositories: Bool(false), - ExcludeAttachments: Bool(false), - URL: String(""), - CreatedAt: String(""), - UpdatedAt: String(""), + ID: Ptr(int64(0)), + GUID: Ptr(""), + State: Ptr(""), + LockRepositories: Ptr(false), + ExcludeAttachments: Ptr(false), + URL: Ptr(""), + CreatedAt: Ptr(""), + UpdatedAt: Ptr(""), } want := `github.Migration{ID:0, GUID:"", State:"", LockRepositories:false, ExcludeAttachments:false, URL:"", CreatedAt:"", UpdatedAt:""}` if got := v.String(); got != want { @@ -732,31 +1154,37 @@ func TestMigration_String(t *testing.T) { } func TestMilestone_String(t *testing.T) { + t.Parallel() v := Milestone{ - URL: String(""), - HTMLURL: String(""), - LabelsURL: String(""), - ID: Int64(0), - Number: Int(0), - State: String(""), - Title: String(""), - Description: String(""), + URL: Ptr(""), + HTMLURL: Ptr(""), + LabelsURL: Ptr(""), + ID: Ptr(int64(0)), + Number: Ptr(0), + State: Ptr(""), + Title: Ptr(""), + Description: Ptr(""), Creator: &User{}, - OpenIssues: Int(0), - ClosedIssues: Int(0), - NodeID: String(""), + OpenIssues: Ptr(0), + ClosedIssues: Ptr(0), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + ClosedAt: &Timestamp{}, + DueOn: &Timestamp{}, + NodeID: Ptr(""), } - want := `github.Milestone{URL:"", HTMLURL:"", LabelsURL:"", ID:0, Number:0, State:"", Title:"", Description:"", Creator:github.User{}, OpenIssues:0, ClosedIssues:0, NodeID:""}` + want := `github.Milestone{URL:"", HTMLURL:"", LabelsURL:"", ID:0, Number:0, State:"", Title:"", Description:"", Creator:github.User{}, OpenIssues:0, ClosedIssues:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, DueOn:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:""}` if got := v.String(); got != want { t.Errorf("Milestone.String = %v, want %v", got, want) } } func TestMilestoneStats_String(t *testing.T) { + t.Parallel() v := MilestoneStats{ - TotalMilestones: Int(0), - OpenMilestones: Int(0), - ClosedMilestones: Int(0), + TotalMilestones: Ptr(0), + OpenMilestones: Ptr(0), + ClosedMilestones: Ptr(0), } want := `github.MilestoneStats{TotalMilestones:0, OpenMilestones:0, ClosedMilestones:0}` if got := v.String(); got != want { @@ -764,27 +1192,26 @@ func TestMilestoneStats_String(t *testing.T) { } } -func TestNewTeam_String(t *testing.T) { - v := NewTeam{ - Name: "", - Description: String(""), - ParentTeamID: Int64(0), - Permission: String(""), - Privacy: String(""), - LDAPDN: String(""), +func TestOAuthAPP_String(t *testing.T) { + t.Parallel() + v := OAuthAPP{ + URL: Ptr(""), + Name: Ptr(""), + ClientID: Ptr(""), } - want := `github.NewTeam{Name:"", Description:"", ParentTeamID:0, Permission:"", Privacy:"", LDAPDN:""}` + want := `github.OAuthAPP{URL:"", Name:"", ClientID:""}` if got := v.String(); got != want { - t.Errorf("NewTeam.String = %v, want %v", got, want) + t.Errorf("OAuthAPP.String = %v, want %v", got, want) } } func TestOrgStats_String(t *testing.T) { + t.Parallel() v := OrgStats{ - TotalOrgs: Int(0), - DisabledOrgs: Int(0), - TotalTeams: Int(0), - TotalTeamMembers: Int(0), + TotalOrgs: Ptr(0), + DisabledOrgs: Ptr(0), + TotalTeams: Ptr(0), + TotalTeamMembers: Ptr(0), } want := `github.OrgStats{TotalOrgs:0, DisabledOrgs:0, TotalTeams:0, TotalTeamMembers:0}` if got := v.String(); got != want { @@ -793,52 +1220,329 @@ func TestOrgStats_String(t *testing.T) { } func TestOrganization_String(t *testing.T) { + t.Parallel() v := Organization{ - Login: String(""), - ID: Int64(0), - NodeID: String(""), - AvatarURL: String(""), - HTMLURL: String(""), - Name: String(""), - Company: String(""), - Blog: String(""), - Location: String(""), - Email: String(""), - Description: String(""), - PublicRepos: Int(0), - PublicGists: Int(0), - Followers: Int(0), - Following: Int(0), - TotalPrivateRepos: Int(0), - OwnedPrivateRepos: Int(0), - PrivateGists: Int(0), - DiskUsage: Int(0), - Collaborators: Int(0), - BillingEmail: String(""), - Type: String(""), + Login: Ptr(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + AvatarURL: Ptr(""), + HTMLURL: Ptr(""), + Name: Ptr(""), + Company: Ptr(""), + Blog: Ptr(""), + Location: Ptr(""), + Email: Ptr(""), + TwitterUsername: Ptr(""), + Description: Ptr(""), + PublicRepos: Ptr(0), + PublicGists: Ptr(0), + Followers: Ptr(0), + Following: Ptr(0), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + ArchivedAt: &Timestamp{}, + TotalPrivateRepos: Ptr(int64(0)), + OwnedPrivateRepos: Ptr(int64(0)), + PrivateGists: Ptr(0), + DiskUsage: Ptr(0), + Collaborators: Ptr(0), + BillingEmail: Ptr(""), + Type: Ptr(""), Plan: &Plan{}, - TwoFactorRequirementEnabled: Bool(false), - DefaultRepoPermission: String(""), - DefaultRepoSettings: String(""), - MembersCanCreateRepos: Bool(false), - MembersAllowedRepositoryCreationType: String(""), - URL: String(""), - EventsURL: String(""), - HooksURL: String(""), - IssuesURL: String(""), - MembersURL: String(""), - PublicMembersURL: String(""), - ReposURL: String(""), - } - want := `github.Organization{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", Name:"", Company:"", Blog:"", Location:"", Email:"", Description:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, BillingEmail:"", Type:"", Plan:github.Plan{}, TwoFactorRequirementEnabled:false, DefaultRepoPermission:"", DefaultRepoSettings:"", MembersCanCreateRepos:false, MembersAllowedRepositoryCreationType:"", URL:"", EventsURL:"", HooksURL:"", IssuesURL:"", MembersURL:"", PublicMembersURL:"", ReposURL:""}` + TwoFactorRequirementEnabled: Ptr(false), + IsVerified: Ptr(false), + HasOrganizationProjects: Ptr(false), + HasRepositoryProjects: Ptr(false), + DefaultRepoPermission: Ptr(""), + DefaultRepoSettings: Ptr(""), + MembersCanCreateRepos: Ptr(false), + MembersCanCreatePublicRepos: Ptr(false), + MembersCanCreatePrivateRepos: Ptr(false), + MembersCanCreateInternalRepos: Ptr(false), + MembersCanForkPrivateRepos: Ptr(false), + DeployKeysEnabledForRepositories: Ptr(false), + MembersAllowedRepositoryCreationType: Ptr(""), + MembersCanCreatePages: Ptr(false), + MembersCanCreatePublicPages: Ptr(false), + MembersCanCreatePrivatePages: Ptr(false), + WebCommitSignoffRequired: Ptr(false), + AdvancedSecurityEnabledForNewRepos: Ptr(false), + DependabotAlertsEnabledForNewRepos: Ptr(false), + DependabotSecurityUpdatesEnabledForNewRepos: Ptr(false), + DependencyGraphEnabledForNewRepos: Ptr(false), + SecretScanningEnabledForNewRepos: Ptr(false), + SecretScanningPushProtectionEnabledForNewRepos: Ptr(false), + SecretScanningValidityChecksEnabled: Ptr(false), + SecretScanningPushProtectionCustomLinkEnabled: Ptr(false), + SecretScanningPushProtectionCustomLink: Ptr(""), + MembersCanDeleteRepositories: Ptr(false), + MembersCanChangeRepoVisibility: Ptr(false), + MembersCanInviteOutsideCollaborators: Ptr(false), + MembersCanDeleteIssues: Ptr(false), + DisplayCommenterFullNameSettingEnabled: Ptr(false), + ReadersCanCreateDiscussions: Ptr(false), + MembersCanCreateTeams: Ptr(false), + MembersCanViewDependencyInsights: Ptr(false), + DefaultRepositoryBranch: Ptr(""), + URL: Ptr(""), + EventsURL: Ptr(""), + HooksURL: Ptr(""), + IssuesURL: Ptr(""), + MembersURL: Ptr(""), + PublicMembersURL: Ptr(""), + ReposURL: Ptr(""), + } + want := `github.Organization{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", Name:"", Company:"", Blog:"", Location:"", Email:"", TwitterUsername:"", Description:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ArchivedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, BillingEmail:"", Type:"", Plan:github.Plan{}, TwoFactorRequirementEnabled:false, IsVerified:false, HasOrganizationProjects:false, HasRepositoryProjects:false, DefaultRepoPermission:"", DefaultRepoSettings:"", MembersCanCreateRepos:false, MembersCanCreatePublicRepos:false, MembersCanCreatePrivateRepos:false, MembersCanCreateInternalRepos:false, MembersCanForkPrivateRepos:false, DeployKeysEnabledForRepositories:false, MembersAllowedRepositoryCreationType:"", MembersCanCreatePages:false, MembersCanCreatePublicPages:false, MembersCanCreatePrivatePages:false, WebCommitSignoffRequired:false, AdvancedSecurityEnabledForNewRepos:false, DependabotAlertsEnabledForNewRepos:false, DependabotSecurityUpdatesEnabledForNewRepos:false, DependencyGraphEnabledForNewRepos:false, SecretScanningEnabledForNewRepos:false, SecretScanningPushProtectionEnabledForNewRepos:false, SecretScanningValidityChecksEnabled:false, SecretScanningPushProtectionCustomLinkEnabled:false, SecretScanningPushProtectionCustomLink:"", MembersCanDeleteRepositories:false, MembersCanChangeRepoVisibility:false, MembersCanInviteOutsideCollaborators:false, MembersCanDeleteIssues:false, DisplayCommenterFullNameSettingEnabled:false, ReadersCanCreateDiscussions:false, MembersCanCreateTeams:false, MembersCanViewDependencyInsights:false, DefaultRepositoryBranch:"", URL:"", EventsURL:"", HooksURL:"", IssuesURL:"", MembersURL:"", PublicMembersURL:"", ReposURL:""}` if got := v.String(); got != want { t.Errorf("Organization.String = %v, want %v", got, want) } } +func TestPackage_String(t *testing.T) { + t.Parallel() + v := Package{ + ID: Ptr(int64(0)), + Name: Ptr(""), + PackageType: Ptr(""), + HTMLURL: Ptr(""), + Visibility: Ptr(""), + Owner: &User{}, + Repository: &Repository{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + Namespace: Ptr(""), + Description: Ptr(""), + Ecosystem: Ptr(""), + PackageVersion: &PackageVersion{}, + Registry: &PackageRegistry{}, + URL: Ptr(""), + VersionCount: Ptr(int64(0)), + } + want := `github.Package{ID:0, Name:"", PackageType:"", HTMLURL:"", Visibility:"", Owner:github.User{}, Repository:github.Repository{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Namespace:"", Description:"", Ecosystem:"", PackageVersion:github.PackageVersion{}, Registry:github.PackageRegistry{}, URL:"", VersionCount:0}` + if got := v.String(); got != want { + t.Errorf("Package.String = %v, want %v", got, want) + } +} + +func TestPackageContainerMetadata_String(t *testing.T) { + t.Parallel() + v := PackageContainerMetadata{ + Tags: []string{""}, + } + want := `github.PackageContainerMetadata{Tags:[""]}` + if got := v.String(); got != want { + t.Errorf("PackageContainerMetadata.String = %v, want %v", got, want) + } +} + +func TestPackageEventContainerMetadata_String(t *testing.T) { + t.Parallel() + v := PackageEventContainerMetadata{ + Tag: &PackageEventContainerMetadataTag{}, + } + want := `github.PackageEventContainerMetadata{Tag:github.PackageEventContainerMetadataTag{}}` + if got := v.String(); got != want { + t.Errorf("PackageEventContainerMetadata.String = %v, want %v", got, want) + } +} + +func TestPackageEventContainerMetadataTag_String(t *testing.T) { + t.Parallel() + v := PackageEventContainerMetadataTag{ + Name: Ptr(""), + Digest: Ptr(""), + } + want := `github.PackageEventContainerMetadataTag{Name:"", Digest:""}` + if got := v.String(); got != want { + t.Errorf("PackageEventContainerMetadataTag.String = %v, want %v", got, want) + } +} + +func TestPackageFile_String(t *testing.T) { + t.Parallel() + v := PackageFile{ + DownloadURL: Ptr(""), + ID: Ptr(int64(0)), + Name: Ptr(""), + SHA256: Ptr(""), + SHA1: Ptr(""), + MD5: Ptr(""), + ContentType: Ptr(""), + State: Ptr(""), + Author: &User{}, + Size: Ptr(int64(0)), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + } + want := `github.PackageFile{DownloadURL:"", ID:0, Name:"", SHA256:"", SHA1:"", MD5:"", ContentType:"", State:"", Author:github.User{}, Size:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("PackageFile.String = %v, want %v", got, want) + } +} + +func TestPackageMetadata_String(t *testing.T) { + t.Parallel() + v := PackageMetadata{ + PackageType: Ptr(""), + Container: &PackageContainerMetadata{}, + } + want := `github.PackageMetadata{PackageType:"", Container:github.PackageContainerMetadata{}}` + if got := v.String(); got != want { + t.Errorf("PackageMetadata.String = %v, want %v", got, want) + } +} + +func TestPackageNPMMetadata_String(t *testing.T) { + t.Parallel() + v := PackageNPMMetadata{ + Name: Ptr(""), + Version: Ptr(""), + NPMUser: Ptr(""), + Description: Ptr(""), + GitHead: Ptr(""), + Homepage: Ptr(""), + License: Ptr(""), + Main: Ptr(""), + ID: Ptr(""), + NodeVersion: Ptr(""), + NPMVersion: Ptr(""), + HasShrinkwrap: Ptr(false), + Maintainers: []any{nil}, + Contributors: []any{nil}, + Keywords: []string{""}, + Files: []string{""}, + OS: []string{""}, + CPU: []string{""}, + Readme: Ptr(""), + InstallationCommand: Ptr(""), + ReleaseID: Ptr(int64(0)), + CommitOID: Ptr(""), + PublishedViaActions: Ptr(false), + DeletedByID: Ptr(int64(0)), + } + want := `github.PackageNPMMetadata{Name:"", Version:"", NPMUser:"", Description:"", GitHead:"", Homepage:"", License:"", Main:"", ID:"", NodeVersion:"", NPMVersion:"", HasShrinkwrap:false, Maintainers:[], Contributors:[], Keywords:[""], Files:[""], OS:[""], CPU:[""], Readme:"", InstallationCommand:"", ReleaseID:0, CommitOID:"", PublishedViaActions:false, DeletedByID:0}` + if got := v.String(); got != want { + t.Errorf("PackageNPMMetadata.String = %v, want %v", got, want) + } +} + +func TestPackageNugetMetadata_String(t *testing.T) { + t.Parallel() + v := PackageNugetMetadata{ + Name: Ptr(""), + } + want := `github.PackageNugetMetadata{Name:""}` + if got := v.String(); got != want { + t.Errorf("PackageNugetMetadata.String = %v, want %v", got, want) + } +} + +func TestPackageRegistry_String(t *testing.T) { + t.Parallel() + v := PackageRegistry{ + AboutURL: Ptr(""), + Name: Ptr(""), + Type: Ptr(""), + URL: Ptr(""), + Vendor: Ptr(""), + } + want := `github.PackageRegistry{AboutURL:"", Name:"", Type:"", URL:"", Vendor:""}` + if got := v.String(); got != want { + t.Errorf("PackageRegistry.String = %v, want %v", got, want) + } +} + +func TestPackageRelease_String(t *testing.T) { + t.Parallel() + v := PackageRelease{ + URL: Ptr(""), + HTMLURL: Ptr(""), + ID: Ptr(int64(0)), + TagName: Ptr(""), + TargetCommitish: Ptr(""), + Name: Ptr(""), + Draft: Ptr(false), + Author: &User{}, + Prerelease: Ptr(false), + CreatedAt: &Timestamp{}, + PublishedAt: &Timestamp{}, + } + want := `github.PackageRelease{URL:"", HTMLURL:"", ID:0, TagName:"", TargetCommitish:"", Name:"", Draft:false, Author:github.User{}, Prerelease:false, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PublishedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("PackageRelease.String = %v, want %v", got, want) + } +} + +func TestPackageVersion_String(t *testing.T) { + t.Parallel() + v := PackageVersion{ + ID: Ptr(int64(0)), + Name: Ptr(""), + URL: Ptr(""), + PackageHTMLURL: Ptr(""), + License: Ptr(""), + Description: Ptr(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + Version: Ptr(""), + Summary: Ptr(""), + BodyHTML: Ptr(""), + Release: &PackageRelease{}, + Manifest: Ptr(""), + HTMLURL: Ptr(""), + TagName: Ptr(""), + TargetCommitish: Ptr(""), + TargetOID: Ptr(""), + Draft: Ptr(false), + Prerelease: Ptr(false), + ContainerMetadata: &PackageEventContainerMetadata{}, + DockerMetadata: []any{nil}, + NPMMetadata: &PackageNPMMetadata{}, + PackageURL: Ptr(""), + Author: &User{}, + SourceURL: Ptr(""), + InstallationCommand: Ptr(""), + DeletedAt: &Timestamp{}, + } + want := `github.PackageVersion{ID:0, Name:"", URL:"", PackageHTMLURL:"", License:"", Description:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Version:"", Summary:"", BodyHTML:"", Release:github.PackageRelease{}, Manifest:"", HTMLURL:"", TagName:"", TargetCommitish:"", TargetOID:"", Draft:false, Prerelease:false, ContainerMetadata:github.PackageEventContainerMetadata{}, DockerMetadata:[], NPMMetadata:github.PackageNPMMetadata{}, PackageURL:"", Author:github.User{}, SourceURL:"", InstallationCommand:"", DeletedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("PackageVersion.String = %v, want %v", got, want) + } +} + +func TestPackageVersionBody_String(t *testing.T) { + t.Parallel() + v := PackageVersionBody{ + Repo: &Repository{}, + Info: &PackageVersionBodyInfo{}, + } + want := `github.PackageVersionBody{Repo:github.Repository{}, Info:github.PackageVersionBodyInfo{}}` + if got := v.String(); got != want { + t.Errorf("PackageVersionBody.String = %v, want %v", got, want) + } +} + +func TestPackageVersionBodyInfo_String(t *testing.T) { + t.Parallel() + v := PackageVersionBodyInfo{ + Type: Ptr(""), + OID: Ptr(""), + Mode: Ptr(int64(0)), + Name: Ptr(""), + Path: Ptr(""), + Size: Ptr(int64(0)), + Collection: Ptr(false), + } + want := `github.PackageVersionBodyInfo{Type:"", OID:"", Mode:0, Name:"", Path:"", Size:0, Collection:false}` + if got := v.String(); got != want { + t.Errorf("PackageVersionBodyInfo.String = %v, want %v", got, want) + } +} + func TestPageStats_String(t *testing.T) { + t.Parallel() v := PageStats{ - TotalPages: Int(0), + TotalPages: Ptr(0), } want := `github.PageStats{TotalPages:0}` if got := v.String(); got != want { @@ -847,24 +1551,28 @@ func TestPageStats_String(t *testing.T) { } func TestPlan_String(t *testing.T) { + t.Parallel() v := Plan{ - Name: String(""), - Space: Int(0), - Collaborators: Int(0), - PrivateRepos: Int(0), + Name: Ptr(""), + Space: Ptr(0), + Collaborators: Ptr(0), + PrivateRepos: Ptr(int64(0)), + FilledSeats: Ptr(0), + Seats: Ptr(0), } - want := `github.Plan{Name:"", Space:0, Collaborators:0, PrivateRepos:0}` + want := `github.Plan{Name:"", Space:0, Collaborators:0, PrivateRepos:0, FilledSeats:0, Seats:0}` if got := v.String(); got != want { t.Errorf("Plan.String = %v, want %v", got, want) } } func TestPreReceiveHook_String(t *testing.T) { + t.Parallel() v := PreReceiveHook{ - ID: Int64(0), - Name: String(""), - Enforcement: String(""), - ConfigURL: String(""), + ID: Ptr(int64(0)), + Name: Ptr(""), + Enforcement: Ptr(""), + ConfigURL: Ptr(""), } want := `github.PreReceiveHook{ID:0, Name:"", Enforcement:"", ConfigURL:""}` if got := v.String(); got != want { @@ -872,135 +1580,171 @@ func TestPreReceiveHook_String(t *testing.T) { } } -func TestProject_String(t *testing.T) { - v := Project{ - ID: Int64(0), - URL: String(""), - HTMLURL: String(""), - ColumnsURL: String(""), - OwnerURL: String(""), - Name: String(""), - Body: String(""), - Number: Int(0), - State: String(""), - CreatedAt: &Timestamp{}, - UpdatedAt: &Timestamp{}, - NodeID: String(""), - Creator: &User{}, - } - want := `github.Project{ID:0, URL:"", HTMLURL:"", ColumnsURL:"", OwnerURL:"", Name:"", Body:"", Number:0, State:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, NodeID:"", Creator:github.User{}}` - if got := v.String(); got != want { - t.Errorf("Project.String = %v, want %v", got, want) +func TestProjectV2_String(t *testing.T) { + t.Parallel() + v := ProjectV2{ + ID: Ptr(int64(0)), + NodeID: Ptr(""), + Owner: &User{}, + Creator: &User{}, + Title: Ptr(""), + Description: Ptr(""), + Public: Ptr(false), + ClosedAt: &Timestamp{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + DeletedAt: &Timestamp{}, + Number: Ptr(0), + ShortDescription: Ptr(""), + DeletedBy: &User{}, + State: Ptr(""), + LatestStatusUpdate: &ProjectV2StatusUpdate{}, + IsTemplate: Ptr(false), + URL: Ptr(""), + HTMLURL: Ptr(""), + ColumnsURL: Ptr(""), + OwnerURL: Ptr(""), + Name: Ptr(""), + Body: Ptr(""), + OrganizationPermission: Ptr(""), + Private: Ptr(false), + } + want := `github.ProjectV2{ID:0, NodeID:"", Owner:github.User{}, Creator:github.User{}, Title:"", Description:"", Public:false, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, DeletedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Number:0, ShortDescription:"", DeletedBy:github.User{}, State:"", LatestStatusUpdate:github.ProjectV2StatusUpdate{}, IsTemplate:false, URL:"", HTMLURL:"", ColumnsURL:"", OwnerURL:"", Name:"", Body:"", OrganizationPermission:"", Private:false}` + if got := v.String(); got != want { + t.Errorf("ProjectV2.String = %v, want %v", got, want) } } func TestPullRequest_String(t *testing.T) { + t.Parallel() v := PullRequest{ - ID: Int64(0), - Number: Int(0), - State: String(""), - Locked: Bool(false), - Title: String(""), - Body: String(""), + ID: Ptr(int64(0)), + Number: Ptr(0), + State: Ptr(""), + Locked: Ptr(false), + Title: Ptr(""), + Body: Ptr(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + ClosedAt: &Timestamp{}, + MergedAt: &Timestamp{}, User: &User{}, - Draft: Bool(false), - Merged: Bool(false), - Mergeable: Bool(false), - MergeableState: String(""), - MergedBy: &User{}, - MergeCommitSHA: String(""), - Rebaseable: Bool(false), - Comments: Int(0), - Commits: Int(0), - Additions: Int(0), - Deletions: Int(0), - ChangedFiles: Int(0), - URL: String(""), - HTMLURL: String(""), - IssueURL: String(""), - StatusesURL: String(""), - DiffURL: String(""), - PatchURL: String(""), - CommitsURL: String(""), - CommentsURL: String(""), - ReviewCommentsURL: String(""), - ReviewCommentURL: String(""), - ReviewComments: Int(0), + Draft: Ptr(false), + URL: Ptr(""), + HTMLURL: Ptr(""), + IssueURL: Ptr(""), + StatusesURL: Ptr(""), + DiffURL: Ptr(""), + PatchURL: Ptr(""), + CommitsURL: Ptr(""), + CommentsURL: Ptr(""), + ReviewCommentsURL: Ptr(""), + ReviewCommentURL: Ptr(""), Assignee: &User{}, Milestone: &Milestone{}, - MaintainerCanModify: Bool(false), - AuthorAssociation: String(""), - NodeID: String(""), + AuthorAssociation: Ptr(""), + NodeID: Ptr(""), + AutoMerge: &PullRequestAutoMerge{}, + Merged: Ptr(false), + Mergeable: Ptr(false), + MergeableState: Ptr(""), + Rebaseable: Ptr(false), + MergedBy: &User{}, + MergeCommitSHA: Ptr(""), + Comments: Ptr(0), + Commits: Ptr(0), + Additions: Ptr(0), + Deletions: Ptr(0), + ChangedFiles: Ptr(0), + MaintainerCanModify: Ptr(false), + ReviewComments: Ptr(0), Links: &PRLinks{}, Head: &PullRequestBranch{}, Base: &PullRequestBranch{}, - ActiveLockReason: String(""), + Stack: &PullRequestStack{}, + ActiveLockReason: Ptr(""), } - want := `github.PullRequest{ID:0, Number:0, State:"", Locked:false, Title:"", Body:"", User:github.User{}, Draft:false, Merged:false, Mergeable:false, MergeableState:"", MergedBy:github.User{}, MergeCommitSHA:"", Rebaseable:false, Comments:0, Commits:0, Additions:0, Deletions:0, ChangedFiles:0, URL:"", HTMLURL:"", IssueURL:"", StatusesURL:"", DiffURL:"", PatchURL:"", CommitsURL:"", CommentsURL:"", ReviewCommentsURL:"", ReviewCommentURL:"", ReviewComments:0, Assignee:github.User{}, Milestone:github.Milestone{}, MaintainerCanModify:false, AuthorAssociation:"", NodeID:"", Links:github.PRLinks{}, Head:github.PullRequestBranch{}, Base:github.PullRequestBranch{}, ActiveLockReason:""}` + want := `github.PullRequest{ID:0, Number:0, State:"", Locked:false, Title:"", Body:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, ClosedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, MergedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, User:github.User{}, Draft:false, URL:"", HTMLURL:"", IssueURL:"", StatusesURL:"", DiffURL:"", PatchURL:"", CommitsURL:"", CommentsURL:"", ReviewCommentsURL:"", ReviewCommentURL:"", Assignee:github.User{}, Milestone:github.Milestone{}, AuthorAssociation:"", NodeID:"", AutoMerge:github.PullRequestAutoMerge{}, Merged:false, Mergeable:false, MergeableState:"", Rebaseable:false, MergedBy:github.User{}, MergeCommitSHA:"", Comments:0, Commits:0, Additions:0, Deletions:0, ChangedFiles:0, MaintainerCanModify:false, ReviewComments:0, Links:github.PRLinks{}, Head:github.PullRequestBranch{}, Base:github.PullRequestBranch{}, Stack:github.PullRequestStack{}, ActiveLockReason:""}` if got := v.String(); got != want { t.Errorf("PullRequest.String = %v, want %v", got, want) } } func TestPullRequestComment_String(t *testing.T) { + t.Parallel() v := PullRequestComment{ - ID: Int64(0), - NodeID: String(""), - InReplyTo: Int64(0), - Body: String(""), - Path: String(""), - DiffHunk: String(""), - PullRequestReviewID: Int64(0), - Position: Int(0), - OriginalPosition: Int(0), - CommitID: String(""), - OriginalCommitID: String(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + InReplyTo: Ptr(int64(0)), + Body: Ptr(""), + Path: Ptr(""), + DiffHunk: Ptr(""), + PullRequestReviewID: Ptr(int64(0)), + Position: Ptr(0), + OriginalPosition: Ptr(0), + StartLine: Ptr(0), + Line: Ptr(0), + OriginalLine: Ptr(0), + OriginalStartLine: Ptr(0), + Side: Ptr(""), + StartSide: Ptr(""), + CommitID: Ptr(""), + OriginalCommitID: Ptr(""), User: &User{}, Reactions: &Reactions{}, - AuthorAssociation: String(""), - URL: String(""), - HTMLURL: String(""), - PullRequestURL: String(""), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + AuthorAssociation: Ptr(""), + URL: Ptr(""), + HTMLURL: Ptr(""), + PullRequestURL: Ptr(""), + SubjectType: Ptr(""), } - want := `github.PullRequestComment{ID:0, NodeID:"", InReplyTo:0, Body:"", Path:"", DiffHunk:"", PullRequestReviewID:0, Position:0, OriginalPosition:0, CommitID:"", OriginalCommitID:"", User:github.User{}, Reactions:github.Reactions{}, AuthorAssociation:"", URL:"", HTMLURL:"", PullRequestURL:""}` + want := `github.PullRequestComment{ID:0, NodeID:"", InReplyTo:0, Body:"", Path:"", DiffHunk:"", PullRequestReviewID:0, Position:0, OriginalPosition:0, StartLine:0, Line:0, OriginalLine:0, OriginalStartLine:0, Side:"", StartSide:"", CommitID:"", OriginalCommitID:"", User:github.User{}, Reactions:github.Reactions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, AuthorAssociation:"", URL:"", HTMLURL:"", PullRequestURL:"", SubjectType:""}` if got := v.String(); got != want { t.Errorf("PullRequestComment.String = %v, want %v", got, want) } } -func TestPullRequestReview_String(t *testing.T) { - v := PullRequestReview{ - ID: Int64(0), - NodeID: String(""), - User: &User{}, - Body: String(""), - CommitID: String(""), - HTMLURL: String(""), - PullRequestURL: String(""), - State: String(""), +func TestPullRequestDismissReviewRequest_String(t *testing.T) { + t.Parallel() + v := PullRequestDismissReviewRequest{ + Message: "", + Event: Ptr(""), } - want := `github.PullRequestReview{ID:0, NodeID:"", User:github.User{}, Body:"", CommitID:"", HTMLURL:"", PullRequestURL:"", State:""}` + want := `github.PullRequestDismissReviewRequest{Message:"", Event:""}` if got := v.String(); got != want { - t.Errorf("PullRequestReview.String = %v, want %v", got, want) + t.Errorf("PullRequestDismissReviewRequest.String = %v, want %v", got, want) } } -func TestPullRequestReviewDismissalRequest_String(t *testing.T) { - v := PullRequestReviewDismissalRequest{ - Message: String(""), +func TestPullRequestReview_String(t *testing.T) { + t.Parallel() + v := PullRequestReview{ + ID: Ptr(int64(0)), + NodeID: Ptr(""), + User: &User{}, + Body: Ptr(""), + SubmittedAt: &Timestamp{}, + CommitID: Ptr(""), + HTMLURL: Ptr(""), + PullRequestURL: Ptr(""), + State: Ptr(""), + AuthorAssociation: Ptr(""), } - want := `github.PullRequestReviewDismissalRequest{Message:""}` + want := `github.PullRequestReview{ID:0, NodeID:"", User:github.User{}, Body:"", SubmittedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, CommitID:"", HTMLURL:"", PullRequestURL:"", State:"", AuthorAssociation:""}` if got := v.String(); got != want { - t.Errorf("PullRequestReviewDismissalRequest.String = %v, want %v", got, want) + t.Errorf("PullRequestReview.String = %v, want %v", got, want) } } func TestPullRequestReviewRequest_String(t *testing.T) { + t.Parallel() v := PullRequestReviewRequest{ - NodeID: String(""), - CommitID: String(""), - Body: String(""), - Event: String(""), + NodeID: Ptr(""), + CommitID: Ptr(""), + Body: Ptr(""), + Event: Ptr(""), } want := `github.PullRequestReviewRequest{NodeID:"", CommitID:"", Body:"", Event:""}` if got := v.String(); got != want { @@ -1008,94 +1752,98 @@ func TestPullRequestReviewRequest_String(t *testing.T) { } } +func TestPullRequestThread_String(t *testing.T) { + t.Parallel() + v := PullRequestThread{ + ID: Ptr(int64(0)), + NodeID: Ptr(""), + } + want := `github.PullRequestThread{ID:0, NodeID:""}` + if got := v.String(); got != want { + t.Errorf("PullRequestThread.String = %v, want %v", got, want) + } +} + func TestPullStats_String(t *testing.T) { + t.Parallel() v := PullStats{ - TotalPulls: Int(0), - MergedPulls: Int(0), - MergablePulls: Int(0), - UnmergablePulls: Int(0), + TotalPulls: Ptr(0), + MergedPulls: Ptr(0), + MergeablePulls: Ptr(0), + UnmergeablePulls: Ptr(0), } - want := `github.PullStats{TotalPulls:0, MergedPulls:0, MergablePulls:0, UnmergablePulls:0}` + want := `github.PullStats{TotalPulls:0, MergedPulls:0, MergeablePulls:0, UnmergeablePulls:0}` if got := v.String(); got != want { t.Errorf("PullStats.String = %v, want %v", got, want) } } func TestPushEvent_String(t *testing.T) { + t.Parallel() v := PushEvent{ - PushID: Int64(0), - Head: String(""), - Ref: String(""), - Size: Int(0), - Before: String(""), - DistinctSize: Int(0), - After: String(""), - Created: Bool(false), - Deleted: Bool(false), - Forced: Bool(false), - BaseRef: String(""), - Compare: String(""), + PushID: Ptr(int64(0)), + Head: Ptr(""), + Ref: Ptr(""), + Size: Ptr(0), + Before: Ptr(""), + DistinctSize: Ptr(0), + Action: Ptr(""), + After: Ptr(""), + Created: Ptr(false), + Deleted: Ptr(false), + Forced: Ptr(false), + BaseRef: Ptr(""), + Compare: Ptr(""), Repo: &PushEventRepository{}, - HeadCommit: &PushEventCommit{}, - Pusher: &User{}, + HeadCommit: &HeadCommit{}, + Pusher: &CommitAuthor{}, Sender: &User{}, Installation: &Installation{}, + Organization: &Organization{}, } - want := `github.PushEvent{PushID:0, Head:"", Ref:"", Size:0, Before:"", DistinctSize:0, After:"", Created:false, Deleted:false, Forced:false, BaseRef:"", Compare:"", Repo:github.PushEventRepository{}, HeadCommit:github.PushEventCommit{}, Pusher:github.User{}, Sender:github.User{}, Installation:github.Installation{}}` + want := `github.PushEvent{PushID:0, Head:"", Ref:"", Size:0, Before:"", DistinctSize:0, Action:"", After:"", Created:false, Deleted:false, Forced:false, BaseRef:"", Compare:"", Repo:github.PushEventRepository{}, HeadCommit:github.HeadCommit{}, Pusher:github.CommitAuthor{}, Sender:github.User{}, Installation:github.Installation{}, Organization:github.Organization{}}` if got := v.String(); got != want { t.Errorf("PushEvent.String = %v, want %v", got, want) } } -func TestPushEventCommit_String(t *testing.T) { - v := PushEventCommit{ - Message: String(""), - Author: &CommitAuthor{}, - URL: String(""), - Distinct: Bool(false), - SHA: String(""), - ID: String(""), - TreeID: String(""), - Timestamp: &Timestamp{}, - Committer: &CommitAuthor{}, - } - want := `github.PushEventCommit{Message:"", Author:github.CommitAuthor{}, URL:"", Distinct:false, SHA:"", ID:"", TreeID:"", Timestamp:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Committer:github.CommitAuthor{}}` - if got := v.String(); got != want { - t.Errorf("PushEventCommit.String = %v, want %v", got, want) - } -} - func TestRate_String(t *testing.T) { + t.Parallel() v := Rate{ Limit: 0, Remaining: 0, + Used: 0, Reset: Timestamp{}, + Resource: "", } - want := `github.Rate{Limit:0, Remaining:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + want := `github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}` if got := v.String(); got != want { t.Errorf("Rate.String = %v, want %v", got, want) } } func TestReaction_String(t *testing.T) { + t.Parallel() v := Reaction{ - ID: Int64(0), - User: &User{}, - NodeID: String(""), - Content: String(""), + ID: Ptr(int64(0)), + User: &User{}, + NodeID: Ptr(""), + Content: Ptr(""), + CreatedAt: &Timestamp{}, } - want := `github.Reaction{ID:0, User:github.User{}, NodeID:"", Content:""}` + want := `github.Reaction{ID:0, User:github.User{}, NodeID:"", Content:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("Reaction.String = %v, want %v", got, want) } } func TestReference_String(t *testing.T) { + t.Parallel() v := Reference{ - Ref: String(""), - URL: String(""), + Ref: Ptr(""), + URL: Ptr(""), Object: &GitObject{}, - NodeID: String(""), + NodeID: Ptr(""), } want := `github.Reference{Ref:"", URL:"", Object:github.GitObject{}, NodeID:""}` if got := v.String(); got != want { @@ -1104,31 +1852,34 @@ func TestReference_String(t *testing.T) { } func TestReleaseAsset_String(t *testing.T) { + t.Parallel() v := ReleaseAsset{ - ID: Int64(0), - URL: String(""), - Name: String(""), - Label: String(""), - State: String(""), - ContentType: String(""), - Size: Int(0), - DownloadCount: Int(0), + ID: Ptr(int64(0)), + URL: Ptr(""), + Name: Ptr(""), + Label: Ptr(""), + State: Ptr(""), + ContentType: Ptr(""), + Size: Ptr(0), + DownloadCount: Ptr(0), CreatedAt: &Timestamp{}, UpdatedAt: &Timestamp{}, - BrowserDownloadURL: String(""), + BrowserDownloadURL: Ptr(""), Uploader: &User{}, - NodeID: String(""), + NodeID: Ptr(""), + Digest: Ptr(""), } - want := `github.ReleaseAsset{ID:0, URL:"", Name:"", Label:"", State:"", ContentType:"", Size:0, DownloadCount:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, BrowserDownloadURL:"", Uploader:github.User{}, NodeID:""}` + want := `github.ReleaseAsset{ID:0, URL:"", Name:"", Label:"", State:"", ContentType:"", Size:0, DownloadCount:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, BrowserDownloadURL:"", Uploader:github.User{}, NodeID:"", Digest:""}` if got := v.String(); got != want { t.Errorf("ReleaseAsset.String = %v, want %v", got, want) } } func TestRename_String(t *testing.T) { + t.Parallel() v := Rename{ - From: String(""), - To: String(""), + From: Ptr(""), + To: Ptr(""), } want := `github.Rename{From:"", To:""}` if got := v.String(); got != want { @@ -1137,13 +1888,14 @@ func TestRename_String(t *testing.T) { } func TestRepoStats_String(t *testing.T) { + t.Parallel() v := RepoStats{ - TotalRepos: Int(0), - RootRepos: Int(0), - ForkRepos: Int(0), - OrgRepos: Int(0), - TotalPushes: Int(0), - TotalWikis: Int(0), + TotalRepos: Ptr(0), + RootRepos: Ptr(0), + ForkRepos: Ptr(0), + OrgRepos: Ptr(0), + TotalPushes: Ptr(0), + TotalWikis: Ptr(0), } want := `github.RepoStats{TotalRepos:0, RootRepos:0, ForkRepos:0, OrgRepos:0, TotalPushes:0, TotalWikis:0}` if got := v.String(); got != want { @@ -1152,146 +1904,175 @@ func TestRepoStats_String(t *testing.T) { } func TestRepoStatus_String(t *testing.T) { + t.Parallel() v := RepoStatus{ - ID: Int64(0), - NodeID: String(""), - URL: String(""), - State: String(""), - TargetURL: String(""), - Description: String(""), - Context: String(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + URL: Ptr(""), + State: Ptr(""), + TargetURL: Ptr(""), + Description: Ptr(""), + Context: Ptr(""), + AvatarURL: Ptr(""), Creator: &User{}, + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, } - want := `github.RepoStatus{ID:0, NodeID:"", URL:"", State:"", TargetURL:"", Description:"", Context:"", Creator:github.User{}}` + want := `github.RepoStatus{ID:0, NodeID:"", URL:"", State:"", TargetURL:"", Description:"", Context:"", AvatarURL:"", Creator:github.User{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("RepoStatus.String = %v, want %v", got, want) } } func TestRepository_String(t *testing.T) { + t.Parallel() v := Repository{ - ID: Int64(0), - NodeID: String(""), - Owner: &User{}, - Name: String(""), - FullName: String(""), - Description: String(""), - Homepage: String(""), - CodeOfConduct: &CodeOfConduct{}, - DefaultBranch: String(""), - MasterBranch: String(""), - CreatedAt: &Timestamp{}, - PushedAt: &Timestamp{}, - UpdatedAt: &Timestamp{}, - HTMLURL: String(""), - CloneURL: String(""), - GitURL: String(""), - MirrorURL: String(""), - SSHURL: String(""), - SVNURL: String(""), - Language: String(""), - Fork: Bool(false), - ForksCount: Int(0), - NetworkCount: Int(0), - OpenIssuesCount: Int(0), - StargazersCount: Int(0), - SubscribersCount: Int(0), - WatchersCount: Int(0), - Size: Int(0), - AutoInit: Bool(false), - Parent: &Repository{}, - Source: &Repository{}, - TemplateRepository: &Repository{}, - Organization: &Organization{}, - AllowRebaseMerge: Bool(false), - AllowSquashMerge: Bool(false), - AllowMergeCommit: Bool(false), - Archived: Bool(false), - Disabled: Bool(false), - License: &License{}, - Private: Bool(false), - HasIssues: Bool(false), - HasWiki: Bool(false), - HasPages: Bool(false), - HasProjects: Bool(false), - HasDownloads: Bool(false), - IsTemplate: Bool(false), - LicenseTemplate: String(""), - GitignoreTemplate: String(""), - TeamID: Int64(0), - URL: String(""), - ArchiveURL: String(""), - AssigneesURL: String(""), - BlobsURL: String(""), - BranchesURL: String(""), - CollaboratorsURL: String(""), - CommentsURL: String(""), - CommitsURL: String(""), - CompareURL: String(""), - ContentsURL: String(""), - ContributorsURL: String(""), - DeploymentsURL: String(""), - DownloadsURL: String(""), - EventsURL: String(""), - ForksURL: String(""), - GitCommitsURL: String(""), - GitRefsURL: String(""), - GitTagsURL: String(""), - HooksURL: String(""), - IssueCommentURL: String(""), - IssueEventsURL: String(""), - IssuesURL: String(""), - KeysURL: String(""), - LabelsURL: String(""), - LanguagesURL: String(""), - MergesURL: String(""), - MilestonesURL: String(""), - NotificationsURL: String(""), - PullsURL: String(""), - ReleasesURL: String(""), - StargazersURL: String(""), - StatusesURL: String(""), - SubscribersURL: String(""), - SubscriptionURL: String(""), - TagsURL: String(""), - TreesURL: String(""), - TeamsURL: String(""), - } - want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, AllowRebaseMerge:false, AllowSquashMerge:false, AllowMergeCommit:false, Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:""}` + ID: Ptr(int64(0)), + NodeID: Ptr(""), + Owner: &User{}, + Name: Ptr(""), + FullName: Ptr(""), + Description: Ptr(""), + Homepage: Ptr(""), + CodeOfConduct: &CodeOfConduct{}, + DefaultBranch: Ptr(""), + MasterBranch: Ptr(""), + CreatedAt: &Timestamp{}, + PushedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + HTMLURL: Ptr(""), + CloneURL: Ptr(""), + GitURL: Ptr(""), + MirrorURL: Ptr(""), + SSHURL: Ptr(""), + SVNURL: Ptr(""), + Language: Ptr(""), + Fork: Ptr(false), + ForksCount: Ptr(0), + NetworkCount: Ptr(0), + OpenIssuesCount: Ptr(0), + OpenIssues: Ptr(0), + StargazersCount: Ptr(0), + SubscribersCount: Ptr(0), + WatchersCount: Ptr(0), + Watchers: Ptr(0), + Size: Ptr(0), + AutoInit: Ptr(false), + Parent: &Repository{}, + Source: &Repository{}, + TemplateRepository: &Repository{}, + Organization: &Organization{}, + Permissions: &RepositoryPermissions{}, + AllowRebaseMerge: Ptr(false), + AllowUpdateBranch: Ptr(false), + AllowSquashMerge: Ptr(false), + AllowMergeCommit: Ptr(false), + AllowAutoMerge: Ptr(false), + AllowForking: Ptr(false), + WebCommitSignoffRequired: Ptr(false), + DeleteBranchOnMerge: Ptr(false), + UseSquashPRTitleAsDefault: Ptr(false), + SquashMergeCommitTitle: Ptr(""), + SquashMergeCommitMessage: Ptr(""), + MergeCommitTitle: Ptr(""), + MergeCommitMessage: Ptr(""), + Topics: []string{""}, + Archived: Ptr(false), + Disabled: Ptr(false), + License: &License{}, + Private: Ptr(false), + HasIssues: Ptr(false), + HasWiki: Ptr(false), + HasPages: Ptr(false), + HasProjects: Ptr(false), + HasDownloads: Ptr(false), + HasDiscussions: Ptr(false), + HasPullRequests: Ptr(false), + PullRequestCreationPolicy: Ptr(""), + IsTemplate: Ptr(false), + LicenseTemplate: Ptr(""), + GitignoreTemplate: Ptr(""), + SecurityAndAnalysis: &SecurityAndAnalysis{}, + TeamID: Ptr(int64(0)), + URL: Ptr(""), + ArchiveURL: Ptr(""), + AssigneesURL: Ptr(""), + BlobsURL: Ptr(""), + BranchesURL: Ptr(""), + CollaboratorsURL: Ptr(""), + CommentsURL: Ptr(""), + CommitsURL: Ptr(""), + CompareURL: Ptr(""), + ContentsURL: Ptr(""), + ContributorsURL: Ptr(""), + DeploymentsURL: Ptr(""), + DownloadsURL: Ptr(""), + EventsURL: Ptr(""), + ForksURL: Ptr(""), + GitCommitsURL: Ptr(""), + GitRefsURL: Ptr(""), + GitTagsURL: Ptr(""), + HooksURL: Ptr(""), + IssueCommentURL: Ptr(""), + IssueEventsURL: Ptr(""), + IssuesURL: Ptr(""), + KeysURL: Ptr(""), + LabelsURL: Ptr(""), + LanguagesURL: Ptr(""), + MergesURL: Ptr(""), + MilestonesURL: Ptr(""), + NotificationsURL: Ptr(""), + PullsURL: Ptr(""), + ReleasesURL: Ptr(""), + StargazersURL: Ptr(""), + StatusesURL: Ptr(""), + SubscribersURL: Ptr(""), + SubscriptionURL: Ptr(""), + TagsURL: Ptr(""), + TreesURL: Ptr(""), + TeamsURL: Ptr(""), + Visibility: Ptr(""), + RoleName: Ptr(""), + } + want := `github.Repository{ID:0, NodeID:"", Owner:github.User{}, Name:"", FullName:"", Description:"", Homepage:"", CodeOfConduct:github.CodeOfConduct{}, DefaultBranch:"", MasterBranch:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PushedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", CloneURL:"", GitURL:"", MirrorURL:"", SSHURL:"", SVNURL:"", Language:"", Fork:false, ForksCount:0, NetworkCount:0, OpenIssuesCount:0, OpenIssues:0, StargazersCount:0, SubscribersCount:0, WatchersCount:0, Watchers:0, Size:0, AutoInit:false, Parent:github.Repository{}, Source:github.Repository{}, TemplateRepository:github.Repository{}, Organization:github.Organization{}, Permissions:github.RepositoryPermissions{}, AllowRebaseMerge:false, AllowUpdateBranch:false, AllowSquashMerge:false, AllowMergeCommit:false, AllowAutoMerge:false, AllowForking:false, WebCommitSignoffRequired:false, DeleteBranchOnMerge:false, UseSquashPRTitleAsDefault:false, SquashMergeCommitTitle:"", SquashMergeCommitMessage:"", MergeCommitTitle:"", MergeCommitMessage:"", Topics:[""], Archived:false, Disabled:false, License:github.License{}, Private:false, HasIssues:false, HasWiki:false, HasPages:false, HasProjects:false, HasDownloads:false, HasDiscussions:false, HasPullRequests:false, PullRequestCreationPolicy:"", IsTemplate:false, LicenseTemplate:"", GitignoreTemplate:"", SecurityAndAnalysis:github.SecurityAndAnalysis{}, TeamID:0, URL:"", ArchiveURL:"", AssigneesURL:"", BlobsURL:"", BranchesURL:"", CollaboratorsURL:"", CommentsURL:"", CommitsURL:"", CompareURL:"", ContentsURL:"", ContributorsURL:"", DeploymentsURL:"", DownloadsURL:"", EventsURL:"", ForksURL:"", GitCommitsURL:"", GitRefsURL:"", GitTagsURL:"", HooksURL:"", IssueCommentURL:"", IssueEventsURL:"", IssuesURL:"", KeysURL:"", LabelsURL:"", LanguagesURL:"", MergesURL:"", MilestonesURL:"", NotificationsURL:"", PullsURL:"", ReleasesURL:"", StargazersURL:"", StatusesURL:"", SubscribersURL:"", SubscriptionURL:"", TagsURL:"", TreesURL:"", TeamsURL:"", Visibility:"", RoleName:""}` if got := v.String(); got != want { t.Errorf("Repository.String = %v, want %v", got, want) } } func TestRepositoryComment_String(t *testing.T) { + t.Parallel() v := RepositoryComment{ - HTMLURL: String(""), - URL: String(""), - ID: Int64(0), - NodeID: String(""), - CommitID: String(""), + HTMLURL: Ptr(""), + URL: Ptr(""), + ID: Ptr(int64(0)), + NodeID: Ptr(""), + CommitID: Ptr(""), User: &User{}, Reactions: &Reactions{}, - Body: String(""), - Path: String(""), - Position: Int(0), + CreatedAt: &Timestamp{}, + UpdatedAt: &Timestamp{}, + Body: Ptr(""), + Path: Ptr(""), + Position: Ptr(0), } - want := `github.RepositoryComment{HTMLURL:"", URL:"", ID:0, NodeID:"", CommitID:"", User:github.User{}, Reactions:github.Reactions{}, Body:"", Path:"", Position:0}` + want := `github.RepositoryComment{HTMLURL:"", URL:"", ID:0, NodeID:"", CommitID:"", User:github.User{}, Reactions:github.Reactions{}, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Body:"", Path:"", Position:0}` if got := v.String(); got != want { t.Errorf("RepositoryComment.String = %v, want %v", got, want) } } func TestRepositoryCommit_String(t *testing.T) { + t.Parallel() v := RepositoryCommit{ - NodeID: String(""), - SHA: String(""), + NodeID: Ptr(""), + SHA: Ptr(""), Commit: &Commit{}, Author: &User{}, Committer: &User{}, - HTMLURL: String(""), - URL: String(""), - CommentsURL: String(""), + HTMLURL: Ptr(""), + URL: Ptr(""), + CommentsURL: Ptr(""), Stats: &CommitStats{}, } want := `github.RepositoryCommit{NodeID:"", SHA:"", Commit:github.Commit{}, Author:github.User{}, Committer:github.User{}, HTMLURL:"", URL:"", CommentsURL:"", Stats:github.CommitStats{}}` @@ -1301,39 +2082,42 @@ func TestRepositoryCommit_String(t *testing.T) { } func TestRepositoryContent_String(t *testing.T) { + t.Parallel() v := RepositoryContent{ - Type: String(""), - Target: String(""), - Encoding: String(""), - Size: Int(0), - Name: String(""), - Path: String(""), - Content: String(""), - SHA: String(""), - URL: String(""), - GitURL: String(""), - HTMLURL: String(""), - DownloadURL: String(""), - } - want := `github.RepositoryContent{Type:"", Target:"", Encoding:"", Size:0, Name:"", Path:"", Content:"", SHA:"", URL:"", GitURL:"", HTMLURL:"", DownloadURL:""}` + Type: Ptr(""), + Target: Ptr(""), + Encoding: Ptr(""), + Size: Ptr(0), + Name: Ptr(""), + Path: Ptr(""), + Content: Ptr(""), + SHA: Ptr(""), + URL: Ptr(""), + GitURL: Ptr(""), + HTMLURL: Ptr(""), + DownloadURL: Ptr(""), + SubmoduleGitURL: Ptr(""), + } + want := `github.RepositoryContent{Type:"", Target:"", Encoding:"", Size:0, Name:"", Path:"", Content:"", SHA:"", URL:"", GitURL:"", HTMLURL:"", DownloadURL:"", SubmoduleGitURL:""}` if got := v.String(); got != want { t.Errorf("RepositoryContent.String = %v, want %v", got, want) } } func TestRepositoryLicense_String(t *testing.T) { + t.Parallel() v := RepositoryLicense{ - Name: String(""), - Path: String(""), - SHA: String(""), - Size: Int(0), - URL: String(""), - HTMLURL: String(""), - GitURL: String(""), - DownloadURL: String(""), - Type: String(""), - Content: String(""), - Encoding: String(""), + Name: Ptr(""), + Path: Ptr(""), + SHA: Ptr(""), + Size: Ptr(0), + URL: Ptr(""), + HTMLURL: Ptr(""), + GitURL: Ptr(""), + DownloadURL: Ptr(""), + Type: Ptr(""), + Content: Ptr(""), + Encoding: Ptr(""), License: &License{}, } want := `github.RepositoryLicense{Name:"", Path:"", SHA:"", Size:0, URL:"", HTMLURL:"", GitURL:"", DownloadURL:"", Type:"", Content:"", Encoding:"", License:github.License{}}` @@ -1342,41 +2126,148 @@ func TestRepositoryLicense_String(t *testing.T) { } } +func TestRepositoryParticipation_String(t *testing.T) { + t.Parallel() + v := RepositoryParticipation{ + All: []int{0}, + Owner: []int{0}, + } + want := `github.RepositoryParticipation{All:[0], Owner:[0]}` + if got := v.String(); got != want { + t.Errorf("RepositoryParticipation.String = %v, want %v", got, want) + } +} + func TestRepositoryRelease_String(t *testing.T) { + t.Parallel() v := RepositoryRelease{ - TagName: String(""), - TargetCommitish: String(""), - Name: String(""), - Body: String(""), - Draft: Bool(false), - Prerelease: Bool(false), - ID: Int64(0), - CreatedAt: &Timestamp{}, + TagName: "", + TargetCommitish: "", + Name: Ptr(""), + Body: Ptr(""), + Draft: false, + Prerelease: false, + Immutable: Ptr(false), + ID: 0, + CreatedAt: Timestamp{}, PublishedAt: &Timestamp{}, - URL: String(""), - HTMLURL: String(""), - AssetsURL: String(""), - UploadURL: String(""), - ZipballURL: String(""), - TarballURL: String(""), + UpdatedAt: &Timestamp{}, + URL: "", + HTMLURL: "", + AssetsURL: "", + UploadURL: "", + ZipballURL: Ptr(""), + TarballURL: Ptr(""), Author: &User{}, - NodeID: String(""), + NodeID: "", + BodyHTML: Ptr(""), + BodyText: Ptr(""), + MentionsCount: Ptr(0), + DiscussionURL: Ptr(""), + Reactions: &Reactions{}, } - want := `github.RepositoryRelease{TagName:"", TargetCommitish:"", Name:"", Body:"", Draft:false, Prerelease:false, ID:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PublishedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", HTMLURL:"", AssetsURL:"", UploadURL:"", ZipballURL:"", TarballURL:"", Author:github.User{}, NodeID:""}` + want := `github.RepositoryRelease{TagName:"", TargetCommitish:"", Name:"", Body:"", Draft:false, Prerelease:false, Immutable:false, ID:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, PublishedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", HTMLURL:"", AssetsURL:"", UploadURL:"", ZipballURL:"", TarballURL:"", Author:github.User{}, NodeID:"", BodyHTML:"", BodyText:"", MentionsCount:0, DiscussionURL:"", Reactions:github.Reactions{}}` if got := v.String(); got != want { t.Errorf("RepositoryRelease.String = %v, want %v", got, want) } } +func TestSBOM_String(t *testing.T) { + t.Parallel() + v := SBOM{ + SBOM: &SBOMInfo{}, + } + want := `github.SBOM{SBOM:github.SBOMInfo{}}` + if got := v.String(); got != want { + t.Errorf("SBOM.String = %v, want %v", got, want) + } +} + +func TestSSHSigningKey_String(t *testing.T) { + t.Parallel() + v := SSHSigningKey{ + ID: Ptr(int64(0)), + Key: Ptr(""), + Title: Ptr(""), + CreatedAt: &Timestamp{}, + } + want := `github.SSHSigningKey{ID:0, Key:"", Title:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + if got := v.String(); got != want { + t.Errorf("SSHSigningKey.String = %v, want %v", got, want) + } +} + +func TestSecretScanning_String(t *testing.T) { + t.Parallel() + v := SecretScanning{ + Status: Ptr(""), + } + want := `github.SecretScanning{Status:""}` + if got := v.String(); got != want { + t.Errorf("SecretScanning.String = %v, want %v", got, want) + } +} + +func TestSecretScanningPushProtection_String(t *testing.T) { + t.Parallel() + v := SecretScanningPushProtection{ + Status: Ptr(""), + } + want := `github.SecretScanningPushProtection{Status:""}` + if got := v.String(); got != want { + t.Errorf("SecretScanningPushProtection.String = %v, want %v", got, want) + } +} + +func TestSecurityAndAnalysis_String(t *testing.T) { + t.Parallel() + v := SecurityAndAnalysis{ + AdvancedSecurity: &AdvancedSecurity{}, + SecretScanning: &SecretScanning{}, + SecretScanningPushProtection: &SecretScanningPushProtection{}, + DependabotSecurityUpdates: &DependabotSecurityUpdates{}, + SecretScanningValidityChecks: &SecretScanningValidityChecks{}, + CodeSecurity: &CodeSecurity{}, + } + want := `github.SecurityAndAnalysis{AdvancedSecurity:github.AdvancedSecurity{}, SecretScanning:github.SecretScanning{}, SecretScanningPushProtection:github.SecretScanningPushProtection{}, DependabotSecurityUpdates:github.DependabotSecurityUpdates{}, SecretScanningValidityChecks:github.SecretScanningValidityChecks{}, CodeSecurity:github.CodeSecurity{}}` + if got := v.String(); got != want { + t.Errorf("SecurityAndAnalysis.String = %v, want %v", got, want) + } +} + +func TestSelfHostRunnerPermissionsEnterprise_String(t *testing.T) { + t.Parallel() + v := SelfHostRunnerPermissionsEnterprise{ + DisableSelfHostedRunnersForAllOrgs: Ptr(false), + } + want := `github.SelfHostRunnerPermissionsEnterprise{DisableSelfHostedRunnersForAllOrgs:false}` + if got := v.String(); got != want { + t.Errorf("SelfHostRunnerPermissionsEnterprise.String = %v, want %v", got, want) + } +} + +func TestSelfHostedRunnersSettingsOrganization_String(t *testing.T) { + t.Parallel() + v := SelfHostedRunnersSettingsOrganization{ + EnabledRepositories: Ptr(""), + SelectedRepositoriesURL: Ptr(""), + } + want := `github.SelfHostedRunnersSettingsOrganization{EnabledRepositories:"", SelectedRepositoriesURL:""}` + if got := v.String(); got != want { + t.Errorf("SelfHostedRunnersSettingsOrganization.String = %v, want %v", got, want) + } +} + func TestSourceImportAuthor_String(t *testing.T) { + t.Parallel() v := SourceImportAuthor{ - ID: Int64(0), - RemoteID: String(""), - RemoteName: String(""), - Email: String(""), - Name: String(""), - URL: String(""), - ImportURL: String(""), + ID: Ptr(int64(0)), + RemoteID: Ptr(""), + RemoteName: Ptr(""), + Email: Ptr(""), + Name: Ptr(""), + URL: Ptr(""), + ImportURL: Ptr(""), } want := `github.SourceImportAuthor{ID:0, RemoteID:"", RemoteName:"", Email:"", Name:"", URL:"", ImportURL:""}` if got := v.String(); got != want { @@ -1385,48 +2276,55 @@ func TestSourceImportAuthor_String(t *testing.T) { } func TestTeam_String(t *testing.T) { + t.Parallel() v := Team{ - ID: Int64(0), - NodeID: String(""), - Name: String(""), - Description: String(""), - URL: String(""), - Slug: String(""), - Permission: String(""), - Privacy: String(""), - MembersCount: Int(0), - ReposCount: Int(0), - Organization: &Organization{}, - MembersURL: String(""), - RepositoriesURL: String(""), - Parent: &Team{}, - LDAPDN: String(""), - } - want := `github.Team{ID:0, NodeID:"", Name:"", Description:"", URL:"", Slug:"", Permission:"", Privacy:"", MembersCount:0, ReposCount:0, Organization:github.Organization{}, MembersURL:"", RepositoriesURL:"", Parent:github.Team{}, LDAPDN:""}` + ID: Ptr(int64(0)), + NodeID: Ptr(""), + Name: Ptr(""), + Description: Ptr(""), + URL: Ptr(""), + Slug: Ptr(""), + Permission: Ptr(""), + Privacy: Ptr(""), + NotificationSetting: Ptr(""), + MembersCount: Ptr(0), + ReposCount: Ptr(0), + Organization: &Organization{}, + HTMLURL: Ptr(""), + MembersURL: Ptr(""), + RepositoriesURL: Ptr(""), + Parent: &Team{}, + LDAPDN: Ptr(""), + Assignment: Ptr(""), + Type: Ptr(""), + AccessSource: Ptr(""), + } + want := `github.Team{ID:0, NodeID:"", Name:"", Description:"", URL:"", Slug:"", Permission:"", Privacy:"", NotificationSetting:"", MembersCount:0, ReposCount:0, Organization:github.Organization{}, HTMLURL:"", MembersURL:"", RepositoriesURL:"", Parent:github.Team{}, LDAPDN:"", Assignment:"", Type:"", AccessSource:""}` if got := v.String(); got != want { t.Errorf("Team.String = %v, want %v", got, want) } } func TestTeamDiscussion_String(t *testing.T) { + t.Parallel() v := TeamDiscussion{ Author: &User{}, - Body: String(""), - BodyHTML: String(""), - BodyVersion: String(""), - CommentsCount: Int(0), - CommentsURL: String(""), + Body: Ptr(""), + BodyHTML: Ptr(""), + BodyVersion: Ptr(""), + CommentsCount: Ptr(0), + CommentsURL: Ptr(""), CreatedAt: &Timestamp{}, LastEditedAt: &Timestamp{}, - HTMLURL: String(""), - NodeID: String(""), - Number: Int(0), - Pinned: Bool(false), - Private: Bool(false), - TeamURL: String(""), - Title: String(""), + HTMLURL: Ptr(""), + NodeID: Ptr(""), + Number: Ptr(0), + Pinned: Ptr(false), + Private: Ptr(false), + TeamURL: Ptr(""), + Title: Ptr(""), UpdatedAt: &Timestamp{}, - URL: String(""), + URL: Ptr(""), Reactions: &Reactions{}, } want := `github.TeamDiscussion{Author:github.User{}, Body:"", BodyHTML:"", BodyVersion:"", CommentsCount:0, CommentsURL:"", CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, LastEditedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, HTMLURL:"", NodeID:"", Number:0, Pinned:false, Private:false, TeamURL:"", Title:"", UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", Reactions:github.Reactions{}}` @@ -1436,17 +2334,18 @@ func TestTeamDiscussion_String(t *testing.T) { } func TestTeamLDAPMapping_String(t *testing.T) { + t.Parallel() v := TeamLDAPMapping{ - ID: Int64(0), - LDAPDN: String(""), - URL: String(""), - Name: String(""), - Slug: String(""), - Description: String(""), - Privacy: String(""), - Permission: String(""), - MembersURL: String(""), - RepositoriesURL: String(""), + ID: Ptr(int64(0)), + LDAPDN: Ptr(""), + URL: Ptr(""), + Name: Ptr(""), + Slug: Ptr(""), + Description: Ptr(""), + Privacy: Ptr(""), + Permission: Ptr(""), + MembersURL: Ptr(""), + RepositoriesURL: Ptr(""), } want := `github.TeamLDAPMapping{ID:0, LDAPDN:"", URL:"", Name:"", Slug:"", Description:"", Privacy:"", Permission:"", MembersURL:"", RepositoriesURL:""}` if got := v.String(); got != want { @@ -1455,11 +2354,12 @@ func TestTeamLDAPMapping_String(t *testing.T) { } func TestTextMatch_String(t *testing.T) { + t.Parallel() v := TextMatch{ - ObjectURL: String(""), - ObjectType: String(""), - Property: String(""), - Fragment: String(""), + ObjectURL: Ptr(""), + ObjectType: Ptr(""), + Property: Ptr(""), + Fragment: Ptr(""), } want := `github.TextMatch{ObjectURL:"", ObjectType:"", Property:"", Fragment:""}` if got := v.String(); got != want { @@ -1468,9 +2368,10 @@ func TestTextMatch_String(t *testing.T) { } func TestTree_String(t *testing.T) { + t.Parallel() v := Tree{ - SHA: String(""), - Truncated: Bool(false), + SHA: Ptr(""), + Truncated: Ptr(false), } want := `github.Tree{SHA:"", Truncated:false}` if got := v.String(); got != want { @@ -1479,14 +2380,15 @@ func TestTree_String(t *testing.T) { } func TestTreeEntry_String(t *testing.T) { + t.Parallel() v := TreeEntry{ - SHA: String(""), - Path: String(""), - Mode: String(""), - Type: String(""), - Size: Int(0), - Content: String(""), - URL: String(""), + SHA: Ptr(""), + Path: Ptr(""), + Mode: Ptr(""), + Type: Ptr(""), + Size: Ptr(0), + Content: Ptr(""), + URL: Ptr(""), } want := `github.TreeEntry{SHA:"", Path:"", Mode:"", Type:"", Size:0, Content:"", URL:""}` if got := v.String(); got != want { @@ -1494,74 +2396,103 @@ func TestTreeEntry_String(t *testing.T) { } } +func TestUpdateTeamRequest_String(t *testing.T) { + t.Parallel() + v := UpdateTeamRequest{ + Name: Ptr(""), + Description: Ptr(""), + Privacy: Ptr(""), + NotificationSetting: Ptr(""), + Permission: Ptr(""), + ParentTeamID: Ptr(int64(0)), + ParentTeamSlug: Ptr(""), + RemoveParentTeam: false, + } + want := `github.UpdateTeamRequest{Name:"", Description:"", Privacy:"", NotificationSetting:"", Permission:"", ParentTeamID:0, ParentTeamSlug:"", RemoveParentTeam:false}` + if got := v.String(); got != want { + t.Errorf("UpdateTeamRequest.String = %v, want %v", got, want) + } +} + func TestUser_String(t *testing.T) { + t.Parallel() v := User{ - Login: String(""), - ID: Int64(0), - NodeID: String(""), - AvatarURL: String(""), - HTMLURL: String(""), - GravatarID: String(""), - Name: String(""), - Company: String(""), - Blog: String(""), - Location: String(""), - Email: String(""), - Hireable: Bool(false), - Bio: String(""), - PublicRepos: Int(0), - PublicGists: Int(0), - Followers: Int(0), - Following: Int(0), + Login: Ptr(""), + ID: Ptr(int64(0)), + UserViewType: Ptr(""), + NodeID: Ptr(""), + AvatarURL: Ptr(""), + HTMLURL: Ptr(""), + GravatarID: Ptr(""), + Name: Ptr(""), + Company: Ptr(""), + Blog: Ptr(""), + Location: Ptr(""), + Email: Ptr(""), + NotificationEmail: Ptr(""), + Hireable: Ptr(false), + Bio: Ptr(""), + TwitterUsername: Ptr(""), + PublicRepos: Ptr(0), + PublicGists: Ptr(0), + Followers: Ptr(0), + Following: Ptr(0), CreatedAt: &Timestamp{}, UpdatedAt: &Timestamp{}, SuspendedAt: &Timestamp{}, - Type: String(""), - SiteAdmin: Bool(false), - TotalPrivateRepos: Int(0), - OwnedPrivateRepos: Int(0), - PrivateGists: Int(0), - DiskUsage: Int(0), - Collaborators: Int(0), - TwoFactorAuthentication: Bool(false), + Type: Ptr(""), + SiteAdmin: Ptr(false), + TotalPrivateRepos: Ptr(int64(0)), + OwnedPrivateRepos: Ptr(int64(0)), + PrivateGists: Ptr(0), + DiskUsage: Ptr(0), + Collaborators: Ptr(0), + TwoFactorAuthentication: Ptr(false), Plan: &Plan{}, - LdapDn: String(""), - URL: String(""), - EventsURL: String(""), - FollowingURL: String(""), - FollowersURL: String(""), - GistsURL: String(""), - OrganizationsURL: String(""), - ReceivedEventsURL: String(""), - ReposURL: String(""), - StarredURL: String(""), - SubscriptionsURL: String(""), - } - want := `github.User{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", GravatarID:"", Name:"", Company:"", Blog:"", Location:"", Email:"", Hireable:false, Bio:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, SuspendedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Type:"", SiteAdmin:false, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, TwoFactorAuthentication:false, Plan:github.Plan{}, LdapDn:"", URL:"", EventsURL:"", FollowingURL:"", FollowersURL:"", GistsURL:"", OrganizationsURL:"", ReceivedEventsURL:"", ReposURL:"", StarredURL:"", SubscriptionsURL:""}` + BusinessPlus: Ptr(false), + LdapDn: Ptr(""), + URL: Ptr(""), + EventsURL: Ptr(""), + FollowingURL: Ptr(""), + FollowersURL: Ptr(""), + GistsURL: Ptr(""), + OrganizationsURL: Ptr(""), + ReceivedEventsURL: Ptr(""), + ReposURL: Ptr(""), + StarredURL: Ptr(""), + SubscriptionsURL: Ptr(""), + Permissions: &RepositoryPermissions{}, + RoleName: Ptr(""), + Assignment: Ptr(""), + Role: Ptr(""), + Inherited: Ptr(false), + } + want := `github.User{Login:"", ID:0, UserViewType:"", NodeID:"", AvatarURL:"", HTMLURL:"", GravatarID:"", Name:"", Company:"", Blog:"", Location:"", Email:"", NotificationEmail:"", Hireable:false, Bio:"", TwitterUsername:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, UpdatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, SuspendedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Type:"", SiteAdmin:false, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, TwoFactorAuthentication:false, Plan:github.Plan{}, BusinessPlus:false, LdapDn:"", URL:"", EventsURL:"", FollowingURL:"", FollowersURL:"", GistsURL:"", OrganizationsURL:"", ReceivedEventsURL:"", ReposURL:"", StarredURL:"", SubscriptionsURL:"", Permissions:github.RepositoryPermissions{}, RoleName:"", Assignment:"", Role:"", Inherited:false}` if got := v.String(); got != want { t.Errorf("User.String = %v, want %v", got, want) } } func TestUserLDAPMapping_String(t *testing.T) { + t.Parallel() v := UserLDAPMapping{ - ID: Int64(0), - LDAPDN: String(""), - Login: String(""), - AvatarURL: String(""), - GravatarID: String(""), - Type: String(""), - SiteAdmin: Bool(false), - URL: String(""), - EventsURL: String(""), - FollowingURL: String(""), - FollowersURL: String(""), - GistsURL: String(""), - OrganizationsURL: String(""), - ReceivedEventsURL: String(""), - ReposURL: String(""), - StarredURL: String(""), - SubscriptionsURL: String(""), + ID: Ptr(int64(0)), + LDAPDN: Ptr(""), + Login: Ptr(""), + AvatarURL: Ptr(""), + GravatarID: Ptr(""), + Type: Ptr(""), + SiteAdmin: Ptr(false), + URL: Ptr(""), + EventsURL: Ptr(""), + FollowingURL: Ptr(""), + FollowersURL: Ptr(""), + GistsURL: Ptr(""), + OrganizationsURL: Ptr(""), + ReceivedEventsURL: Ptr(""), + ReposURL: Ptr(""), + StarredURL: Ptr(""), + SubscriptionsURL: Ptr(""), } want := `github.UserLDAPMapping{ID:0, LDAPDN:"", Login:"", AvatarURL:"", GravatarID:"", Type:"", SiteAdmin:false, URL:"", EventsURL:"", FollowingURL:"", FollowersURL:"", GistsURL:"", OrganizationsURL:"", ReceivedEventsURL:"", ReposURL:"", StarredURL:"", SubscriptionsURL:""}` if got := v.String(); got != want { @@ -1570,15 +2501,16 @@ func TestUserLDAPMapping_String(t *testing.T) { } func TestUserMigration_String(t *testing.T) { + t.Parallel() v := UserMigration{ - ID: Int64(0), - GUID: String(""), - State: String(""), - LockRepositories: Bool(false), - ExcludeAttachments: Bool(false), - URL: String(""), - CreatedAt: String(""), - UpdatedAt: String(""), + ID: Ptr(int64(0)), + GUID: Ptr(""), + State: Ptr(""), + LockRepositories: Ptr(false), + ExcludeAttachments: Ptr(false), + URL: Ptr(""), + CreatedAt: Ptr(""), + UpdatedAt: Ptr(""), } want := `github.UserMigration{ID:0, GUID:"", State:"", LockRepositories:false, ExcludeAttachments:false, URL:"", CreatedAt:"", UpdatedAt:""}` if got := v.String(); got != want { @@ -1587,10 +2519,11 @@ func TestUserMigration_String(t *testing.T) { } func TestUserStats_String(t *testing.T) { + t.Parallel() v := UserStats{ - TotalUsers: Int(0), - AdminUsers: Int(0), - SuspendedUsers: Int(0), + TotalUsers: Ptr(0), + AdminUsers: Ptr(0), + SuspendedUsers: Ptr(0), } want := `github.UserStats{TotalUsers:0, AdminUsers:0, SuspendedUsers:0}` if got := v.String(); got != want { @@ -1598,72 +2531,43 @@ func TestUserStats_String(t *testing.T) { } } -func TestWebHookAuthor_String(t *testing.T) { - v := WebHookAuthor{ - Email: String(""), - Name: String(""), - Username: String(""), - } - want := `github.WebHookAuthor{Email:"", Name:"", Username:""}` - if got := v.String(); got != want { - t.Errorf("WebHookAuthor.String = %v, want %v", got, want) - } -} - -func TestWebHookCommit_String(t *testing.T) { - v := WebHookCommit{ - Author: &WebHookAuthor{}, - Committer: &WebHookAuthor{}, - Distinct: Bool(false), - ID: String(""), - Message: String(""), - } - want := `github.WebHookCommit{Author:github.WebHookAuthor{}, Committer:github.WebHookAuthor{}, Distinct:false, ID:"", Message:""}` - if got := v.String(); got != want { - t.Errorf("WebHookCommit.String = %v, want %v", got, want) - } -} - -func TestWebHookPayload_String(t *testing.T) { - v := WebHookPayload{ - After: String(""), - Before: String(""), - Compare: String(""), - Created: Bool(false), - Deleted: Bool(false), - Forced: Bool(false), - HeadCommit: &WebHookCommit{}, - Pusher: &User{}, - Ref: String(""), - Repo: &Repository{}, - Sender: &User{}, - } - want := `github.WebHookPayload{After:"", Before:"", Compare:"", Created:false, Deleted:false, Forced:false, HeadCommit:github.WebHookCommit{}, Pusher:github.User{}, Ref:"", Repo:github.Repository{}, Sender:github.User{}}` - if got := v.String(); got != want { - t.Errorf("WebHookPayload.String = %v, want %v", got, want) - } -} - func TestWeeklyCommitActivity_String(t *testing.T) { + t.Parallel() v := WeeklyCommitActivity{ - Total: Int(0), + Days: []int{0}, + Total: Ptr(0), Week: &Timestamp{}, } - want := `github.WeeklyCommitActivity{Total:0, Week:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` + want := `github.WeeklyCommitActivity{Days:[0], Total:0, Week:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}` if got := v.String(); got != want { t.Errorf("WeeklyCommitActivity.String = %v, want %v", got, want) } } func TestWeeklyStats_String(t *testing.T) { + t.Parallel() v := WeeklyStats{ Week: &Timestamp{}, - Additions: Int(0), - Deletions: Int(0), - Commits: Int(0), + Additions: Ptr(0), + Deletions: Ptr(0), + Commits: Ptr(0), } want := `github.WeeklyStats{Week:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Additions:0, Deletions:0, Commits:0}` if got := v.String(); got != want { t.Errorf("WeeklyStats.String = %v, want %v", got, want) } } + +func TestWorkflowsPermissions_String(t *testing.T) { + t.Parallel() + v := WorkflowsPermissions{ + RunWorkflowsFromForkPullRequests: Ptr(false), + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(false), + RequireApprovalForForkPRWorkflows: Ptr(false), + } + want := `github.WorkflowsPermissions{RunWorkflowsFromForkPullRequests:false, SendWriteTokensToWorkflows:false, SendSecretsAndVariables:false, RequireApprovalForForkPRWorkflows:false}` + if got := v.String(); got != want { + t.Errorf("WorkflowsPermissions.String = %v, want %v", got, want) + } +} diff --git a/github/github.go b/github/github.go index 6ab801f2284..0bf88591777 100644 --- a/github/github.go +++ b/github/github.go @@ -4,7 +4,9 @@ // license that can be found in the LICENSE file. //go:generate go run gen-accessors.go +//go:generate go run gen-iterators.go //go:generate go run gen-stringify-test.go +//go:generate sh ../script/metadata.sh update-go package github @@ -15,10 +17,10 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "net/url" - "reflect" + "regexp" + "slices" "strconv" "strings" "sync" @@ -28,14 +30,28 @@ import ( ) const ( - defaultBaseURL = "https://api.github.com/" - uploadBaseURL = "https://uploads.github.com/" - userAgent = "go-github" + Version = "v90.0.0" - headerRateLimit = "X-RateLimit-Limit" - headerRateRemaining = "X-RateLimit-Remaining" - headerRateReset = "X-RateLimit-Reset" - headerOTP = "X-GitHub-OTP" + HeaderRateLimit = "X-Ratelimit-Limit" + HeaderRateRemaining = "X-Ratelimit-Remaining" + HeaderRateReset = "X-Ratelimit-Reset" + HeaderRateResource = "X-Ratelimit-Resource" + HeaderRateUsed = "X-Ratelimit-Used" + HeaderRequestID = "X-Github-Request-Id" + + // https://docs.github.com/en/rest/about-the-rest-api/api-versions#about-api-versioning + api20221128 = "2022-11-28" + api20260310 = "2026-03-10" + + defaultBaseURL = "https://api.github.com/" + defaultUserAgent = "go-github" + "/" + Version + uploadBaseURL = "https://uploads.github.com/" + + headerAPIVersion = "X-Github-Api-Version" + headerOTP = "X-Github-Otp" + headerRetryAfter = "Retry-After" + + headerTokenExpiration = "Github-Authentication-Token-Expiration" mediaTypeV3 = "application/vnd.github.v3+json" defaultMediaType = "application/octet-stream" @@ -43,11 +59,26 @@ const ( mediaTypeV3Diff = "application/vnd.github.v3.diff" mediaTypeV3Patch = "application/vnd.github.v3.patch" mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json" - - // Media Type values to access preview APIs - - // https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/ - mediaTypeStarringPreview = "application/vnd.github.v3.star+json" + mediaTypeIssueImportAPI = "application/vnd.github.golden-comet-preview+json" + mediaTypeStarring = "application/vnd.github.star+json" + mediaTypeSCIM = "application/scim+json" + + // Media Type values to access preview APIs. + // These media types will be added to the API request as headers + // and used to enable particular features on GitHub API that are still in preview. + // After some time, specific media types will be promoted (to a "stable" state). + // From then on, the preview headers are not required anymore to activate the additional + // feature on GitHub.com's API. However, this API header might still be needed for users + // to run a GitHub Enterprise Server on-premise. + // It's not uncommon for GitHub Enterprise Server customers to run older versions which + // would probably rely on the preview headers for some time. + // While the header promotion is going out for GitHub.com, it may be some time before it + // even arrives in GitHub Enterprise Server. + // We keep those preview headers around to avoid breaking older GitHub Enterprise Server + // versions. Additionally, non-functional (preview) headers don't create any side effects + // on GitHub Cloud version. + // + // See https://github.com/google/go-github/pull/2125 and https://github.com/google/go-github/pull/2188 for full context. // https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/ mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json" @@ -58,63 +89,30 @@ const ( // https://developer.github.com/changes/2018-10-16-deployments-environments-states-and-auto-inactive-updates/ mediaTypeExpandDeploymentStatusPreview = "application/vnd.github.flash-preview+json" - // https://developer.github.com/changes/2016-02-19-source-import-preview-api/ - mediaTypeImportPreview = "application/vnd.github.barred-rock-preview" - // https://developer.github.com/changes/2016-05-12-reactions-api-preview/ mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview" // https://developer.github.com/changes/2016-05-23-timeline-preview-api/ mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json" - // https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/ - mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json" - // https://developer.github.com/changes/2016-09-14-projects-api/ mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json" - // https://developer.github.com/changes/2016-09-14-Integrations-Early-Access/ - mediaTypeIntegrationPreview = "application/vnd.github.machine-man-preview+json" - // https://developer.github.com/changes/2017-01-05-commit-search-api/ mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json" // https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/ mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json" - // https://developer.github.com/changes/2017-02-09-community-health/ - mediaTypeRepositoryCommunityHealthMetricsPreview = "application/vnd.github.black-panther-preview+json" - // https://developer.github.com/changes/2017-05-23-coc-api/ mediaTypeCodesOfConductPreview = "application/vnd.github.scarlet-witch-preview+json" // https://developer.github.com/changes/2017-07-17-update-topics-on-repositories/ mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json" - // https://developer.github.com/changes/2017-08-30-preview-nested-teams/ - mediaTypeNestedTeamsPreview = "application/vnd.github.hellcat-preview+json" - - // https://developer.github.com/changes/2017-11-09-repository-transfer-api-preview/ - mediaTypeRepositoryTransferPreview = "application/vnd.github.nightshade-preview+json" - - // https://developer.github.com/changes/2018-01-25-organization-invitation-api-preview/ - mediaTypeOrganizationInvitationPreview = "application/vnd.github.dazzler-preview+json" - // https://developer.github.com/changes/2018-03-16-protected-branches-required-approving-reviews/ mediaTypeRequiredApprovingReviewsPreview = "application/vnd.github.luke-cage-preview+json" - // https://developer.github.com/changes/2018-02-22-label-description-search-preview/ - mediaTypeLabelDescriptionSearchPreview = "application/vnd.github.symmetra-preview+json" - - // https://developer.github.com/changes/2018-02-07-team-discussions-api/ - mediaTypeTeamDiscussionsPreview = "application/vnd.github.echo-preview+json" - - // https://developer.github.com/changes/2018-03-21-hovercard-api-preview/ - mediaTypeHovercardPreview = "application/vnd.github.hagar-preview+json" - - // https://developer.github.com/changes/2018-01-10-lock-reason-api-preview/ - mediaTypeLockReasonPreview = "application/vnd.github.sailor-v-preview+json" - // https://developer.github.com/changes/2018-05-07-new-checks-api-public-beta/ mediaTypeCheckRunsPreview = "application/vnd.github.antiope-preview+json" @@ -130,85 +128,142 @@ const ( // https://developer.github.com/changes/2018-12-18-interactions-preview/ mediaTypeInteractionRestrictionsPreview = "application/vnd.github.sombra-preview+json" - // https://developer.github.com/changes/2019-02-14-draft-pull-requests/ - mediaTypeDraftPreview = "application/vnd.github.shadow-cat-preview+json" - // https://developer.github.com/changes/2019-03-14-enabling-disabling-pages/ mediaTypeEnablePagesAPIPreview = "application/vnd.github.switcheroo-preview+json" // https://developer.github.com/changes/2019-04-24-vulnerability-alerts/ mediaTypeRequiredVulnerabilityAlertsPreview = "application/vnd.github.dorian-preview+json" - // https://developer.github.com/changes/2019-06-04-automated-security-fixes/ - mediaTypeRequiredAutomatedSecurityFixesPreview = "application/vnd.github.london-preview+json" - // https://developer.github.com/changes/2019-05-29-update-branch-api/ mediaTypeUpdatePullRequestBranchPreview = "application/vnd.github.lydian-preview+json" // https://developer.github.com/changes/2019-04-11-pulls-branches-for-commit/ mediaTypeListPullsOrBranchesForCommitPreview = "application/vnd.github.groot-preview+json" - // https://developer.github.com/changes/2019-06-12-team-sync/ - mediaTypeTeamSyncPreview = "application/vnd.github.team-sync-preview+json" - - // https://developer.github.com/v3/previews/#repository-creation-permissions + // https://docs.github.com/rest/previews/#repository-creation-permissions mediaTypeMemberAllowedRepoCreationTypePreview = "application/vnd.github.surtur-preview+json" - // https://developer.github.com/v3/previews/#create-and-use-repository-templates + // https://docs.github.com/rest/previews/#create-and-use-repository-templates mediaTypeRepositoryTemplatePreview = "application/vnd.github.baptiste-preview+json" + + // https://developer.github.com/changes/2019-10-03-multi-line-comments/ + mediaTypeMultiLineCommentsPreview = "application/vnd.github.comfort-fade-preview+json" + + // https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/ + mediaTypeOAuthAppPreview = "application/vnd.github.doctor-strange-preview+json" + + // https://developer.github.com/changes/2019-12-03-internal-visibility-changes/ + mediaTypeRepositoryVisibilityPreview = "application/vnd.github.nebula-preview+json" + + // https://developer.github.com/changes/2018-12-10-content-attachments-api/ + mediaTypeContentAttachmentsPreview = "application/vnd.github.corsair-preview+json" ) +// ErrPathForbidden is returned when a URL path contains ".." as a path +// segment, which could allow path traversal attacks. +var ErrPathForbidden = errors.New("path must not contain '..' due to auth vulnerability issue") + // A Client manages communication with the GitHub API. type Client struct { - clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func. - client *http.Client // HTTP client used to communicate with the API. + client *http.Client // HTTP client used to communicate with the API. + clientIgnoreRedirects *http.Client // HTTP client used to communicate with the API on endpoints where we don't want to follow redirects. // Base URL for API requests. Defaults to the public GitHub API, but can be - // set to a domain endpoint to use with GitHub Enterprise. BaseURL should + // set to a domain endpoint to use with GitHub Enterprise. baseURL should // always be specified with a trailing slash. - BaseURL *url.URL + baseURL *url.URL // Base URL for uploading files. - UploadURL *url.URL + uploadURL *url.URL + + // Default API version to set in the X-Github-Api-Version header. + apiVersionDefault string + // Minimum API version that the client can use. + apiVersionMin string + // Maximum API version that the client can use. + apiVersionMax string // User agent used when communicating with the GitHub API. - UserAgent string + userAgent string + + // disableRateLimitCheck stops the client checking for rate limits or tracking + // them. This is different to setting BypassRateLimitCheck in the context, + // as that still tracks the rate limits. + disableRateLimitCheck bool + + rateMu sync.Mutex + rateLimits [Categories]Rate // Rate limits for the client as determined by the most recent API calls. + secondaryRateLimitReset time.Time // Secondary rate limit reset for the client as determined by the most recent API calls. + + // If specified, Client will block requests for at most this duration in case of reaching a secondary + // rate limit + maxSecondaryRateLimitRetryAfterDuration time.Duration - rateMu sync.Mutex - rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls. + // Whether to respect rate limit headers on endpoints that return 302 redirections to artifacts + rateLimitRedirectionalEndpoints bool common service // Reuse a single struct instead of allocating one for each service on the heap. // Services used for talking to different parts of the GitHub API. - Activity *ActivityService - Admin *AdminService - Apps *AppsService - Authorizations *AuthorizationsService - Checks *ChecksService - Gists *GistsService - Git *GitService - Gitignores *GitignoresService - Interactions *InteractionsService - Issues *IssuesService - Licenses *LicensesService - Marketplace *MarketplaceService - Migrations *MigrationService - Organizations *OrganizationsService - Projects *ProjectsService - PullRequests *PullRequestsService - Reactions *ReactionsService - Repositories *RepositoriesService - Search *SearchService - Teams *TeamsService - Users *UsersService + Actions *ActionsService + Activity *ActivityService + Admin *AdminService + Apps *AppsService + Authorizations *AuthorizationsService + Billing *BillingService + Checks *ChecksService + Classroom *ClassroomService + CodeQuality *CodeQualityService + CodeScanning *CodeScanningService + CodesOfConduct *CodesOfConductService + Codespaces *CodespacesService + Copilot *CopilotService + Credentials *CredentialsService + Dependabot *DependabotService + DependencyGraph *DependencyGraphService + Emojis *EmojisService + Enterprise *EnterpriseService + Gists *GistsService + Git *GitService + Gitignores *GitignoresService + Interactions *InteractionsService + IssueImport *IssueImportService + Issues *IssuesService + Licenses *LicensesService + Markdown *MarkdownService + Marketplace *MarketplaceService + Meta *MetaService + Migrations *MigrationService + Organizations *OrganizationsService + PrivateRegistries *PrivateRegistriesService + Projects *ProjectsService + PullRequests *PullRequestsService + RateLimit *RateLimitService + Reactions *ReactionsService + Repositories *RepositoriesService + SCIM *SCIMService + Search *SearchService + SecretScanning *SecretScanningService + SecurityAdvisories *SecurityAdvisoriesService + SubIssue *SubIssueService + Teams *TeamsService + Users *UsersService } type service struct { client *Client } +// Client returns the http.Client used by this GitHub client. +// This should only be used for requests to the GitHub API because +// request headers will contain an authorization token. +func (c *Client) Client() *http.Client { + clientCopy := *c.client + return &clientCopy +} + // ListOptions specifies the optional parameters to various List methods that -// support pagination. +// support offset pagination. type ListOptions struct { // For paginated result sets, page of results to retrieve. Page int `url:"page,omitempty"` @@ -217,6 +272,33 @@ type ListOptions struct { PerPage int `url:"per_page,omitempty"` } +// ListCursorOptions specifies the optional parameters to various List methods that +// support cursor pagination. +type ListCursorOptions struct { + // For paginated result sets, page of results to retrieve. + Page string `url:"page,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty"` + + // For paginated result sets, the number of results per page (max 100), starting from the first matching result. + // This parameter must not be used in combination with last. + First int `url:"first,omitempty"` + + // For paginated result sets, the number of results per page (max 100), starting from the last matching result. + // This parameter must not be used in combination with first. + Last int `url:"last,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for events after this cursor. + After string `url:"after,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for events before this cursor. + Before string `url:"before,omitempty"` + + // A cursor, as given in the Link header. If specified, the query continues the search using this cursor. + Cursor string `url:"cursor,omitempty"` +} + // UploadOptions specifies the parameters to methods that support uploads. type UploadOptions struct { Name string `url:"name,omitempty"` @@ -240,11 +322,12 @@ type RawOptions struct { Type RawType } -// addOptions adds the parameters in opt as URL query parameters to s. opt +type structPtr[T any] interface{ *T } + +// addOptions adds the parameters in opts as URL query parameters to s. opts // must be a struct whose fields may contain "url" tags. -func addOptions(s string, opt interface{}) (string, error) { - v := reflect.ValueOf(opt) - if v.Kind() == reflect.Ptr && v.IsNil() { +func addOptions[P structPtr[T], T any](s string, opts P) (string, error) { + if opts == nil { return s, nil } @@ -253,7 +336,7 @@ func addOptions(s string, opt interface{}) (string, error) { return s, err } - qs, err := query.Values(opt) + qs, err := query.Values(opts) if err != nil { return s, err } @@ -262,72 +345,454 @@ func addOptions(s string, opt interface{}) (string, error) { return u.String(), nil } -// NewClient returns a new GitHub API client. If a nil httpClient is -// provided, a new http.Client will be used. To use API methods which require -// authentication, provide an http.Client that will perform the authentication -// for you (such as that provided by the golang.org/x/oauth2 library). -func NewClient(httpClient *http.Client) *Client { - if httpClient == nil { - httpClient = &http.Client{} +// errUninitialized is returned when an uninitialized Client is used. +var errUninitialized = errors.New("client is not initialized") + +// clientOptions holds the configuration options for a Client. +type clientOptions struct { + httpClient *http.Client + transport http.RoundTripper + timeout *time.Duration + apiVersionMin *string + apiVersionMax *string + userAgent *string + envProxy bool + token *string + baseURL *url.URL + uploadURL *url.URL + disableRateLimitCheck bool + rateLimitRedirectionalEndpoints bool + maxSecondaryRateLimitRetryAfterDuration *time.Duration + marketplaceStubbed bool +} + +// ClientOptionsFunc is a functional option for providing configuration options +// to a Client. +type ClientOptionsFunc func(*clientOptions) error + +// WithHTTPClient returns a ClientOptionsFunc that sets the http.Client +// for a Client. If not set, a default http.Client will be used. +func WithHTTPClient(httpClient *http.Client) ClientOptionsFunc { + return func(o *clientOptions) error { + if httpClient == nil { + return errors.New("http client must not be nil") + } + + httpClient := *httpClient + o.httpClient = &httpClient + return nil + } +} + +// WithTransport returns a ClientOptionsFunc that sets the http.RoundTripper +// for a Client. This overrides the transport set by [WithHTTPClient]. If not +// set and no HTTP client is provided, the default http.RoundTripper will be used. +func WithTransport(transport http.RoundTripper) ClientOptionsFunc { + return func(o *clientOptions) error { + if transport == nil { + return errors.New("transport must not be nil") + } + + o.transport = transport + return nil + } +} + +// WithTimeout returns a ClientOptionsFunc that sets the timeout for a Client. +// This overrides the timeout set by [WithHTTPClient]. If not set and no HTTP +// client is provided, the default http.Client with no timeout will be used. +// It is recommended to provide a timeout for production environments. +func WithTimeout(timeout time.Duration) ClientOptionsFunc { + return func(o *clientOptions) error { + if timeout < 0 { + return errors.New("timeout must not be negative") + } + + o.timeout = &timeout + return nil + } +} + +// WithUserAgent returns a ClientOptionsFunc that sets the User-Agent header +// for a Client. If not set, a default User-Agent will be used. +func WithUserAgent(userAgent string) ClientOptionsFunc { + return func(o *clientOptions) error { + o.userAgent = &userAgent + return nil + } +} + +// WithEnvProxy returns a ClientOptionsFunc that configures the Client to use +// the HTTP proxy settings from the environment variables +// (e.g., HTTP_PROXY, HTTPS_PROXY, NO_PROXY). +// If not set, the client will not use environment proxy settings. +func WithEnvProxy() ClientOptionsFunc { + return func(o *clientOptions) error { + o.envProxy = true + return nil + } +} + +// WithAuthToken returns a ClientOptionsFunc that sets the authentication token +// for a Client. If not set, the client will make unauthenticated requests. +func WithAuthToken(token string) ClientOptionsFunc { + return func(o *clientOptions) error { + if token == "" { + return errors.New("token must not be empty") + } + + o.token = &token + return nil + } +} + +// WithURLs returns a ClientOptionsFunc that sets the base and upload URLs +// while only validating the URL format. Nil values will be ignored and default +// URLs will be used. +func WithURLs(baseURL, uploadURL *string) ClientOptionsFunc { + return func(o *clientOptions) error { + if baseURL != nil { + b, err := parseURL(*baseURL) + if err != nil { + return fmt.Errorf("invalid base url: %w", err) + } + + o.baseURL = b + } + + if uploadURL != nil { + u, err := parseURL(*uploadURL) + if err != nil { + return fmt.Errorf("invalid upload url: %w", err) + } + + o.uploadURL = u + } + + return nil + } +} + +// WithEnterpriseURLs returns a ClientOptionsFunc that sets the base and upload +// URLs for a Client. +func WithEnterpriseURLs(baseURL, uploadURL string) ClientOptionsFunc { + return func(o *clientOptions) error { + b, err := parseURL(baseURL) + if err != nil { + return fmt.Errorf("invalid base url: %w", err) + } + + if !strings.HasSuffix(b.Path, "/api/v3/") && + !strings.HasPrefix(b.Host, "api.") && + !strings.Contains(b.Host, ".api.") { + b.Path += "api/v3/" + } + + o.baseURL = b + + u, err := parseURL(uploadURL) + if err != nil { + return fmt.Errorf("invalid upload url: %w", err) + } + + if !strings.HasSuffix(u.Path, "/api/uploads/") && + !strings.HasPrefix(u.Host, "api.") && + !strings.Contains(u.Host, ".api.") && + !strings.HasPrefix(u.Host, "uploads.") { + u.Path += "api/uploads/" + } + + o.uploadURL = u + + return nil + } +} + +// WithDisableRateLimitCheck returns a ClientOptionsFunc that disables rate +// limit checking for a Client. If not set, the client will check for rate +// limits and track them. +func WithDisableRateLimitCheck() ClientOptionsFunc { + return func(o *clientOptions) error { + o.disableRateLimitCheck = true + return nil + } +} + +// WithRateLimitRedirectionalEndpoints returns a ClientOptionsFunc that +// configures the Client to respect rate limit headers on endpoints that +// return 302 redirection to artifacts. If not set, the client will not +// respect rate limit headers on these endpoints. +func WithRateLimitRedirectionalEndpoints() ClientOptionsFunc { + return func(o *clientOptions) error { + o.rateLimitRedirectionalEndpoints = true + return nil + } +} + +// WithMaxSecondaryRateLimitRetryAfterDuration returns a ClientOptionsFunc +// that configures the Client secondary rate limit max retry after duration. +func WithMaxSecondaryRateLimitRetryAfterDuration(duration time.Duration) ClientOptionsFunc { + return func(o *clientOptions) error { + o.maxSecondaryRateLimitRetryAfterDuration = &duration + return nil + } +} + +// NewClient returns a new GitHub API client configured with the provided +// options. The default configuration is suitable for making unauthenticated +// requests to the public GitHub API. +// +// For GitHub Enterprise, use [WithEnterpriseURLs] to set the base and upload +// URLs. +// +// To make authenticated requests, use [WithAuthToken] or [WithHTTPClient] to +// pass in a [http.Client] that performs authentication +// (e.g., using golang.org/x/oauth2). +// +// For production usage it is recommended to provide a timeout using +// [WithTimeout] or by providing a custom [http.Client] with an appropriate +// timeout using [WithHTTPClient]. +func NewClient(opts ...ClientOptionsFunc) (*Client, error) { + o := clientOptions{} + for _, opt := range opts { + if err := opt(&o); err != nil { + return nil, err + } + } + + return newClient(o) +} + +// newClient creates a new Client with the provided options. This is an internal +// helper function that is called by [NewClient] and [Client.Clone]. +func newClient(opts clientOptions) (*Client, error) { + c := &Client{ + apiVersionDefault: api20221128, + apiVersionMin: api20221128, + apiVersionMax: api20260310, + } + + if opts.httpClient != nil { + c.client = opts.httpClient + } else { + c.client = &http.Client{} + } + + if opts.transport != nil { + c.client.Transport = opts.transport + } + + if opts.timeout != nil { + c.client.Timeout = *opts.timeout + } + + if opts.envProxy { + transport := c.client.Transport + if transport == nil { + transport = http.DefaultTransport + } + + t, ok := transport.(*http.Transport) + if !ok { + return nil, errors.New("cannot set environment proxy on non-http transport") + } + + t2 := t.Clone() + t2.Proxy = http.ProxyFromEnvironment + c.client.Transport = t2 + } + + if opts.token != nil { + transport := c.client.Transport + if transport == nil { + transport = http.DefaultTransport + } + c.client.Transport = roundTripperFunc(func(req *http.Request) (*http.Response, error) { + req = req.Clone(req.Context()) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %v", *opts.token)) + return transport.RoundTrip(req) + }) + } + + c.clientIgnoreRedirects = &http.Client{ + Transport: c.client.Transport, + Timeout: c.client.Timeout, + Jar: c.client.Jar, + CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, + } + + if opts.apiVersionMin != nil { + c.apiVersionMin = *opts.apiVersionMin + } + + if opts.apiVersionMax != nil { + c.apiVersionMax = *opts.apiVersionMax + } + + if opts.userAgent != nil { + c.userAgent = *opts.userAgent + } else { + c.userAgent = defaultUserAgent + } + + if opts.baseURL != nil { + c.baseURL = opts.baseURL + } else { + c.baseURL, _ = url.Parse(defaultBaseURL) + } + + if opts.uploadURL != nil { + c.uploadURL = opts.uploadURL + } else { + c.uploadURL, _ = url.Parse(uploadBaseURL) + } + + c.disableRateLimitCheck = opts.disableRateLimitCheck + + if !c.disableRateLimitCheck { + c.rateLimitRedirectionalEndpoints = opts.rateLimitRedirectionalEndpoints + + if opts.maxSecondaryRateLimitRetryAfterDuration != nil { + c.maxSecondaryRateLimitRetryAfterDuration = *opts.maxSecondaryRateLimitRetryAfterDuration + } } - baseURL, _ := url.Parse(defaultBaseURL) - uploadURL, _ := url.Parse(uploadBaseURL) - c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL} c.common.client = c + c.Actions = (*ActionsService)(&c.common) c.Activity = (*ActivityService)(&c.common) c.Admin = (*AdminService)(&c.common) c.Apps = (*AppsService)(&c.common) c.Authorizations = (*AuthorizationsService)(&c.common) + c.Billing = (*BillingService)(&c.common) c.Checks = (*ChecksService)(&c.common) + c.Classroom = (*ClassroomService)(&c.common) + c.CodeQuality = (*CodeQualityService)(&c.common) + c.CodeScanning = (*CodeScanningService)(&c.common) + c.Codespaces = (*CodespacesService)(&c.common) + c.CodesOfConduct = (*CodesOfConductService)(&c.common) + c.Copilot = (*CopilotService)(&c.common) + c.Credentials = (*CredentialsService)(&c.common) + c.Dependabot = (*DependabotService)(&c.common) + c.DependencyGraph = (*DependencyGraphService)(&c.common) + c.Emojis = (*EmojisService)(&c.common) + c.Enterprise = (*EnterpriseService)(&c.common) c.Gists = (*GistsService)(&c.common) c.Git = (*GitService)(&c.common) c.Gitignores = (*GitignoresService)(&c.common) c.Interactions = (*InteractionsService)(&c.common) + c.IssueImport = (*IssueImportService)(&c.common) c.Issues = (*IssuesService)(&c.common) c.Licenses = (*LicensesService)(&c.common) - c.Marketplace = &MarketplaceService{client: c} + c.Markdown = (*MarkdownService)(&c.common) + c.Marketplace = &MarketplaceService{client: c, Stubbed: opts.marketplaceStubbed} + c.Meta = (*MetaService)(&c.common) c.Migrations = (*MigrationService)(&c.common) c.Organizations = (*OrganizationsService)(&c.common) + c.PrivateRegistries = (*PrivateRegistriesService)(&c.common) c.Projects = (*ProjectsService)(&c.common) c.PullRequests = (*PullRequestsService)(&c.common) + c.RateLimit = (*RateLimitService)(&c.common) c.Reactions = (*ReactionsService)(&c.common) c.Repositories = (*RepositoriesService)(&c.common) + c.SCIM = (*SCIMService)(&c.common) c.Search = (*SearchService)(&c.common) + c.SecretScanning = (*SecretScanningService)(&c.common) + c.SecurityAdvisories = (*SecurityAdvisoriesService)(&c.common) + c.SubIssue = (*SubIssueService)(&c.common) c.Teams = (*TeamsService)(&c.common) c.Users = (*UsersService)(&c.common) - return c + + return c, nil } -// NewEnterpriseClient returns a new GitHub API client with provided -// base URL and upload URL (often the same URL). -// If either URL does not have a trailing slash, one is added automatically. -// If a nil httpClient is provided, a new http.Client will be used. -// -// Note that NewEnterpriseClient is a convenience helper only; -// its behavior is equivalent to using NewClient, followed by setting -// the BaseURL and UploadURL fields. -func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) { - baseEndpoint, err := url.Parse(baseURL) - if err != nil { - return nil, err +// UserAgent returns the User-Agent header value for the client. +func (c *Client) UserAgent() string { + return c.userAgent +} + +// BaseURL returns the base URL for API requests. +func (c *Client) BaseURL() string { + if c.baseURL == nil { + return "" } - if !strings.HasSuffix(baseEndpoint.Path, "/") { - baseEndpoint.Path += "/" + return c.baseURL.String() +} + +// UploadURL returns the base URL for upload API requests. +func (c *Client) UploadURL() string { + if c.uploadURL == nil { + return "" } + return c.uploadURL.String() +} - uploadEndpoint, err := url.Parse(uploadURL) +// Clone returns a copy of the client with the same configuration and services. +// The returned client has its own http.Client but shares the client +// configuration such as transport and timeout. The returned client starts with +// the same rate limit information as the original client, but it is not +// updated when the original client's rate limit information is updated. +// The returned client is independent of the original client and can be +// modified without affecting the original client. +func (c *Client) Clone(opts ...ClientOptionsFunc) (*Client, error) { + if c.client == nil { + return nil, errUninitialized + } + + o := clientOptions{ + apiVersionMin: &c.apiVersionMin, + apiVersionMax: &c.apiVersionMax, + userAgent: &c.userAgent, + baseURL: Ptr(*c.baseURL), + uploadURL: Ptr(*c.uploadURL), + disableRateLimitCheck: c.disableRateLimitCheck, + rateLimitRedirectionalEndpoints: c.rateLimitRedirectionalEndpoints, + maxSecondaryRateLimitRetryAfterDuration: &c.maxSecondaryRateLimitRetryAfterDuration, + } + + if c.Marketplace != nil { + o.marketplaceStubbed = c.Marketplace.Stubbed + } + + for _, opt := range opts { + if err := opt(&o); err != nil { + return nil, err + } + } + + if o.httpClient == nil { + o.httpClient = &http.Client{ + Transport: c.client.Transport, + CheckRedirect: c.client.CheckRedirect, + Jar: c.client.Jar, + Timeout: c.client.Timeout, + } + } + + clone, err := newClient(o) if err != nil { return nil, err } - if !strings.HasSuffix(uploadEndpoint.Path, "/") { - uploadEndpoint.Path += "/" + + if !clone.disableRateLimitCheck { + c.rateMu.Lock() + clone.rateLimits = c.rateLimits + clone.secondaryRateLimitReset = c.secondaryRateLimitReset + c.rateMu.Unlock() } - c := NewClient(httpClient) - c.BaseURL = baseEndpoint - c.UploadURL = uploadEndpoint - return c, nil + return clone, nil +} + +// RequestOption represents an option that can modify an http.Request. +type RequestOption func(req *http.Request) + +// WithVersion overrides the GitHub v3 API version for this individual request. +// For more information, see: +// https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/ +func WithVersion(version string) RequestOption { + return func(req *http.Request) { + req.Header.Set(headerAPIVersion, version) + } } // NewRequest creates an API request. A relative URL can be provided in urlStr, @@ -335,18 +800,23 @@ func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*C // Relative URLs should always be specified without a preceding slash. If // specified, the value pointed to by body is JSON encoded and included as the // request body. -func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { - if !strings.HasSuffix(c.BaseURL.Path, "/") { - return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) +func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body any, opts ...RequestOption) (*http.Request, error) { + if !strings.HasSuffix(c.baseURL.Path, "/") { + return nil, fmt.Errorf("baseURL must have a trailing slash, but %q does not", c.baseURL) + } + + if err := checkURLPathTraversal(urlStr); err != nil { + return nil, err } - u, err := c.BaseURL.Parse(urlStr) + + u, err := c.baseURL.Parse(urlStr) if err != nil { return nil, err } var buf io.ReadWriter if body != nil { - buf = new(bytes.Buffer) + buf = &bytes.Buffer{} enc := json.NewEncoder(buf) enc.SetEscapeHTML(false) err := enc.Encode(body) @@ -355,7 +825,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ } } - req, err := http.NewRequest(method, u.String(), buf) + req, err := http.NewRequestWithContext(ctx, method, u.String(), buf) if err != nil { return nil, err } @@ -364,39 +834,153 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ req.Header.Set("Content-Type", "application/json") } req.Header.Set("Accept", mediaTypeV3) - if c.UserAgent != "" { - req.Header.Set("User-Agent", c.UserAgent) + if c.userAgent != "" { + req.Header.Set("User-Agent", c.userAgent) + } + req.Header.Set(headerAPIVersion, c.apiVersionDefault) + + for _, opt := range opts { + opt(req) } + return req, nil } +// NewFormRequest creates an API request. A relative URL can be provided in urlStr, +// in which case it is resolved relative to the BaseURL of the Client. +// Relative URLs should always be specified without a preceding slash. +// Body is sent with Content-Type: application/x-www-form-urlencoded. +func (c *Client) NewFormRequest(ctx context.Context, urlStr string, body io.Reader, opts ...RequestOption) (*http.Request, error) { + if !strings.HasSuffix(c.baseURL.Path, "/") { + return nil, fmt.Errorf("baseURL must have a trailing slash, but %q does not", c.baseURL) + } + + if err := checkURLPathTraversal(urlStr); err != nil { + return nil, err + } + + u, err := c.baseURL.Parse(urlStr) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Accept", mediaTypeV3) + if c.userAgent != "" { + req.Header.Set("User-Agent", c.userAgent) + } + req.Header.Set(headerAPIVersion, c.apiVersionDefault) + + for _, opt := range opts { + opt(req) + } + + return req, nil +} + +// checkURLPathTraversal returns ErrPathForbidden if urlStr contains ".." as a +// path segment (e.g. "a/../b"), preventing path traversal attacks. Percent- +// encoded equivalents such as "%2e%2e" are also rejected because url.Parse +// decodes them to ".." before the check runs. It does not match ".." embedded +// within a segment (e.g. "file..txt"). The check is performed only on the path +// portion of the URL, ignoring any query string or fragment. +func checkURLPathTraversal(urlStr string) error { + u, err := url.Parse(urlStr) + if err != nil { + return err + } + if slices.Contains(strings.Split(u.Path, "/"), "..") { + return ErrPathForbidden + } + return nil +} + // NewUploadRequest creates an upload request. A relative URL can be provided in // urlStr, in which case it is resolved relative to the UploadURL of the Client. // Relative URLs should always be specified without a preceding slash. -func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { - if !strings.HasSuffix(c.UploadURL.Path, "/") { - return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL) +func (c *Client) NewUploadRequest(ctx context.Context, urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption) (*http.Request, error) { + if !strings.HasSuffix(c.uploadURL.Path, "/") { + return nil, fmt.Errorf("uploadURL must have a trailing slash, but %q does not", c.uploadURL) } - u, err := c.UploadURL.Parse(urlStr) + + if err := checkURLPathTraversal(urlStr); err != nil { + return nil, err + } + + u, err := c.uploadURL.Parse(urlStr) if err != nil { return nil, err } - req, err := http.NewRequest("POST", u.String(), reader) + requestBody := reader + if reader != nil { + // Wrap the provided reader so transport code does not observe concrete body types + // (for example *os.File) and switch to platform-specific sendfile fast paths. + // + // Why this exists: + // race-enabled test runs on Windows have surfaced data races in the sendfile path + // while request read/write loops run concurrently. Hiding concrete type information + // keeps uploads on the generic io.Reader copy path, which is race-stable and preserves + // request semantics (same bytes, same headers, same content length). + requestBody = uploadRequestBodyReader{Reader: reader} + } + + req, err := http.NewRequestWithContext(ctx, "POST", u.String(), requestBody) if err != nil { return nil, err } + req.ContentLength = size + // Set GetBody so HTTP/2 transports can retry the request when a stream is + // refused (REFUSED_STREAM / GOAWAY) after the body has been written. Without + // GetBody, net/http2 fails with "cannot retry ... after Request.Body was + // written". See https://github.com/google/go-github/issues/2113. + // + // http.Request.GetBody must return an independent copy of the body, so we + // require io.ReaderAt (to read without moving the original body's position) + // in addition to io.Seeker (to record the body's starting offset). This is + // satisfied by *os.File, *bytes.Reader and *strings.Reader. Readers that + // cannot provide an independent, rewindable view are left without GetBody so + // that large uploads are never buffered in memory. + seeker, isSeeker := reader.(io.Seeker) + readerAt, isReaderAt := reader.(io.ReaderAt) + if isSeeker && isReaderAt { + offset, err := seeker.Seek(0, io.SeekCurrent) + if err != nil { + return nil, err + } + req.GetBody = func() (io.ReadCloser, error) { + return io.NopCloser(uploadRequestBodyReader{Reader: io.NewSectionReader(readerAt, offset, size)}), nil + } + } + if mediaType == "" { mediaType = defaultMediaType } req.Header.Set("Content-Type", mediaType) req.Header.Set("Accept", mediaTypeV3) - req.Header.Set("User-Agent", c.UserAgent) + req.Header.Set("User-Agent", c.userAgent) + req.Header.Set(headerAPIVersion, c.apiVersionDefault) + + for _, opt := range opts { + opt(req) + } + return req, nil } +// uploadRequestBodyReader intentionally wraps an io.Reader to hide concrete reader types. +// See NewUploadRequest for why this prevents race-prone transport optimizations. +type uploadRequestBodyReader struct { + io.Reader +} + // Response is a GitHub API response. This wraps the standard http.Response // returned from GitHub and provides convenient access to things like // pagination links. @@ -407,15 +991,43 @@ type Response struct { // results. Any or all of these may be set to the zero value for // responses that are not part of a paginated set, or for which there // are no additional pages. - + // + // These fields support what is called "offset pagination" and should + // be used with the ListOptions struct. NextPage int PrevPage int FirstPage int LastPage int + // Additionally, some APIs support "cursor pagination" instead of offset. + // This means that a token points directly to the next record which + // can lead to O(1) performance compared to O(n) performance provided + // by offset pagination. + // + // For APIs that support cursor pagination (such as + // TeamsService.ListIDPGroupsInOrganization), the following field + // will be populated to point to the next page. + // + // To use this token, set ListCursorOptions.Page to this value before + // calling the endpoint again. + NextPageToken string + + // For APIs that support cursor pagination, such as RepositoriesService.ListHookDeliveries, + // the following field will be populated to point to the next page. + // Set ListCursorOptions.Cursor to this value when calling the endpoint again. + Cursor string + + // For APIs that support before/after pagination, such as OrganizationsService.AuditLog. + Before string + After string + // Explicitly specify the Rate type so Rate's String() receiver doesn't // propagate to Response. Rate Rate + + // token's expiration date. Timestamp is 0001-01-01 when token doesn't expire. + // So it is valid for TokenExpiration.Equal(Timestamp{}) or TokenExpiration.Time.After(time.Now()) + TokenExpiration Timestamp } // newResponse creates a new Response for the provided http.Response. @@ -424,6 +1036,7 @@ func newResponse(r *http.Response) *Response { response := &Response{Response: r} response.populatePageValues() response.Rate = parseRate(r) + response.TokenExpiration = parseTokenExpiration(r) return response } @@ -431,7 +1044,7 @@ func newResponse(r *http.Response) *Response { // various pagination link values in the Response. func (r *Response) populatePageValues() { if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 { - for _, link := range strings.Split(links[0], ",") { + for link := range strings.SplitSeq(links[0], ",") { segments := strings.Split(strings.TrimSpace(link), ";") // link must at least have href and rel @@ -449,138 +1062,369 @@ func (r *Response) populatePageValues() { if err != nil { continue } - page := url.Query().Get("page") - if page == "" { + + q := url.Query() + + if cursor := q.Get("cursor"); cursor != "" { + for _, segment := range segments[1:] { + if strings.TrimSpace(segment) == `rel="next"` { + r.Cursor = cursor + } + } + + continue + } + + page := q.Get("page") + since := q.Get("since") + before := q.Get("before") + after := q.Get("after") + + if page == "" && before == "" && after == "" && since == "" { continue } + if since != "" && page == "" { + page = since + } + for _, segment := range segments[1:] { switch strings.TrimSpace(segment) { case `rel="next"`: - r.NextPage, _ = strconv.Atoi(page) + if r.NextPage, err = strconv.Atoi(page); err != nil { + r.NextPageToken = page + } + r.After = after case `rel="prev"`: r.PrevPage, _ = strconv.Atoi(page) + r.Before = before case `rel="first"`: r.FirstPage, _ = strconv.Atoi(page) case `rel="last"`: r.LastPage, _ = strconv.Atoi(page) } + } + } + } +} + +// parseRate parses the rate related headers. +func parseRate(r *http.Response) Rate { + var rate Rate + if limit := r.Header.Get(HeaderRateLimit); limit != "" { + rate.Limit, _ = strconv.Atoi(limit) + } + if remaining := r.Header.Get(HeaderRateRemaining); remaining != "" { + rate.Remaining, _ = strconv.Atoi(remaining) + } + if used := r.Header.Get(HeaderRateUsed); used != "" { + rate.Used, _ = strconv.Atoi(used) + } + if reset := r.Header.Get(HeaderRateReset); reset != "" { + if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { + rate.Reset = Timestamp{time.Unix(v, 0)} + } + } + if resource := r.Header.Get(HeaderRateResource); resource != "" { + rate.Resource = resource + } + return rate +} + +// parseSecondaryRate parses the secondary rate related headers, +// and returns the time to retry after. +func parseSecondaryRate(r *http.Response) *time.Duration { + // According to GitHub support, the "Retry-After" header value will be + // an integer which represents the number of seconds that one should + // wait before resuming making requests. + if v := r.Header.Get(headerRetryAfter); v != "" { + retryAfterSeconds, _ := strconv.ParseInt(v, 10, 64) // Error handling is noop. + retryAfter := time.Duration(retryAfterSeconds) * time.Second + return &retryAfter + } + + // According to GitHub support, endpoints might return x-ratelimit-reset instead, + // as an integer which represents the number of seconds since epoch UTC, + // representing the time to resume making requests. + if v := r.Header.Get(HeaderRateReset); v != "" { + secondsSinceEpoch, _ := strconv.ParseInt(v, 10, 64) // Error handling is noop. + retryAfter := time.Until(time.Unix(secondsSinceEpoch, 0)) + return &retryAfter + } + + return nil +} + +// parseTokenExpiration parses the TokenExpiration related headers. +// Returns 0001-01-01 if the header is not defined or could not be parsed. +func parseTokenExpiration(r *http.Response) Timestamp { + if v := r.Header.Get(headerTokenExpiration); v != "" { + if t, err := time.Parse("2006-01-02 15:04:05 MST", v); err == nil { + return Timestamp{t.Local()} + } + // Some tokens include the timezone offset instead of the timezone. + // https://github.com/google/go-github/issues/2649 + if t, err := time.Parse("2006-01-02 15:04:05 -0700", v); err == nil { + return Timestamp{t.Local()} + } + } + return Timestamp{} // 0001-01-01 00:00:00 +} + +type requestContext uint8 + +const ( + // BypassRateLimitCheck prevents a pre-emptive check for exceeded primary rate limits + // Specify this by providing a context with this key, e.g. + // context.WithValue(context.Background(), github.BypassRateLimitCheck, true) + BypassRateLimitCheck requestContext = iota + + SleepUntilPrimaryRateLimitResetWhenRateLimited +) + +// maxErrorBodySize is the maximum number of bytes read from an HTTP error +// response body. Limits memory allocation when a server returns an +// unexpectedly large error body. +const maxErrorBodySize = 1 * 1024 * 1024 // 1 MiB + +// ErrUnsupportedAPIVersion is returned when the API version specified in the +// request is not supported by the client. +var ErrUnsupportedAPIVersion = errors.New("unsupported api version") + +// checkRequestAPIVersionBeforeDo checks if the API version specified in the +// request is supported by the client before making the API call. If the +// version is not supported, it returns [ErrUnsupportedAPIVersion]. If the +// version is empty it returns nil. +func (c *Client) checkRequestAPIVersionBeforeDo(req *http.Request) error { + reqAPIVersion := req.Header.Get(headerAPIVersion) - } - } + if reqAPIVersion == "" { + return nil } -} -// parseRate parses the rate related headers. -func parseRate(r *http.Response) Rate { - var rate Rate - if limit := r.Header.Get(headerRateLimit); limit != "" { - rate.Limit, _ = strconv.Atoi(limit) + if reqAPIVersion < c.apiVersionMin || reqAPIVersion > c.apiVersionMax { + return ErrUnsupportedAPIVersion } - if remaining := r.Header.Get(headerRateRemaining); remaining != "" { - rate.Remaining, _ = strconv.Atoi(remaining) - } - if reset := r.Header.Get(headerRateReset); reset != "" { - if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { - rate.Reset = Timestamp{time.Unix(v, 0)} - } - } - return rate + + return nil } -// Do sends an API request and returns the API response. The API response is -// JSON decoded and stored in the value pointed to by v, or returned as an -// error if an API error has occurred. If v implements the io.Writer -// interface, the raw response body will be written to v, without attempting to -// first decode it. If rate limit is exceeded and reset time is in the future, -// Do returns *RateLimitError immediately without making a network API call. -// -// The provided ctx must be non-nil. If it is canceled or times out, -// ctx.Err() will be returned. -func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) { - req = withContext(ctx, req) +// bareDo sends an API request using `caller` http.Client passed in the parameters +// and lets you handle the api response. If an error or API Error occurs, the error +// will contain more information. Otherwise, you are supposed to read and close the +// response's Body. If rate limit is exceeded and reset time is in the future, +// bareDo returns *RateLimitError immediately without making a network API call. +func (c *Client) bareDo(caller *http.Client, req *http.Request) (*Response, error) { + ctx := req.Context() + + if err := c.checkRequestAPIVersionBeforeDo(req); err != nil { + return nil, err + } + + rateLimitCategory := CoreCategory - rateLimitCategory := category(req.URL.Path) + if !c.disableRateLimitCheck { + rateLimitCategory = GetRateLimitCategory(req.Method, req.URL.Path) - // If we've hit rate limit, don't make further requests before Reset time. - if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil { - return &Response{ - Response: err.Response, - Rate: err.Rate, - }, err + if bypass := ctx.Value(BypassRateLimitCheck); bypass == nil { + // If we've hit rate limit, don't make further requests before Reset time. + if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil { + return &Response{ + Response: err.Response, + Rate: err.Rate, + }, err + } + + // If we've hit a secondary rate limit, don't make further requests before Retry After. + if err := c.checkSecondaryRateLimitBeforeDo(req); err != nil { + return &Response{ + Response: err.Response, + }, err + } + } + } + + resp, err := caller.Do(req) + var response *Response + if resp != nil { + response = newResponse(resp) } - resp, err := c.client.Do(req) if err != nil { // If we got an error, and the context has been canceled, // the context's error is probably more useful. select { case <-ctx.Done(): - return nil, ctx.Err() + return response, ctx.Err() default: } // If the error type is *url.Error, sanitize its URL before returning. - if e, ok := err.(*url.Error); ok { + var e *url.Error + if errors.As(err, &e) { if url, err := url.Parse(e.URL); err == nil { e.URL = sanitizeURL(url).String() - return nil, e + return response, e } } - return nil, err + return response, err } - defer resp.Body.Close() - - response := newResponse(resp) - c.rateMu.Lock() - c.rateLimits[rateLimitCategory] = response.Rate - c.rateMu.Unlock() + // Don't update the rate limits if the client has rate limits disabled or if + // this was a cached response. The X-From-Cache is set by + // https://github.com/bartventer/httpcache if it's enabled. + if !c.disableRateLimitCheck && response.Header.Get("X-From-Cache") == "" { + c.rateMu.Lock() + c.rateLimits[rateLimitCategory] = response.Rate + c.rateMu.Unlock() + } err = CheckResponse(resp) if err != nil { + defer resp.Body.Close() // Special case for AcceptedErrors. If an AcceptedError // has been encountered, the response's payload will be // added to the AcceptedError and returned. // // Issue #1022 - aerr, ok := err.(*AcceptedError) - if ok { - b, readErr := ioutil.ReadAll(resp.Body) + var aerr *AcceptedError + if errors.As(err, &aerr) { + b, readErr := io.ReadAll(io.LimitReader(resp.Body, maxErrorBodySize)) if readErr != nil { return response, readErr } aerr.Raw = b - return response, aerr + err = aerr } - return response, err + var rateLimitError *RateLimitError + if errors.As(err, &rateLimitError) && + ctx.Value(SleepUntilPrimaryRateLimitResetWhenRateLimited) != nil { + if err := sleepUntilResetWithBuffer(ctx, rateLimitError.Rate.Reset.Time); err != nil { + return response, err + } + // retry the request now the rate limit should have been reset + newReq := req.Clone(context.WithValue(ctx, SleepUntilPrimaryRateLimitResetWhenRateLimited, nil)) + return c.bareDo(caller, newReq) + } + + // Update the secondary rate limit if we hit it. + var rerr *AbuseRateLimitError + if errors.As(err, &rerr) && rerr.RetryAfter != nil { + // if a max duration is specified, make sure that we are waiting at most this duration + if c.maxSecondaryRateLimitRetryAfterDuration > 0 && rerr.GetRetryAfter() > c.maxSecondaryRateLimitRetryAfterDuration { + rerr.RetryAfter = &c.maxSecondaryRateLimitRetryAfterDuration + } + c.rateMu.Lock() + c.secondaryRateLimitReset = time.Now().Add(*rerr.RetryAfter) + c.rateMu.Unlock() + } } + return response, err +} + +// BareDo sends an API request and lets you handle the api response. If an error +// or API Error occurs, the error will contain more information. Otherwise, you +// are supposed to read and close the response's Body. If rate limit is exceeded +// and reset time is in the future, BareDo returns *RateLimitError immediately +// without making a network API call. +func (c *Client) BareDo(req *http.Request) (*Response, error) { + return c.bareDo(c.client, req) +} + +// bareDoIgnoreRedirects has the exact same behavior as BareDo but stops at the first +// redirection code returned by the API. If a redirection is returned by the api, bareDoIgnoreRedirects +// returns a *RedirectionError. +func (c *Client) bareDoIgnoreRedirects(req *http.Request) (*Response, error) { + return c.bareDo(c.clientIgnoreRedirects, req) +} + +var errInvalidLocation = errors.New("invalid or empty Location header in redirection response") - if v != nil { - if w, ok := v.(io.Writer); ok { - io.Copy(w, resp.Body) - } else { - decErr := json.NewDecoder(resp.Body).Decode(v) - if decErr == io.EOF { - decErr = nil // ignore EOF errors caused by empty response body +// bareDoUntilFound has the exact same behavior as BareDo but only follows 301s, up to maxRedirects times. If it receives +// a 302, it will parse the Location header into a *url.URL and return that. +// This is useful for endpoints that return a 302 in successful cases but still might return 301s for +// permanent redirections. +func (c *Client) bareDoUntilFound(req *http.Request, maxRedirects int) (*url.URL, *Response, error) { + response, err := c.bareDoIgnoreRedirects(req) + if err != nil { + var rerr *RedirectionError + if errors.As(err, &rerr) { + // If we receive a 302, transform potential relative locations into absolute and return it. + if rerr.StatusCode == http.StatusFound { + if rerr.Location == nil { + return nil, nil, errInvalidLocation + } + newURL := c.baseURL.ResolveReference(rerr.Location) + return newURL, response, nil + } + // If permanent redirect response is returned, follow it + if maxRedirects > 0 && rerr.StatusCode == http.StatusMovedPermanently { + if rerr.Location == nil { + return nil, nil, errInvalidLocation + } + newURL := c.baseURL.ResolveReference(rerr.Location) + // Refuse to follow a permanent redirect to a different host: + // req.Clone preserves Authorization headers added by the auth + // transport, so a cross-host target would leak credentials. + if newURL.Host != c.baseURL.Host { + return nil, response, fmt.Errorf("refusing to follow cross-host redirect from %q to %q", c.baseURL.Host, newURL.Host) + } + newRequest := req.Clone(req.Context()) + newRequest.URL = newURL + return c.bareDoUntilFound(newRequest, maxRedirects-1) } - if decErr != nil { - err = decErr + // If we reached the maximum amount of redirections, return an error + if maxRedirects <= 0 && rerr.StatusCode == http.StatusMovedPermanently { + return nil, response, fmt.Errorf("reached the maximum amount of redirections: %w", err) } + return nil, response, fmt.Errorf("unexpected redirection response: %w", err) } } - return response, err + // If we don't receive a redirection, forward the response and potential error + return nil, response, err +} + +// Do sends an API request and returns the API response. The API response is +// JSON decoded and stored in the value pointed to by v, or returned as an +// error if an API error has occurred. If v implements the io.Writer interface, +// the raw response body will be written to v, without attempting to first +// decode it. If v is nil, and no error happens, the response is returned as is. +// If rate limit is exceeded and reset time is in the future, Do returns +// *RateLimitError immediately without making a network API call. +func (c *Client) Do(req *http.Request, v any) (*Response, error) { + resp, err := c.BareDo(req) + if err != nil { + return resp, err + } + defer resp.Body.Close() + + switch v := v.(type) { + case nil: + case io.Writer: + _, err = io.Copy(v, resp.Body) + default: + decErr := json.NewDecoder(resp.Body).Decode(v) + if decErr == io.EOF { + decErr = nil // ignore EOF errors caused by empty response body + } + if decErr != nil { + err = decErr + } + } + return resp, err } // checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from // current client state in order to quickly check if *RateLimitError can be immediately returned // from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily. // Otherwise it returns nil, and Client.Do should proceed normally. -func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) *RateLimitError { +func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory RateLimitCategory) *RateLimitError { + ctx := req.Context() + c.rateMu.Lock() rate := c.rateLimits[rateLimitCategory] c.rateMu.Unlock() @@ -591,8 +1435,20 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat StatusCode: http.StatusForbidden, Request: req, Header: make(http.Header), - Body: ioutil.NopCloser(strings.NewReader("")), + Body: io.NopCloser(strings.NewReader("")), + } + + if ctx.Value(SleepUntilPrimaryRateLimitResetWhenRateLimited) != nil { + if err := sleepUntilResetWithBuffer(ctx, rate.Reset.Time); err == nil { + return nil + } + return &RateLimitError{ + Rate: rate, + Response: resp, + Message: fmt.Sprintf("Context cancelled while waiting for rate limit to reset until %v, not making remote request.", rate.Reset.Time), + } } + return &RateLimitError{ Rate: rate, Response: resp, @@ -603,32 +1459,131 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat return nil } +// checkSecondaryRateLimitBeforeDo does not make any network calls, but uses existing knowledge from +// current client state in order to quickly check if *AbuseRateLimitError can be immediately returned +// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily. +// Otherwise it returns nil, and Client.Do should proceed normally. +func (c *Client) checkSecondaryRateLimitBeforeDo(req *http.Request) *AbuseRateLimitError { + c.rateMu.Lock() + secondary := c.secondaryRateLimitReset + c.rateMu.Unlock() + if !secondary.IsZero() && time.Now().Before(secondary) { + // Create a fake response. + resp := &http.Response{ + Status: http.StatusText(http.StatusForbidden), + StatusCode: http.StatusForbidden, + Request: req, + Header: make(http.Header), + Body: io.NopCloser(strings.NewReader("")), + } + + retryAfter := time.Until(secondary) + return &AbuseRateLimitError{ + Response: resp, + Message: fmt.Sprintf("API secondary rate limit exceeded until %v, not making remote request.", secondary), + RetryAfter: &retryAfter, + } + } + + return nil +} + +// compareHTTPResponse returns whether two http.Response objects are equal or not. +// Currently, only StatusCode is checked. This function is used when implementing the +// Is(error) bool interface for the custom error types in this package. +func compareHTTPResponse(r1, r2 *http.Response) bool { + if r1 == nil && r2 == nil { + return true + } + + if r1 != nil && r2 != nil { + return r1.StatusCode == r2.StatusCode + } + return false +} + /* An ErrorResponse reports one or more errors caused by an API request. -GitHub API docs: https://developer.github.com/v3/#client-errors +GitHub API docs: https://docs.github.com/rest?apiVersion=2022-11-28#client-errors */ type ErrorResponse struct { - Response *http.Response // HTTP response that caused this error + Response *http.Response `json:"-"` // HTTP response that caused this error Message string `json:"message"` // error message - Errors []Error `json:"errors"` // more detail on individual errors + //nolint:sliceofpointers + Errors []Error `json:"errors"` // more detail on individual errors // Block is only populated on certain types of errors such as code 451. - // See https://developer.github.com/changes/2016-03-17-the-451-status-code-is-now-supported/ - // for more information. - Block *struct { - Reason string `json:"reason,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - } `json:"block,omitempty"` + Block *ErrorBlock `json:"block,omitempty"` // Most errors will also include a documentation_url field pointing // to some content that might help you resolve the error, see - // https://developer.github.com/v3/#client-errors + // https://docs.github.com/rest?apiVersion=2022-11-28#client-errors DocumentationURL string `json:"documentation_url,omitempty"` } +// ErrorBlock contains a further explanation for the reason of an error. +// See https://developer.github.com/changes/2016-03-17-the-451-status-code-is-now-supported/ +// for more information. +type ErrorBlock struct { + Reason string `json:"reason,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + func (r *ErrorResponse) Error() string { - return fmt.Sprintf("%v %v: %d %v %+v", - r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), - r.Response.StatusCode, r.Message, r.Errors) + if r.Response != nil && r.Response.Request != nil { + return fmt.Sprintf("%v %v: %v %v %+v", + r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), + r.Response.StatusCode, r.Message, r.Errors) + } + + if r.Response != nil { + return fmt.Sprintf("%v %v %+v", r.Response.StatusCode, r.Message, r.Errors) + } + + return fmt.Sprintf("%v %+v", r.Message, r.Errors) +} + +// Is returns whether the provided error equals this error. +func (r *ErrorResponse) Is(target error) bool { + var v *ErrorResponse + if !errors.As(target, &v) { + return false + } + + if r.Message != v.Message || (r.DocumentationURL != v.DocumentationURL) || + !compareHTTPResponse(r.Response, v.Response) { + return false + } + + // Compare Errors. + if len(r.Errors) != len(v.Errors) { + return false + } + for idx := range r.Errors { + if r.Errors[idx] != v.Errors[idx] { + return false + } + } + + // Compare Block. + if (r.Block != nil && v.Block == nil) || (r.Block == nil && v.Block != nil) { + return false + } + if r.Block != nil && v.Block != nil { + if r.Block.Reason != v.Block.Reason { + return false + } + if (r.Block.CreatedAt != nil && v.Block.CreatedAt == nil) || (r.Block.CreatedAt == + nil && v.Block.CreatedAt != nil) { + return false + } + if r.Block.CreatedAt != nil && v.Block.CreatedAt != nil { + if *(r.Block.CreatedAt) != *(v.Block.CreatedAt) { + return false + } + } + } + + return true } // TwoFactorAuthError occurs when using HTTP Basic Authentication for a user @@ -639,7 +1594,7 @@ type TwoFactorAuthError ErrorResponse func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() } // RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit -// remaining value of 0, and error message starts with "API rate limit exceeded for ". +// remaining value of 0. type RateLimitError struct { Rate Rate // Rate specifies last known rate limit for the client Response *http.Response // HTTP response that caused this error @@ -647,11 +1602,23 @@ type RateLimitError struct { } func (r *RateLimitError) Error() string { - return fmt.Sprintf("%v %v: %d %v %v", + return fmt.Sprintf("%v %v: %v %v %v", r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), r.Response.StatusCode, r.Message, formatRateReset(time.Until(r.Rate.Reset.Time))) } +// Is returns whether the provided error equals this error. +func (r *RateLimitError) Is(target error) bool { + var v *RateLimitError + if !errors.As(target, &v) { + return false + } + + return r.Rate == v.Rate && + r.Message == v.Message && + compareHTTPResponse(r.Response, v.Response) +} + // AcceptedError occurs when GitHub returns 202 Accepted response with an // empty body, which means a job was scheduled on the GitHub side to process // the information needed and cache it. @@ -667,8 +1634,17 @@ func (*AcceptedError) Error() string { return "job scheduled on GitHub side; try again later" } +// Is returns whether the provided error equals this error. +func (ae *AcceptedError) Is(target error) bool { + var v *AcceptedError + if !errors.As(target, &v) { + return false + } + return bytes.Equal(ae.Raw, v.Raw) +} + // AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the -// "documentation_url" field value equal to "https://developer.github.com/v3/#abuse-rate-limits". +// "documentation_url" field value equal to "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits". type AbuseRateLimitError struct { Response *http.Response // HTTP response that caused this error Message string `json:"message"` // error message @@ -680,20 +1656,85 @@ type AbuseRateLimitError struct { } func (r *AbuseRateLimitError) Error() string { - return fmt.Sprintf("%v %v: %d %v", + retryInfo := "" + if r.RetryAfter != nil && *r.RetryAfter > 0 { + retryInfo = fmt.Sprintf(" [retry after %v]", r.RetryAfter.Round(time.Second)) + } + return fmt.Sprintf("%v %v: %v %v%v", + r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), + r.Response.StatusCode, r.Message, retryInfo) +} + +// Is returns whether the provided error equals this error. +func (r *AbuseRateLimitError) Is(target error) bool { + var v *AbuseRateLimitError + if !errors.As(target, &v) { + return false + } + + return r.Message == v.Message && + equalDurationPtr(r.RetryAfter, v.RetryAfter) && + compareHTTPResponse(r.Response, v.Response) +} + +func equalDurationPtr(a, b *time.Duration) bool { + if a == nil || b == nil { + return a == b + } + return *a == *b +} + +// RedirectionError represents a response that returned a redirect status code: +// +// 301 (Moved Permanently) +// 302 (Found) +// 303 (See Other) +// 307 (Temporary Redirect) +// 308 (Permanent Redirect) +// +// If there was a valid Location header included, it will be parsed to a URL. You should use +// `BaseURL.ResolveReference()` to enrich it with the correct hostname where needed. +type RedirectionError struct { + Response *http.Response // HTTP response that caused this error + StatusCode int + Location *url.URL // location header of the redirection if present +} + +func (r *RedirectionError) Error() string { + return fmt.Sprintf("%v %v: %v location %v", r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), - r.Response.StatusCode, r.Message) + r.StatusCode, sanitizeURL(r.Location)) +} + +// Is returns whether the provided error equals this error. +func (r *RedirectionError) Is(target error) bool { + var v *RedirectionError + if !errors.As(target, &v) { + return false + } + + return r.StatusCode == v.StatusCode && + (r.Location == v.Location || // either both locations are nil or exactly the same pointer + r.Location != nil && v.Location != nil && r.Location.String() == v.Location.String()) // or they are both not nil and marshaled identically } -// sanitizeURL redacts the client_secret parameter from the URL which may be +var sensitiveParams = []string{"client_secret", "access_token", "token"} + +// sanitizeURL redacts sensitive parameters from the URL which may be // exposed to the user. func sanitizeURL(uri *url.URL) *url.URL { if uri == nil { return nil } params := uri.Query() - if len(params.Get("client_secret")) > 0 { - params.Set("client_secret", "REDACTED") + var redacted bool + for _, p := range sensitiveParams { + if len(params.Get(p)) > 0 { + params.Set(p, "REDACTED") + redacted = true + } + } + if redacted { uri.RawQuery = params.Encode() } return uri @@ -703,19 +1744,23 @@ func sanitizeURL(uri *url.URL) *url.URL { An Error reports more details on an individual error in an ErrorResponse. These are the possible validation error codes: - missing: - resource does not exist - missing_field: - a required field on a resource has not been set - invalid: - the formatting of a field is invalid - already_exists: - another resource has the same valid as this field - custom: - some resources return this (e.g. github.User.CreateKey()), additional - information is set in the Message field of the Error - -GitHub API docs: https://developer.github.com/v3/#client-errors + missing: + resource does not exist + missing_field: + a required field on a resource has not been set + invalid: + the formatting of a field is invalid + already_exists: + another resource has the same valid as this field + custom: + some resources return this (e.g. github.User.CreateKey()), additional + information is set in the Message field of the Error + +GitHub error responses structure are often undocumented and inconsistent. +Sometimes error is just a simple string (Issue #540). +In such cases, Message represents an error message as a workaround. + +GitHub API docs: https://docs.github.com/rest?apiVersion=2022-11-28#client-errors */ type Error struct { Resource string `json:"resource"` // resource on which the error occurred @@ -729,16 +1774,25 @@ func (e *Error) Error() string { e.Code, e.Field, e.Resource) } +// UnmarshalJSON implements the json.Unmarshaler interface. +func (e *Error) UnmarshalJSON(data []byte) error { + type aliasError Error // avoid infinite recursion by using type alias. + if err := json.Unmarshal(data, (*aliasError)(e)); err != nil { + return json.Unmarshal(data, &e.Message) // data can be json string. + } + return nil +} + // CheckResponse checks the API response for errors, and returns them if // present. A response is considered an error if it has a status code outside // the 200 range or equal to 202 Accepted. -// API error responses are expected to have either no response -// body, or a JSON response body that maps to ErrorResponse. Any other -// response body will be silently ignored. +// API error responses are expected to have response +// body, and a JSON response body that maps to [ErrorResponse]. // -// The error type will be *RateLimitError for rate limit exceeded errors, -// *AcceptedError for 202 Accepted status codes, -// and *TwoFactorAuthError for two-factor authentication errors. +// The error type will be *[RateLimitError] for rate limit exceeded errors, +// *[AcceptedError] for 202 Accepted status codes, +// *[TwoFactorAuthError] for two-factor authentication errors, +// and *[RedirectionError] for redirect status codes (only happens when ignoring redirections). func CheckResponse(r *http.Response) error { if r.StatusCode == http.StatusAccepted { return &AcceptedError{} @@ -746,34 +1800,64 @@ func CheckResponse(r *http.Response) error { if c := r.StatusCode; 200 <= c && c <= 299 { return nil } + errorResponse := &ErrorResponse{Response: r} - data, err := ioutil.ReadAll(r.Body) + data, err := io.ReadAll(io.LimitReader(r.Body, maxErrorBodySize)) if err == nil && data != nil { - json.Unmarshal(data, errorResponse) + err = json.Unmarshal(data, errorResponse) + if err != nil { + // reset the response as if this never happened + errorResponse = &ErrorResponse{Response: r} + } } + // Re-populate error response body because GitHub error responses are often + // undocumented and inconsistent. + // Issue #1136, #540. + r.Body = io.NopCloser(bytes.NewBuffer(data)) switch { case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"): return (*TwoFactorAuthError)(errorResponse) - case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0" && strings.HasPrefix(errorResponse.Message, "API rate limit exceeded for "): + // Primary rate limit exceeded: GitHub returns 403 or 429 with X-RateLimit-Remaining: 0 + // See: https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28 + case (r.StatusCode == http.StatusForbidden || r.StatusCode == http.StatusTooManyRequests) && + r.Header.Get(HeaderRateRemaining) == "0": return &RateLimitError{ Rate: parseRate(r), Response: errorResponse.Response, Message: errorResponse.Message, } - case r.StatusCode == http.StatusForbidden && strings.HasSuffix(errorResponse.DocumentationURL, "/v3/#abuse-rate-limits"): + // Secondary rate limit exceeded: GitHub returns 403 or 429 with specific documentation_url + // See: https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits + case (r.StatusCode == http.StatusForbidden || r.StatusCode == http.StatusTooManyRequests) && + (strings.HasSuffix(errorResponse.DocumentationURL, "#abuse-rate-limits") || + strings.HasSuffix(errorResponse.DocumentationURL, "secondary-rate-limits")): abuseRateLimitError := &AbuseRateLimitError{ Response: errorResponse.Response, Message: errorResponse.Message, } - if v := r.Header["Retry-After"]; len(v) > 0 { - // According to GitHub support, the "Retry-After" header value will be - // an integer which represents the number of seconds that one should - // wait before resuming making requests. - retryAfterSeconds, _ := strconv.ParseInt(v[0], 10, 64) // Error handling is noop. - retryAfter := time.Duration(retryAfterSeconds) * time.Second - abuseRateLimitError.RetryAfter = &retryAfter + if retryAfter := parseSecondaryRate(r); retryAfter != nil { + abuseRateLimitError.RetryAfter = retryAfter } return abuseRateLimitError + // Check that the status code is a redirection and return a sentinel error that can be used to handle special cases + // where 302 is considered a successful result. + // This should never happen with the default `CheckRedirect`, because it would return a `url.Error` that should be handled upstream. + case r.StatusCode == http.StatusMovedPermanently || + r.StatusCode == http.StatusFound || + r.StatusCode == http.StatusSeeOther || + r.StatusCode == http.StatusTemporaryRedirect || + r.StatusCode == http.StatusPermanentRedirect: + + locationStr := r.Header.Get("Location") + var location *url.URL + if locationStr != "" { + location, _ = url.Parse(locationStr) + } + return &RedirectionError{ + Response: errorResponse.Response, + StatusCode: r.StatusCode, + Location: location, + } default: return errorResponse } @@ -789,7 +1873,8 @@ func parseBoolResponse(err error) (bool, error) { return true, nil } - if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound { + var rerr *ErrorResponse + if errors.As(err, &rerr) && rerr.Response.StatusCode == http.StatusNotFound { // Simply false. In this one case, we do not pass the error through. return false, nil } @@ -798,89 +1883,102 @@ func parseBoolResponse(err error) (bool, error) { return false, err } -// Rate represents the rate limit for the current client. -type Rate struct { - // The number of requests per hour the client is currently limited to. - Limit int `json:"limit"` - - // The number of remaining requests the client can make this hour. - Remaining int `json:"remaining"` - - // The time at which the current rate limit will reset. - Reset Timestamp `json:"reset"` -} - -func (r Rate) String() string { - return Stringify(r) -} - -// RateLimits represents the rate limits for the current client. -type RateLimits struct { - // The rate limit for non-search API requests. Unauthenticated - // requests are limited to 60 per hour. Authenticated requests are - // limited to 5,000 per hour. - // - // GitHub API docs: https://developer.github.com/v3/#rate-limiting - Core *Rate `json:"core"` - - // The rate limit for search API requests. Unauthenticated requests - // are limited to 10 requests per minutes. Authenticated requests are - // limited to 30 per minute. - // - // GitHub API docs: https://developer.github.com/v3/search/#rate-limit - Search *Rate `json:"search"` -} - -func (r RateLimits) String() string { - return Stringify(r) -} - -type rateLimitCategory uint8 +// RateLimitCategory represents the enumeration of rate limit categories. +type RateLimitCategory uint8 const ( - coreCategory rateLimitCategory = iota - searchCategory - - categories // An array of this length will be able to contain all rate limit categories. + CoreCategory RateLimitCategory = iota + SearchCategory + GraphqlCategory + IntegrationManifestCategory + SourceImportCategory + CodeScanningUploadCategory + ActionsRunnerRegistrationCategory + ScimCategory + DependencySnapshotsCategory + CodeSearchCategory + AuditLogCategory + DependencySBOMCategory + + Categories // An array of this length will be able to contain all rate limit categories. ) -// category returns the rate limit category of the endpoint, determined by Request.URL.Path. -func category(path string) rateLimitCategory { +// GetRateLimitCategory returns the rate limit RateLimitCategory of the endpoint, determined by HTTP method and Request.URL.Path. +func GetRateLimitCategory(method, path string) RateLimitCategory { switch { + // https://docs.github.com/rest/rate-limit?apiVersion=2022-11-28#about-rate-limits default: - return coreCategory + // NOTE: coreCategory is returned for actionsRunnerRegistrationCategory too, + // because no API found for this category. + return CoreCategory + + // https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-code + case strings.HasPrefix(path, "/search/code") && + method == "GET": + return CodeSearchCategory + case strings.HasPrefix(path, "/search/"): - return searchCategory + return SearchCategory + case path == "/graphql": + return GraphqlCategory + case strings.HasPrefix(path, "/app-manifests/") && + strings.HasSuffix(path, "/conversions") && + method == "POST": + return IntegrationManifestCategory + + // https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#start-an-import + case strings.HasPrefix(path, "/repos/") && + strings.HasSuffix(path, "/import") && + method == "PUT": + return SourceImportCategory + + // https://docs.github.com/rest/code-scanning?apiVersion=2022-11-28#upload-an-analysis-as-sarif-data + case strings.HasSuffix(path, "/code-scanning/sarifs"): + return CodeScanningUploadCategory + + // https://docs.github.com/enterprise-cloud@latest/rest/scim?apiVersion=2022-11-28 + case strings.HasPrefix(path, "/scim/"): + return ScimCategory + + // https://docs.github.com/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository + case strings.HasPrefix(path, "/repos/") && + strings.HasSuffix(path, "/dependency-graph/snapshots") && + method == "POST": + return DependencySnapshotsCategory + + // https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#get-the-audit-log-for-an-organization + case strings.HasSuffix(path, "/audit-log"): + return AuditLogCategory + + // https://docs.github.com/rest/dependency-graph/sboms?apiVersion=2022-11-28#export-a-software-bill-of-materials-sbom-for-a-repository + case strings.HasPrefix(path, "/repos/") && + strings.HasSuffix(path, "/dependency-graph/sbom"): + return DependencySBOMCategory } } // RateLimits returns the rate limits for the current client. +// +// Deprecated: Use RateLimitService.Get instead. func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error) { - req, err := c.NewRequest("GET", "rate_limit", nil) - if err != nil { - return nil, nil, err - } + return c.RateLimit.Get(ctx) +} - response := new(struct { - Resources *RateLimits `json:"resources"` - }) - resp, err := c.Do(ctx, req, response) - if err != nil { - return nil, nil, err - } +func setCredentialsAsHeaders(req *http.Request, id, secret string) *http.Request { + // To set extra headers, we must make a copy of the Request so + // that we don't modify the Request we were given. This is required by the + // specification of http.RoundTripper. + // + // Since we are going to modify only req.Header here, we only need a deep copy + // of req.Header. + convertedRequest := *req + convertedRequest.Header = make(http.Header, len(req.Header)) - if response.Resources != nil { - c.rateMu.Lock() - if response.Resources.Core != nil { - c.rateLimits[coreCategory] = *response.Resources.Core - } - if response.Resources.Search != nil { - c.rateLimits[searchCategory] = *response.Resources.Search - } - c.rateMu.Unlock() + for k, s := range req.Header { + convertedRequest.Header[k] = append([]string(nil), s...) } - - return response.Resources, resp, nil + convertedRequest.SetBasicAuth(id, secret) + return &convertedRequest } /* @@ -893,11 +1991,11 @@ that need to use a higher rate limit associated with your OAuth application. } client := github.NewClient(t.Client()) -This will append the querystring params client_id=xxx&client_secret=yyy to all -requests. +This will add the client id and secret as a base64-encoded string in the format +ClientID:ClientSecret and apply it as an "Authorization": "Basic" header. -See https://developer.github.com/v3/#unauthenticated-rate-limited-requests for -more information. +See https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#primary-rate-limit-for-oauth-apps +for more information. */ type UnauthenticatedRateLimitedTransport struct { // ClientID is the GitHub OAuth client ID of the current application, which @@ -923,22 +2021,7 @@ func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*htt return nil, errors.New("t.ClientSecret is empty") } - // To set extra querystring params, we must make a copy of the Request so - // that we don't modify the Request we were given. This is required by the - // specification of http.RoundTripper. - // - // Since we are going to modify only req.URL here, we only need a deep copy - // of req.URL. - req2 := new(http.Request) - *req2 = *req - req2.URL = new(url.URL) - *req2.URL = *req.URL - - q := req2.URL.Query() - q.Set("client_id", t.ClientID) - q.Set("client_secret", t.ClientSecret) - req2.URL.RawQuery = q.Encode() - + req2 := setCredentialsAsHeaders(req, t.ClientID, t.ClientSecret) // Make the HTTP request. return t.transport().RoundTrip(req2) } @@ -972,20 +2055,7 @@ type BasicAuthTransport struct { // RoundTrip implements the RoundTripper interface. func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { - // To set extra headers, we must make a copy of the Request so - // that we don't modify the Request we were given. This is required by the - // specification of http.RoundTripper. - // - // Since we are going to modify only req.Header here, we only need a deep copy - // of req.Header. - req2 := new(http.Request) - *req2 = *req - req2.Header = make(http.Header, len(req.Header)) - for k, s := range req.Header { - req2.Header[k] = append([]string(nil), s...) - } - - req2.SetBasicAuth(t.Username, t.Password) + req2 := setCredentialsAsHeaders(req, t.Username, t.Password) if t.OTP != "" { req2.Header.Set(headerOTP, t.OTP) } @@ -1019,9 +2089,9 @@ func formatRateReset(d time.Duration) string { var timeString string if minutes > 0 { - timeString = fmt.Sprintf("%dm%02ds", minutes, seconds) + timeString = fmt.Sprintf("%vm%02ds", minutes, seconds) } else { - timeString = fmt.Sprintf("%ds", seconds) + timeString = fmt.Sprintf("%vs", seconds) } if isNegative { @@ -1030,18 +2100,128 @@ func formatRateReset(d time.Duration) string { return fmt.Sprintf("[rate reset in %v]", timeString) } +func sleepUntilResetWithBuffer(ctx context.Context, reset time.Time) error { + buffer := time.Second + timer := time.NewTimer(time.Until(reset) + buffer) + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return ctx.Err() + case <-timer.C: + } + return nil +} + +// When using roundTripWithOptionalFollowRedirect, note that it +// is the responsibility of the caller to close the response body. +func (c *Client) roundTripWithOptionalFollowRedirect(ctx context.Context, u string, maxRedirects int, opts ...RequestOption) (*http.Response, error) { + req, err := c.NewRequest(ctx, "GET", u, nil, opts...) + if err != nil { + return nil, err + } + + var resp *http.Response + // Use http.DefaultTransport if no custom Transport is configured + if c.client.Transport == nil { + resp, err = http.DefaultTransport.RoundTrip(req) + } else { + resp, err = c.client.Transport.RoundTrip(req) + } + if err != nil { + return nil, err + } + + // If redirect response is returned, follow it + if maxRedirects > 0 && resp.StatusCode == http.StatusMovedPermanently { + _ = resp.Body.Close() + u = resp.Header.Get("Location") + if err := c.checkRedirectHost(u); err != nil { + return nil, err + } + resp, err = c.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects-1, opts...) + } + return resp, err +} + +// checkRedirectHost returns an error if the redirect target is on a different +// host than the client's configured BaseURL. This prevents credentials attached +// by the auth transport from being sent to an attacker-controlled host when a +// compromised or malicious API response returns a cross-origin Location header. +// An empty Location is also rejected. +func (c *Client) checkRedirectHost(location string) error { + if location == "" { + return errInvalidLocation + } + target, err := url.Parse(location) + if err != nil { + return fmt.Errorf("invalid redirect location %q: %w", location, err) + } + // Resolve relative locations against BaseURL so relative paths are allowed. + target = c.baseURL.ResolveReference(target) + if target.Host != c.baseURL.Host { + return fmt.Errorf("refusing to follow cross-host redirect from %q to %q", c.baseURL.Host, target.Host) + } + return nil +} + +// Ptr is a helper routine that allocates a new T value +// to store v and returns a pointer to it. +func Ptr[T any](v T) *T { + return &v +} + // Bool is a helper routine that allocates a new bool value // to store v and returns a pointer to it. -func Bool(v bool) *bool { return &v } +// +// Deprecated: use Ptr instead. +// +//go:fix inline +func Bool(v bool) *bool { return Ptr(v) } // Int is a helper routine that allocates a new int value // to store v and returns a pointer to it. -func Int(v int) *int { return &v } +// +// Deprecated: use Ptr instead. +// +//go:fix inline +func Int(v int) *int { return Ptr(v) } // Int64 is a helper routine that allocates a new int64 value // to store v and returns a pointer to it. -func Int64(v int64) *int64 { return &v } +// +// Deprecated: use Ptr instead. +// +//go:fix inline +func Int64(v int64) *int64 { return Ptr(v) } // String is a helper routine that allocates a new string value // to store v and returns a pointer to it. -func String(v string) *string { return &v } +// +// Deprecated: use Ptr instead. +// +//go:fix inline +func String(v string) *string { return Ptr(v) } + +// roundTripperFunc creates a RoundTripper (transport). +type roundTripperFunc func(*http.Request) (*http.Response, error) + +func (fn roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { + return fn(r) +} + +var runIDFromURLRE = regexp.MustCompile(`repos/.*/actions/runs/(\d+)/deployment_protection_rule$`) + +// GetRunID is a Helper Function used to extract the workflow RunID from the *DeploymentProtectionRuleEvent.DeploymentCallBackURL. +func (e *DeploymentProtectionRuleEvent) GetRunID() (int64, error) { + match := runIDFromURLRE.FindStringSubmatch(*e.DeploymentCallbackURL) + if len(match) != 2 { + return -1, errors.New("no match") + } + runID, err := strconv.ParseInt(match[1], 10, 64) + if err != nil { + return -1, err + } + return runID, nil +} diff --git a/github/github_test.go b/github/github_test.go index 8dd75e4d7e6..ae585cede4d 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -9,17 +9,24 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" - "io/ioutil" + "io" + "net" "net/http" "net/http/httptest" "net/url" "os" - "path" + "path/filepath" "reflect" + "strconv" "strings" + "sync/atomic" "testing" + "testing/synctest" "time" + + "github.com/google/go-cmp/cmp" ) const ( @@ -28,10 +35,42 @@ const ( baseURLPath = "/api-v3" ) +// raceSafeTestConn wraps a net.Conn to hide concrete connection types such as *net.TCPConn. +// +// Go's HTTP transport may enable OS-level sendfile optimizations when it sees a concrete +// TCP connection and an *os.File request body. Under the race detector on Windows, that +// specific optimized path can trigger a known data race in internal polling structures. +// Returning this wrapper from DialContext keeps behavior identical for tests while forcing +// the transport onto the generic copy path, which is stable under -race. +type raceSafeTestConn struct { + net.Conn +} + +// mustNewClient is a helper function that creates a new Client and fails the test if there is an error. +func mustNewClient(t *testing.T, opts ...ClientOptionsFunc) *Client { + t.Helper() + c, err := NewClient(opts...) + if err != nil { + t.Fatal(err) + } + return c +} + +// mustParseURL is a helper function that parses a URL and fails the test if there is an error. +func mustParseURL(t *testing.T, rawurl string) *url.URL { + t.Helper() + u, err := url.Parse(rawurl) + if err != nil { + t.Fatalf("Failed to parse URL %q: %v", rawurl, err) + } + return u +} + // setup sets up a test HTTP server along with a github.Client that is // configured to talk to that test server. Tests should register handlers on // mux which provide mock responses for the API method being tested. -func setup() (client *Client, mux *http.ServeMux, serverURL string, teardown func()) { +func setup(t *testing.T) (client *Client, mux *http.ServeMux, serverURL string) { + t.Helper() // mux is the HTTP request multiplexer used with the test server. mux = http.NewServeMux() @@ -53,44 +92,72 @@ func setup() (client *Client, mux *http.ServeMux, serverURL string, teardown fun // server is a test HTTP server used to provide mock API responses. server := httptest.NewServer(apiHandler) + testDialer := &net.Dialer{Timeout: 30 * time.Second} + + // Create a custom transport with isolated connection pool + transport := &http.Transport{ + // Wrap dialed connections so transport does not take concrete-TCP sendfile fast paths + // that can race under Windows + -race in upload tests. + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + conn, err := testDialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + return &raceSafeTestConn{Conn: conn}, nil + }, + // Controls connection reuse - false allows reuse, true forces new connections for each request + DisableKeepAlives: false, + // Maximum concurrent connections per host (active + idle) + MaxConnsPerHost: 10, + // Maximum idle connections maintained per host for reuse + MaxIdleConnsPerHost: 5, + // Maximum total idle connections across all hosts + MaxIdleConns: 20, + // How long an idle connection remains in the pool before being closed + IdleConnTimeout: 20 * time.Second, + } + + // Create HTTP client with the isolated transport + httpClient := &http.Client{ + Transport: transport, + Timeout: 30 * time.Second, + } // client is the GitHub client being tested and is // configured to use test server. - client = NewClient(nil) + client = mustNewClient(t, WithHTTPClient(httpClient)) + url, _ := url.Parse(server.URL + baseURLPath + "/") - client.BaseURL = url - client.UploadURL = url + client.baseURL = url + client.uploadURL = url + + t.Cleanup(server.Close) - return client, mux, server.URL, server.Close + return client, mux, server.URL } // openTestFile creates a new file with the given name and content for testing. // In order to ensure the exact file name, this function will create a new temp -// directory, and create the file in that directory. It is the caller's -// responsibility to remove the directory and its contents when no longer needed. -func openTestFile(name, content string) (file *os.File, dir string, err error) { - dir, err = ioutil.TempDir("", "go-github") +// directory, and create the file in that directory. The file is automatically +// cleaned up after the test. +func openTestFile(t *testing.T, name, content string) *os.File { + t.Helper() + fname := filepath.Join(t.TempDir(), name) + err := os.WriteFile(fname, []byte(content), 0o600) if err != nil { - return nil, dir, err + t.Fatal(err) } - - file, err = os.OpenFile(path.Join(dir, name), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + file, err := os.Open(fname) if err != nil { - return nil, dir, err + t.Fatal(err) } - fmt.Fprint(file, content) - - // close and re-open the file to keep file.Stat() happy - file.Close() - file, err = os.Open(file.Name()) - if err != nil { - return nil, dir, err - } + t.Cleanup(func() { file.Close() }) - return file, dir, err + return file } func testMethod(t *testing.T, r *http.Request, want string) { + t.Helper() if got := r.Method; got != want { t.Errorf("Request method: %v, want %v", got, want) } @@ -98,754 +165,4014 @@ func testMethod(t *testing.T, r *http.Request, want string) { type values map[string]string -func testFormValues(t *testing.T, r *http.Request, values values) { - want := url.Values{} - for k, v := range values { - want.Set(k, v) +// testFormValues checks that the request form values match the expected values. +// It expects exactly one value per key. +func testFormValues(t *testing.T, r *http.Request, want values) { + t.Helper() + + wantValues := url.Values{} + for k, v := range want { + wantValues.Add(k, v) + } + + assertNilError(t, r.ParseForm()) + if got := r.Form; !cmp.Equal(got, wantValues) { + t.Errorf("Request query parameters: %v, want %v", got, wantValues) } +} - r.ParseForm() - if got := r.Form; !reflect.DeepEqual(got, want) { - t.Errorf("Request parameters: %v, want %v", got, want) +// testFormValuesList checks that the request form values match the expected values. +// It allows for multiple values per key. +func testFormValuesList(t *testing.T, r *http.Request, want url.Values) { + t.Helper() + + assertNilError(t, r.ParseForm()) + if got := r.Form; !cmp.Equal(got, want) { + t.Errorf("Request query parameters: %v, want %v", got, want) } } -func testHeader(t *testing.T, r *http.Request, header string, want string) { +func testHeader(t *testing.T, r *http.Request, header, want string) { + t.Helper() if got := r.Header.Get(header); got != want { t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want) } } func testURLParseError(t *testing.T, err error) { + t.Helper() if err == nil { - t.Errorf("Expected error to be returned") + t.Error("Expected error to be returned") } - if err, ok := err.(*url.Error); !ok || err.Op != "parse" { + var uerr *url.Error + if !errors.As(err, &uerr) || uerr.Op != "parse" { t.Errorf("Expected URL parse error, got %+v", err) } } -func testBody(t *testing.T, r *http.Request, want string) { - b, err := ioutil.ReadAll(r.Body) +func testPlainBody(t *testing.T, r *http.Request, want string) { + t.Helper() + b, err := io.ReadAll(r.Body) if err != nil { t.Errorf("Error reading request body: %v", err) } if got := string(b); got != want { - t.Errorf("request Body is %s, want %s", got, want) + t.Errorf("request Body is %v, want %v", got, want) } } -// Helper function to test that a value is marshalled to JSON as expected. -func testJSONMarshal(t *testing.T, v interface{}, want string) { - j, err := json.Marshal(v) +func testJSONBody[T any](t *testing.T, r *http.Request, want T, opts ...cmp.Option) { + t.Helper() + b, err := io.ReadAll(r.Body) if err != nil { - t.Errorf("Unable to marshal JSON for %v", v) + t.Errorf("Error reading request body: %v", err) } - w := new(bytes.Buffer) - err = json.Compact(w, []byte(want)) - if err != nil { - t.Errorf("String is not valid json: %s", want) - } + var got T - if w.String() != string(j) { - t.Errorf("json.Marshal(%q) returned %s, want %s", v, j, w) + if err := json.Unmarshal(b, &got); err != nil { + t.Errorf("Error unmarshaling request body JSON: %v", err) } - // now go the other direction and make sure things unmarshal as expected - u := reflect.ValueOf(v).Interface() - if err := json.Unmarshal([]byte(want), u); err != nil { - t.Errorf("Unable to unmarshal JSON for %v: %v", want, err) + if diff := cmp.Diff(want, got, opts...); diff != "" { + t.Errorf("request JSON body mismatch (-want +got):\n%v", diff) } +} - if !reflect.DeepEqual(v, u) { - t.Errorf("json.Unmarshal(%q) returned %s, want %s", want, u, v) - } +// testJSONMarshal tests both JSON marshaling and unmarshaling of a value by comparing +// the marshaled output with the expected JSON string. +// +// This is the recommended function for most use cases. +// It performs a round-trip test that ensures both marshaling (Go value to JSON) +// and unmarshaling (JSON to Go value) work correctly and produce semantically equivalent results. +func testJSONMarshal[T any](t *testing.T, v T, want string, opts ...cmp.Option) { + t.Helper() + + testJSONMarshalOnly(t, v, want) + testJSONUnmarshalOnly(t, v, want, opts...) } -func TestNewClient(t *testing.T) { - c := NewClient(nil) +// testJSONMarshalOnly tests JSON marshaling by comparing the marshaled output with the expected JSON string. +// +// This function compares JSON by unmarshaling both values into any and using cmp.Diff. +// This means the comparison ignores: +// - White space differences +// - Key ordering in objects +// - Numeric type differences (e.g., int vs float with same value) +// +// In most cases, use testJSONMarshal instead. +// Only use this function in rare cases where you need to test marshaling behavior in isolation. +func testJSONMarshalOnly[T any](t *testing.T, v T, want string) { + t.Helper() - if got, want := c.BaseURL.String(), defaultBaseURL; got != want { - t.Errorf("NewClient BaseURL is %v, want %v", got, want) - } - if got, want := c.UserAgent, userAgent; got != want { - t.Errorf("NewClient UserAgent is %v, want %v", got, want) + got, err := json.Marshal(v) + if err != nil { + t.Fatalf("Unable to marshal got JSON for %#v: %v", v, err) } - c2 := NewClient(nil) - if c.client == c2.client { - t.Error("NewClient returned same http.Clients, but they should differ") + // Unmarshal both the marshaled output and expected JSON into any + // to enable semantic comparison that ignores formatting differences + var gotAny any + if err := json.Unmarshal(got, &gotAny); err != nil { + t.Fatalf("Unable to unmarshal got JSON %v: %v", got, err) } -} -func TestNewEnterpriseClient(t *testing.T) { - baseURL := "https://custom-url/" - uploadURL := "https://custom-upload-url/" - c, err := NewEnterpriseClient(baseURL, uploadURL, nil) - if err != nil { - t.Fatalf("NewEnterpriseClient returned unexpected error: %v", err) + var wantAny any + if err := json.Unmarshal([]byte(want), &wantAny); err != nil { + t.Fatalf("Unable to unmarshal want JSON %v: %v", want, err) } - if got, want := c.BaseURL.String(), baseURL; got != want { - t.Errorf("NewClient BaseURL is %v, want %v", got, want) - } - if got, want := c.UploadURL.String(), uploadURL; got != want { - t.Errorf("NewClient UploadURL is %v, want %v", got, want) + // Compare the semantic content + if diff := cmp.Diff(wantAny, gotAny); diff != "" { + t.Errorf("json.Marshal returned:\n%v\nwant:\n%v\ndiff:\n%v", got, want, diff) } } -func TestNewEnterpriseClient_addsTrailingSlashToURLs(t *testing.T) { - baseURL := "https://custom-url" - uploadURL := "https://custom-upload-url" - formattedBaseURL := baseURL + "/" - formattedUploadURL := uploadURL + "/" - - c, err := NewEnterpriseClient(baseURL, uploadURL, nil) - if err != nil { - t.Fatalf("NewEnterpriseClient returned unexpected error: %v", err) - } +// testJSONUnmarshalOnly tests JSON unmarshaling by parsing the JSON string +// and comparing the result with the expected value. +// +// In most cases, use testJSONMarshal instead. +// Only use this function in rare cases where you need to test unmarshaling behavior in isolation. +func testJSONUnmarshalOnly[T any](t *testing.T, want T, v string, opts ...cmp.Option) { + t.Helper() - if got, want := c.BaseURL.String(), formattedBaseURL; got != want { - t.Errorf("NewClient BaseURL is %v, want %v", got, want) - } - if got, want := c.UploadURL.String(), formattedUploadURL; got != want { - t.Errorf("NewClient UploadURL is %v, want %v", got, want) + var got T + if err := json.Unmarshal([]byte(v), &got); err != nil { + t.Fatalf("Unable to unmarshal JSON %v: %v", v, err) } -} -// Ensure that length of Client.rateLimits is the same as number of fields in RateLimits struct. -func TestClient_rateLimits(t *testing.T) { - if got, want := len(Client{}.rateLimits), reflect.TypeOf(RateLimits{}).NumField(); got != want { - t.Errorf("len(Client{}.rateLimits) is %v, want %v", got, want) + if diff := cmp.Diff(want, got, opts...); diff != "" { + t.Errorf("json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v", got, want, diff) } } -func TestRateLimits_String(t *testing.T) { - v := RateLimits{ - Core: &Rate{}, - Search: &Rate{}, +// Test how bad options are handled. Method f under test should +// return an error. +func testBadOptions(t *testing.T, methodName string, f func() error) { + t.Helper() + if methodName == "" { + t.Error("testBadOptions: must supply method methodName") } - want := `github.RateLimits{Core:github.Rate{Limit:0, Remaining:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}, Search:github.Rate{Limit:0, Remaining:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}}}` - if got := v.String(); got != want { - t.Errorf("RateLimits.String = %v, want %v", got, want) + if err := f(); err == nil { + t.Errorf("bad options %v err = nil, want error", methodName) } } -func TestNewRequest(t *testing.T) { - c := NewClient(nil) - - inURL, outURL := "/foo", defaultBaseURL+"foo" - inBody, outBody := &User{Login: String("l")}, `{"login":"l"}`+"\n" - req, _ := c.NewRequest("GET", inURL, inBody) +// Test function under NewRequest failure and then s.client.Do failure. +// Method f should be a regular call that would normally succeed, but +// should return an error when NewRequest or s.client.Do fails. +func testNewRequestAndDoFailure(t *testing.T, methodName string, client *Client, f func() (*Response, error)) { + testNewRequestAndDoFailureCategory(t, methodName, client, CoreCategory, f) +} - // test that relative URL was expanded - if got, want := req.URL.String(), outURL; got != want { - t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want) +// testNewRequestAndDoFailureCategory works Like testNewRequestAndDoFailure, but allows setting the category. +func testNewRequestAndDoFailureCategory(t *testing.T, methodName string, client *Client, category RateLimitCategory, f func() (*Response, error)) { + t.Helper() + if methodName == "" { + t.Error("testNewRequestAndDoFailure: must supply method methodName") } - // test that body was JSON encoded - body, _ := ioutil.ReadAll(req.Body) - if got, want := string(body), outBody; got != want { - t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want) + client.baseURL.Path = "" + resp, err := f() + if resp != nil { + t.Errorf("client.BaseURL.Path='' %v resp = %#v, want nil", methodName, resp) + } + if err == nil { + t.Errorf("client.BaseURL.Path='' %v err = nil, want error", methodName) } - // test that default user-agent is attached to the request - if got, want := req.Header.Get("User-Agent"), c.UserAgent; got != want { - t.Errorf("NewRequest() User-Agent is %v, want %v", got, want) + client.baseURL.Path = "/api-v3/" + client.rateLimits[category].Reset.Time = time.Now().Add(10 * time.Minute) + resp, err = f() + if client.disableRateLimitCheck { + return + } + if bypass := resp.Request.Context().Value(BypassRateLimitCheck); bypass != nil { + return + } + if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { + if resp != nil { + t.Errorf("rate.Reset.Time > now %v resp = %#v, want StatusCode=%v", methodName, resp.Response, want) + } else { + t.Errorf("rate.Reset.Time > now %v resp = nil, want StatusCode=%v", methodName, want) + } + } + if err == nil { + t.Errorf("rate.Reset.Time > now %v err = nil, want error", methodName) } } -func TestNewRequest_invalidJSON(t *testing.T) { - c := NewClient(nil) +// Test that all error response types contain the status code. +func testErrorResponseForStatusCode(t *testing.T, code int) { + t.Helper() + client, mux, _ := setup(t) - type T struct { - A map[interface{}]interface{} - } - _, err := c.NewRequest("GET", ".", &T{}) + mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(code) + }) - if err == nil { - t.Error("Expected error to be returned.") - } - if err, ok := err.(*json.UnsupportedTypeError); !ok { - t.Errorf("Expected a JSON error; got %#v.", err) + _, _, err := client.Repositories.ListHooks(t.Context(), "o", "r", nil) + + var abuseErr *AbuseRateLimitError + switch { + case errors.As(err, new(*ErrorResponse)): + case errors.As(err, new(*RateLimitError)): + case errors.As(err, &abuseErr): + if code != abuseErr.Response.StatusCode { + t.Error("Error response does not contain status code") + } + default: + t.Error("Unknown error response type") } } -func TestNewRequest_badURL(t *testing.T) { - c := NewClient(nil) - _, err := c.NewRequest("GET", ":", nil) - testURLParseError(t, err) +func assertNoDiff(t *testing.T, want, got any) { + t.Helper() + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("diff mismatch (-want +got):\n%v", diff) + } } -// ensure that no User-Agent header is set if the client's UserAgent is empty. -// This caused a problem with Google's internal http client. -func TestNewRequest_emptyUserAgent(t *testing.T) { - c := NewClient(nil) - c.UserAgent = "" - req, err := c.NewRequest("GET", ".", nil) +func assertNilError(t *testing.T, err error) { + t.Helper() if err != nil { - t.Fatalf("NewRequest returned unexpected error: %v", err) - } - if _, ok := req.Header["User-Agent"]; ok { - t.Fatal("constructed request contains unexpected User-Agent header") + t.Errorf("unexpected error: %v", err) } } -// If a nil body is passed to github.NewRequest, make sure that nil is also -// passed to http.NewRequest. In most cases, passing an io.Reader that returns -// no content is fine, since there is no difference between an HTTP request -// body that is an empty string versus one that is not set at all. However in -// certain cases, intermediate systems may treat these differently resulting in -// subtle errors. -func TestNewRequest_emptyBody(t *testing.T) { - c := NewClient(nil) - req, err := c.NewRequest("GET", ".", nil) - if err != nil { - t.Fatalf("NewRequest returned unexpected error: %v", err) - } - if req.Body != nil { - t.Fatalf("constructed request contains a non-nil Body") - } +func assertWrite(t *testing.T, w io.Writer, data []byte) { + t.Helper() + _, err := w.Write(data) + assertNilError(t, err) } -func TestNewRequest_errorForNoTrailingSlash(t *testing.T) { - tests := []struct { - rawurl string - wantError bool - }{ - {rawurl: "https://example.com/api/v3", wantError: true}, - {rawurl: "https://example.com/api/v3/", wantError: false}, - } - c := NewClient(nil) - for _, test := range tests { - u, err := url.Parse(test.rawurl) +func TestWithHTTPClient(t *testing.T) { + t.Parallel() + + t.Run("nil_client", func(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithHTTPClient(nil)(&opts) + if err == nil || err.Error() != "http client must not be nil" { + t.Errorf("WithHTTPClient errored: %v", err) + } + }) + + t.Run("non_nil_client", func(t *testing.T) { + t.Parallel() + + customClient := &http.Client{Timeout: 10 * time.Second} + opts := clientOptions{} + err := WithHTTPClient(customClient)(&opts) if err != nil { - t.Fatalf("url.Parse returned unexpected error: %v.", err) + t.Fatalf("WithHTTPClient errored: %v", err) } - c.BaseURL = u - if _, err := c.NewRequest(http.MethodGet, "test", nil); test.wantError && err == nil { - t.Fatalf("Expected error to be returned.") - } else if !test.wantError && err != nil { - t.Fatalf("NewRequest returned unexpected error: %v.", err) + + if opts.httpClient == nil { + t.Fatal("httpClient is nil") } - } -} -func TestNewUploadRequest_errorForNoTrailingSlash(t *testing.T) { - tests := []struct { - rawurl string - wantError bool - }{ - {rawurl: "https://example.com/api/uploads", wantError: true}, - {rawurl: "https://example.com/api/uploads/", wantError: false}, - } - c := NewClient(nil) - for _, test := range tests { - u, err := url.Parse(test.rawurl) - if err != nil { - t.Fatalf("url.Parse returned unexpected error: %v.", err) + if opts.httpClient == customClient { + t.Error("httpClient should be a shallow copy of the provided client, but is the same instance") } - c.UploadURL = u - if _, err = c.NewUploadRequest("test", nil, 0, ""); test.wantError && err == nil { - t.Fatalf("Expected error to be returned.") - } else if !test.wantError && err != nil { - t.Fatalf("NewUploadRequest returned unexpected error: %v.", err) + + if opts.httpClient.Timeout != customClient.Timeout { + t.Errorf("httpClient Timeout = %v, want %v", opts.httpClient.Timeout, customClient.Timeout) } - } + }) } -func TestResponse_populatePageValues(t *testing.T) { - r := http.Response{ - Header: http.Header{ - "Link": {`; rel="first",` + - ` ; rel="prev",` + - ` ; rel="next",` + - ` ; rel="last"`, - }, - }, - } +func TestWithTransport(t *testing.T) { + t.Parallel() - response := newResponse(&r) - if got, want := response.FirstPage, 1; got != want { - t.Errorf("response.FirstPage: %v, want %v", got, want) - } - if got, want := response.PrevPage, 2; want != got { - t.Errorf("response.PrevPage: %v, want %v", got, want) - } - if got, want := response.NextPage, 4; want != got { - t.Errorf("response.NextPage: %v, want %v", got, want) - } - if got, want := response.LastPage, 5; want != got { - t.Errorf("response.LastPage: %v, want %v", got, want) - } -} + t.Run("nil_transport", func(t *testing.T) { + t.Parallel() -func TestResponse_populatePageValues_invalid(t *testing.T) { - r := http.Response{ - Header: http.Header{ - "Link": {`,` + - `; rel="first",` + - `https://api.github.com/?page=2; rel="prev",` + - `; rel="next",` + - `; rel="last"`, - }, - }, - } + opts := clientOptions{} + err := WithTransport(nil)(&opts) + if err == nil || err.Error() != "transport must not be nil" { + t.Errorf("WithTransport errored: %v", err) + } + }) - response := newResponse(&r) - if got, want := response.FirstPage, 0; got != want { - t.Errorf("response.FirstPage: %v, want %v", got, want) - } - if got, want := response.PrevPage, 0; got != want { - t.Errorf("response.PrevPage: %v, want %v", got, want) - } - if got, want := response.NextPage, 0; got != want { - t.Errorf("response.NextPage: %v, want %v", got, want) - } - if got, want := response.LastPage, 0; got != want { - t.Errorf("response.LastPage: %v, want %v", got, want) - } + t.Run("non_nil_transport", func(t *testing.T) { + t.Parallel() - // more invalid URLs - r = http.Response{ - Header: http.Header{ - "Link": {`; rel="first"`}, - }, - } + customTransport := &http.Transport{IdleConnTimeout: 10 * time.Second} + opts := clientOptions{} + err := WithTransport(customTransport)(&opts) + if err != nil { + t.Fatalf("WithTransport errored: %v", err) + } - response = newResponse(&r) - if got, want := response.FirstPage, 0; got != want { - t.Errorf("response.FirstPage: %v, want %v", got, want) - } + if opts.transport == nil { + t.Fatal("transport is nil") + } + + if opts.transport != customTransport { + t.Errorf("transport = %v, want %v", opts.transport, customTransport) + } + }) } -func TestDo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestWithTimeout(t *testing.T) { + t.Parallel() - type foo struct { - A string - } + t.Run("negative_timeout", func(t *testing.T) { + t.Parallel() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"A":"a"}`) + opts := clientOptions{} + err := WithTimeout(-1)(&opts) + if err == nil || err.Error() != "timeout must not be negative" { + t.Errorf("WithTimeout errored: %v", err) + } }) - req, _ := client.NewRequest("GET", ".", nil) - body := new(foo) - client.Do(context.Background(), req, body) - - want := &foo{"a"} - if !reflect.DeepEqual(body, want) { - t.Errorf("Response body = %v, want %v", body, want) - } -} + t.Run("zero_timeout", func(t *testing.T) { + t.Parallel() -func TestDo_httpError(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + opts := clientOptions{} + err := WithTimeout(0)(&opts) + if err != nil { + t.Fatalf("WithTimeout errored: %v", err) + } - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - http.Error(w, "Bad Request", 400) + if opts.timeout == nil || *opts.timeout != 0 { + t.Errorf("timeout = %v, want 0", opts.timeout) + } }) - req, _ := client.NewRequest("GET", ".", nil) - resp, err := client.Do(context.Background(), req, nil) + t.Run("valid_timeout", func(t *testing.T) { + t.Parallel() - if err == nil { - t.Fatal("Expected HTTP 400 error, got no error.") - } - if resp.StatusCode != 400 { - t.Errorf("Expected HTTP 400 error, got %d status code.", resp.StatusCode) - } + timeout := 10 * time.Second + opts := clientOptions{} + err := WithTimeout(timeout)(&opts) + if err != nil { + t.Fatalf("WithTimeout errored: %v", err) + } + + if opts.timeout == nil || *opts.timeout != timeout { + t.Errorf("timeout = %v, want %v", opts.timeout, timeout) + } + }) } -// Test handling of an error caused by the internal http client's Do() -// function. A redirect loop is pretty unlikely to occur within the GitHub -// API, but does allow us to exercise the right code path. -func TestDo_redirectLoop(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestWithUserAgent(t *testing.T) { + t.Parallel() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, baseURLPath, http.StatusFound) + t.Run("empty_user_agent", func(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithUserAgent("")(&opts) + if err != nil { + t.Fatalf("WithUserAgent errored: %v", err) + } + + if *opts.userAgent != "" { + t.Errorf("userAgent = %v, want empty string", opts.userAgent) + } }) - req, _ := client.NewRequest("GET", ".", nil) - _, err := client.Do(context.Background(), req, nil) + t.Run("custom_user_agent", func(t *testing.T) { + t.Parallel() - if err == nil { - t.Error("Expected error to be returned.") - } - if err, ok := err.(*url.Error); !ok { - t.Errorf("Expected a URL error; got %#v.", err) - } + customUserAgent := "MyCustomUserAgent/1.0" + opts := clientOptions{} + err := WithUserAgent(customUserAgent)(&opts) + if err != nil { + t.Fatalf("WithUserAgent errored: %v", err) + } + + if opts.userAgent == nil || *opts.userAgent != customUserAgent { + t.Errorf("userAgent = %v, want %v", opts.userAgent, customUserAgent) + } + }) } -// Test that an error caused by the internal http client's Do() function -// does not leak the client secret. -func TestDo_sanitizeURL(t *testing.T) { - tp := &UnauthenticatedRateLimitedTransport{ - ClientID: "id", - ClientSecret: "secret", - } - unauthedClient := NewClient(tp.Client()) - unauthedClient.BaseURL = &url.URL{Scheme: "http", Host: "127.0.0.1:0", Path: "/"} // Use port 0 on purpose to trigger a dial TCP error, expect to get "dial tcp 127.0.0.1:0: connect: can't assign requested address". - req, err := unauthedClient.NewRequest("GET", ".", nil) +func TestWithEnvProxy(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithEnvProxy()(&opts) if err != nil { - t.Fatalf("NewRequest returned unexpected error: %v", err) - } - _, err = unauthedClient.Do(context.Background(), req, nil) - if err == nil { - t.Fatal("Expected error to be returned.") + t.Fatalf("WithEnvProxy errored: %v", err) } - if strings.Contains(err.Error(), "client_secret=secret") { - t.Errorf("Do error contains secret, should be redacted:\n%q", err) + + if !opts.envProxy { + t.Error("envProxy is false, want true") } } -func TestDo_rateLimit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestWithAuthToken(t *testing.T) { + t.Parallel() + + t.Run("empty_token", func(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithAuthToken("")(&opts) + if err == nil || err.Error() != "token must not be empty" { + t.Error("expected error for empty token, got nil") + } + }) + + t.Run("valid_token", func(t *testing.T) { + t.Parallel() + + validToken := "ghp_exampletoken1234567890" + opts := clientOptions{} + err := WithAuthToken(validToken)(&opts) + if err != nil { + t.Fatalf("WithAuthToken errored: %v", err) + } + + if opts.token == nil || *opts.token != validToken { + t.Errorf("token = %v, want %v", opts.token, validToken) + } + }) +} + +func TestWithURLs(t *testing.T) { + t.Parallel() + for _, tt := range []struct { + name string + baseURL *string + wantBaseURL *string + uploadURL *string + wantUploadURL *string + wantErr string + }{ + { + name: "does_not_modify_urls_with_trailing_slash", + baseURL: Ptr("https://example.com/"), + wantBaseURL: Ptr("https://example.com/"), + uploadURL: Ptr("https://upload.example.com/"), + wantUploadURL: Ptr("https://upload.example.com/"), + }, + { + name: "adds_trailing_slash", + baseURL: Ptr("https://example.com"), + wantBaseURL: Ptr("https://example.com/"), + uploadURL: Ptr("https://upload.example.com"), + wantUploadURL: Ptr("https://upload.example.com/"), + }, + { + name: "skips_unset", + }, + { + name: "error_on_empty_base_url", + baseURL: Ptr(""), + wantErr: "invalid base url: url cannot be empty", + }, + { + name: "error_on_bad_base_url", + baseURL: Ptr("bogus\nbase\nURL"), + wantErr: "invalid base url: invalid url", + }, + { + name: "error_on_empty_upload_url", + baseURL: Ptr("https://example.com/"), + uploadURL: Ptr(""), + wantErr: "invalid upload url: url cannot be empty", + }, + { + name: "error_on_bad_upload_url", + baseURL: Ptr("https://example.com/"), + uploadURL: Ptr("bogus\nupload\nURL"), + wantErr: "invalid upload url: invalid url", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithURLs(tt.baseURL, tt.uploadURL)(&opts) + if err != nil { + if tt.wantErr == "" { + t.Fatalf("unexpected error: %v", err) + } + + if !strings.Contains(err.Error(), tt.wantErr) { + t.Fatalf("expected error to contain %v, got %v", tt.wantErr, err) + } + + return + } + + if tt.wantErr != "" { + t.Fatalf("expected error to contain %v, got nil", tt.wantErr) + return + } + + if (opts.baseURL != nil) != (tt.wantBaseURL != nil) { + t.Errorf("BaseURL set = %v, want %v", opts.baseURL != nil, tt.wantBaseURL != nil) + } + if opts.baseURL != nil && opts.baseURL.String() != *tt.wantBaseURL { + t.Errorf("BaseURL is %v, want %v", opts.baseURL.String(), *tt.wantBaseURL) + } + + if (opts.uploadURL != nil) != (tt.wantUploadURL != nil) { + t.Errorf("UploadURL set = %v, want %v", opts.uploadURL != nil, tt.wantUploadURL != nil) + } + if opts.uploadURL != nil && opts.uploadURL.String() != *tt.wantUploadURL { + t.Errorf("UploadURL is %v, want %v", opts.uploadURL.String(), *tt.wantUploadURL) + } + }) + } +} + +func TestWithEnterpriseURLs(t *testing.T) { + t.Parallel() + for _, tt := range []struct { + name string + baseURL string + wantBaseURL string + uploadURL string + wantUploadURL string + wantErr string + }{ + { + name: "does_not_modify_properly_formed_urls", + baseURL: "https://custom-url/api/v3/", + wantBaseURL: "https://custom-url/api/v3/", + uploadURL: "https://custom-upload-url/api/uploads/", + wantUploadURL: "https://custom-upload-url/api/uploads/", + }, + { + name: "adds_trailing_slash", + baseURL: "https://custom-url/api/v3", + wantBaseURL: "https://custom-url/api/v3/", + uploadURL: "https://custom-upload-url/api/uploads", + wantUploadURL: "https://custom-upload-url/api/uploads/", + }, + { + name: "adds_enterprise_suffix", + baseURL: "https://custom-url/", + wantBaseURL: "https://custom-url/api/v3/", + uploadURL: "https://custom-upload-url/", + wantUploadURL: "https://custom-upload-url/api/uploads/", + }, + { + name: "adds_enterprise_suffix_and_trailing_slash", + baseURL: "https://custom-url", + wantBaseURL: "https://custom-url/api/v3/", + uploadURL: "https://custom-upload-url", + wantUploadURL: "https://custom-upload-url/api/uploads/", + }, + { + name: "url_has_existing_api_prefix_adds_trailing_slash", + baseURL: "https://api.custom-url", + wantBaseURL: "https://api.custom-url/", + uploadURL: "https://api.custom-upload-url", + wantUploadURL: "https://api.custom-upload-url/", + }, + { + name: "url_has_existing_api_prefix_and_trailing_slash", + baseURL: "https://api.custom-url/", + wantBaseURL: "https://api.custom-url/", + uploadURL: "https://api.custom-upload-url/", + wantUploadURL: "https://api.custom-upload-url/", + }, + { + name: "url_has_api_subdomain_adds_trailing_slash", + baseURL: "https://catalog.api.custom-url", + wantBaseURL: "https://catalog.api.custom-url/", + uploadURL: "https://catalog.api.custom-upload-url", + wantUploadURL: "https://catalog.api.custom-upload-url/", + }, + { + name: "url_has_api_subdomain_and_trailing_slash", + baseURL: "https://catalog.api.custom-url/", + wantBaseURL: "https://catalog.api.custom-url/", + uploadURL: "https://catalog.api.custom-upload-url/", + wantUploadURL: "https://catalog.api.custom-upload-url/", + }, + { + name: "url_is_not_a_proper_api_subdomain_adds_enterprise_suffix_and_trailing_slash", + baseURL: "https://cloud-api.custom-url", + wantBaseURL: "https://cloud-api.custom-url/api/v3/", + uploadURL: "https://cloud-api.custom-upload-url", + wantUploadURL: "https://cloud-api.custom-upload-url/api/uploads/", + }, + { + name: "url_is_not_a_proper_api_subdomain_adds_enterprise_suffix", + baseURL: "https://cloud-api.custom-url/", + wantBaseURL: "https://cloud-api.custom-url/api/v3/", + uploadURL: "https://cloud-api.custom-upload-url/", + wantUploadURL: "https://cloud-api.custom-upload-url/api/uploads/", + }, + { + name: "url_has_uploads_subdomain_does_not_modify", + baseURL: "https://api.custom-url/", + wantBaseURL: "https://api.custom-url/", + uploadURL: "https://uploads.custom-upload-url/", + wantUploadURL: "https://uploads.custom-upload-url/", + }, + { + name: "empty_base_url", + baseURL: "", + uploadURL: "https://custom-upload-url/api/uploads/", + wantErr: "invalid base url: url cannot be empty", + }, + { + name: "invalid_base_url", + baseURL: "bogus\nbase\nURL", + uploadURL: "https://custom-upload-url/api/uploads/", + wantErr: `invalid base url: invalid url`, + }, + { + name: "empty_upload_url", + baseURL: "https://custom-url/api/v3/", + uploadURL: "", + wantErr: "invalid upload url: url cannot be empty", + }, + { + name: "invalid_upload_url", + baseURL: "https://custom-url/api/v3/", + uploadURL: "bogus\nupload\nURL", + wantErr: `invalid upload url: invalid url`, + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithEnterpriseURLs(tt.baseURL, tt.uploadURL)(&opts) + if err != nil { + if tt.wantErr == "" { + t.Fatalf("WithEnterpriseURLs returned unexpected error: %v", err) + } + if !strings.Contains(err.Error(), tt.wantErr) { + t.Fatalf("error does not contain expected string %q: %v", tt.wantErr, err) + } + return + } + if tt.wantErr != "" { + t.Fatalf("WithEnterpriseURLs did not return expected error containing %q", tt.wantErr) + } + + if opts.baseURL.String() != tt.wantBaseURL { + t.Errorf("BaseURL is %v, want %v", opts.baseURL, tt.wantBaseURL) + } + if opts.uploadURL.String() != tt.wantUploadURL { + t.Errorf("UploadURL is %v, want %v", opts.uploadURL, tt.wantUploadURL) + } + }) + } +} + +func TestWithDisableRateLimitCheck(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithDisableRateLimitCheck()(&opts) + if err != nil { + t.Fatalf("WithDisableRateLimitCheck errored: %v", err) + } + + if !opts.disableRateLimitCheck { + t.Error("disableRateLimitCheck is false, want true") + } +} + +func TestWithRateLimitRedirectionalEndpoints(t *testing.T) { + t.Parallel() + + opts := clientOptions{} + err := WithRateLimitRedirectionalEndpoints()(&opts) + if err != nil { + t.Fatalf("WithRateLimitRedirectionalEndpoints errored: %v", err) + } + + if !opts.rateLimitRedirectionalEndpoints { + t.Error("rateLimitRedirectionalEndpoints is false, want true") + } +} + +func TestWithMaxSecondaryRateLimitRetryAfterDuration(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + duration time.Duration + }{ + { + name: "duration_zero", + duration: 0, + }, + { + name: "duration_set", + duration: time.Minute, + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + opts := clientOptions{} + err := WithMaxSecondaryRateLimitRetryAfterDuration(tt.duration)(&opts) + if err != nil { + t.Fatalf("WithMaxSecondaryRateLimitRetryAfterDuration errored: %v", err) + } + if *opts.maxSecondaryRateLimitRetryAfterDuration != tt.duration { + t.Errorf("maxSecondaryRateLimitRetryAfterDuration is %v, want %v", *opts.maxSecondaryRateLimitRetryAfterDuration, tt.duration) + } + }) + } +} + +func TestNewClient(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + opts []ClientOptionsFunc + wantErr string + }{ + { + name: "no_options", + opts: []ClientOptionsFunc{}, + wantErr: "", + }, + { + name: "with_options", + opts: []ClientOptionsFunc{ + WithHTTPClient(&http.Client{Timeout: 10 * time.Second}), + }, + wantErr: "", + }, + { + name: "with_bad_options", + opts: []ClientOptionsFunc{ + func(_ *clientOptions) error { + return errors.New("bad option error") + }, + }, + wantErr: "bad option error", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + c, err := NewClient(tt.opts...) + if err != nil { + if tt.wantErr == "" { + t.Fatalf("NewClient returned unexpected error: %v", err) + } + if !strings.Contains(err.Error(), tt.wantErr) { + t.Fatalf("error does not contain expected string %q: %v", tt.wantErr, err) + } + return + } + if tt.wantErr != "" { + t.Fatalf("NewClient did not return expected error containing %q", tt.wantErr) + } + + if c.client == nil { + t.Error("NewClient client is not initialized") + } + }) + } +} + +func Test_newClient(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + opts clientOptions + wantErr string + }{ + { + name: "default_options", + opts: clientOptions{}, + wantErr: "", + }, + { + name: "with_http_client", + opts: clientOptions{ + httpClient: &http.Client{Transport: &http.Transport{IdleConnTimeout: 5 * time.Second}}, + }, + wantErr: "", + }, + { + name: "with_transport", + opts: clientOptions{ + transport: &http.Transport{IdleConnTimeout: 10 * time.Second}, + }, + wantErr: "", + }, + { + name: "with_all_options", + opts: clientOptions{ + httpClient: &http.Client{Transport: &http.Transport{IdleConnTimeout: 5 * time.Second}}, + transport: &http.Transport{IdleConnTimeout: 10 * time.Second}, + timeout: Ptr(15 * time.Second), + apiVersionMin: Ptr(api20221128), + apiVersionMax: Ptr(api20221128), + userAgent: Ptr("CustomUserAgent/1.0"), + baseURL: mustParseURL(t, "https://custom-url/api/v3/"), + uploadURL: mustParseURL(t, "https://custom-upload-url/api/uploads/"), + disableRateLimitCheck: true, + rateLimitRedirectionalEndpoints: true, + maxSecondaryRateLimitRetryAfterDuration: Ptr(2 * time.Minute), + }, + wantErr: "", + }, + { + name: "with_rate_limit_options", + opts: clientOptions{ + disableRateLimitCheck: false, + rateLimitRedirectionalEndpoints: true, + maxSecondaryRateLimitRetryAfterDuration: Ptr(2 * time.Minute), + }, + wantErr: "", + }, + { + name: "with_env_proxy", + opts: clientOptions{ + envProxy: true, + }, + wantErr: "", + }, + { + name: "with_incompatible_transport_for_env_proxy", + opts: clientOptions{ + transport: roundTripperFunc(func(_ *http.Request) (*http.Response, error) { + return nil, nil + }), + envProxy: true, + }, + wantErr: "cannot set environment proxy on non-http transport", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + c, err := newClient(tt.opts) + if err != nil { + if tt.wantErr == "" { + t.Fatalf("newClient returned unexpected error: %v", err) + } + if !strings.Contains(err.Error(), tt.wantErr) { + t.Fatalf("error does not contain expected string %q: %v", tt.wantErr, err) + } + return + } + if tt.wantErr != "" { + t.Fatalf("newClient did not return expected error containing %q", tt.wantErr) + } + + if c.client == nil { + t.Error("newClient http.Client is not initialized") + } + if tt.opts.httpClient != nil && c.client != tt.opts.httpClient { + t.Error("newClient http.Client should be the same instance as the provided httpClient option") + } + if tt.opts.transport != nil && !tt.opts.envProxy && tt.opts.token == nil && c.client.Transport != tt.opts.transport { + t.Error("newClient http.Client.Transport should be the same instance as the provided transport option") + } + if tt.opts.timeout != nil && c.client.Timeout != *tt.opts.timeout { + t.Errorf("newClient http.Client.Timeout is %v, want %v", c.client.Timeout, *tt.opts.timeout) + } + + if c.clientIgnoreRedirects == nil { + t.Error("newClient http.Client used for redirects is not initialized") + } + if c.clientIgnoreRedirects.Transport != c.client.Transport { + t.Error("newClient http.Client and http.Client used for redirects should share the same Transport instance") + } + if c.clientIgnoreRedirects.Timeout != c.client.Timeout { + t.Errorf("newClient http.Client and http.Client used for redirects should have the same Timeout, got %v and %v", c.client.Timeout, c.clientIgnoreRedirects.Timeout) + } + if c.clientIgnoreRedirects.Jar != c.client.Jar { + t.Error("newClient http.Client and http.Client used for redirects should share the same Jar instance") + } + if c.clientIgnoreRedirects.CheckRedirect == nil { + t.Error("newClient http.Client used for redirects should have a CheckRedirect function") + } + + if tt.opts.apiVersionMin != nil && c.apiVersionMin != *tt.opts.apiVersionMin { + t.Errorf("newClient apiVersionMin is %v, want %v", c.apiVersionMin, *tt.opts.apiVersionMin) + } + if tt.opts.apiVersionMin == nil && c.apiVersionMin != api20221128 { + t.Errorf("newClient apiVersionMin is %v, want %v", c.apiVersionMin, api20221128) + } + + if tt.opts.apiVersionMax != nil && c.apiVersionMax != *tt.opts.apiVersionMax { + t.Errorf("newClient apiVersionMax is %v, want %v", c.apiVersionMax, *tt.opts.apiVersionMax) + } + if tt.opts.apiVersionMax == nil && c.apiVersionMax != api20260310 { + t.Errorf("newClient apiVersionMax is %v, want %v", c.apiVersionMax, api20260310) + } + + if tt.opts.userAgent != nil && c.userAgent != *tt.opts.userAgent { + t.Errorf("newClient userAgent is %v, want %v", c.userAgent, *tt.opts.userAgent) + } + if tt.opts.userAgent == nil && c.userAgent != defaultUserAgent { + t.Errorf("newClient userAgent is %v, want %v", c.userAgent, defaultUserAgent) + } + + if tt.opts.baseURL != nil && c.baseURL.String() != tt.opts.baseURL.String() { + t.Errorf("newClient baseURL is %v, want %v", c.baseURL.String(), tt.opts.baseURL.String()) + } + if tt.opts.baseURL == nil && c.baseURL.String() != defaultBaseURL { + t.Errorf("newClient baseURL is %v, want %v", c.baseURL.String(), defaultBaseURL) + } + + if tt.opts.uploadURL != nil && c.uploadURL.String() != tt.opts.uploadURL.String() { + t.Errorf("newClient uploadURL is %v, want %v", c.uploadURL.String(), tt.opts.uploadURL.String()) + } + if tt.opts.uploadURL == nil && c.uploadURL.String() != uploadBaseURL { + t.Errorf("newClient uploadURL is %v, want %v", c.uploadURL.String(), uploadBaseURL) + } + + if c.disableRateLimitCheck != tt.opts.disableRateLimitCheck { + t.Errorf("newClient disableRateLimitCheck is %v, want %v", c.disableRateLimitCheck, tt.opts.disableRateLimitCheck) + } + if tt.opts.disableRateLimitCheck && (c.rateLimitRedirectionalEndpoints || c.maxSecondaryRateLimitRetryAfterDuration != 0) { + t.Error("newClient should not set rate limit options when disableRateLimitCheck is true") + } + + if !tt.opts.disableRateLimitCheck && c.rateLimitRedirectionalEndpoints != tt.opts.rateLimitRedirectionalEndpoints { + t.Errorf("newClient rateLimitRedirectionalEndpoints is %v, want %v", c.rateLimitRedirectionalEndpoints, tt.opts.rateLimitRedirectionalEndpoints) + } + if !tt.opts.disableRateLimitCheck && tt.opts.maxSecondaryRateLimitRetryAfterDuration != nil && c.maxSecondaryRateLimitRetryAfterDuration != *tt.opts.maxSecondaryRateLimitRetryAfterDuration { + t.Errorf("newClient maxSecondaryRateLimitRetryAfterDuration is %v, want %v", c.maxSecondaryRateLimitRetryAfterDuration, *tt.opts.maxSecondaryRateLimitRetryAfterDuration) + } + + if c.common.client != c { + t.Error("newClient common.client is not initialized or does not point to the client") + } + if c.Actions == nil || c.Activity == nil || c.Admin == nil || c.Apps == nil || c.Authorizations == nil || c.Billing == nil || c.Checks == nil || c.Classroom == nil || c.CodeScanning == nil || c.CodesOfConduct == nil || c.Codespaces == nil || c.Copilot == nil || c.Credentials == nil || c.Dependabot == nil || c.DependencyGraph == nil || c.Emojis == nil || c.Enterprise == nil || c.Gists == nil || c.Git == nil || c.Gitignores == nil || c.Interactions == nil || c.IssueImport == nil || c.Issues == nil || c.Licenses == nil || c.Markdown == nil || c.Marketplace == nil || c.Meta == nil || c.Migrations == nil || c.Organizations == nil || c.PrivateRegistries == nil || c.Projects == nil || c.PullRequests == nil || c.RateLimit == nil || c.Reactions == nil || c.Repositories == nil || c.SCIM == nil || c.Search == nil || c.SecretScanning == nil || c.SecurityAdvisories == nil || c.SubIssue == nil || c.Teams == nil || c.Users == nil { + t.Error("newClient service fields are not all initialized") + } + + if c.Marketplace.Stubbed != tt.opts.marketplaceStubbed { + t.Errorf("newClient marketplaceStubbed is %v, want %v", c.Marketplace.Stubbed, tt.opts.marketplaceStubbed) + } + }) + } +} + +func TestClient_UserAgent(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + + if got, want := c.UserAgent(), defaultUserAgent; got != want { + t.Errorf("Client.UserAgent() = %v, want %v", got, want) + } + + customUserAgent := "CustomUserAgent/1.0" + c.userAgent = customUserAgent + + if got, want := c.UserAgent(), customUserAgent; got != want { + t.Errorf("Client.UserAgent() = %v, want %v", got, want) + } +} + +func TestClient_BaseURL(t *testing.T) { + t.Parallel() + + customBaseURL := "https://custom-url/api/v3/" + + for _, tt := range []struct { + name string + client *Client + wantBaseURL string + }{ + { + name: "default_base_url", + client: mustNewClient(t), + wantBaseURL: defaultBaseURL, + }, + { + name: "custom_base_url", + client: &Client{baseURL: mustParseURL(t, customBaseURL)}, + wantBaseURL: customBaseURL, + }, + { + name: "missing_base_url", + client: &Client{}, + wantBaseURL: "", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + if got, want := tt.client.BaseURL(), tt.wantBaseURL; got != want { + t.Errorf("Client.BaseURL() = %v, want %v", got, want) + } + }) + } +} + +func TestClient_UploadURL(t *testing.T) { + t.Parallel() + + customUploadURL := "https://custom-upload-url/api/uploads/" + + for _, tt := range []struct { + name string + client *Client + wantUploadURL string + }{ + { + name: "default_upload_url", + client: mustNewClient(t), + wantUploadURL: uploadBaseURL, + }, + { + name: "custom_upload_url", + client: &Client{uploadURL: mustParseURL(t, customUploadURL)}, + wantUploadURL: customUploadURL, + }, + { + name: "missing_upload_url", + client: &Client{}, + wantUploadURL: "", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + if got, want := tt.client.UploadURL(), tt.wantUploadURL; got != want { + t.Errorf("Client.UploadURL() = %v, want %v", got, want) + } + }) + } +} + +func TestClient_Clone(t *testing.T) { + t.Parallel() + + t.Run("uninitialized_client", func(t *testing.T) { + t.Parallel() + + var c Client + + _, err := c.Clone() + if err == nil || !errors.Is(err, errUninitialized) { + t.Fatalf("Client.Clone returned unexpected error: %v", err) + } + }) + + t.Run("initialized_client_opts_err", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + + _, err := c.Clone(func(_ *clientOptions) error { + return errors.New("test options error") + }) + if err == nil || err.Error() != "test options error" { + t.Fatalf("Client.Clone returned unexpected error: %v", err) + } + }) + + t.Run("initialized_client_new_client_err", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + + _, err := c.Clone(WithTransport(roundTripperFunc(func(_ *http.Request) (*http.Response, error) { + return nil, nil + })), WithEnvProxy()) + if err == nil || err.Error() != "cannot set environment proxy on non-http transport" { + t.Fatalf("Client.Clone returned unexpected error: %v", err) + } + }) + + t.Run("initialized_client", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.apiVersionMin = api20221128 + c.apiVersionMax = api20221128 + c.userAgent = "CustomUserAgent/1.0" + c.baseURL.Path = "/custom/" + c.uploadURL.Path = "/custom-upload/" + c.disableRateLimitCheck = false + c.rateLimitRedirectionalEndpoints = true + c.maxSecondaryRateLimitRetryAfterDuration = 2 * time.Minute + c.Marketplace.Stubbed = true + c.client.Transport = &http.Transport{IdleConnTimeout: 10 * time.Second} + c.client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return nil } + c.client.Timeout = 15 * time.Second + c.rateLimits[CoreCategory].Remaining = 100 + c.secondaryRateLimitReset = time.Now().Add(30 * time.Second) + + cloned, err := c.Clone() + if err != nil { + t.Fatalf("Client.Clone returned error: %v", err) + } + + if cloned.client == c.client { + t.Error("Cloned Client has same http.Client instance, but should be different") + } + if cloned.client.Transport != c.client.Transport { + t.Error("Cloned Client http.Client.Transport is not the same instance as original") + } + if cloned.client.CheckRedirect == nil || fmt.Sprintf("%p", cloned.client.CheckRedirect) != fmt.Sprintf("%p", c.client.CheckRedirect) { + t.Error("Cloned Client http.Client.CheckRedirect is not the same function instance as original") + } + if cloned.client.Jar != c.client.Jar { + t.Error("Cloned Client http.Client.Jar is not the same instance as original") + } + if cloned.client.Timeout != c.client.Timeout { + t.Errorf("Cloned Client http.Client.Timeout is %v, want %v", cloned.client.Timeout, c.client.Timeout) + } + if got, want := cloned.userAgent, c.userAgent; got != want { + t.Errorf("Cloned Client userAgent is %v, want %v", got, want) + } + if got, want := cloned.baseURL.String(), c.baseURL.String(); got != want { + t.Errorf("Cloned Client baseURL is %v, want %v", got, want) + } + if got, want := cloned.uploadURL.String(), c.uploadURL.String(); got != want { + t.Errorf("Cloned Client uploadURL is %v, want %v", got, want) + } + if cloned.disableRateLimitCheck != c.disableRateLimitCheck { + t.Errorf("Cloned Client disableRateLimitCheck is %v, want %v", cloned.disableRateLimitCheck, c.disableRateLimitCheck) + } + if cloned.rateLimitRedirectionalEndpoints != c.rateLimitRedirectionalEndpoints { + t.Errorf("Cloned Client rateLimitRedirectionalEndpoints is %v, want %v", cloned.rateLimitRedirectionalEndpoints, c.rateLimitRedirectionalEndpoints) + } + if cloned.maxSecondaryRateLimitRetryAfterDuration != c.maxSecondaryRateLimitRetryAfterDuration { + t.Errorf("Cloned Client maxSecondaryRateLimitRetryAfterDuration is %v, want %v", cloned.maxSecondaryRateLimitRetryAfterDuration, c.maxSecondaryRateLimitRetryAfterDuration) + } + if cloned.Marketplace.Stubbed != c.Marketplace.Stubbed { + t.Errorf("Cloned Client Marketplace.Stubbed is %v, want %v", cloned.Marketplace.Stubbed, c.Marketplace.Stubbed) + } + if cloned.rateLimits[CoreCategory].Remaining != c.rateLimits[CoreCategory].Remaining { + t.Errorf("Cloned Client rateLimits[CoreCategory].Remaining is %v, want %v", cloned.rateLimits[CoreCategory].Remaining, c.rateLimits[CoreCategory].Remaining) + } + if !cloned.secondaryRateLimitReset.Equal(c.secondaryRateLimitReset) { + t.Errorf("Cloned Client secondaryRateLimitReset is %v, want %v", cloned.secondaryRateLimitReset, c.secondaryRateLimitReset) + } + }) + + t.Run("initialized_client_no_rate_limit_check", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.userAgent = "CustomUserAgent/1.0" + c.baseURL.Path = "/custom/" + c.uploadURL.Path = "/custom-upload/" + c.disableRateLimitCheck = true + c.Marketplace.Stubbed = true + c.client.Transport = &http.Transport{IdleConnTimeout: 10 * time.Second} + c.client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return nil } + c.client.Timeout = 15 * time.Second + c.rateLimits[CoreCategory].Remaining = 100 + c.secondaryRateLimitReset = time.Now().Add(30 * time.Second) + + cloned, err := c.Clone() + if err != nil { + t.Fatalf("Client.Clone returned error: %v", err) + } + + if cloned.client == c.client { + t.Error("Cloned Client has same http.Client instance, but should be different") + } + if cloned.client.Transport != c.client.Transport { + t.Error("Cloned Client http.Client.Transport is not the same instance as original") + } + if cloned.client.CheckRedirect == nil || fmt.Sprintf("%p", cloned.client.CheckRedirect) != fmt.Sprintf("%p", c.client.CheckRedirect) { + t.Error("Cloned Client http.Client.CheckRedirect is not the same function instance as original") + } + if cloned.client.Jar != c.client.Jar { + t.Error("Cloned Client http.Client.Jar is not the same instance as original") + } + if cloned.client.Timeout != c.client.Timeout { + t.Errorf("Cloned Client http.Client.Timeout is %v, want %v", cloned.client.Timeout, c.client.Timeout) + } + if got, want := cloned.userAgent, c.userAgent; got != want { + t.Errorf("Cloned Client userAgent is %v, want %v", got, want) + } + if got, want := cloned.baseURL.String(), c.baseURL.String(); got != want { + t.Errorf("Cloned Client baseURL is %v, want %v", got, want) + } + if got, want := cloned.uploadURL.String(), c.uploadURL.String(); got != want { + t.Errorf("Cloned Client uploadURL is %v, want %v", got, want) + } + if cloned.disableRateLimitCheck != c.disableRateLimitCheck { + t.Errorf("Cloned Client disableRateLimitCheck is %v, want %v", cloned.disableRateLimitCheck, c.disableRateLimitCheck) + } + if cloned.Marketplace.Stubbed != c.Marketplace.Stubbed { + t.Errorf("Cloned Client Marketplace.Stubbed is %v, want %v", cloned.Marketplace.Stubbed, c.Marketplace.Stubbed) + } + }) + + t.Run("initialized_client_with_http_client", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.client.Transport = &http.Transport{IdleConnTimeout: 10 * time.Second} + c.client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return nil } + c.client.Timeout = 15 * time.Second + + h := &http.Client{ + Transport: &http.Transport{IdleConnTimeout: 20 * time.Second}, + CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse }, + Timeout: 25 * time.Second, + } + + cloned, err := c.Clone(WithHTTPClient(h)) + if err != nil { + t.Fatalf("Client.Clone returned error: %v", err) + } + + if cloned.client == h { + t.Error("Cloned Client has same http.Client instance as provided in WithHTTPClient, but should be a different instance") + } + if cloned.client.Transport != h.Transport { + t.Error("Cloned Client http.Client.Transport is not the same instance as original") + } + if cloned.client.CheckRedirect == nil || fmt.Sprintf("%p", cloned.client.CheckRedirect) != fmt.Sprintf("%p", h.CheckRedirect) { + t.Error("Cloned Client http.Client.CheckRedirect is not the same function instance as original") + } + if cloned.client.Jar != h.Jar { + t.Error("Cloned Client http.Client.Jar is not the same instance as original") + } + if cloned.client.Timeout != h.Timeout { + t.Errorf("Cloned Client http.Client.Timeout is %v, want %v", cloned.client.Timeout, h.Timeout) + } + }) + + t.Run("initialized_client_with_transport", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.client.Transport = &http.Transport{IdleConnTimeout: 10 * time.Second} + c.client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return nil } + c.client.Timeout = 15 * time.Second + + tr := &http.Transport{IdleConnTimeout: 30 * time.Second} + + cloned, err := c.Clone(WithTransport(tr)) + if err != nil { + t.Fatalf("Client.Clone returned error: %v", err) + } + + if cloned.client == c.client { + t.Error("Cloned Client has same http.Client instance, but should be different") + } + if cloned.client.Transport != tr { + t.Error("Cloned Client http.Client.Transport is not the same instance as original") + } + if cloned.client.CheckRedirect == nil || fmt.Sprintf("%p", cloned.client.CheckRedirect) != fmt.Sprintf("%p", c.client.CheckRedirect) { + t.Error("Cloned Client http.Client.CheckRedirect is not the same function instance as original") + } + if cloned.client.Jar != c.client.Jar { + t.Error("Cloned Client http.Client.Jar is not the same instance as original") + } + if cloned.client.Timeout != c.client.Timeout { + t.Errorf("Cloned Client http.Client.Timeout is %v, want %v", cloned.client.Timeout, c.client.Timeout) + } + }) + + t.Run("initialized_client_with_timeout", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.client.Transport = &http.Transport{IdleConnTimeout: 10 * time.Second} + c.client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return nil } + c.client.Timeout = 15 * time.Second + + timeout := 30 * time.Second + + cloned, err := c.Clone(WithTimeout(timeout)) + if err != nil { + t.Fatalf("Client.Clone returned error: %v", err) + } + + if cloned.client == c.client { + t.Error("Cloned Client has same http.Client instance, but should be different") + } + if cloned.client.Transport != c.client.Transport { + t.Error("Cloned Client http.Client.Transport is not the same instance as original") + } + if cloned.client.CheckRedirect == nil || fmt.Sprintf("%p", cloned.client.CheckRedirect) != fmt.Sprintf("%p", c.client.CheckRedirect) { + t.Error("Cloned Client http.Client.CheckRedirect is not the same function instance as original") + } + if cloned.client.Jar != c.client.Jar { + t.Error("Cloned Client http.Client.Jar is not the same instance as original") + } + if cloned.client.Timeout != timeout { + t.Errorf("Cloned Client http.Client.Timeout is %v, want %v", cloned.client.Timeout, timeout) + } + }) + + t.Run("initialized_client_with_http_client_transport_timeout", func(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.client.Transport = &http.Transport{IdleConnTimeout: 10 * time.Second} + c.client.CheckRedirect = func(_ *http.Request, _ []*http.Request) error { return nil } + c.client.Timeout = 15 * time.Second + + h := &http.Client{ + Transport: &http.Transport{IdleConnTimeout: 20 * time.Second}, + CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse }, + Timeout: 25 * time.Second, + } + + tr := &http.Transport{IdleConnTimeout: 30 * time.Second} + + timeout := 45 * time.Second + + cloned, err := c.Clone(WithHTTPClient(h), WithTransport(tr), WithTimeout(timeout)) + if err != nil { + t.Fatalf("Client.Clone returned error: %v", err) + } + + if cloned.client == h { + t.Error("Cloned Client has same http.Client instance as provided in WithHTTPClient, but should be a different instance") + } + if cloned.client.Transport != tr { + t.Error("Cloned Client http.Client.Transport is not the same instance as original") + } + if cloned.client.CheckRedirect == nil || fmt.Sprintf("%p", cloned.client.CheckRedirect) != fmt.Sprintf("%p", h.CheckRedirect) { + t.Error("Cloned Client http.Client.CheckRedirect is not the same function instance as original") + } + if cloned.client.Jar != h.Jar { + t.Error("Cloned Client http.Client.Jar is not the same instance as original") + } + if cloned.client.Timeout != timeout { + t.Errorf("Cloned Client http.Client.Timeout is %v, want %v", cloned.client.Timeout, timeout) + } + }) +} + +func TestClient_Client(t *testing.T) { + t.Parallel() + c, err := NewClient() + if err != nil { + t.Fatalf("NewClient returned error: %v", err) + } + + c2 := c.Client() + if c.client == c2 { + t.Error("Client returned same http.Client, but should be different") + } +} + +// Ensure that length of Client.rateLimits is the same as number of fields in RateLimits struct. +func TestClient_rateLimits(t *testing.T) { + t.Parallel() + if got, want := len(Client{}.rateLimits), reflect.TypeFor[RateLimits]().NumField(); got != want { + t.Errorf("len(Client{}.rateLimits) is %v, want %v", got, want) + } +} + +func TestNewRequest(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + inURL, outURL := "/foo", defaultBaseURL+"foo" + inBody, outBody := &User{Login: Ptr("l")}, `{"login":"l"}`+"\n" + req, _ := c.NewRequest(t.Context(), "GET", inURL, inBody) + + // test that relative URL was expanded + if got, want := req.URL.String(), outURL; got != want { + t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want) + } + + // test that body was JSON encoded + body, _ := io.ReadAll(req.Body) + if got, want := string(body), outBody; got != want { + t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want) + } + + userAgent := req.Header.Get("User-Agent") + + // test that default user-agent is attached to the request + if got, want := userAgent, c.userAgent; got != want { + t.Errorf("NewRequest() User-Agent is %v, want %v", got, want) + } + + if !strings.Contains(userAgent, Version) { + t.Errorf("NewRequest() User-Agent should contain %v, found %v", Version, userAgent) + } + + apiVersion := req.Header.Get(headerAPIVersion) + if got, want := apiVersion, api20221128; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } + + req, _ = c.NewRequest(t.Context(), "GET", inURL, inBody, WithVersion("2022-11-29")) + apiVersion = req.Header.Get(headerAPIVersion) + if got, want := apiVersion, "2022-11-29"; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } +} + +func TestNewRequest_invalidJSON(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + type T struct { + F func() + } + _, err := c.NewRequest(t.Context(), "GET", ".", &T{}) + + if err == nil { + t.Error("Expected error to be returned.") + } +} + +func TestNewRequest_badURL(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + _, err := c.NewRequest(t.Context(), "GET", ":", nil) + testURLParseError(t, err) +} + +func TestNewRequest_badMethod(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + if _, err := c.NewRequest(t.Context(), "BOGUS\nMETHOD", ".", nil); err == nil { + t.Fatal("NewRequest returned nil; expected error") + } +} + +// ensure that no User-Agent header is set if the client's UserAgent is empty. +// This caused a problem with Google's internal http client. +func TestNewRequest_emptyUserAgent(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + c.userAgent = "" + req, err := c.NewRequest(t.Context(), "GET", ".", nil) + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + if _, ok := req.Header["User-Agent"]; ok { + t.Fatal("constructed request contains unexpected User-Agent header") + } +} + +// If a nil body is passed to github.NewRequest, make sure that nil is also +// passed to http.NewRequest. In most cases, passing an io.Reader that returns +// no content is fine, since there is no difference between an HTTP request +// body that is an empty string versus one that is not set at all. However in +// certain cases, intermediate systems may treat these differently resulting in +// subtle errors. +func TestNewRequest_emptyBody(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + req, err := c.NewRequest(t.Context(), "GET", ".", nil) + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + if req.Body != nil { + t.Fatal("constructed request contains a non-nil Body") + } +} + +func TestNewRequest_errorForNoTrailingSlash(t *testing.T) { + t.Parallel() + tests := []struct { + rawurl string + wantError bool + }{ + {rawurl: "https://example.com/api/v3", wantError: true}, + {rawurl: "https://example.com/api/v3/", wantError: false}, + } + c := mustNewClient(t) + for _, test := range tests { + u, err := url.Parse(test.rawurl) + if err != nil { + t.Fatalf("url.Parse returned unexpected error: %v", err) + } + c.baseURL = u + if _, err := c.NewRequest(t.Context(), "GET", "test", nil); test.wantError && err == nil { + t.Fatal("Expected error to be returned.") + } else if !test.wantError && err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + } +} + +func TestCheckURLPathTraversal(t *testing.T) { + t.Parallel() + tests := []struct { + input string + wantErr error + }{ + {"repos/o/r/contents/file.txt", nil}, + {"repos/o/r/contents/dir/file.txt", nil}, + {"repos/o/r/contents/file..txt", nil}, + {"repos/o/r?q=a..b", nil}, + {"repos/../admin/users", ErrPathForbidden}, + {"repos/x/../../../admin", ErrPathForbidden}, + {"../admin", ErrPathForbidden}, + {"repos/o/r/contents/..", ErrPathForbidden}, + {"repos/o/r/contents/../secrets", ErrPathForbidden}, + // Full URLs with scheme. + {"https://api.github.com/repos/../admin", ErrPathForbidden}, + {"https://api.github.com/repos/o/r/contents/file.txt", nil}, + {"https://api.github.com/repos/o/r/contents/file..txt", nil}, + // URL with fragment. + {"repos/o/r/contents/file.txt#section", nil}, + {"repos/../admin#frag", ErrPathForbidden}, + // URL with userinfo. + {"https://user:pass@api.github.com/repos/../admin", ErrPathForbidden}, + {"https://user:pass@api.github.com/repos/o/r", nil}, + // Percent-encoded dots (%2e%2e) — url.Parse decodes them to ".." in Path. + {"repos/%2e%2e/admin", ErrPathForbidden}, + {"repos/%2E%2E/admin", ErrPathForbidden}, + {"repos/x/%2e%2e/%2e%2e/%2e%2e/admin", ErrPathForbidden}, + {"x/%2e%2e/%2e%2e/%2e%2e/admin/users", ErrPathForbidden}, + {"repos/o/r/contents/file%2e%2etxt", nil}, + } + for _, tt := range tests { + err := checkURLPathTraversal(tt.input) + if !errors.Is(err, tt.wantErr) { + t.Errorf("checkURLPathTraversal(%q) = %v, want %v", tt.input, err, tt.wantErr) + } + } +} + +func TestNewRequest_pathTraversal(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + tests := []struct { + urlStr string + wantError bool + }{ + {"repos/o/r/readme", false}, + {"repos/o/r/contents/file..txt", false}, + {"repos/x/../../../admin/users", true}, + {"repos/../admin", true}, + } + for _, tt := range tests { + _, err := c.NewRequest(t.Context(), "GET", tt.urlStr, nil) + if tt.wantError && !errors.Is(err, ErrPathForbidden) { + t.Errorf("NewRequest(%q): want ErrPathForbidden, got %v", tt.urlStr, err) + } else if !tt.wantError && err != nil { + t.Errorf("NewRequest(%q): unexpected error: %v", tt.urlStr, err) + } + } +} + +func TestNewFormRequest_pathTraversal(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + _, err := c.NewFormRequest(t.Context(), "repos/x/../../../admin", nil) + if !errors.Is(err, ErrPathForbidden) { + t.Fatalf("NewFormRequest with path traversal: want ErrPathForbidden, got %v", err) + } +} + +func TestNewUploadRequest_pathTraversal(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + _, err := c.NewUploadRequest(t.Context(), "repos/x/../../../admin", nil, 0, "") + if !errors.Is(err, ErrPathForbidden) { + t.Fatalf("NewUploadRequest with path traversal: want ErrPathForbidden, got %v", err) + } +} + +func TestNewUploadRequest_setsGetBodyForSeekableReader(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + const content = "upload content" + file := openTestFile(t, "upload.txt", content) + + req, err := c.NewUploadRequest(t.Context(), "https://example.com/", file, int64(len(content)), "text/plain") + if err != nil { + t.Fatalf("NewUploadRequest returned unexpected error: %v", err) + } + + // GetBody must be set for rewindable readers so HTTP/2 can retry the request + // after a refused stream. See issue #2113. + if req.GetBody == nil { + t.Fatal("NewUploadRequest did not set req.GetBody for a seekable/ReaderAt reader") + } + + // Each GetBody call must return an independent copy yielding the identical + // body so retries send the same bytes. + for i := range 2 { + body, err := req.GetBody() + if err != nil { + t.Fatalf("GetBody call %v returned unexpected error: %v", i, err) + } + got, err := io.ReadAll(body) + body.Close() + if err != nil { + t.Fatalf("reading GetBody result on call %v: %v", i, err) + } + if string(got) != content { + t.Errorf("GetBody call %v body = %q, want %q", i, got, content) + } + } + + // Reading via GetBody must not have disturbed the original body's read + // position: req.Body must still yield the full content. + gotBody, err := io.ReadAll(req.Body) + if err != nil { + t.Fatalf("reading req.Body: %v", err) + } + if string(gotBody) != content { + t.Errorf("req.Body after GetBody calls = %q, want %q", gotBody, content) + } +} + +// seekerOnlyReader implements io.Reader and io.Seeker but deliberately not +// io.ReaderAt, so it cannot provide an independent body copy. +type seekerOnlyReader struct { + r *strings.Reader +} + +func (s *seekerOnlyReader) Read(p []byte) (int, error) { return s.r.Read(p) } + +func (s *seekerOnlyReader) Seek(offset int64, whence int) (int64, error) { + return s.r.Seek(offset, whence) +} + +func TestNewUploadRequest_noGetBodyWithoutReaderAt(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + tests := []struct { + name string + reader io.Reader + }{ + // *bytes.Buffer is neither io.Seeker nor io.ReaderAt. + {"non-seekable", bytes.NewBufferString("upload content")}, + // io.Seeker alone is insufficient: without io.ReaderAt we cannot return + // an independent body copy, so GetBody must stay nil rather than risk + // disturbing the original body's read position. + {"seeker without ReaderAt", &seekerOnlyReader{strings.NewReader("upload content")}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + req, err := c.NewUploadRequest(t.Context(), "https://example.com/", tt.reader, 14, "text/plain") + if err != nil { + t.Fatalf("NewUploadRequest returned unexpected error: %v", err) + } + if req.GetBody != nil { + t.Errorf("NewUploadRequest set req.GetBody for %v reader, want nil", tt.name) + } + }) + } +} + +func TestNewUploadRequest_returnsErrorWhenSeekFails(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + const content = "upload content" + file := openTestFile(t, "upload.txt", content) + // Closing the file makes Seek fail, exercising the GetBody offset-probe + // error path (issue #2113). + if err := file.Close(); err != nil { + t.Fatalf("closing test file: %v", err) + } + + _, err := c.NewUploadRequest(t.Context(), "https://example.com/", file, int64(len(content)), "text/plain") + if err == nil { + t.Error("NewUploadRequest returned nil error when Seek failed, want error") + } +} + +func TestNewFormRequest(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + + inURL, outURL := "/foo", defaultBaseURL+"foo" + form := url.Values{} + form.Add("login", "l") + inBody, outBody := strings.NewReader(form.Encode()), "login=l" + req, err := c.NewFormRequest(t.Context(), inURL, inBody) + if err != nil { + t.Fatalf("NewFormRequest returned unexpected error: %v", err) + } + + // test that relative URL was expanded + if got, want := req.URL.String(), outURL; got != want { + t.Errorf("NewFormRequest(%q) URL is %v, want %v", inURL, got, want) + } + + // test that body was form encoded + body, err := io.ReadAll(req.Body) + if err != nil { + t.Fatalf("Error reading request body: %v", err) + } + if got, want := string(body), outBody; got != want { + t.Errorf("NewFormRequest() Body is %v, want %v", got, want) + } + + // test that default user-agent is attached to the request + if got, want := req.Header.Get("User-Agent"), c.userAgent; got != want { + t.Errorf("NewFormRequest() User-Agent is %v, want %v", got, want) + } + + apiVersion := req.Header.Get(headerAPIVersion) + if got, want := apiVersion, api20221128; got != want { + t.Errorf("NewFormRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } + + req, err = c.NewFormRequest(t.Context(), inURL, inBody, WithVersion("2022-11-29")) + if err != nil { + t.Fatalf("NewFormRequest with WithVersion returned unexpected error: %v", err) + } + apiVersion = req.Header.Get(headerAPIVersion) + if got, want := apiVersion, "2022-11-29"; got != want { + t.Errorf("NewFormRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } +} + +func TestNewFormRequest_badURL(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + _, err := c.NewFormRequest(t.Context(), ":", nil) + testURLParseError(t, err) +} + +func TestNewFormRequest_emptyUserAgent(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + c.userAgent = "" + req, err := c.NewFormRequest(t.Context(), ".", nil) + if err != nil { + t.Fatalf("NewFormRequest returned unexpected error: %v", err) + } + if _, ok := req.Header["User-Agent"]; ok { + t.Fatal("constructed request contains unexpected User-Agent header") + } +} + +func TestNewFormRequest_emptyBody(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + req, err := c.NewFormRequest(t.Context(), ".", nil) + if err != nil { + t.Fatalf("NewFormRequest returned unexpected error: %v", err) + } + if req.Body != nil { + t.Fatal("constructed request contains a non-nil Body") + } +} + +func TestNewFormRequest_errorForNoTrailingSlash(t *testing.T) { + t.Parallel() + tests := []struct { + rawURL string + wantError bool + }{ + {rawURL: "https://example.com/api/v3", wantError: true}, + {rawURL: "https://example.com/api/v3/", wantError: false}, + } + c := mustNewClient(t) + for _, test := range tests { + u, err := url.Parse(test.rawURL) + if err != nil { + t.Fatalf("url.Parse returned unexpected error: %v", err) + } + c.baseURL = u + if _, err := c.NewFormRequest(t.Context(), "test", nil); test.wantError && err == nil { + t.Fatal("Expected error to be returned.") + } else if !test.wantError && err != nil { + t.Fatalf("NewFormRequest returned unexpected error: %v", err) + } + } +} + +func TestNewUploadRequest_WithVersion(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + req, _ := c.NewUploadRequest(t.Context(), "https://example.com/", nil, 0, "") + + apiVersion := req.Header.Get(headerAPIVersion) + if got, want := apiVersion, api20221128; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } + + req, _ = c.NewUploadRequest(t.Context(), "https://example.com/", nil, 0, "", WithVersion("2022-11-29")) + apiVersion = req.Header.Get(headerAPIVersion) + if got, want := apiVersion, "2022-11-29"; got != want { + t.Errorf("NewRequest() %v header is %v, want %v", headerAPIVersion, got, want) + } +} + +func TestNewUploadRequest_badURL(t *testing.T) { + t.Parallel() + c := mustNewClient(t) + _, err := c.NewUploadRequest(t.Context(), ":", nil, 0, "") + testURLParseError(t, err) + + const methodName = "NewUploadRequest" + testBadOptions(t, methodName, func() (err error) { + _, err = c.NewUploadRequest(t.Context(), "\n", nil, -1, "\n") + return err + }) +} + +func TestNewUploadRequest_errorForNoTrailingSlash(t *testing.T) { + t.Parallel() + tests := []struct { + rawurl string + wantError bool + }{ + {rawurl: "https://example.com/api/uploads", wantError: true}, + {rawurl: "https://example.com/api/uploads/", wantError: false}, + } + c := mustNewClient(t) + for _, test := range tests { + u, err := url.Parse(test.rawurl) + if err != nil { + t.Fatalf("url.Parse returned unexpected error: %v", err) + } + c.uploadURL = u + if _, err = c.NewUploadRequest(t.Context(), "test", nil, 0, ""); test.wantError && err == nil { + t.Fatal("Expected error to be returned.") + } else if !test.wantError && err != nil { + t.Fatalf("NewUploadRequest returned unexpected error: %v", err) + } + } +} + +func TestResponse_populatePageValues(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Link": { + `; rel="first",` + + ` ; rel="prev",` + + ` ; rel="next",` + + ` ; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 1; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 2; want != got { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 4; want != got { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 5; want != got { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + if got, want := response.NextPageToken, ""; want != got { + t.Errorf("response.NextPageToken: %v, want %v", got, want) + } +} + +func TestResponse_populateSinceValues(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Link": { + `; rel="first",` + + ` ; rel="prev",` + + ` ; rel="next",` + + ` ; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 1; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 2; want != got { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 4; want != got { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 5; want != got { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + if got, want := response.NextPageToken, ""; want != got { + t.Errorf("response.NextPageToken: %v, want %v", got, want) + } +} + +func TestResponse_SinceWithPage(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Link": { + `; rel="first",` + + ` ; rel="prev",` + + ` ; rel="next",` + + ` ; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 1; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 2; want != got { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 4; want != got { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 5; want != got { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + if got, want := response.NextPageToken, ""; want != got { + t.Errorf("response.NextPageToken: %v, want %v", got, want) + } +} + +func TestResponse_cursorPagination(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Status": {"200 OK"}, + "Link": {`; rel="next"`}, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 0; want != got { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 0; want != got { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 0; want != got { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + if got, want := response.NextPageToken, "url-encoded-next-page-token"; want != got { + t.Errorf("response.NextPageToken: %v, want %v", got, want) + } + + // cursor-based pagination with "cursor" param + r = http.Response{ + Header: http.Header{ + "Link": { + `; rel="next"`, + }, + }, + } + + response = newResponse(&r) + if got, want := response.Cursor, "v1_12345678"; got != want { + t.Errorf("response.Cursor: %v, want %v", got, want) + } +} + +func TestResponse_beforeAfterPagination(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Link": { + `; rel="next",` + + ` ; rel="first",` + + ` ; rel="prev",`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.Before, "d4e5f6"; got != want { + t.Errorf("response.Before: %v, want %v", got, want) + } + if got, want := response.After, "a1b2c3"; got != want { + t.Errorf("response.After: %v, want %v", got, want) + } + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 0; want != got { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 0; want != got { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 0; want != got { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + if got, want := response.NextPageToken, ""; want != got { + t.Errorf("response.NextPageToken: %v, want %v", got, want) + } +} + +func TestResponse_populatePageValues_invalid(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Link": { + `,` + + `; rel="first",` + + `https://api.github.com/?page=2; rel="prev",` + + `; rel="next",` + + `; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 0; got != want { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 0; got != want { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 0; got != want { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + + // more invalid URLs + r = http.Response{ + Header: http.Header{ + "Link": {`; rel="first"`}, + }, + } + + response = newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } +} + +func TestResponse_populateSinceValues_invalid(t *testing.T) { + t.Parallel() + r := http.Response{ + Header: http.Header{ + "Link": { + `,` + + `; rel="first",` + + `https://api.github.com/?since=2; rel="prev",` + + `; rel="next",` + + `; rel="last"`, + }, + }, + } + + response := newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } + if got, want := response.PrevPage, 0; got != want { + t.Errorf("response.PrevPage: %v, want %v", got, want) + } + if got, want := response.NextPage, 0; got != want { + t.Errorf("response.NextPage: %v, want %v", got, want) + } + if got, want := response.LastPage, 0; got != want { + t.Errorf("response.LastPage: %v, want %v", got, want) + } + + // more invalid URLs + r = http.Response{ + Header: http.Header{ + "Link": {`; rel="first"`}, + }, + } + + response = newResponse(&r) + if got, want := response.FirstPage, 0; got != want { + t.Errorf("response.FirstPage: %v, want %v", got, want) + } +} + +func TestDo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + type foo struct { + A string + } + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"A":"a"}`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + var body *foo + _, err := client.Do(req, &body) + assertNilError(t, err) + + want := &foo{"a"} + if !cmp.Equal(body, want) { + t.Errorf("Response body = %v, want %v", body, want) + } +} + +func TestDo_httpError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + http.Error(w, "Bad Request", 400) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + resp, err := client.Do(req, nil) + + if err == nil { + t.Fatal("Expected HTTP 400 error, got no error.") + } + if resp.StatusCode != 400 { + t.Errorf("Expected HTTP 400 error, got %v status code.", resp.StatusCode) + } +} + +// Test handling of an error caused by the internal http client's Do() +// function. A redirect loop is pretty unlikely to occur within the GitHub +// API, but does allow us to exercise the right code path. +func TestDo_redirectLoop(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(headerRateLimit, "60") - w.Header().Set(headerRateRemaining, "59") - w.Header().Set(headerRateReset, "1372700873") + http.Redirect(w, r, baseURLPath, http.StatusFound) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + if !errors.As(err, new(*url.Error)) { + t.Errorf("Expected a URL error; got %#v", err) + } +} + +func TestDo_preservesResponseInHTTPError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNotFound) + fmt.Fprint(w, `{ + "message": "Resource not found", + "documentation_url": "https://docs.github.com/rest/reference/repos#get-a-repository" + }`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + var resp *Response + var data any + resp, err := client.Do(req, &data) + + if err == nil { + t.Fatal("Expected error response") + } + + // Verify error type and access to status code + var errResp *ErrorResponse + if !errors.As(err, &errResp) { + t.Fatalf("Expected *ErrorResponse error, got %T", err) + } + + // Verify status code is accessible from both Response and ErrorResponse + if resp == nil { + t.Fatal("Expected response to be returned even with error") + } + if got, want := resp.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Response status = %v, want %v", got, want) + } + if got, want := errResp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Error response status = %v, want %v", got, want) + } + + // Verify error contains proper message + if !strings.Contains(errResp.Message, "Resource not found") { + t.Errorf("Error message = %q, want to contain 'Resource not found'", errResp.Message) + } +} + +// TestDo_AcceptedError_LargeBodyTruncated verifies that when the API returns a +// 202 Accepted with a body larger than maxErrorBodySize, the client reads at +// most maxErrorBodySize bytes into AcceptedError.Raw and does not allocate +// unbounded memory. +func TestDo_AcceptedError_LargeBodyTruncated(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // Serve a 202 response whose body exceeds the cap by one byte. + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, strings.Repeat("x", maxErrorBodySize+1)) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + if err == nil { + t.Fatal("Expected AcceptedError, got nil") + } + + var aerr *AcceptedError + if !errors.As(err, &aerr) { + t.Fatalf("Expected *AcceptedError, got %T: %v", err, err) + } + + if got, want := len(aerr.Raw), maxErrorBodySize; got != want { + t.Errorf("AcceptedError.Raw length = %v, want %v (maxErrorBodySize)", got, want) + } +} + +// Test that an error caused by the internal http client's Do() function +// does not leak the client secret. +func TestDo_sanitizeURL(t *testing.T) { + t.Parallel() + tp := &UnauthenticatedRateLimitedTransport{ + ClientID: "id", + ClientSecret: "secret", + } + unauthedClient := mustNewClient(t, WithHTTPClient(tp.Client())) + unauthedClient.baseURL = &url.URL{Scheme: "http", Host: "127.0.0.1:0", Path: "/"} // Use port 0 on purpose to trigger a dial TCP error, expect to get "dial tcp 127.0.0.1:0: connect: can't assign requested address". + req, err := unauthedClient.NewRequest(t.Context(), "GET", ".", nil) + if err != nil { + t.Fatalf("NewRequest returned unexpected error: %v", err) + } + _, err = unauthedClient.Do(req, nil) + if err == nil { + t.Fatal("Expected error to be returned.") + } + if strings.Contains(err.Error(), "client_secret=secret") { + t.Errorf("Do error contains secret, should be redacted:\n%q", err) + } +} + +func TestDo_rateLimit(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "59") + w.Header().Set(HeaderRateUsed, "1") + w.Header().Set(HeaderRateReset, referenceUnixTimeStr) + w.Header().Set(HeaderRateResource, "core") + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + resp, err := client.Do(req, nil) + if err != nil { + t.Errorf("Do returned unexpected error: %v", err) + } + if got, want := resp.Rate.Limit, 60; got != want { + t.Errorf("Client rate limit = %v, want %v", got, want) + } + if got, want := resp.Rate.Remaining, 59; got != want { + t.Errorf("Client rate remaining = %v, want %v", got, want) + } + if got, want := resp.Rate.Used, 1; got != want { + t.Errorf("Client rate used = %v, want %v", got, want) + } + if !resp.Rate.Reset.UTC().Equal(referenceTime) { + t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset.UTC(), referenceTime) + } + if got, want := resp.Rate.Resource, "core"; got != want { + t.Errorf("Client rate resource = %v, want %v", got, want) + } +} + +func TestDo_rateLimitCategory(t *testing.T) { + t.Parallel() + tests := []struct { + method string + url string + category RateLimitCategory + }{ + { + method: "GET", + url: "/", + category: CoreCategory, + }, + { + method: "GET", + url: "/search/issues?q=rate", + category: SearchCategory, + }, + { + method: "GET", + url: "/graphql", + category: GraphqlCategory, + }, + { + method: "POST", + url: "/app-manifests/code/conversions", + category: IntegrationManifestCategory, + }, + { + method: "GET", + url: "/app-manifests/code/conversions", + category: CoreCategory, // only POST requests are in the integration manifest category + }, + { + method: "PUT", + url: "/repos/google/go-github/import", + category: SourceImportCategory, + }, + { + method: "GET", + url: "/repos/google/go-github/import", + category: CoreCategory, // only PUT requests are in the source import category + }, + { + method: "POST", + url: "/repos/google/go-github/code-scanning/sarifs", + category: CodeScanningUploadCategory, + }, + { + method: "GET", + url: "/scim/v2/organizations/ORG/Users", + category: ScimCategory, + }, + { + method: "POST", + url: "/repos/google/go-github/dependency-graph/snapshots", + category: DependencySnapshotsCategory, + }, + { + method: "GET", + url: "/search/code?q=rate", + category: CodeSearchCategory, + }, + { + method: "GET", + url: "/orgs/google/audit-log", + category: AuditLogCategory, + }, + { + method: "GET", + url: "/repos/google/go-github/dependency-graph/sbom", + category: DependencySBOMCategory, + }, + // missing a check for actionsRunnerRegistrationCategory: API not found + } + + for _, tt := range tests { + if got, want := GetRateLimitCategory(tt.method, tt.url), tt.category; got != want { + t.Errorf("expecting category %v, found %v", got, want) + } + } +} + +// Ensure rate limit is still parsed, even for error responses. +func TestDo_rateLimit_errorResponse(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "59") + w.Header().Set(HeaderRateUsed, "1") + w.Header().Set(HeaderRateReset, referenceUnixTimeStr) + w.Header().Set(HeaderRateResource, "core") + http.Error(w, "Bad Request", 400) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + resp, err := client.Do(req, nil) + if err == nil { + t.Error("Expected error to be returned.") + } + if errors.As(err, new(*RateLimitError)) { + t.Errorf("Did not expect a *RateLimitError error; got %#v", err) + } + if got, want := resp.Rate.Limit, 60; got != want { + t.Errorf("Client rate limit = %v, want %v", got, want) + } + if got, want := resp.Rate.Remaining, 59; got != want { + t.Errorf("Client rate remaining = %v, want %v", got, want) + } + if got, want := resp.Rate.Used, 1; got != want { + t.Errorf("Client rate used = %v, want %v", got, want) + } + if !resp.Rate.Reset.UTC().Equal(referenceTime) { + t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, referenceTime) + } + if got, want := resp.Rate.Resource, "core"; got != want { + t.Errorf("Client rate resource = %v, want %v", got, want) + } +} + +// Ensure *RateLimitError is returned when API rate limit is exceeded. +func TestDo_rateLimit_rateLimitError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "0") + w.Header().Set(HeaderRateUsed, "60") + w.Header().Set(HeaderRateReset, referenceUnixTimeStr) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + var rateLimitErr *RateLimitError + if !errors.As(err, &rateLimitErr) { + t.Fatalf("Expected a *RateLimitError error; got %#v", err) + } + if got, want := rateLimitErr.Rate.Limit, 60; got != want { + t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) + } + if got, want := rateLimitErr.Rate.Remaining, 0; got != want { + t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) + } + if got, want := rateLimitErr.Rate.Used, 60; got != want { + t.Errorf("rateLimitErr rate used = %v, want %v", got, want) + } + if !rateLimitErr.Rate.Reset.UTC().Equal(referenceTime) { + t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), referenceTime) + } + if got, want := rateLimitErr.Rate.Resource, "core"; got != want { + t.Errorf("rateLimitErr rate resource = %v, want %v", got, want) + } +} + +// Ensure a network call is not made when it's known that API rate limit is still exceeded. +func TestDo_rateLimit_noNetworkCall(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(time.Minute).Round(time.Second) // Rate reset is a minute from now, with 1 second precision. + + mux.HandleFunc("/first", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "0") + w.Header().Set(HeaderRateUsed, "60") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + madeNetworkCall := false + mux.HandleFunc("/second", func(http.ResponseWriter, *http.Request) { + madeNetworkCall = true + }) + + // First request is made, and it makes the client aware of rate reset time being in the future. + req, _ := client.NewRequest(t.Context(), "GET", "first", nil) + + _, err := client.Do(req, nil) + if err == nil { + t.Error("Expected error to be returned.") + } + + // Second request should not cause a network call to be made, since client can predict a rate limit error. + req, _ = client.NewRequest(t.Context(), "GET", "second", nil) + _, err = client.Do(req, nil) + + if madeNetworkCall { + t.Fatal("Network call was made, even though rate limit is known to still be exceeded.") + } + + if err == nil { + t.Error("Expected error to be returned.") + } + var rateLimitErr *RateLimitError + if !errors.As(err, &rateLimitErr) { + t.Fatalf("Expected a *RateLimitError error; got %#v", err) + } + if got, want := rateLimitErr.Rate.Limit, 60; got != want { + t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) + } + if got, want := rateLimitErr.Rate.Remaining, 0; got != want { + t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) + } + if got, want := rateLimitErr.Rate.Used, 60; got != want { + t.Errorf("rateLimitErr rate used = %v, want %v", got, want) + } + if !rateLimitErr.Rate.Reset.UTC().Equal(reset) { + t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) + } + if got, want := rateLimitErr.Rate.Resource, "core"; got != want { + t.Errorf("rateLimitErr rate resource = %v, want %v", got, want) + } +} + +// Ignore rate limit headers if the response was served from cache. +func TestDo_rateLimit_ignoredFromCache(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(time.Minute).Round(time.Second) // Rate reset is a minute from now, with 1 second precision. + + // By adding the X-From-Cache header we pretend this is served from a cache. + mux.HandleFunc("/first", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("X-From-Cache", "1") + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "0") + w.Header().Set(HeaderRateUsed, "60") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + madeNetworkCall := false + mux.HandleFunc("/second", func(http.ResponseWriter, *http.Request) { + madeNetworkCall = true + }) + + // First request is made so afterwards we can check the returned rate limit headers were ignored. + req, _ := client.NewRequest(t.Context(), "GET", "first", nil) + _, err := client.Do(req, nil) + if err == nil { + t.Error("Expected error to be returned.") + } + + // Second request should not be hindered by rate limits. + req, _ = client.NewRequest(t.Context(), "GET", "second", nil) + _, err = client.Do(req, nil) + if err != nil { + t.Fatalf("Second request failed, even though the rate limits from the cache should've been ignored: %v", err) + } + if !madeNetworkCall { + t.Fatal("Network call was not made, even though the rate limits from the cache should've been ignored") + } +} + +// Ensure sleeps until the rate limit is reset when the client is rate limited. +func TestDo_rateLimit_sleepUntilResponseResetLimit(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(time.Second) + + firstRequest := true + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + if firstRequest { + firstRequest = false + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "0") + w.Header().Set(HeaderRateUsed, "60") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + return + } + w.Header().Set(HeaderRateLimit, "5000") + w.Header().Set(HeaderRateRemaining, "5000") + w.Header().Set(HeaderRateUsed, "0") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Add(time.Hour).Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{}`) + }) + + req, _ := client.NewRequest(context.WithValue(t.Context(), SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "GET", ".", nil) + resp, err := client.Do(req, nil) + if err != nil { + t.Errorf("Do returned unexpected error: %v", err) + } + if got, want := resp.StatusCode, http.StatusOK; got != want { + t.Errorf("Response status code = %v, want %v", got, want) + } +} + +// Ensure tries to sleep until the rate limit is reset when the client is rate limited, but only once. +func TestDo_rateLimit_sleepUntilResponseResetLimitRetryOnce(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(time.Second) + + var requestCount atomic.Int32 + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + requestCount.Add(1) + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "0") + w.Header().Set(HeaderRateUsed, "60") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest(context.WithValue(t.Context(), SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "GET", ".", nil) + _, err := client.Do(req, nil) + if err == nil { + t.Error("Expected error to be returned.") + } + if got, want := int(requestCount.Load()), 2; got != want { + t.Errorf("Expected 2 requests, got %v", got) + } +} + +// Ensure a network call is not made when it's known that API rate limit is still exceeded. +func TestDo_rateLimit_sleepUntilClientResetLimit(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(time.Second) + client.rateLimits[CoreCategory] = Rate{Limit: 5000, Remaining: 0, Reset: Timestamp{reset}} + var requestCount atomic.Int32 + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + requestCount.Add(1) + w.Header().Set(HeaderRateLimit, "5000") + w.Header().Set(HeaderRateRemaining, "5000") + w.Header().Set(HeaderRateUsed, "0") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Add(time.Hour).Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{}`) + }) + req, _ := client.NewRequest(context.WithValue(t.Context(), SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "GET", ".", nil) + resp, err := client.Do(req, nil) + if err != nil { + t.Errorf("Do returned unexpected error: %v", err) + } + if got, want := resp.StatusCode, http.StatusOK; got != want { + t.Errorf("Response status code = %v, want %v", got, want) + } + if got, want := int(requestCount.Load()), 1; got != want { + t.Errorf("Expected 1 request, got %v", got) + } +} + +// Ensure sleep is aborted when the context is cancelled. +// +// The test verifies that when a request receives a 403 rate-limit response, +// the client begins sleeping until the reset time, and that canceling the +// context during (or just before) that sleep aborts it and returns +// context.Canceled. +// +// Determinism: The handler signals via requestReceived once it has run, +// guaranteeing exactly one request before cancellation. A tiny goroutine +// waits for that signal and then calls cancel, so the context is cancelled +// after the handler returns but independently of wall-clock timing. There +// are no time.After timeouts or time.Sleep delays: the test either completes +// in microseconds or hangs (caught by the test framework's global timeout). +func TestDo_rateLimit_abortSleepContextCancelled(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // We use a 1 minute reset time to ensure the sleep is not completed. + reset := time.Now().UTC().Add(time.Minute) + var requestCount atomic.Int32 + // requestReceived is signaled once the handler has run, so the test can + // verify the request count independently of when the context is cancelled. + requestReceived := make(chan struct{}) + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + requestCount.Add(1) + w.Header().Set(HeaderRateLimit, "60") + w.Header().Set(HeaderRateRemaining, "0") + w.Header().Set(HeaderRateUsed, "60") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + close(requestReceived) + }) + + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + + req, _ := client.NewRequest(context.WithValue(ctx, SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "GET", ".", nil) + + // Cancel the context as soon as the handler has processed the request. + // The handler always runs and returns before the client begins the + // rate-limit sleep (the HTTP transport is synchronous), so cancel fires + // either just before or during the sleep — both paths are handled + // identically by sleepUntilResetWithBuffer's select. + go func() { + <-requestReceived + cancel() + }() + + _, err := client.Do(req, nil) + + if got, want := int(requestCount.Load()), 1; got != want { + t.Errorf("Expected 1 request, got %v", got) + } + if !errors.Is(err, context.Canceled) { + t.Errorf("Expected context cancelled error, got: %v", err) + } +} + +// Ensure sleep is aborted when the context is cancelled on initial request. +// +// Determinism: The context is pre-cancelled before Do is called, so +// checkRateLimitBeforeDo → sleepUntilResetWithBuffer sees ctx.Done() already +// closed and returns immediately. No wall-clock timeout is involved. +func TestDo_rateLimit_abortSleepContextCancelledClientLimit(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(time.Minute) + client.rateLimits[CoreCategory] = Rate{Limit: 5000, Remaining: 0, Reset: Timestamp{reset}} + var requestCount atomic.Int32 + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + requestCount.Add(1) + w.Header().Set(HeaderRateLimit, "5000") + w.Header().Set(HeaderRateRemaining, "5000") + w.Header().Set(HeaderRateUsed, "0") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Add(time.Hour).Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{}`) + }) + // Pre-cancel the context: checkRateLimitBeforeDo will call + // sleepUntilResetWithBuffer which immediately returns ctx.Err() because + // ctx.Done() is already closed. This is fully deterministic — no timing + // race between a short timeout and goroutine scheduling. + ctx, cancel := context.WithCancel(t.Context()) + cancel() + req, _ := client.NewRequest(context.WithValue(ctx, SleepUntilPrimaryRateLimitResetWhenRateLimited, true), "GET", ".", nil) + _, err := client.Do(req, nil) + var rateLimitError *RateLimitError + if !errors.As(err, &rateLimitError) { + t.Fatalf("Expected a *rateLimitError error; got %#v", err) + } + if got, wantSuffix := rateLimitError.Message, "Context cancelled while waiting for rate limit to reset until"; !strings.HasPrefix(got, wantSuffix) { + t.Errorf("Expected request to be prevented because context cancellation, got: %v.", got) + } + if got, want := int(requestCount.Load()), 0; got != want { + t.Errorf("Expected 0 requests, got %v", got) + } +} + +// Ensure *AbuseRateLimitError is returned when the response indicates that +// the client has triggered an abuse detection mechanism. +func TestDo_rateLimit_abuseRateLimitError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + // When the abuse rate limit error is of the "temporarily blocked from content creation" type, + // there is no "Retry-After" header. + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + var abuseRateLimitErr *AbuseRateLimitError + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if got, want := abuseRateLimitErr.RetryAfter, (*time.Duration)(nil); got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } +} + +// Ensure *AbuseRateLimitError is returned when the response indicates that +// the client has triggered an abuse detection mechanism on GitHub Enterprise. +func TestDo_rateLimit_abuseRateLimitErrorEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusForbidden) + // When the abuse rate limit error is of the "temporarily blocked from content creation" type, + // there is no "Retry-After" header. + // This response returns a documentation url like the one returned for GitHub Enterprise, this + // url changes between versions but follows roughly the same format. + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + var abuseRateLimitErr *AbuseRateLimitError + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if got, want := abuseRateLimitErr.RetryAfter, (*time.Duration)(nil); got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } +} + +// Ensure *AbuseRateLimitError.RetryAfter is parsed correctly for the Retry-After header. +func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) { + t.Parallel() + synctest.Test(t, func(t *testing.T) { + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Header().Set(headerRetryAfter, "123") // Retry after value of 123 seconds. + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism ...", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + var abuseRateLimitErr *AbuseRateLimitError + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatal("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } + + // expect prevention of a following request + if _, err = client.Do(req, nil); err == nil { + t.Error("Expected error to be returned.") + } + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatal("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + if got, want := 123*time.Second, *abuseRateLimitErr.RetryAfter; got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } + if got, wantSuffix := abuseRateLimitErr.Message, "not making remote request."; !strings.HasSuffix(got, wantSuffix) { + t.Errorf("Expected request to be prevented because of secondary rate limit, got: %v.", got) + } + }) +} + +// Ensure *AbuseRateLimitError.RetryAfter is parsed correctly for the x-ratelimit-reset header. +func TestDo_rateLimit_abuseRateLimitError_xRateLimitReset(t *testing.T) { + t.Parallel() + synctest.Test(t, func(t *testing.T) { + client, mux, _ := setup(t) + + // x-ratelimit-reset value of 123 seconds into the future. + blockUntil := time.Now().UTC().Add(123 * time.Second).Unix() + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Header().Set(HeaderRateReset, strconv.Itoa(int(blockUntil))) + w.Header().Set(HeaderRateRemaining, "1") // set remaining to a value > 0 to distinct from a primary rate limit + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism ...", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + var abuseRateLimitErr *AbuseRateLimitError + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatal("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + + if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } + + // expect prevention of a following request + if _, err = client.Do(req, nil); err == nil { + t.Error("Expected error to be returned.") + } + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatal("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } + if got, wantSuffix := abuseRateLimitErr.Message, "not making remote request."; !strings.HasSuffix(got, wantSuffix) { + t.Errorf("Expected request to be prevented because of secondary rate limit, got: %v.", got) + } + }) +} + +// Ensure *AbuseRateLimitError.RetryAfter respect a max duration if specified. +func TestDo_rateLimit_abuseRateLimitError_maxDuration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + // specify a max retry after duration of 1 min + client.maxSecondaryRateLimitRetryAfterDuration = 60 * time.Second + + // x-ratelimit-reset value of 1h into the future, to make sure we are way over the max wait time duration. + blockUntil := time.Now().Add(1 * time.Hour).Unix() + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Header().Set(HeaderRateReset, strconv.Itoa(int(blockUntil))) + w.Header().Set(HeaderRateRemaining, "1") // set remaining to a value > 0 to distinct from a primary rate limit + w.WriteHeader(http.StatusForbidden) + fmt.Fprintln(w, `{ + "message": "You have triggered an abuse detection mechanism ...", + "documentation_url": "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits" +}`) }) - req, _ := client.NewRequest("GET", ".", nil) - resp, err := client.Do(context.Background(), req, nil) + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, nil) + + if err == nil { + t.Error("Expected error to be returned.") + } + var abuseRateLimitErr *AbuseRateLimitError + if !errors.As(err, &abuseRateLimitErr) { + t.Fatalf("Expected a *AbuseRateLimitError error; got %#v", err) + } + if abuseRateLimitErr.RetryAfter == nil { + t.Fatal("abuseRateLimitErr RetryAfter is nil, expected not-nil") + } + // check that the retry after is set to be the max allowed duration + if got, want := *abuseRateLimitErr.RetryAfter, client.maxSecondaryRateLimitRetryAfterDuration; got != want { + t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + } +} + +// Make network call if client has disabled the rate limit check. +func TestDo_rateLimit_disableRateLimitCheck(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.disableRateLimitCheck = true + + reset := time.Now().UTC().Add(60 * time.Second) + client.rateLimits[CoreCategory] = Rate{Limit: 5000, Remaining: 0, Reset: Timestamp{reset}} + var requestCount atomic.Int32 + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + requestCount.Add(1) + w.Header().Set(HeaderRateLimit, "5000") + w.Header().Set(HeaderRateRemaining, "5000") + w.Header().Set(HeaderRateUsed, "0") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Add(time.Hour).Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{}`) + }) + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + resp, err := client.Do(req, nil) if err != nil { t.Errorf("Do returned unexpected error: %v", err) } - if got, want := resp.Rate.Limit, 60; got != want { - t.Errorf("Client rate limit = %v, want %v", got, want) + if got, want := resp.StatusCode, http.StatusOK; got != want { + t.Errorf("Response status code = %v, want %v", got, want) } - if got, want := resp.Rate.Remaining, 59; got != want { - t.Errorf("Client rate remaining = %v, want %v", got, want) + if got, want := int(requestCount.Load()), 1; got != want { + t.Errorf("Expected 1 request, got %v", got) } - reset := time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC) - if resp.Rate.Reset.UTC() != reset { - t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, reset) + if got, want := client.rateLimits[CoreCategory].Remaining, 0; got != want { + t.Errorf("Expected 0 requests remaining, got %v", got) } } -// ensure rate limit is still parsed, even for error responses -func TestDo_rateLimit_errorResponse(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +// Make network call if client has bypassed the rate limit check. +func TestDo_rateLimit_bypassRateLimitCheck(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + reset := time.Now().UTC().Add(60 * time.Second) + client.rateLimits[CoreCategory] = Rate{Limit: 5000, Remaining: 0, Reset: Timestamp{reset}} + var requestCount atomic.Int32 + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + requestCount.Add(1) + w.Header().Set(HeaderRateLimit, "5000") + w.Header().Set(HeaderRateRemaining, "5000") + w.Header().Set(HeaderRateUsed, "0") + w.Header().Set(HeaderRateReset, fmt.Sprint(reset.Add(time.Hour).Unix())) + w.Header().Set(HeaderRateResource, "core") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, `{}`) + }) + req, _ := client.NewRequest(context.WithValue(t.Context(), BypassRateLimitCheck, true), "GET", ".", nil) + resp, err := client.Do(req, nil) + if err != nil { + t.Errorf("Do returned unexpected error: %v", err) + } + if got, want := resp.StatusCode, http.StatusOK; got != want { + t.Errorf("Response status code = %v, want %v", got, want) + } + if got, want := int(requestCount.Load()), 1; got != want { + t.Errorf("Expected 1 request, got %v", got) + } + if got, want := client.rateLimits[CoreCategory].Remaining, 5000; got != want { + t.Errorf("Expected 5000 requests remaining, got %v", got) + } +} - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(headerRateLimit, "60") - w.Header().Set(headerRateRemaining, "59") - w.Header().Set(headerRateReset, "1372700873") - http.Error(w, "Bad Request", 400) +func TestDo_noContent(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNoContent) }) - req, _ := client.NewRequest("GET", ".", nil) - resp, err := client.Do(context.Background(), req, nil) - if err == nil { - t.Error("Expected error to be returned.") - } - if _, ok := err.(*RateLimitError); ok { - t.Errorf("Did not expect a *RateLimitError error; got %#v.", err) + var body json.RawMessage + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, err := client.Do(req, &body) + if err != nil { + t.Fatalf("Do returned unexpected error: %v", err) } - if got, want := resp.Rate.Limit, 60; got != want { - t.Errorf("Client rate limit = %v, want %v", got, want) +} + +func TestClient_checkRequestAPIVersionBeforeDo(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + version string + versionMin string + versionMax string + wantErr bool + }{ + { + name: "version_not_set", + version: "", + versionMin: api20221128, + versionMax: api20260310, + wantErr: false, + }, + { + name: "version_less_than_min", + version: "2022-01-01", + versionMin: api20221128, + versionMax: api20260310, + wantErr: true, + }, + { + name: "version_equal_to_min", + version: api20221128, + versionMin: api20221128, + versionMax: api20260310, + wantErr: false, + }, + { + name: "version_between_min_and_max", + version: "2023-01-01", + versionMin: api20221128, + versionMax: api20260310, + wantErr: false, + }, + { + name: "version_equal_to_max", + version: api20260310, + versionMin: api20221128, + versionMax: api20260310, + wantErr: false, + }, + { + name: "version_greater_than_max", + version: api20260310, + versionMin: api20221128, + versionMax: api20221128, + wantErr: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + client := mustNewClient(t) + client.apiVersionMin = tt.versionMin + client.apiVersionMax = tt.versionMax + + req, _ := http.NewRequestWithContext(t.Context(), "GET", ".", nil) + req.Header.Set(headerAPIVersion, tt.version) + + err := client.checkRequestAPIVersionBeforeDo(req) + if tt.wantErr { + if err == nil { + t.Fatal("Expected error to be returned, got nil") + } + if !errors.Is(err, ErrUnsupportedAPIVersion) { + t.Errorf("Expected ErrUnsupportedAPIVersion; got %#v", err) + } + return + } + + if err != nil { + t.Fatalf("Expected no error to be returned, got: %v", err) + } + }) } - if got, want := resp.Rate.Remaining, 59; got != want { - t.Errorf("Client rate remaining = %v, want %v", got, want) +} + +func TestClient_bareDo_errors_with_unsupported_api_version(t *testing.T) { + t.Parallel() + + c := mustNewClient(t) + c.apiVersionMin = api20221128 + c.apiVersionMax = api20221128 + + req, _ := http.NewRequestWithContext(t.Context(), "GET", ".", nil) + req.Header.Set(headerAPIVersion, api20260310) + + _, err := c.bareDo(c.client, req) + if err == nil { + t.Fatal("Expected error to be returned, got nil") } - reset := time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC) - if resp.Rate.Reset.UTC() != reset { - t.Errorf("Client rate reset = %v, want %v", resp.Rate.Reset, reset) + if !errors.Is(err, ErrUnsupportedAPIVersion) { + t.Errorf("Expected ErrUnsupportedAPIVersion; got %#v", err) } } -// Ensure *RateLimitError is returned when API rate limit is exceeded. -func TestDo_rateLimit_rateLimitError(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestBareDoUntilFound_redirectLoop(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(headerRateLimit, "60") - w.Header().Set(headerRateRemaining, "0") - w.Header().Set(headerRateReset, "1372700873") - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusForbidden) - fmt.Fprintln(w, `{ - "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", - "documentation_url": "https://developer.github.com/v3/#rate-limiting" -}`) + http.Redirect(w, r, baseURLPath, http.StatusMovedPermanently) }) - req, _ := client.NewRequest("GET", ".", nil) - _, err := client.Do(context.Background(), req, nil) + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, _, err := client.bareDoUntilFound(req, 1) if err == nil { t.Error("Expected error to be returned.") } - rateLimitErr, ok := err.(*RateLimitError) - if !ok { - t.Fatalf("Expected a *RateLimitError error; got %#v.", err) - } - if got, want := rateLimitErr.Rate.Limit, 60; got != want { - t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) + if !errors.As(err, new(*RedirectionError)) { + t.Errorf("Expected a Redirection error; got %#v", err) } - if got, want := rateLimitErr.Rate.Remaining, 0; got != want { - t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) +} + +func TestBareDoUntilFound_UnexpectedRedirection(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, baseURLPath, http.StatusSeeOther) + }) + + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, _, err := client.bareDoUntilFound(req, 1) + + if err == nil { + t.Error("Expected error to be returned.") } - reset := time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC) - if rateLimitErr.Rate.Reset.UTC() != reset { - t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) + if !errors.As(err, new(*RedirectionError)) { + t.Errorf("Expected a Redirection error; got %#v", err) } } -// Ensure a network call is not made when it's known that API rate limit is still exceeded. -func TestDo_rateLimit_noNetworkCall(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +// TestBareDoUntilFound_RejectsCrossHostRedirect verifies that bareDoUntilFound +// refuses to follow a 301 redirect whose Location points to a different host, +// which would otherwise leak the Authorization header (added by the auth +// transport) to an attacker-controlled server. +func TestBareDoUntilFound_RejectsCrossHostRedirect(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Location", "https://evil.example.com/steal") + w.WriteHeader(http.StatusMovedPermanently) + }) - reset := time.Now().UTC().Add(time.Minute).Round(time.Second) // Rate reset is a minute from now, with 1 second precision. + req, _ := client.NewRequest(t.Context(), "GET", ".", nil) + _, _, err := client.bareDoUntilFound(req, 1) + if err == nil { + t.Fatal("Expected cross-host redirect to be rejected, got nil error.") + } + if !strings.Contains(err.Error(), "cross-host redirect") { + t.Errorf("Expected cross-host redirect error, got: %v", err) + } +} - mux.HandleFunc("/first", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(headerRateLimit, "60") - w.Header().Set(headerRateRemaining, "0") - w.Header().Set(headerRateReset, fmt.Sprint(reset.Unix())) - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusForbidden) - fmt.Fprintln(w, `{ - "message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", - "documentation_url": "https://developer.github.com/v3/#rate-limiting" -}`) +// TestRoundTripWithOptionalFollowRedirect_RejectsCrossHostRedirect verifies +// that roundTripWithOptionalFollowRedirect refuses to follow a 301 redirect to +// a different host, preventing Authorization-header leakage to attacker- +// controlled servers via a malicious or compromised API response. +func TestRoundTripWithOptionalFollowRedirect_RejectsCrossHostRedirect(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Location", "https://evil.example.com/steal") + w.WriteHeader(http.StatusMovedPermanently) }) - madeNetworkCall := false - mux.HandleFunc("/second", func(w http.ResponseWriter, r *http.Request) { - madeNetworkCall = true + _, err := client.roundTripWithOptionalFollowRedirect(t.Context(), ".", 1) + if err == nil { + t.Fatal("Expected cross-host redirect to be rejected, got nil error.") + } + if !strings.Contains(err.Error(), "cross-host redirect") { + t.Errorf("Expected cross-host redirect error, got: %v", err) + } +} + +// TestRoundTripWithOptionalFollowRedirect_AllowsSameHostRedirect ensures the +// cross-host check does not break legitimate same-host 301 follow behavior +// (the path that rate-limit redirection relies on). +func TestRoundTripWithOptionalFollowRedirect_AllowsSameHostRedirect(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + var followed atomic.Bool + mux.HandleFunc("/archive", func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Location", baseURLPath+"/archive-target") + w.WriteHeader(http.StatusMovedPermanently) + }) + mux.HandleFunc("/archive-target", func(w http.ResponseWriter, _ *http.Request) { + followed.Store(true) + w.WriteHeader(http.StatusOK) }) - // First request is made, and it makes the client aware of rate reset time being in the future. - req, _ := client.NewRequest("GET", "first", nil) - client.Do(context.Background(), req, nil) + resp, err := client.roundTripWithOptionalFollowRedirect(t.Context(), "archive", 2) + if err != nil { + t.Fatalf("Unexpected error on same-host redirect: %v", err) + } + if resp != nil && resp.Body != nil { + resp.Body.Close() + } + if !followed.Load() { + t.Error("Expected same-host redirect to be followed.") + } +} - // Second request should not cause a network call to be made, since client can predict a rate limit error. - req, _ = client.NewRequest("GET", "second", nil) - _, err := client.Do(context.Background(), req, nil) +func TestSanitizeURL(t *testing.T) { + t.Parallel() + tests := []struct { + in, want string + }{ + {"/?a=b", "/?a=b"}, + {"/?a=b&client_secret=secret", "/?a=b&client_secret=REDACTED"}, + {"/?a=b&client_id=id&client_secret=secret", "/?a=b&client_id=id&client_secret=REDACTED"}, + {"/?a=b&access_token=secret", "/?a=b&access_token=REDACTED"}, + {"/?a=b&token=secret", "/?a=b&token=REDACTED"}, + {"/?client_secret=s&access_token=t&token=u", "/?access_token=REDACTED&client_secret=REDACTED&token=REDACTED"}, + } - if madeNetworkCall { - t.Fatal("Network call was made, even though rate limit is known to still be exceeded.") + for _, tt := range tests { + inURL, _ := url.Parse(tt.in) + want, _ := url.Parse(tt.want) + + if got := sanitizeURL(inURL); !cmp.Equal(got, want) { + t.Errorf("sanitizeURL(%v) returned %v, want %v", tt.in, got, want) + } + } +} + +func TestCheckResponse(t *testing.T) { + t.Parallel() + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusBadRequest, + Body: io.NopCloser(strings.NewReader(`{"message":"m", + "errors": [{"resource": "r", "field": "f", "code": "c"}], + "block": {"reason": "dmca", "created_at": ` + referenceTimeStr + `}}`)), } + var err *ErrorResponse + errors.As(CheckResponse(res), &err) if err == nil { - t.Error("Expected error to be returned.") + t.Error("Expected error response.") } - rateLimitErr, ok := err.(*RateLimitError) - if !ok { - t.Fatalf("Expected a *RateLimitError error; got %#v.", err) + + want := &ErrorResponse{ + Response: res, + Message: "m", + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Block: &ErrorBlock{ + Reason: "dmca", + CreatedAt: &referenceTimestamp, + }, } - if got, want := rateLimitErr.Rate.Limit, 60; got != want { - t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) + if !errors.Is(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) } - if got, want := rateLimitErr.Rate.Remaining, 0; got != want { - t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) +} + +func TestCheckResponse_RateLimit(t *testing.T) { + t.Parallel() + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusForbidden, + Header: http.Header{}, + Body: io.NopCloser(strings.NewReader(`{"message":"m", + "documentation_url": "url"}`)), } - if rateLimitErr.Rate.Reset.UTC() != reset { - t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) + res.Header.Set(HeaderRateLimit, "60") + res.Header.Set(HeaderRateRemaining, "0") + res.Header.Set(HeaderRateUsed, "1") + res.Header.Set(HeaderRateReset, "243424") + res.Header.Set(HeaderRateResource, "core") + + var err *RateLimitError + errors.As(CheckResponse(res), &err) + + if err == nil { + t.Error("Expected error response.") + } + + want := &RateLimitError{ + Rate: parseRate(res), + Response: res, + Message: "m", + } + if !errors.Is(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) } } -// Ensure *AbuseRateLimitError is returned when the response indicates that -// the client has triggered an abuse detection mechanism. -func TestDo_rateLimit_abuseRateLimitError(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestCheckResponse_AbuseRateLimit(t *testing.T) { + t.Parallel() + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusForbidden, + Body: io.NopCloser(strings.NewReader(`{"message":"m", + "documentation_url": "docs.github.com/en/rest/overview/resources-in-the-rest-api#abuse-rate-limits"}`)), + } + var err *AbuseRateLimitError + errors.As(CheckResponse(res), &err) - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusForbidden) - // When the abuse rate limit error is of the "temporarily blocked from content creation" type, - // there is no "Retry-After" header. - fmt.Fprintln(w, `{ - "message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.", - "documentation_url": "https://developer.github.com/v3/#abuse-rate-limits" -}`) - }) + if err == nil { + t.Error("Expected error response.") + } + + want := &AbuseRateLimitError{ + Response: res, + Message: "m", + } + if !errors.Is(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) + } +} + +// TestCheckResponse_RateLimit_TooManyRequests tests that HTTP 429 with +// X-RateLimit-Remaining: 0 is correctly detected as RateLimitError. +// GitHub API can return either 403 or 429 for rate limiting. +// See: https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28 +func TestCheckResponse_RateLimit_TooManyRequests(t *testing.T) { + t.Parallel() + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusTooManyRequests, + Header: http.Header{}, + Body: io.NopCloser(strings.NewReader(`{"message":"m", + "documentation_url": "url"}`)), + } + res.Header.Set(HeaderRateLimit, "60") + res.Header.Set(HeaderRateRemaining, "0") + res.Header.Set(HeaderRateUsed, "60") + res.Header.Set(HeaderRateReset, "243424") + res.Header.Set(HeaderRateResource, "core") - req, _ := client.NewRequest("GET", ".", nil) - _, err := client.Do(context.Background(), req, nil) + var err *RateLimitError + errors.As(CheckResponse(res), &err) if err == nil { - t.Error("Expected error to be returned.") + t.Error("Expected error response.") } - abuseRateLimitErr, ok := err.(*AbuseRateLimitError) - if !ok { - t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err) + + want := &RateLimitError{ + Rate: parseRate(res), + Response: res, + Message: "m", } - if got, want := abuseRateLimitErr.RetryAfter, (*time.Duration)(nil); got != want { - t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + if !errors.Is(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) } } -// Ensure *AbuseRateLimitError is returned when the response indicates that -// the client has triggered an abuse detection mechanism on GitHub Enterprise. -func TestDo_rateLimit_abuseRateLimitErrorEnterprise(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusForbidden) - // When the abuse rate limit error is of the "temporarily blocked from content creation" type, - // there is no "Retry-After" header. - // This response returns a documentation url like the one returned for GitHub Enterprise, this - // url changes between versions but follows roughly the same format. - fmt.Fprintln(w, `{ - "message": "You have triggered an abuse detection mechanism and have been temporarily blocked from content creation. Please retry your request again later.", - "documentation_url": "https://developer.github.com/enterprise/2.12/v3/#abuse-rate-limits" -}`) - }) +// TestCheckResponse_AbuseRateLimit_TooManyRequests tests that HTTP 429 with +// secondary rate limit documentation_url is correctly detected as AbuseRateLimitError. +// GitHub API can return either 403 or 429 for secondary rate limits. +// See: https://docs.github.com/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits +func TestCheckResponse_AbuseRateLimit_TooManyRequests(t *testing.T) { + t.Parallel() + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusTooManyRequests, + Header: http.Header{}, + Body: io.NopCloser(strings.NewReader(`{"message":"m", + "documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits"}`)), + } + res.Header.Set(headerRetryAfter, "60") - req, _ := client.NewRequest("GET", ".", nil) - _, err := client.Do(context.Background(), req, nil) + var err *AbuseRateLimitError + errors.As(CheckResponse(res), &err) if err == nil { - t.Error("Expected error to be returned.") + t.Fatal("Expected error response.") } - abuseRateLimitErr, ok := err.(*AbuseRateLimitError) - if !ok { - t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err) + + if err.Response != res { + t.Errorf("Response = %v, want %v", err.Response, res) } - if got, want := abuseRateLimitErr.RetryAfter, (*time.Duration)(nil); got != want { - t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + if err.Message != "m" { + t.Errorf("Message = %q, want %q", err.Message, "m") + } + if err.RetryAfter == nil { + t.Error("Expected RetryAfter to be set") + } else if *err.RetryAfter != 60*time.Second { + t.Errorf("RetryAfter = %v, want %v", *err.RetryAfter, 60*time.Second) } } -// Ensure *AbuseRateLimitError.RetryAfter is parsed correctly. -func TestDo_rateLimit_abuseRateLimitError_retryAfter(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestCheckResponse_RedirectionError(t *testing.T) { + t.Parallel() + urlStr := "/foo/bar" - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Header().Set("Retry-After", "123") // Retry after value of 123 seconds. - w.WriteHeader(http.StatusForbidden) - fmt.Fprintln(w, `{ - "message": "You have triggered an abuse detection mechanism ...", - "documentation_url": "https://developer.github.com/v3/#abuse-rate-limits" -}`) - }) + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusFound, + Header: http.Header{}, + Body: io.NopCloser(strings.NewReader(``)), + } + res.Header.Set("Location", urlStr) + var err *RedirectionError + errors.As(CheckResponse(res), &err) + + if err == nil { + t.Error("Expected error response.") + } + + wantedURL, parseErr := url.Parse(urlStr) + if parseErr != nil { + t.Errorf("Error parsing fixture url: %v", parseErr) + } + + want := &RedirectionError{ + Response: res, + StatusCode: http.StatusFound, + Location: wantedURL, + } + if !errors.Is(err, want) { + t.Errorf("Error = %#v, want %#v", err, want) + } +} - req, _ := client.NewRequest("GET", ".", nil) - _, err := client.Do(context.Background(), req, nil) +func TestCompareHttpResponse(t *testing.T) { + t.Parallel() + testcases := map[string]struct { + h1 *http.Response + h2 *http.Response + expected bool + }{ + "both are nil": { + expected: true, + }, + "both are non nil - same StatusCode": { + expected: true, + h1: &http.Response{StatusCode: 200}, + h2: &http.Response{StatusCode: 200}, + }, + "both are non nil - different StatusCode": { + expected: false, + h1: &http.Response{StatusCode: 200}, + h2: &http.Response{StatusCode: 404}, + }, + "one is nil, other is not": { + expected: false, + h2: &http.Response{}, + }, + } - if err == nil { - t.Error("Expected error to be returned.") + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + t.Parallel() + v := compareHTTPResponse(tc.h1, tc.h2) + if tc.expected != v { + t.Errorf("Expected %t, got %t for (%#v, %#v)", tc.expected, v, tc.h1, tc.h2) + } + }) } - abuseRateLimitErr, ok := err.(*AbuseRateLimitError) - if !ok { - t.Fatalf("Expected a *AbuseRateLimitError error; got %#v.", err) +} + +func TestErrorResponse_Is(t *testing.T) { + t.Parallel() + err := &ErrorResponse{ + Response: &http.Response{}, + Message: "m", + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", } - if abuseRateLimitErr.RetryAfter == nil { - t.Fatalf("abuseRateLimitErr RetryAfter is nil, expected not-nil") + testcases := map[string]struct { + wantSame bool + otherError error + }{ + "errors are same": { + wantSame: true, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Message": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m1", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - DocumentationURL": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://google.com", + }, + }, + "errors have different values - Response is nil": { + wantSame: false, + otherError: &ErrorResponse{ + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Errors": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r1", Field: "f1", Code: "c1"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Errors have different length": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Block - one is nil, other is not": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Block - different Reason": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r1", + CreatedAt: &referenceTimestamp, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Block - different CreatedAt #1": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: nil, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different values - Block - different CreatedAt #2": { + wantSame: false, + otherError: &ErrorResponse{ + Response: &http.Response{}, + Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, + Message: "m", + Block: &ErrorBlock{ + Reason: "r", + CreatedAt: &Timestamp{time.Date(2017, time.March, 17, 15, 39, 46, 0, time.UTC)}, + }, + DocumentationURL: "https://github.com", + }, + }, + "errors have different types": { + wantSame: false, + otherError: errors.New("github"), + }, } - if got, want := *abuseRateLimitErr.RetryAfter, 123*time.Second; got != want { - t.Errorf("abuseRateLimitErr RetryAfter = %v, want %v", got, want) + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + t.Parallel() + if tc.wantSame != err.Is(tc.otherError) { + t.Errorf("Error = %#v, want %#v", err, tc.otherError) + } + }) } } -func TestDo_noContent(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRateLimitError_Is(t *testing.T) { + t.Parallel() + err := &RateLimitError{ + Response: &http.Response{}, + Message: "Github", + } + testcases := map[string]struct { + wantSame bool + err *RateLimitError + otherError error + }{ + "errors are same": { + wantSame: true, + err: err, + otherError: &RateLimitError{ + Response: &http.Response{}, + Message: "Github", + }, + }, + "errors are same - Response is nil": { + wantSame: true, + err: &RateLimitError{ + Message: "Github", + }, + otherError: &RateLimitError{ + Message: "Github", + }, + }, + "errors have different values - Rate": { + wantSame: false, + err: err, + otherError: &RateLimitError{ + Rate: Rate{Limit: 10}, + Response: &http.Response{}, + Message: "Gitlab", + }, + }, + "errors have different values - Response is nil": { + wantSame: false, + err: err, + otherError: &RateLimitError{ + Message: "Github", + }, + }, + "errors have different values - StatusCode": { + wantSame: false, + err: err, + otherError: &RateLimitError{ + Response: &http.Response{StatusCode: 200}, + Message: "Github", + }, + }, + "errors have different types": { + wantSame: false, + err: err, + otherError: errors.New("github"), + }, + } - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNoContent) - }) + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + t.Parallel() + if tc.wantSame != tc.err.Is(tc.otherError) { + t.Errorf("Error = %#v, want %#v", tc.err, tc.otherError) + } + }) + } +} - var body json.RawMessage +func TestAbuseRateLimitError_Is(t *testing.T) { + t.Parallel() + t1 := 1 * time.Second + t2 := 2 * time.Second + err := &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: &t1, + } + testcases := map[string]struct { + wantSame bool + err *AbuseRateLimitError + otherError error + }{ + "errors are same": { + wantSame: true, + err: err, + otherError: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: &t1, + }, + }, + "errors are same - Response is nil": { + wantSame: true, + err: &AbuseRateLimitError{ + Message: "Github", + RetryAfter: &t1, + }, + otherError: &AbuseRateLimitError{ + Message: "Github", + RetryAfter: &t1, + }, + }, + "errors have different values - Message": { + wantSame: false, + err: err, + otherError: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Gitlab", + RetryAfter: nil, + }, + }, + "errors have different values - RetryAfter": { + wantSame: false, + err: err, + otherError: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: &t2, + }, + }, + "errors have different values - Response is nil": { + wantSame: false, + err: err, + otherError: &AbuseRateLimitError{ + Message: "Github", + RetryAfter: &t1, + }, + }, + "errors have different values - StatusCode": { + wantSame: false, + err: err, + otherError: &AbuseRateLimitError{ + Response: &http.Response{StatusCode: 200}, + Message: "Github", + RetryAfter: &t1, + }, + }, + "errors have different types": { + wantSame: false, + err: err, + otherError: errors.New("github"), + }, + "errors are same - RetryAfter equal value but distinct pointers": { + wantSame: true, + err: err, + otherError: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: &t1, + }, + }, + "errors are same - RetryAfter both nil": { + wantSame: true, + err: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: nil, + }, + otherError: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: nil, + }, + }, + "errors differ - one RetryAfter nil, other non-nil": { + wantSame: false, + err: err, + otherError: &AbuseRateLimitError{ + Response: &http.Response{}, + Message: "Github", + RetryAfter: nil, + }, + }, + } - req, _ := client.NewRequest("GET", ".", nil) - _, err := client.Do(context.Background(), req, &body) - if err != nil { - t.Fatalf("Do returned unexpected error: %v", err) + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + t.Parallel() + if tc.wantSame != tc.err.Is(tc.otherError) { + t.Errorf("Error = %#v, want %#v", tc.err, tc.otherError) + } + }) } } -func TestSanitizeURL(t *testing.T) { - tests := []struct { - in, want string +func TestAcceptedError_Is(t *testing.T) { + t.Parallel() + err := &AcceptedError{Raw: []byte("Github")} + testcases := map[string]struct { + wantSame bool + otherError error }{ - {"/?a=b", "/?a=b"}, - {"/?a=b&client_secret=secret", "/?a=b&client_secret=REDACTED"}, - {"/?a=b&client_id=id&client_secret=secret", "/?a=b&client_id=id&client_secret=REDACTED"}, + "errors are same": { + wantSame: true, + otherError: &AcceptedError{Raw: []byte("Github")}, + }, + "errors have different values": { + wantSame: false, + otherError: &AcceptedError{Raw: []byte("Gitlab")}, + }, + "errors have different types": { + wantSame: false, + otherError: errors.New("github"), + }, } - for _, tt := range tests { - inURL, _ := url.Parse(tt.in) - want, _ := url.Parse(tt.want) - - if got := sanitizeURL(inURL); !reflect.DeepEqual(got, want) { - t.Errorf("sanitizeURL(%v) returned %v, want %v", tt.in, got, want) - } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + t.Parallel() + if tc.wantSame != err.Is(tc.otherError) { + t.Errorf("Error = %#v, want %#v", err, tc.otherError) + } + }) } } -func TestCheckResponse(t *testing.T) { +// Ensure that we properly handle API errors that do not contain a response body. +func TestCheckResponse_noBody(t *testing.T) { + t.Parallel() res := &http.Response{ Request: &http.Request{}, StatusCode: http.StatusBadRequest, - Body: ioutil.NopCloser(strings.NewReader(`{"message":"m", - "errors": [{"resource": "r", "field": "f", "code": "c"}], - "block": {"reason": "dmca", "created_at": "2016-03-17T15:39:46Z"}}`)), + Body: io.NopCloser(strings.NewReader("")), } - err := CheckResponse(res).(*ErrorResponse) + var err *ErrorResponse + errors.As(CheckResponse(res), &err) if err == nil { - t.Errorf("Expected error response.") + t.Error("Expected error response.") } want := &ErrorResponse{ Response: res, - Message: "m", - Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, - Block: &struct { - Reason string `json:"reason,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - }{ - Reason: "dmca", - CreatedAt: &Timestamp{time.Date(2016, time.March, 17, 15, 39, 46, 0, time.UTC)}, - }, } - if !reflect.DeepEqual(err, want) { + if !errors.Is(err, want) { t.Errorf("Error = %#v, want %#v", err, want) } } -// ensure that we properly handle API errors that do not contain a response body -func TestCheckResponse_noBody(t *testing.T) { +func TestCheckResponse_unexpectedErrorStructure(t *testing.T) { + t.Parallel() + httpBody := `{"message":"m", "errors": ["error 1"]}` res := &http.Response{ Request: &http.Request{}, StatusCode: http.StatusBadRequest, - Body: ioutil.NopCloser(strings.NewReader("")), + Body: io.NopCloser(strings.NewReader(httpBody)), } - err := CheckResponse(res).(*ErrorResponse) + var err *ErrorResponse + errors.As(CheckResponse(res), &err) if err == nil { - t.Errorf("Expected error response.") + t.Error("Expected error response.") } want := &ErrorResponse{ Response: res, + Message: "m", + Errors: []Error{{Message: "error 1"}}, } - if !reflect.DeepEqual(err, want) { + if !errors.Is(err, want) { t.Errorf("Error = %#v, want %#v", err, want) } + data, err2 := io.ReadAll(err.Response.Body) + if err2 != nil { + t.Fatalf("failed to read response body: %v", err) + } + if got := string(data); got != httpBody { + t.Errorf("ErrorResponse.Response.Body = %q, want %q", got, httpBody) + } +} + +// TestCheckResponse_LargeBodyTruncated verifies that CheckResponse reads at +// most maxErrorBodySize bytes from an error response body, so that a +// malicious or buggy server cannot cause the client to allocate unbounded +// memory. +func TestCheckResponse_LargeBodyTruncated(t *testing.T) { + t.Parallel() + // Build a body that is one byte larger than the cap. + body := strings.Repeat("x", maxErrorBodySize+1) + res := &http.Response{ + Request: &http.Request{}, + StatusCode: http.StatusBadRequest, + Body: io.NopCloser(strings.NewReader(body)), + } + + // CheckResponse should not return an error from the read itself; the HTTP + // error status is the expected error. + if err := CheckResponse(res); err == nil { + t.Fatal("Expected error from CheckResponse, got nil") + } + + // After CheckResponse, the body is restored with the (truncated) bytes that + // were actually read. Verify the restored body is exactly maxErrorBodySize + // bytes — not the full maxErrorBodySize+1 that the server sent. + restored, err := io.ReadAll(res.Body) + if err != nil { + t.Fatalf("io.ReadAll on restored body: %v", err) + } + if got, want := len(restored), maxErrorBodySize; got != want { + t.Errorf("restored body length = %v, want %v (maxErrorBodySize)", got, want) + } } func TestParseBooleanResponse_true(t *testing.T) { + t.Parallel() result, err := parseBoolResponse(nil) if err != nil { t.Errorf("parseBoolResponse returned error: %+v", err) @@ -857,6 +4184,7 @@ func TestParseBooleanResponse_true(t *testing.T) { } func TestParseBooleanResponse_false(t *testing.T) { + t.Parallel() v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusNotFound}} result, err := parseBoolResponse(v) if err != nil { @@ -869,11 +4197,12 @@ func TestParseBooleanResponse_false(t *testing.T) { } func TestParseBooleanResponse_error(t *testing.T) { + t.Parallel() v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusBadRequest}} result, err := parseBoolResponse(v) if err == nil { - t.Errorf("Expected error to be returned.") + t.Error("Expected error to be returned.") } if want := false; result != want { @@ -882,94 +4211,93 @@ func TestParseBooleanResponse_error(t *testing.T) { } func TestErrorResponse_Error(t *testing.T) { + t.Parallel() res := &http.Response{Request: &http.Request{}} err := ErrorResponse{Message: "m", Response: res} if err.Error() == "" { - t.Errorf("Expected non-empty ErrorResponse.Error()") + t.Error("Expected non-empty ErrorResponse.Error()") + } + + // don't panic if request is nil + res = &http.Response{} + err = ErrorResponse{Message: "m", Response: res} + if err.Error() == "" { + t.Error("Expected non-empty ErrorResponse.Error()") + } + + // don't panic if response is nil + err = ErrorResponse{Message: "m"} + if err.Error() == "" { + t.Error("Expected non-empty ErrorResponse.Error()") } } func TestError_Error(t *testing.T) { + t.Parallel() err := Error{} if err.Error() == "" { - t.Errorf("Expected non-empty Error.Error()") + t.Error("Expected non-empty Error.Error()") } } -func TestRateLimits(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"resources":{ - "core": {"limit":2,"remaining":1,"reset":1372700873}, - "search": {"limit":3,"remaining":2,"reset":1372700874} - }}`) - }) +func TestSetCredentialsAsHeaders(t *testing.T) { + t.Parallel() + req := new(http.Request) + id, secret := "id", "secret" + modifiedRequest := setCredentialsAsHeaders(req, id, secret) - rate, _, err := client.RateLimits(context.Background()) - if err != nil { - t.Errorf("RateLimits returned error: %v", err) + actualID, actualSecret, ok := modifiedRequest.BasicAuth() + if !ok { + t.Error("request does not contain basic credentials") } - want := &RateLimits{ - Core: &Rate{ - Limit: 2, - Remaining: 1, - Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 53, 0, time.UTC).Local()}, - }, - Search: &Rate{ - Limit: 3, - Remaining: 2, - Reset: Timestamp{time.Date(2013, time.July, 1, 17, 47, 54, 0, time.UTC).Local()}, - }, - } - if !reflect.DeepEqual(rate, want) { - t.Errorf("RateLimits returned %+v, want %+v", rate, want) + if actualID != id { + t.Errorf("id is %v, want %v", actualID, id) } - if got, want := client.rateLimits[coreCategory], *want.Core; got != want { - t.Errorf("client.rateLimits[coreCategory] is %+v, want %+v", got, want) - } - if got, want := client.rateLimits[searchCategory], *want.Search; got != want { - t.Errorf("client.rateLimits[searchCategory] is %+v, want %+v", got, want) + if actualSecret != secret { + t.Errorf("secret is %v, want %v", actualSecret, secret) } } func TestUnauthenticatedRateLimitedTransport(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - var v, want string - q := r.URL.Query() - if v, want = q.Get("client_id"), "id"; v != want { - t.Errorf("OAuth Client ID = %v, want %v", v, want) + clientID, clientSecret := "id", "secret" + mux.HandleFunc("/", func(_ http.ResponseWriter, r *http.Request) { + id, secret, ok := r.BasicAuth() + if !ok { + t.Error("request does not contain basic auth credentials") } - if v, want = q.Get("client_secret"), "secret"; v != want { - t.Errorf("OAuth Client Secret = %v, want %v", v, want) + if id != clientID { + t.Errorf("request contained basic auth username %q, want %q", id, clientID) + } + if secret != clientSecret { + t.Errorf("request contained basic auth password %q, want %q", secret, clientSecret) } }) tp := &UnauthenticatedRateLimitedTransport{ - ClientID: "id", - ClientSecret: "secret", - } - unauthedClient := NewClient(tp.Client()) - unauthedClient.BaseURL = client.BaseURL - req, _ := unauthedClient.NewRequest("GET", ".", nil) - unauthedClient.Do(context.Background(), req, nil) + ClientID: clientID, + ClientSecret: clientSecret, + } + unauthedClient := mustNewClient(t, WithHTTPClient(tp.Client())) + unauthedClient.baseURL = client.baseURL + req, _ := unauthedClient.NewRequest(t.Context(), "GET", ".", nil) + _, err := unauthedClient.Do(req, nil) + assertNilError(t, err) } func TestUnauthenticatedRateLimitedTransport_missingFields(t *testing.T) { + t.Parallel() // missing ClientID tp := &UnauthenticatedRateLimitedTransport{ ClientSecret: "secret", } _, err := tp.RoundTrip(nil) if err == nil { - t.Errorf("Expected error to be returned") + t.Error("Expected error to be returned") } // missing ClientSecret @@ -978,18 +4306,19 @@ func TestUnauthenticatedRateLimitedTransport_missingFields(t *testing.T) { } _, err = tp.RoundTrip(nil) if err == nil { - t.Errorf("Expected error to be returned") + t.Error("Expected error to be returned") } } func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) { + t.Parallel() // default transport tp := &UnauthenticatedRateLimitedTransport{ ClientID: "id", ClientSecret: "secret", } if tp.transport() != http.DefaultTransport { - t.Errorf("Expected http.DefaultTransport to be used.") + t.Error("Expected http.DefaultTransport to be used.") } // custom transport @@ -999,20 +4328,20 @@ func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) { Transport: &http.Transport{}, } if tp.transport() == http.DefaultTransport { - t.Errorf("Expected custom transport to be used.") + t.Error("Expected custom transport to be used.") } } func TestBasicAuthTransport(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) username, password, otp := "u", "p", "123456" - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/", func(_ http.ResponseWriter, r *http.Request) { u, p, ok := r.BasicAuth() if !ok { - t.Errorf("request does not contain basic auth credentials") + t.Error("request does not contain basic auth credentials") } if u != username { t.Errorf("request contained basic auth username %q, want %q", u, username) @@ -1030,17 +4359,19 @@ func TestBasicAuthTransport(t *testing.T) { Password: password, OTP: otp, } - basicAuthClient := NewClient(tp.Client()) - basicAuthClient.BaseURL = client.BaseURL - req, _ := basicAuthClient.NewRequest("GET", ".", nil) - basicAuthClient.Do(context.Background(), req, nil) + basicAuthClient := mustNewClient(t, WithHTTPClient(tp.Client())) + basicAuthClient.baseURL = client.baseURL + req, _ := basicAuthClient.NewRequest(t.Context(), "GET", ".", nil) + _, err := basicAuthClient.Do(req, nil) + assertNilError(t, err) } func TestBasicAuthTransport_transport(t *testing.T) { + t.Parallel() // default transport tp := &BasicAuthTransport{} if tp.transport() != http.DefaultTransport { - t.Errorf("Expected http.DefaultTransport to be used.") + t.Error("Expected http.DefaultTransport to be used.") } // custom transport @@ -1048,11 +4379,12 @@ func TestBasicAuthTransport_transport(t *testing.T) { Transport: &http.Transport{}, } if tp.transport() == http.DefaultTransport { - t.Errorf("Expected custom transport to be used.") + t.Error("Expected custom transport to be used.") } } func TestFormatRateReset(t *testing.T) { + t.Parallel() d := 120*time.Minute + 12*time.Second got := formatRateReset(d) want := "[rate reset in 120m12s]" @@ -1090,6 +4422,7 @@ func TestFormatRateReset(t *testing.T) { } func TestNestedStructAccessorNoPanic(t *testing.T) { + t.Parallel() issue := &Issue{User: nil} got := issue.GetUser().GetPlan().GetName() want := "" @@ -1097,3 +4430,293 @@ func TestNestedStructAccessorNoPanic(t *testing.T) { t.Errorf("Issues.Get.GetUser().GetPlan().GetName() returned %+v, want %+v", got, want) } } + +func TestTwoFactorAuthError(t *testing.T) { + t.Parallel() + u, err := url.Parse("https://example.com") + if err != nil { + t.Fatal(err) + } + + e := &TwoFactorAuthError{ + Response: &http.Response{ + Request: &http.Request{Method: "PUT", URL: u}, + StatusCode: http.StatusTooManyRequests, + }, + Message: "", + } + if got, want := e.Error(), "PUT https://example.com: 429 []"; got != want { + t.Errorf("TwoFactorAuthError = %q, want %q", got, want) + } +} + +func TestRateLimitError(t *testing.T) { + t.Parallel() + u, err := url.Parse("https://example.com") + if err != nil { + t.Fatal(err) + } + + r := &RateLimitError{ + Response: &http.Response{ + Request: &http.Request{Method: "PUT", URL: u}, + StatusCode: http.StatusTooManyRequests, + }, + Message: "", + } + if got, want := r.Error(), "PUT https://example.com: 429 [rate limit was reset"; !strings.Contains(got, want) { + t.Errorf("RateLimitError = %q, want %q", got, want) + } +} + +func TestAcceptedError(t *testing.T) { + t.Parallel() + a := &AcceptedError{} + if got, want := a.Error(), "try again later"; !strings.Contains(got, want) { + t.Errorf("AcceptedError = %q, want %q", got, want) + } +} + +func TestAbuseRateLimitError(t *testing.T) { + t.Parallel() + u, err := url.Parse("https://example.com") + if err != nil { + t.Fatal(err) + } + + t.Run("nil RetryAfter", func(t *testing.T) { + t.Parallel() + r := &AbuseRateLimitError{ + Response: &http.Response{ + Request: &http.Request{Method: "PUT", URL: u}, + StatusCode: http.StatusTooManyRequests, + }, + Message: "", + } + if got, want := r.Error(), "PUT https://example.com: 429 "; got != want { + t.Errorf("AbuseRateLimitError = %q, want %q", got, want) + } + }) + + t.Run("with RetryAfter", func(t *testing.T) { + t.Parallel() + d := 60 * time.Second + r := &AbuseRateLimitError{ + Response: &http.Response{ + Request: &http.Request{Method: "GET", URL: u}, + StatusCode: http.StatusForbidden, + }, + Message: "rate limited", + RetryAfter: &d, + } + if got, want := r.Error(), "GET https://example.com: 403 rate limited [retry after 1m0s]"; got != want { + t.Errorf("AbuseRateLimitError = %q, want %q", got, want) + } + }) + + t.Run("zero RetryAfter", func(t *testing.T) { + t.Parallel() + d := 0 * time.Second + r := &AbuseRateLimitError{ + Response: &http.Response{ + Request: &http.Request{Method: "POST", URL: u}, + StatusCode: http.StatusForbidden, + }, + Message: "rate limited", + RetryAfter: &d, + } + if got, want := r.Error(), "POST https://example.com: 403 rate limited"; got != want { + t.Errorf("AbuseRateLimitError = %q, want %q", got, want) + } + }) +} + +func TestBareDo_returnsOpenBody(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + expectedBody := "Hello from the other side !" + + mux.HandleFunc("/test-url", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, expectedBody) + }) + + req, err := client.NewRequest(t.Context(), "GET", "test-url", nil) + if err != nil { + t.Fatalf("client.NewRequest returned error: %v", err) + } + + resp, err := client.BareDo(req) + if err != nil { + t.Fatalf("client.BareDo returned error: %v", err) + } + + got, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatalf("io.ReadAll returned error: %v", err) + } + if string(got) != expectedBody { + t.Fatalf("Expected %q, got %q", expectedBody, string(got)) + } + if err := resp.Body.Close(); err != nil { + t.Fatalf("resp.Body.Close() returned error: %v", err) + } +} + +func TestError_UnmarshalJSON(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &Error{}, `{ + "resource": "", + "field": "", + "code": "", + "message": "" + }`) + + u := &Error{ + Resource: "res", + Field: "field", + Code: "code", + Message: "msg", + } + + want := `{ + "resource": "res", + "field": "field", + "code": "code", + "message": "msg" + }` + + testJSONMarshal(t, u, want) +} + +func TestParseTokenExpiration(t *testing.T) { + t.Parallel() + tests := []struct { + header string + want Timestamp + }{ + { + header: "", + want: Timestamp{}, + }, + { + header: "this is a garbage", + want: Timestamp{}, + }, + { + header: "2021-09-03 02:34:04 UTC", + want: Timestamp{time.Date(2021, time.September, 3, 2, 34, 4, 0, time.UTC)}, + }, + { + header: "2021-09-03 14:34:04 UTC", + want: Timestamp{time.Date(2021, time.September, 3, 14, 34, 4, 0, time.UTC)}, + }, + // Some tokens include the timezone offset instead of the timezone. + // https://github.com/google/go-github/issues/2649 + { + header: "2023-04-26 20:23:26 +0200", + want: Timestamp{time.Date(2023, time.April, 26, 18, 23, 26, 0, time.UTC)}, + }, + } + + for _, tt := range tests { + res := &http.Response{ + Request: &http.Request{}, + Header: http.Header{}, + } + + res.Header.Set(headerTokenExpiration, tt.header) + exp := parseTokenExpiration(res) + if !exp.Equal(tt.want) { + t.Errorf("parseTokenExpiration of %q\nreturned %#v\n want %#v", tt.header, exp, tt.want) + } + } +} + +func TestClientCopy_leak_transport(t *testing.T) { + t.Parallel() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + accessToken := r.Header.Get("Authorization") + _, _ = fmt.Fprintf(w, `{"login": "%v"}`, accessToken) + })) + clientPreconfiguredWithURLs := mustNewClient(t, WithURLs(&srv.URL, &srv.URL)) + + aliceClient, err := clientPreconfiguredWithURLs.Clone(WithAuthToken("alice")) + if err != nil { + t.Fatal(err) + } + bobClient, err := clientPreconfiguredWithURLs.Clone(WithAuthToken("bob")) + if err != nil { + t.Fatal(err) + } + + alice, _, err := aliceClient.Users.Get(t.Context(), "") + if err != nil { + t.Fatal(err) + } + + assertNoDiff(t, "Bearer alice", alice.GetLogin()) + + bob, _, err := bobClient.Users.Get(t.Context(), "") + if err != nil { + t.Fatal(err) + } + + assertNoDiff(t, "Bearer bob", bob.GetLogin()) +} + +func TestPtr(t *testing.T) { + t.Parallel() + equal := func(t *testing.T, want, got any) { + t.Helper() + if !cmp.Equal(want, got) { + t.Errorf("want %#v, got %#v", want, got) + } + } + + equal(t, true, *Ptr(true)) + equal(t, int(10), *Ptr(int(10))) + equal(t, int64(-10), *Ptr(int64(-10))) + equal(t, "str", *Ptr("str")) +} + +func TestDeploymentProtectionRuleEvent_GetRunID(t *testing.T) { + t.Parallel() + + var want int64 = 123456789 + url := "https://api.github.com/repos/dummy-org/dummy-repo/actions/runs/123456789/deployment_protection_rule" + + e := DeploymentProtectionRuleEvent{ + DeploymentCallbackURL: &url, + } + + got, _ := e.GetRunID() + if got != want { + t.Errorf("want %#v, got %#v", want, got) + } + + want = 123456789 + url = "repos/dummy-org/dummy-repo/actions/runs/123456789/deployment_protection_rule" + + e = DeploymentProtectionRuleEvent{ + DeploymentCallbackURL: &url, + } + + got, _ = e.GetRunID() + if got != want { + t.Errorf("want %#v, got %#v", want, got) + } + + want = -1 + url = "https://api.github.com/repos/dummy-org/dummy-repo/actions/runs/abc123/deployment_protection_rule" + got, err := e.GetRunID() + if err == nil { + t.Error("Expected error to be returned") + } + + if got != want { + t.Errorf("want %#v, got %#v", want, got) + } +} diff --git a/github/gitignore.go b/github/gitignore.go index 2f691bc323e..9a2c8b2d147 100644 --- a/github/gitignore.go +++ b/github/gitignore.go @@ -13,7 +13,7 @@ import ( // GitignoresService provides access to the gitignore related functions in the // GitHub API. // -// GitHub API docs: https://developer.github.com/v3/gitignore/ +// GitHub API docs: https://docs.github.com/rest/gitignore?apiVersion=2022-11-28 type GitignoresService service // Gitignore represents a .gitignore file as returned by the GitHub API. @@ -28,15 +28,17 @@ func (g Gitignore) String() string { // List all available Gitignore templates. // -// GitHub API docs: https://developer.github.com/v3/gitignore/#listing-available-templates -func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error) { - req, err := s.client.NewRequest("GET", "gitignore/templates", nil) +// GitHub API docs: https://docs.github.com/rest/gitignore/gitignore?apiVersion=2022-11-28#get-all-gitignore-templates +// +//meta:operation GET /gitignore/templates +func (s *GitignoresService) List(ctx context.Context) ([]string, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "gitignore/templates", nil) if err != nil { return nil, nil, err } var availableTemplates []string - resp, err := s.client.Do(ctx, req, &availableTemplates) + resp, err := s.client.Do(req, &availableTemplates) if err != nil { return nil, resp, err } @@ -46,16 +48,18 @@ func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error // Get a Gitignore by name. // -// GitHub API docs: https://developer.github.com/v3/gitignore/#get-a-single-template -func (s GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/gitignore/gitignore?apiVersion=2022-11-28#get-a-gitignore-template +// +//meta:operation GET /gitignore/templates/{name} +func (s *GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) { u := fmt.Sprintf("gitignore/templates/%v", name) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - gitignore := new(Gitignore) - resp, err := s.client.Do(ctx, req, gitignore) + var gitignore *Gitignore + resp, err := s.client.Do(req, &gitignore) if err != nil { return nil, resp, err } diff --git a/github/gitignore_test.go b/github/gitignore_test.go index cdb82237ce7..1d21836ea4b 100644 --- a/github/gitignore_test.go +++ b/github/gitignore_test.go @@ -6,57 +6,83 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestGitignoresService_List(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gitignore/templates", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `["C", "Go"]`) }) - available, _, err := client.Gitignores.List(context.Background()) + ctx := t.Context() + available, _, err := client.Gitignores.List(ctx) if err != nil { t.Errorf("Gitignores.List returned error: %v", err) } want := []string{"C", "Go"} - if !reflect.DeepEqual(available, want) { + if !cmp.Equal(available, want) { t.Errorf("Gitignores.List returned %+v, want %+v", available, want) } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gitignores.List(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitignoresService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/gitignore/templates/name", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"name":"Name","source":"template source"}`) }) - gitignore, _, err := client.Gitignores.Get(context.Background(), "name") + ctx := t.Context() + gitignore, _, err := client.Gitignores.Get(ctx, "name") if err != nil { - t.Errorf("Gitignores.List returned error: %v", err) + t.Errorf("Gitignores.Get returned error: %v", err) } - want := &Gitignore{Name: String("Name"), Source: String("template source")} - if !reflect.DeepEqual(gitignore, want) { + want := &Gitignore{Name: Ptr("Name"), Source: Ptr("template source")} + if !cmp.Equal(gitignore, want) { t.Errorf("Gitignores.Get returned %+v, want %+v", gitignore, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Gitignores.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Gitignores.Get(ctx, "name") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestGitignoresService_Get_invalidTemplate(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Gitignores.Get(context.Background(), "%") + ctx := t.Context() + _, _, err := client.Gitignores.Get(ctx, "%") testURLParseError(t, err) } diff --git a/github/interactions.go b/github/interactions.go index b9965491db5..92bd24b4de9 100644 --- a/github/interactions.go +++ b/github/interactions.go @@ -8,7 +8,7 @@ package github // InteractionsService handles communication with the repository and organization related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/interactions/ +// GitHub API docs: https://docs.github.com/rest/interactions?apiVersion=2022-11-28 type InteractionsService service // InteractionRestriction represents the interaction restrictions for repository and organization. diff --git a/github/interactions_orgs.go b/github/interactions_orgs.go index af25f6567db..8ec5e893689 100644 --- a/github/interactions_orgs.go +++ b/github/interactions_orgs.go @@ -12,20 +12,20 @@ import ( // GetRestrictionsForOrg fetches the interaction restrictions for an organization. // -// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#get-interaction-restrictions-for-an-organization +// GitHub API docs: https://docs.github.com/rest/interactions/orgs?apiVersion=2022-11-28#get-interaction-restrictions-for-an-organization +// +//meta:operation GET /orgs/{org}/interaction-limits func (s *InteractionsService) GetRestrictionsForOrg(ctx context.Context, organization string) (*InteractionRestriction, *Response, error) { u := fmt.Sprintf("orgs/%v/interaction-limits", organization) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) - organizationInteractions := new(InteractionRestriction) - - resp, err := s.client.Do(ctx, req, organizationInteractions) + var organizationInteractions *InteractionRestriction + resp, err := s.client.Do(req, &organizationInteractions) if err != nil { return nil, resp, err } @@ -39,23 +39,23 @@ func (s *InteractionsService) GetRestrictionsForOrg(ctx context.Context, organiz // in public repositories for the given organization. // Possible values are: "existing_users", "contributors_only", "collaborators_only". // -// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#add-or-update-interaction-restrictions-for-an-organization +// GitHub API docs: https://docs.github.com/rest/interactions/orgs?apiVersion=2022-11-28#set-interaction-restrictions-for-an-organization +// +//meta:operation PUT /orgs/{org}/interaction-limits func (s *InteractionsService) UpdateRestrictionsForOrg(ctx context.Context, organization, limit string) (*InteractionRestriction, *Response, error) { u := fmt.Sprintf("orgs/%v/interaction-limits", organization) - interaction := &InteractionRestriction{Limit: String(limit)} + interaction := &InteractionRestriction{Limit: &limit} - req, err := s.client.NewRequest("PUT", u, interaction) + req, err := s.client.NewRequest(ctx, "PUT", u, interaction) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) - organizationInteractions := new(InteractionRestriction) - - resp, err := s.client.Do(ctx, req, organizationInteractions) + var organizationInteractions *InteractionRestriction + resp, err := s.client.Do(req, &organizationInteractions) if err != nil { return nil, resp, err } @@ -65,16 +65,17 @@ func (s *InteractionsService) UpdateRestrictionsForOrg(ctx context.Context, orga // RemoveRestrictionsFromOrg removes the interaction restrictions for an organization. // -// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#remove-interaction-restrictions-for-an-organization +// GitHub API docs: https://docs.github.com/rest/interactions/orgs?apiVersion=2022-11-28#remove-interaction-restrictions-for-an-organization +// +//meta:operation DELETE /orgs/{org}/interaction-limits func (s *InteractionsService) RemoveRestrictionsFromOrg(ctx context.Context, organization string) (*Response, error) { u := fmt.Sprintf("orgs/%v/interaction-limits", organization) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/interactions_orgs_test.go b/github/interactions_orgs_test.go index 88b7ff6fbb7..e4acf9603ec 100644 --- a/github/interactions_orgs_test.go +++ b/github/interactions_orgs_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestInteractionsService_GetRestrictionsForOrgs(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/interaction-limits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -24,57 +23,93 @@ func TestInteractionsService_GetRestrictionsForOrgs(t *testing.T) { fmt.Fprint(w, `{"origin":"organization"}`) }) - organizationInteractions, _, err := client.Interactions.GetRestrictionsForOrg(context.Background(), "o") + ctx := t.Context() + organizationInteractions, _, err := client.Interactions.GetRestrictionsForOrg(ctx, "o") if err != nil { t.Errorf("Interactions.GetRestrictionsForOrg returned error: %v", err) } - want := &InteractionRestriction{Origin: String("organization")} - if !reflect.DeepEqual(organizationInteractions, want) { + want := &InteractionRestriction{Origin: Ptr("organization")} + if !cmp.Equal(organizationInteractions, want) { t.Errorf("Interactions.GetRestrictionsForOrg returned %+v, want %+v", organizationInteractions, want) } + + const methodName = "GetRestrictionsForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Interactions.GetRestrictionsForOrg(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Interactions.GetRestrictionsForOrg(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestInteractionsService_UpdateRestrictionsForOrg(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &InteractionRestriction{Limit: String("existing_users")} + input := &InteractionRestriction{Limit: Ptr("existing_users")} mux.HandleFunc("/orgs/o/interaction-limits", func(w http.ResponseWriter, r *http.Request) { - v := new(InteractionRestriction) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") testHeader(t, r, "Accept", mediaTypeInteractionRestrictionsPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"origin":"organization"}`) }) - organizationInteractions, _, err := client.Interactions.UpdateRestrictionsForOrg(context.Background(), "o", input.GetLimit()) + ctx := t.Context() + organizationInteractions, _, err := client.Interactions.UpdateRestrictionsForOrg(ctx, "o", input.GetLimit()) if err != nil { t.Errorf("Interactions.UpdateRestrictionsForOrg returned error: %v", err) } - want := &InteractionRestriction{Origin: String("organization")} - if !reflect.DeepEqual(organizationInteractions, want) { + want := &InteractionRestriction{Origin: Ptr("organization")} + if !cmp.Equal(organizationInteractions, want) { t.Errorf("Interactions.UpdateRestrictionsForOrg returned %+v, want %+v", organizationInteractions, want) } + + const methodName = "UpdateRestrictionsForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Interactions.UpdateRestrictionsForOrg(ctx, "\n", input.GetLimit()) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Interactions.UpdateRestrictionsForOrg(ctx, "o", input.GetLimit()) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestInteractionsService_RemoveRestrictionsFromOrg(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/orgs/o/interaction-limits", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/interaction-limits", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") testHeader(t, r, "Accept", mediaTypeInteractionRestrictionsPreview) }) - _, err := client.Interactions.RemoveRestrictionsFromOrg(context.Background(), "o") + ctx := t.Context() + _, err := client.Interactions.RemoveRestrictionsFromOrg(ctx, "o") if err != nil { t.Errorf("Interactions.RemoveRestrictionsFromOrg returned error: %v", err) } + + const methodName = "RemoveRestrictionsFromOrg" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Interactions.RemoveRestrictionsFromOrg(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Interactions.RemoveRestrictionsFromOrg(ctx, "o") + }) } diff --git a/github/interactions_repos.go b/github/interactions_repos.go index 58234822fdd..8de75e73c71 100644 --- a/github/interactions_repos.go +++ b/github/interactions_repos.go @@ -12,20 +12,20 @@ import ( // GetRestrictionsForRepo fetches the interaction restrictions for a repository. // -// GitHub API docs: https://developer.github.com/v3/interactions/repos/#get-interaction-restrictions-for-a-repository +// GitHub API docs: https://docs.github.com/rest/interactions/repos?apiVersion=2022-11-28#get-interaction-restrictions-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/interaction-limits func (s *InteractionsService) GetRestrictionsForRepo(ctx context.Context, owner, repo string) (*InteractionRestriction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) - repositoryInteractions := new(InteractionRestriction) - - resp, err := s.client.Do(ctx, req, repositoryInteractions) + var repositoryInteractions *InteractionRestriction + resp, err := s.client.Do(req, &repositoryInteractions) if err != nil { return nil, resp, err } @@ -39,23 +39,23 @@ func (s *InteractionsService) GetRestrictionsForRepo(ctx context.Context, owner, // for the given repository. // Possible values are: "existing_users", "contributors_only", "collaborators_only". // -// GitHub API docs: https://developer.github.com/v3/interactions/repos/#add-or-update-interaction-restrictions-for-a-repository +// GitHub API docs: https://docs.github.com/rest/interactions/repos?apiVersion=2022-11-28#set-interaction-restrictions-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/interaction-limits func (s *InteractionsService) UpdateRestrictionsForRepo(ctx context.Context, owner, repo, limit string) (*InteractionRestriction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo) - interaction := &InteractionRestriction{Limit: String(limit)} + interaction := &InteractionRestriction{Limit: &limit} - req, err := s.client.NewRequest("PUT", u, interaction) + req, err := s.client.NewRequest(ctx, "PUT", u, interaction) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) - repositoryInteractions := new(InteractionRestriction) - - resp, err := s.client.Do(ctx, req, repositoryInteractions) + var repositoryInteractions *InteractionRestriction + resp, err := s.client.Do(req, &repositoryInteractions) if err != nil { return nil, resp, err } @@ -65,16 +65,17 @@ func (s *InteractionsService) UpdateRestrictionsForRepo(ctx context.Context, own // RemoveRestrictionsFromRepo removes the interaction restrictions for a repository. // -// GitHub API docs: https://developer.github.com/v3/interactions/repos/#remove-interaction-restrictions-for-a-repository +// GitHub API docs: https://docs.github.com/rest/interactions/repos?apiVersion=2022-11-28#remove-interaction-restrictions-for-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/interaction-limits func (s *InteractionsService) RemoveRestrictionsFromRepo(ctx context.Context, owner, repo string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/interactions_repos_test.go b/github/interactions_repos_test.go index 80857ae0e20..0146f51a62b 100644 --- a/github/interactions_repos_test.go +++ b/github/interactions_repos_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestInteractionsService_GetRestrictionsForRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/interaction-limits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -24,57 +23,93 @@ func TestInteractionsService_GetRestrictionsForRepo(t *testing.T) { fmt.Fprint(w, `{"origin":"repository"}`) }) - repoInteractions, _, err := client.Interactions.GetRestrictionsForRepo(context.Background(), "o", "r") + ctx := t.Context() + repoInteractions, _, err := client.Interactions.GetRestrictionsForRepo(ctx, "o", "r") if err != nil { t.Errorf("Interactions.GetRestrictionsForRepo returned error: %v", err) } - want := &InteractionRestriction{Origin: String("repository")} - if !reflect.DeepEqual(repoInteractions, want) { + want := &InteractionRestriction{Origin: Ptr("repository")} + if !cmp.Equal(repoInteractions, want) { t.Errorf("Interactions.GetRestrictionsForRepo returned %+v, want %+v", repoInteractions, want) } + + const methodName = "GetRestrictionsForRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Interactions.GetRestrictionsForRepo(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Interactions.GetRestrictionsForRepo(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestInteractionsService_UpdateRestrictionsForRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &InteractionRestriction{Limit: String("existing_users")} + input := &InteractionRestriction{Limit: Ptr("existing_users")} mux.HandleFunc("/repos/o/r/interaction-limits", func(w http.ResponseWriter, r *http.Request) { - v := new(InteractionRestriction) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") testHeader(t, r, "Accept", mediaTypeInteractionRestrictionsPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"origin":"repository"}`) }) - repoInteractions, _, err := client.Interactions.UpdateRestrictionsForRepo(context.Background(), "o", "r", input.GetLimit()) + ctx := t.Context() + repoInteractions, _, err := client.Interactions.UpdateRestrictionsForRepo(ctx, "o", "r", input.GetLimit()) if err != nil { t.Errorf("Interactions.UpdateRestrictionsForRepo returned error: %v", err) } - want := &InteractionRestriction{Origin: String("repository")} - if !reflect.DeepEqual(repoInteractions, want) { + want := &InteractionRestriction{Origin: Ptr("repository")} + if !cmp.Equal(repoInteractions, want) { t.Errorf("Interactions.UpdateRestrictionsForRepo returned %+v, want %+v", repoInteractions, want) } + + const methodName = "UpdateRestrictionsForRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Interactions.UpdateRestrictionsForRepo(ctx, "\n", "\n", input.GetLimit()) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Interactions.UpdateRestrictionsForRepo(ctx, "o", "r", input.GetLimit()) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestInteractionsService_RemoveRestrictionsFromRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/interaction-limits", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/interaction-limits", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") testHeader(t, r, "Accept", mediaTypeInteractionRestrictionsPreview) }) - _, err := client.Interactions.RemoveRestrictionsFromRepo(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Interactions.RemoveRestrictionsFromRepo(ctx, "o", "r") if err != nil { t.Errorf("Interactions.RemoveRestrictionsFromRepo returned error: %v", err) } + + const methodName = "RemoveRestrictionsFromRepo" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Interactions.RemoveRestrictionsFromRepo(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Interactions.RemoveRestrictionsFromRepo(ctx, "o", "r") + }) } diff --git a/github/issue_import.go b/github/issue_import.go new file mode 100644 index 00000000000..9916eb6fa17 --- /dev/null +++ b/github/issue_import.go @@ -0,0 +1,152 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" +) + +// IssueImportService handles communication with the issue import related +// methods of the Issue Import GitHub API. +type IssueImportService service + +// IssueImportRequest represents a request to create an issue. +// +// https://gist.github.com/jonmagic/5282384165e0f86ef105#supported-issue-and-comment-fields +type IssueImportRequest struct { + IssueImport IssueImport `json:"issue"` + Comments []*Comment `json:"comments,omitempty"` +} + +// IssueImport represents body of issue to import. +type IssueImport struct { + Title string `json:"title"` + Body string `json:"body"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Assignee *string `json:"assignee,omitempty"` + Milestone *int `json:"milestone,omitempty"` + Closed *bool `json:"closed,omitempty"` + Labels []string `json:"labels,omitempty"` +} + +// Comment represents comments of issue to import. +type Comment struct { + CreatedAt *Timestamp `json:"created_at,omitempty"` + Body string `json:"body"` +} + +// IssueImportResponse represents the response of an issue import create request. +// +// https://gist.github.com/jonmagic/5282384165e0f86ef105#import-issue-response +type IssueImportResponse struct { + ID *int `json:"id,omitempty"` + Status *string `json:"status,omitempty"` + URL *string `json:"url,omitempty"` + ImportIssuesURL *string `json:"import_issues_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Message *string `json:"message,omitempty"` + DocumentationURL *string `json:"documentation_url,omitempty"` + Errors []*IssueImportError `json:"errors,omitempty"` +} + +// IssueImportError represents errors of an issue import create request. +type IssueImportError struct { + Location *string `json:"location,omitempty"` + Resource *string `json:"resource,omitempty"` + Field *string `json:"field,omitempty"` + Value *string `json:"value,omitempty"` + Code *string `json:"code,omitempty"` +} + +// Create a new imported issue on the specified repository. +// +// GitHub API docs: https://gist.github.com/jonmagic/5282384165e0f86ef105#start-an-issue-import +// +//meta:operation POST /repos/{owner}/{repo}/import/issues +func (s *IssueImportService) Create(ctx context.Context, owner, repo string, body *IssueImportRequest) (*IssueImportResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/issues", owner, repo) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeIssueImportAPI) + + var i *IssueImportResponse + resp, err := s.client.Do(req, &i) + if err != nil { + var aerr *AcceptedError + if errors.As(err, &aerr) { + if err := json.Unmarshal(aerr.Raw, &i); err != nil { + return i, resp, err + } + return i, resp, err + } + return nil, resp, err + } + + return i, resp, nil +} + +// CheckStatus checks the status of an imported issue. +// +// GitHub API docs: https://gist.github.com/jonmagic/5282384165e0f86ef105#import-status-request +// +//meta:operation GET /repos/{owner}/{repo}/import/issues/{issue_number} +func (s *IssueImportService) CheckStatus(ctx context.Context, owner, repo string, issueID int64) (*IssueImportResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/issues/%v", owner, repo, issueID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeIssueImportAPI) + + var i *IssueImportResponse + resp, err := s.client.Do(req, &i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} + +// CheckStatusSince checks the status of multiple imported issues since a given date. +// +// GitHub API docs: https://gist.github.com/jonmagic/5282384165e0f86ef105#check-status-of-multiple-issues +// +//meta:operation GET /repos/{owner}/{repo}/import/issues +func (s *IssueImportService) CheckStatusSince(ctx context.Context, owner, repo string, since Timestamp) ([]*IssueImportResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/import/issues?since=%v", owner, repo, since.Format("2006-01-02")) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeIssueImportAPI) + + var b bytes.Buffer + resp, err := s.client.Do(req, &b) + if err != nil { + return nil, resp, err + } + + var i []*IssueImportResponse + err = json.Unmarshal(b.Bytes(), &i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} diff --git a/github/issue_import_test.go b/github/issue_import_test.go new file mode 100644 index 00000000000..e3509145206 --- /dev/null +++ b/github/issue_import_test.go @@ -0,0 +1,276 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestIssueImportService_Create(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &IssueImportRequest{ + IssueImport: IssueImport{ + Assignee: Ptr("developer"), + Body: "Dummy description", + CreatedAt: &referenceTimestamp, + Labels: []string{"l1", "l2"}, + Milestone: Ptr(1), + Title: "Dummy Issue", + }, + Comments: []*Comment{{ + CreatedAt: &referenceTimestamp, + Body: "Comment body", + }}, + } + + mux.HandleFunc("/repos/o/r/import/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeIssueImportAPI) + testJSONBody(t, r, input) + assertWrite(t, w, issueImportResponseJSON) + }) + + ctx := t.Context() + got, _, err := client.IssueImport.Create(ctx, "o", "r", input) + if err != nil { + t.Errorf("Create returned error: %v", err) + } + + want := wantIssueImportResponse + if !cmp.Equal(got, want) { + t.Errorf("Create = %+v, want %+v", got, want) + } + + const methodName = "Create" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.IssueImport.Create(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.IssueImport.Create(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssueImportService_Create_deferred(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &IssueImportRequest{ + IssueImport: IssueImport{ + Assignee: Ptr("developer"), + Body: "Dummy description", + CreatedAt: &referenceTimestamp, + Labels: []string{"l1", "l2"}, + Milestone: Ptr(1), + Title: "Dummy Issue", + }, + Comments: []*Comment{{ + CreatedAt: &referenceTimestamp, + Body: "Comment body", + }}, + } + + mux.HandleFunc("/repos/o/r/import/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeIssueImportAPI) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusAccepted) + assertWrite(t, w, issueImportResponseJSON) + }) + + ctx := t.Context() + got, _, err := client.IssueImport.Create(ctx, "o", "r", input) + + if !errors.As(err, new(*AcceptedError)) { + t.Errorf("Create returned error: %v (want AcceptedError)", err) + } + + want := wantIssueImportResponse + if !cmp.Equal(got, want) { + t.Errorf("Create = %+v, want %+v", got, want) + } +} + +func TestIssueImportService_Create_badResponse(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &IssueImportRequest{ + IssueImport: IssueImport{ + Assignee: Ptr("developer"), + Body: "Dummy description", + CreatedAt: &referenceTimestamp, + Labels: []string{"l1", "l2"}, + Milestone: Ptr(1), + Title: "Dummy Issue", + }, + Comments: []*Comment{{ + CreatedAt: &referenceTimestamp, + Body: "Comment body", + }}, + } + + mux.HandleFunc("/repos/o/r/import/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeIssueImportAPI) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusAccepted) + assertWrite(t, w, []byte("{[}")) + }) + + ctx := t.Context() + _, _, err := client.IssueImport.Create(ctx, "o", "r", input) + + if err == nil || err.Error() != "invalid character '[' looking for beginning of object key string" { + t.Errorf("unexpected error: %v", err) + } +} + +func TestIssueImportService_Create_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.IssueImport.Create(ctx, "%", "r", nil) + testURLParseError(t, err) +} + +func TestIssueImportService_CheckStatus(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/import/issues/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIssueImportAPI) + w.WriteHeader(http.StatusOK) + assertWrite(t, w, issueImportResponseJSON) + }) + + ctx := t.Context() + got, _, err := client.IssueImport.CheckStatus(ctx, "o", "r", 3) + if err != nil { + t.Errorf("CheckStatus returned error: %v", err) + } + + want := wantIssueImportResponse + if !cmp.Equal(got, want) { + t.Errorf("CheckStatus = %+v, want %+v", got, want) + } + + const methodName = "CheckStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.IssueImport.CheckStatus(ctx, "\n", "\n", -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.IssueImport.CheckStatus(ctx, "o", "r", 3) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssueImportService_CheckStatus_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.IssueImport.CheckStatus(ctx, "%", "r", 1) + testURLParseError(t, err) +} + +func TestIssueImportService_CheckStatusSince(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/import/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIssueImportAPI) + w.WriteHeader(http.StatusOK) + assertWrite(t, w, fmt.Appendf(nil, "[%s]", issueImportResponseJSON)) + }) + + ctx := t.Context() + got, _, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", referenceTimestamp) + if err != nil { + t.Errorf("CheckStatusSince returned error: %v", err) + } + + want := []*IssueImportResponse{wantIssueImportResponse} + if !cmp.Equal(want, got) { + t.Errorf("CheckStatusSince = %v, want = %v", got, want) + } + + const methodName = "CheckStatusSince" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.IssueImport.CheckStatusSince(ctx, "\n", "\n", referenceTimestamp) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", referenceTimestamp) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssueImportService_CheckStatusSince_badResponse(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/import/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeIssueImportAPI) + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte("{badly-formed JSON")) + }) + + ctx := t.Context() + if _, _, err := client.IssueImport.CheckStatusSince(ctx, "o", "r", referenceTimestamp); err == nil { + t.Error("CheckStatusSince returned no error, want JSON err") + } +} + +func TestIssueImportService_CheckStatusSince_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.IssueImport.CheckStatusSince(ctx, "%", "r", referenceTimestamp) + testURLParseError(t, err) +} + +var issueImportResponseJSON = []byte(`{ + "id": 3, + "status": "pending", + "url": "https://api.github.com/repos/o/r/import/issues/3", + "import_issues_url": "https://api.github.com/repos/o/r/import/issues", + "repository_url": "https://api.github.com/repos/o/r" +}`) + +var wantIssueImportResponse = &IssueImportResponse{ + ID: Ptr(3), + Status: Ptr("pending"), + URL: Ptr("https://api.github.com/repos/o/r/import/issues/3"), + ImportIssuesURL: Ptr("https://api.github.com/repos/o/r/import/issues"), + RepositoryURL: Ptr("https://api.github.com/repos/o/r"), +} diff --git a/github/issues.go b/github/issues.go index 1e0991ce4ff..05746e5a272 100644 --- a/github/issues.go +++ b/github/issues.go @@ -8,16 +8,30 @@ package github import ( "context" "fmt" - "strings" "time" ) // IssuesService handles communication with the issue related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/issues/ +// GitHub API docs: https://docs.github.com/rest/issues?apiVersion=2022-11-28 type IssuesService service +// IssueDependenciesSummary represents a summary of issue dependency counts. +type IssueDependenciesSummary struct { + BlockedBy *int `json:"blocked_by,omitempty"` + Blocking *int `json:"blocking,omitempty"` + TotalBlockedBy *int `json:"total_blocked_by,omitempty"` + TotalBlocking *int `json:"total_blocking,omitempty"` +} + +// SubIssuesSummary represents a summary of sub-issue progress. +type SubIssuesSummary struct { + Total *int `json:"total,omitempty"` + Completed *int `json:"completed,omitempty"` + PercentCompleted *int `json:"percent_completed,omitempty"` +} + // Issue represents a GitHub issue on a repository. // // Note: As far as the GitHub API is concerned, every pull request is an issue, @@ -26,36 +40,53 @@ type IssuesService service // this is an issue, and if PullRequestLinks is not nil, this is a pull request. // The IsPullRequest helper method can be used to check that. type Issue struct { - ID *int64 `json:"id,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - Locked *bool `json:"locked,omitempty"` - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - User *User `json:"user,omitempty"` - Labels []Label `json:"labels,omitempty"` - Assignee *User `json:"assignee,omitempty"` - Comments *int `json:"comments,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedBy *User `json:"closed_by,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - LabelsURL *string `json:"labels_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` - Repository *Repository `json:"repository,omitempty"` - Reactions *Reactions `json:"reactions,omitempty"` - Assignees []*User `json:"assignees,omitempty"` - NodeID *string `json:"node_id,omitempty"` + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + // StateReason can be one of: "completed", "not_planned", "reopened". + StateReason *string `json:"state_reason,omitempty"` + Locked *bool `json:"locked,omitempty"` + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + // AuthorAssociation is the issue author's relationship to the repository. + // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Issues REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#get-an-issue + AuthorAssociation *string `json:"author_association,omitempty"` + User *User `json:"user,omitempty"` + Labels []*Label `json:"labels,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Comments *int `json:"comments,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClosedBy *User `json:"closed_by,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + LabelsURL *string `json:"labels_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + ParentIssueURL *string `json:"parent_issue_url,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` + Assignees []*User `json:"assignees,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Draft *bool `json:"draft,omitempty"` + Type *IssueType `json:"type,omitempty"` + PinnedComment *IssueComment `json:"pinned_comment,omitempty"` + PerformedViaGithubApp *App `json:"performed_via_github_app,omitempty"` + IssueDependenciesSummary *IssueDependenciesSummary `json:"issue_dependencies_summary,omitempty"` + SubIssuesSummary *SubIssuesSummary `json:"sub_issues_summary,omitempty"` + IssueFieldValues []*IssueFieldValue `json:"issue_field_values,omitempty"` // TextMatches is only populated from search results that request text matches - // See: search.go and https://developer.github.com/v3/search/#text-match-metadata - TextMatches []TextMatch `json:"text_matches,omitempty"` + // See: search.go and https://docs.github.com/rest/search/#text-match-metadata + TextMatches []*TextMatch `json:"text_matches,omitempty"` // ActiveLockReason is populated only when LockReason is provided while locking the issue. // Possible values are: "off-topic", "too heated", "resolved", and "spam". @@ -73,24 +104,100 @@ func (i Issue) IsPullRequest() bool { return i.PullRequestLinks != nil } -// IssueRequest represents a request to create/edit an issue. +// CreateIssueRequest represents a request to create an issue. // It is separate from Issue above because otherwise Labels // and Assignee fail to serialize to the correct JSON. -type IssueRequest struct { - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - Labels *[]string `json:"labels,omitempty"` - Assignee *string `json:"assignee,omitempty"` - State *string `json:"state,omitempty"` - Milestone *int `json:"milestone,omitempty"` - Assignees *[]string `json:"assignees,omitempty"` +type CreateIssueRequest struct { + Title string `json:"title"` + Body *string `json:"body,omitempty"` + Labels []string `json:"labels,omitempty"` + Assignee *string `json:"assignee,omitempty"` + Milestone *int `json:"milestone,omitempty"` + Assignees []string `json:"assignees,omitempty"` + Type *string `json:"type,omitempty"` + IssueFieldValues []*IssueRequestFieldValue `json:"issue_field_values,omitempty"` +} + +// UpdateIssueRequest represents a request to edit an issue. +// It is separate from Issue above because otherwise Labels +// and Assignee fail to serialize to the correct JSON. +type UpdateIssueRequest struct { + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + // Labels: nil leaves existing labels unchanged; a non-nil slice replaces + // them, so []string{} clears all labels. + Labels []string `json:"labels,omitzero"` + Assignee *string `json:"assignee,omitempty"` + State *string `json:"state,omitempty"` + // StateReason can be `completed`, `not_planned`, `duplicate` or `reopened`. + StateReason *string `json:"state_reason,omitempty"` + // DuplicateIssueID is required when state_reason is `duplicate`. + DuplicateIssueID *int `json:"duplicate_issue_id,omitempty"` + Milestone *int `json:"milestone,omitempty"` + // Assignees: nil leaves existing assignees unchanged; a non-nil slice + // replaces them, so []string{} clears all assignees. + Assignees []string `json:"assignees,omitzero"` + Type *string `json:"type,omitempty"` + IssueFieldValues []*IssueRequestFieldValue `json:"issue_field_values,omitempty"` +} + +// PullRequestLinks object is added to the Issue object when it's an issue included +// in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR. +type PullRequestLinks struct { + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + DiffURL *string `json:"diff_url,omitempty"` + PatchURL *string `json:"patch_url,omitempty"` + MergedAt *Timestamp `json:"merged_at,omitempty"` +} + +// IssueType represents the type of issue. +// For now it shows up when receiving an Issue event. +type IssueType struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Color *string `json:"color,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// IssueFieldValueSingleSelectOption represents a single-select option for an issue field value. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#get-an-issue +type IssueFieldValueSingleSelectOption struct { + ID int64 `json:"id"` + Name string `json:"name"` + Color string `json:"color"` } -// IssueListOptions specifies the optional parameters to the IssuesService.List -// and IssuesService.ListByOrg methods. -type IssueListOptions struct { +// IssueRequestFieldValue represents a custom field value to set on an issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#update-an-issue +type IssueRequestFieldValue struct { + FieldID int64 `json:"field_id"` + Value any `json:"value"` +} + +// IssueFieldValue represents a custom field value attached to an issue. +// The Value field contains a string for text, single_select, and date fields, +// or a number for numeric fields. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#get-an-issue +type IssueFieldValue struct { + IssueFieldID int64 `json:"issue_field_id"` + NodeID string `json:"node_id"` + DataType string `json:"data_type"` + Value any `json:"value"` + SingleSelectOption *IssueFieldValueSingleSelectOption `json:"single_select_option,omitempty"` +} + +// ListAllIssuesOptions specifies the optional parameters to the +// IssuesService.ListAllIssues method. +type ListAllIssuesOptions struct { // Filter specifies which issues to list. Possible values are: assigned, - // created, mentioned, subscribed, all. Default is "assigned". + // created, mentioned, subscribed, repos, all. Default is "assigned". Filter string `url:"filter,omitempty"` // State filters issues based on their state. Possible values are: open, @@ -111,60 +218,153 @@ type IssueListOptions struct { // Since filters issues by time. Since time.Time `url:"since,omitempty"` + Collab bool `url:"collab,omitempty"` + Orgs bool `url:"orgs,omitempty"` + Owned bool `url:"owned,omitempty"` + Pulls bool `url:"pulls,omitempty"` + ListOptions } -// PullRequestLinks object is added to the Issue object when it's an issue included -// in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR. -type PullRequestLinks struct { - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - DiffURL *string `json:"diff_url,omitempty"` - PatchURL *string `json:"patch_url,omitempty"` +// ListAllIssues gets issues assigned to the authenticated user across all visible repositories including owned repositories, +// member repositories, and organization repositories. +// You can use the filter query parameter to fetch issues that are not necessarily assigned to you. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#list-issues-assigned-to-the-authenticated-user +// +//meta:operation GET /issues +func (s *IssuesService) ListAllIssues(ctx context.Context, opts *ListAllIssuesOptions) ([]*Issue, *Response, error) { + u := "issues" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var issues []*Issue + resp, err := s.client.Do(req, &issues) + if err != nil { + return nil, resp, err + } + + return issues, resp, nil +} + +// ListUserIssuesOptions specifies the optional parameters to the +// IssuesService.ListUserIssues method. +type ListUserIssuesOptions struct { + // Filter specifies which issues to list. Possible values are: assigned, + // created, mentioned, subscribed, repos, all. Default is "assigned". + Filter string `url:"filter,omitempty"` + + // State filters issues based on their state. Possible values are: open, + // closed, all. Default is "open". + State string `url:"state,omitempty"` + + // Labels filters issues based on their label. + Labels []string `url:"labels,comma,omitempty"` + + // Sort specifies how to sort issues. Possible values are: created, updated, + // and comments. Default value is "created". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort issues. Possible values are: asc, desc. + // Default is "desc". + Direction string `url:"direction,omitempty"` + + // Since filters issues by time. + Since time.Time `url:"since,omitempty"` + + ListOptions } -// List the issues for the authenticated user. If all is true, list issues -// across all the user's visible repositories including owned, member, and -// organization repositories; if false, list only owned and member -// repositories. +// ListUserIssues gets issues across owned and member repositories assigned to the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#list-user-account-issues-assigned-to-the-authenticated-user // -// GitHub API docs: https://developer.github.com/v3/issues/#list-issues -func (s *IssuesService) List(ctx context.Context, all bool, opt *IssueListOptions) ([]*Issue, *Response, error) { - var u string - if all { - u = "issues" - } else { - u = "user/issues" +//meta:operation GET /user/issues +func (s *IssuesService) ListUserIssues(ctx context.Context, opts *ListUserIssuesOptions) ([]*Issue, *Response, error) { + u := "user/issues" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err } - return s.listIssues(ctx, u, opt) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var issues []*Issue + resp, err := s.client.Do(req, &issues) + if err != nil { + return nil, resp, err + } + + return issues, resp, nil +} + +// IssueListByOrgOptions specifies the optional parameters to the +// IssuesService.ListByOrg method. +type IssueListByOrgOptions struct { + // Filter specifies which issues to list. Possible values are: assigned, + // created, mentioned, subscribed, repos, all. Default is "assigned". + Filter string `url:"filter,omitempty"` + + // State filters issues based on their state. Possible values are: open, + // closed, all. Default is "open". + State string `url:"state,omitempty"` + + // Labels filters issues based on their label. + Labels []string `url:"labels,comma,omitempty"` + + // Type can be the name of an issue type. + Type string `url:"type,omitempty"` + + // Sort specifies how to sort issues. Possible values are: created, updated, + // and comments. Default value is "created". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort issues. Possible values are: asc, desc. + // Default is "desc". + Direction string `url:"direction,omitempty"` + + // Since filters issues by time. + Since time.Time `url:"since,omitempty"` + + ListOptions } // ListByOrg fetches the issues in the specified organization for the // authenticated user. // -// GitHub API docs: https://developer.github.com/v3/issues/#list-issues -func (s *IssuesService) ListByOrg(ctx context.Context, org string, opt *IssueListOptions) ([]*Issue, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#list-organization-issues-assigned-to-the-authenticated-user +// +//meta:operation GET /orgs/{org}/issues +func (s *IssuesService) ListByOrg(ctx context.Context, org string, opts *IssueListByOrgOptions) ([]*Issue, *Response, error) { u := fmt.Sprintf("orgs/%v/issues", org) - return s.listIssues(ctx, u, opt) -} - -func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) { - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeReactionsPreview) var issues []*Issue - resp, err := s.client.Do(ctx, req, &issues) + resp, err := s.client.Do(req, &issues) if err != nil { return nil, resp, err } @@ -189,6 +389,11 @@ type IssueListByRepoOptions struct { // any assigned user. Assignee string `url:"assignee,omitempty"` + // Type can be the name of an issue type. + // If the string * is passed, issues with any type are accepted. + // If the string none is passed, issues without type are returned. + Type string `url:"type,omitempty"` + // Creator filters issues based on their creator. Creator string `url:"creator,omitempty"` @@ -209,30 +414,35 @@ type IssueListByRepoOptions struct { // Since filters issues by time. Since time.Time `url:"since,omitempty"` + // ListCursorOptions specifies the optional parameters for cursor pagination. + ListCursorOptions + + // Add ListOptions so offset pagination with integer type "page" query parameter is accepted + // since ListCursorOptions accepts "page" as string only. ListOptions } // ListByRepo lists the issues for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/issues/#list-issues-for-a-repository -func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues +// +//meta:operation GET /repos/{owner}/{repo}/issues +func (s *IssuesService) ListByRepo(ctx context.Context, owner, repo string, opts *IssueListByRepoOptions) ([]*Issue, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeReactionsPreview) var issues []*Issue - resp, err := s.client.Do(ctx, req, &issues) + resp, err := s.client.Do(req, &issues) if err != nil { return nil, resp, err } @@ -242,20 +452,20 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo strin // Get a single issue. // -// GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-issue -func (s *IssuesService) Get(ctx context.Context, owner string, repo string, number int) (*Issue, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#get-an-issue +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number} +func (s *IssuesService) Get(ctx context.Context, owner, repo string, number int) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeReactionsPreview) - issue := new(Issue) - resp, err := s.client.Do(ctx, req, issue) + var issue *Issue + resp, err := s.client.Do(req, &issue) if err != nil { return nil, resp, err } @@ -265,19 +475,18 @@ func (s *IssuesService) Get(ctx context.Context, owner string, repo string, numb // Create a new issue on the specified repository. // -// GitHub API docs: https://developer.github.com/v3/issues/#create-an-issue -func (s *IssuesService) Create(ctx context.Context, owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#create-an-issue +// +//meta:operation POST /repos/{owner}/{repo}/issues +func (s *IssuesService) Create(ctx context.Context, owner, repo string, body CreateIssueRequest) (*Issue, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) - req, err := s.client.NewRequest("POST", u, issue) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - - i := new(Issue) - resp, err := s.client.Do(ctx, req, i) + var i *Issue + resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } @@ -285,21 +494,45 @@ func (s *IssuesService) Create(ctx context.Context, owner string, repo string, i return i, resp, nil } -// Edit an issue. +// Update an issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#update-an-issue // -// GitHub API docs: https://developer.github.com/v3/issues/#edit-an-issue -func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) - req, err := s.client.NewRequest("PATCH", u, issue) +//meta:operation PATCH /repos/{owner}/{repo}/issues/{issue_number} +func (s *IssuesService) Update(ctx context.Context, owner, repo string, number int, body UpdateIssueRequest) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) + var i *Issue + resp, err := s.client.Do(req, &i) + if err != nil { + return nil, resp, err + } - i := new(Issue) - resp, err := s.client.Do(ctx, req, i) + return i, resp, nil +} + +// RemoveMilestone removes a milestone from an issue. +// +// This is a helper method to explicitly update an issue with a `null` milestone, thereby removing it. +// +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#update-an-issue +// +//meta:operation PATCH /repos/{owner}/{repo}/issues/{issue_number} +func (s *IssuesService) RemoveMilestone(ctx context.Context, owner, repo string, issueNumber int) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v", owner, repo, issueNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, &struct { + Milestone *Milestone `json:"milestone"` + }{}) + if err != nil { + return nil, nil, err + } + + var i *Issue + resp, err := s.client.Do(req, &i) if err != nil { return nil, resp, err } @@ -318,30 +551,30 @@ type LockIssueOptions struct { // Lock an issue's conversation. // -// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue -func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opt *LockIssueOptions) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) - req, err := s.client.NewRequest("PUT", u, opt) +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#lock-an-issue +// +//meta:operation PUT /repos/{owner}/{repo}/issues/{issue_number}/lock +func (s *IssuesService) Lock(ctx context.Context, owner, repo string, number int, body *LockIssueOptions) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/lock", owner, repo, number) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, err } - if opt != nil { - req.Header.Set("Accept", mediaTypeLockReasonPreview) - } - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // Unlock an issue's conversation. // -// GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue -func (s *IssuesService) Unlock(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/issues?apiVersion=2022-11-28#unlock-an-issue +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/lock +func (s *IssuesService) Unlock(ctx context.Context, owner, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/lock", owner, repo, number) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/issues_assignees.go b/github/issues_assignees.go index 9cb366f50a3..3b2dd4d3a06 100644 --- a/github/issues_assignees.go +++ b/github/issues_assignees.go @@ -13,20 +13,23 @@ import ( // ListAssignees fetches all available assignees (owners and collaborators) to // which issues may be assigned. // -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#list-assignees -func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/assignees?apiVersion=2022-11-28#list-assignees +// +//meta:operation GET /repos/{owner}/{repo}/assignees +func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opts *ListOptions) ([]*User, *Response, error) { u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } + var assignees []*User - resp, err := s.client.Do(ctx, req, &assignees) + resp, err := s.client.Do(req, &assignees) if err != nil { return nil, resp, err } @@ -36,50 +39,65 @@ func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, o // IsAssignee checks if a user is an assignee for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-assignee +// GitHub API docs: https://docs.github.com/rest/issues/assignees?apiVersion=2022-11-28#check-if-a-user-can-be-assigned +// +//meta:operation GET /repos/{owner}/{repo}/assignees/{assignee} func (s *IssuesService) IsAssignee(ctx context.Context, owner, repo, user string) (bool, *Response, error) { u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + + resp, err := s.client.Do(req, nil) assignee, err := parseBoolResponse(err) return assignee, resp, err } // AddAssignees adds the provided GitHub users as assignees to the issue. // -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue +// GitHub API docs: https://docs.github.com/rest/issues/assignees?apiVersion=2022-11-28#add-assignees-to-an-issue +// +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/assignees func (s *IssuesService) AddAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { users := &struct { Assignees []string `json:"assignees,omitempty"` }{Assignees: assignees} u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) - req, err := s.client.NewRequest("POST", u, users) + req, err := s.client.NewRequest(ctx, "POST", u, users) if err != nil { return nil, nil, err } - issue := &Issue{} - resp, err := s.client.Do(ctx, req, issue) - return issue, resp, err + var issue *Issue + resp, err := s.client.Do(req, &issue) + if err != nil { + return nil, resp, err + } + + return issue, resp, nil } // RemoveAssignees removes the provided GitHub users as assignees from the issue. // -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue +// GitHub API docs: https://docs.github.com/rest/issues/assignees?apiVersion=2022-11-28#remove-assignees-from-an-issue +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees func (s *IssuesService) RemoveAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { users := &struct { Assignees []string `json:"assignees,omitempty"` }{Assignees: assignees} u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, users) + req, err := s.client.NewRequest(ctx, "DELETE", u, users) if err != nil { return nil, nil, err } - issue := &Issue{} - resp, err := s.client.Do(ctx, req, issue) - return issue, resp, err + var issue *Issue + resp, err := s.client.Do(req, &issue) + if err != nil { + return nil, resp, err + } + + return issue, resp, nil } diff --git a/github/issues_assignees_test.go b/github/issues_assignees_test.go index bb2bf4ff035..62002f95073 100644 --- a/github/issues_assignees_test.go +++ b/github/issues_assignees_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestIssuesService_ListAssignees(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/assignees", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,140 +24,227 @@ func TestIssuesService_ListAssignees(t *testing.T) { }) opt := &ListOptions{Page: 2} - assignees, _, err := client.Issues.ListAssignees(context.Background(), "o", "r", opt) + ctx := t.Context() + assignees, _, err := client.Issues.ListAssignees(ctx, "o", "r", opt) if err != nil { t.Errorf("Issues.ListAssignees returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(assignees, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(assignees, want) { t.Errorf("Issues.ListAssignees returned %+v, want %+v", assignees, want) } + + const methodName = "ListAssignees" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListAssignees(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListAssignees(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListAssignees_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ListAssignees(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Issues.ListAssignees(ctx, "%", "r", nil) testURLParseError(t, err) } func TestIssuesService_IsAssignee_true(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/assignees/u", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") }) - assignee, _, err := client.Issues.IsAssignee(context.Background(), "o", "r", "u") + ctx := t.Context() + assignee, _, err := client.Issues.IsAssignee(ctx, "o", "r", "u") if err != nil { t.Errorf("Issues.IsAssignee returned error: %v", err) } if want := true; assignee != want { t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) } + + const methodName = "IsAssignee" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.IsAssignee(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.IsAssignee(ctx, "o", "r", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestIssuesService_IsAssignee_false(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - assignee, _, err := client.Issues.IsAssignee(context.Background(), "o", "r", "u") + ctx := t.Context() + assignee, _, err := client.Issues.IsAssignee(ctx, "o", "r", "u") if err != nil { t.Errorf("Issues.IsAssignee returned error: %v", err) } if want := false; assignee != want { t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) } + + const methodName = "IsAssignee" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.IsAssignee(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.IsAssignee(ctx, "o", "r", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestIssuesService_IsAssignee_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") http.Error(w, "BadRequest", http.StatusBadRequest) }) - assignee, _, err := client.Issues.IsAssignee(context.Background(), "o", "r", "u") + ctx := t.Context() + assignee, _, err := client.Issues.IsAssignee(ctx, "o", "r", "u") if err == nil { - t.Errorf("Expected HTTP 400 response") + t.Error("Expected HTTP 400 response") } if want := false; assignee != want { t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) } + + const methodName = "IsAssignee" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.IsAssignee(ctx, "o", "r", "u") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.IsAssignee(ctx, "o", "r", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestIssuesService_IsAssignee_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.IsAssignee(context.Background(), "%", "r", "u") + ctx := t.Context() + _, _, err := client.Issues.IsAssignee(ctx, "%", "r", "u") testURLParseError(t, err) } func TestIssuesService_AddAssignees(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + assignees := struct { + Assignees []string `json:"assignees,omitempty"` + }{ + Assignees: []string{"user1", "user2"}, + } mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { - var assignees struct { - Assignees []string `json:"assignees,omitempty"` - } - json.NewDecoder(r.Body).Decode(&assignees) - testMethod(t, r, "POST") - want := []string{"user1", "user2"} - if !reflect.DeepEqual(assignees.Assignees, want) { - t.Errorf("assignees = %+v, want %+v", assignees, want) - } + testJSONBody(t, r, assignees) fmt.Fprint(w, `{"number":1,"assignees":[{"login":"user1"},{"login":"user2"}]}`) }) - got, _, err := client.Issues.AddAssignees(context.Background(), "o", "r", 1, []string{"user1", "user2"}) + ctx := t.Context() + got, _, err := client.Issues.AddAssignees(ctx, "o", "r", 1, assignees.Assignees) if err != nil { t.Errorf("Issues.AddAssignees returned error: %v", err) } - want := &Issue{Number: Int(1), Assignees: []*User{{Login: String("user1")}, {Login: String("user2")}}} - if !reflect.DeepEqual(got, want) { + want := &Issue{Number: Ptr(1), Assignees: []*User{{Login: Ptr("user1")}, {Login: Ptr("user2")}}} + if !cmp.Equal(got, want) { t.Errorf("Issues.AddAssignees = %+v, want %+v", got, want) } + + const methodName = "AddAssignees" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.AddAssignees(ctx, "\n", "\n", -1, []string{"\n", "\n"}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.AddAssignees(ctx, "o", "r", 1, []string{"user1", "user2"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_RemoveAssignees(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { - var assignees struct { - Assignees []string `json:"assignees,omitempty"` - } - json.NewDecoder(r.Body).Decode(&assignees) + assignees := struct { + Assignees []string `json:"assignees,omitempty"` + }{ + Assignees: []string{"user1", "user2"}, + } + mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - want := []string{"user1", "user2"} - if !reflect.DeepEqual(assignees.Assignees, want) { - t.Errorf("assignees = %+v, want %+v", assignees, want) - } + testJSONBody(t, r, assignees) fmt.Fprint(w, `{"number":1,"assignees":[]}`) }) - got, _, err := client.Issues.RemoveAssignees(context.Background(), "o", "r", 1, []string{"user1", "user2"}) + ctx := t.Context() + got, _, err := client.Issues.RemoveAssignees(ctx, "o", "r", 1, assignees.Assignees) if err != nil { t.Errorf("Issues.RemoveAssignees returned error: %v", err) } - want := &Issue{Number: Int(1), Assignees: []*User{}} - if !reflect.DeepEqual(got, want) { + want := &Issue{Number: Ptr(1), Assignees: []*User{}} + if !cmp.Equal(got, want) { t.Errorf("Issues.RemoveAssignees = %+v, want %+v", got, want) } + + const methodName = "RemoveAssignees" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.RemoveAssignees(ctx, "\n", "\n", -1, []string{"\n", "\n"}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.RemoveAssignees(ctx, "o", "r", 1, []string{"user1", "user2"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/issues_comments.go b/github/issues_comments.go index ab68afe2fa4..453ad70ad97 100644 --- a/github/issues_comments.go +++ b/github/issues_comments.go @@ -18,10 +18,14 @@ type IssueComment struct { Body *string `json:"body,omitempty"` User *User `json:"user,omitempty"` Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` // AuthorAssociation is the comment author's relationship to the issue's repository. // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Issue Comments REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#get-an-issue-comment AuthorAssociation *string `json:"author_association,omitempty"` URL *string `json:"url,omitempty"` HTMLURL *string `json:"html_url,omitempty"` @@ -36,13 +40,13 @@ func (i IssueComment) String() string { // IssuesService.ListComments method. type IssueListCommentsOptions struct { // Sort specifies how to sort comments. Possible values are: created, updated. - Sort string `url:"sort,omitempty"` + Sort *string `url:"sort,omitempty"` // Direction in which to sort comments. Possible values are: asc, desc. - Direction string `url:"direction,omitempty"` + Direction *string `url:"direction,omitempty"` // Since filters comments by time. - Since time.Time `url:"since,omitempty"` + Since *time.Time `url:"since,omitempty"` ListOptions } @@ -50,29 +54,33 @@ type IssueListCommentsOptions struct { // ListComments lists all comments on the specified issue. Specifying an issue // number of 0 will return all comments on all issues for the repository. // -// GitHub API docs: https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue -func (s *IssuesService) ListComments(ctx context.Context, owner string, repo string, number int, opt *IssueListCommentsOptions) ([]*IssueComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments +// +// GitHub API docs: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#list-issue-comments-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/issues/comments +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/comments +func (s *IssuesService) ListComments(ctx context.Context, owner, repo string, number int, opts *IssueListCommentsOptions) ([]*IssueComment, *Response, error) { var u string if number == 0 { u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo) } else { - u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) + u = fmt.Sprintf("repos/%v/%v/issues/%v/comments", owner, repo, number) } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeReactionsPreview) var comments []*IssueComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -82,20 +90,21 @@ func (s *IssuesService) ListComments(ctx context.Context, owner string, repo str // GetComment fetches the specified issue comment. // -// GitHub API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment -func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, commentID int64) (*IssueComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID) +// GitHub API docs: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#get-an-issue-comment +// +//meta:operation GET /repos/{owner}/{repo}/issues/comments/{comment_id} +func (s *IssuesService) GetComment(ctx context.Context, owner, repo string, commentID int64) (*IssueComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%v", owner, repo, commentID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeReactionsPreview) - comment := new(IssueComment) - resp, err := s.client.Do(ctx, req, comment) + var comment *IssueComment + resp, err := s.client.Do(req, &comment) if err != nil { return nil, resp, err } @@ -105,15 +114,17 @@ func (s *IssuesService) GetComment(ctx context.Context, owner string, repo strin // CreateComment creates a new comment on the specified issue. // -// GitHub API docs: https://developer.github.com/v3/issues/comments/#create-a-comment -func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) - req, err := s.client.NewRequest("POST", u, comment) +// GitHub API docs: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment +// +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/comments +func (s *IssuesService) CreateComment(ctx context.Context, owner, repo string, number int, body *IssueComment) (*IssueComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/comments", owner, repo, number) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - c := new(IssueComment) - resp, err := s.client.Do(ctx, req, c) + var c *IssueComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -124,15 +135,17 @@ func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo st // EditComment updates an issue comment. // A non-nil comment.Body must be provided. Other comment fields should be left nil. // -// GitHub API docs: https://developer.github.com/v3/issues/comments/#edit-a-comment -func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, commentID int64, comment *IssueComment) (*IssueComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID) - req, err := s.client.NewRequest("PATCH", u, comment) +// GitHub API docs: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#update-an-issue-comment +// +//meta:operation PATCH /repos/{owner}/{repo}/issues/comments/{comment_id} +func (s *IssuesService) EditComment(ctx context.Context, owner, repo string, commentID int64, body *IssueComment) (*IssueComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%v", owner, repo, commentID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - c := new(IssueComment) - resp, err := s.client.Do(ctx, req, c) + var c *IssueComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -142,12 +155,14 @@ func (s *IssuesService) EditComment(ctx context.Context, owner string, repo stri // DeleteComment deletes an issue comment. // -// GitHub API docs: https://developer.github.com/v3/issues/comments/#delete-a-comment -func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, commentID int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/comments?apiVersion=2022-11-28#delete-an-issue-comment +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/comments/{comment_id} +func (s *IssuesService) DeleteComment(ctx context.Context, owner, repo string, commentID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/comments/%v", owner, repo, commentID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/issues_comments_test.go b/github/issues_comments_test.go index 6911a798539..70a317b5308 100644 --- a/github/issues_comments_test.go +++ b/github/issues_comments_test.go @@ -6,18 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestIssuesService_ListComments_allIssues(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,32 +23,47 @@ func TestIssuesService_ListComments_allIssues(t *testing.T) { testFormValues(t, r, values{ "sort": "updated", "direction": "desc", - "since": "2002-02-10T15:30:00Z", + "since": referenceTimeRaw, "page": "2", }) fmt.Fprint(w, `[{"id":1}]`) }) opt := &IssueListCommentsOptions{ - Sort: "updated", - Direction: "desc", - Since: time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), + Sort: Ptr("updated"), + Direction: Ptr("desc"), + Since: &referenceTime, ListOptions: ListOptions{Page: 2}, } - comments, _, err := client.Issues.ListComments(context.Background(), "o", "r", 0, opt) + ctx := t.Context() + comments, _, err := client.Issues.ListComments(ctx, "o", "r", 0, opt) if err != nil { t.Errorf("Issues.ListComments returned error: %v", err) } - want := []*IssueComment{{ID: Int64(1)}} - if !reflect.DeepEqual(comments, want) { + want := []*IssueComment{{ID: Ptr(int64(1))}} + if !cmp.Equal(comments, want) { t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) } + + const methodName = "ListComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListComments(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListComments(ctx, "o", "r", 0, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListComments_specificIssue(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -58,28 +71,44 @@ func TestIssuesService_ListComments_specificIssue(t *testing.T) { fmt.Fprint(w, `[{"id":1}]`) }) - comments, _, err := client.Issues.ListComments(context.Background(), "o", "r", 1, nil) + ctx := t.Context() + comments, _, err := client.Issues.ListComments(ctx, "o", "r", 1, nil) if err != nil { t.Errorf("Issues.ListComments returned error: %v", err) } - want := []*IssueComment{{ID: Int64(1)}} - if !reflect.DeepEqual(comments, want) { + want := []*IssueComment{{ID: Ptr(int64(1))}} + if !cmp.Equal(comments, want) { t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) } + + const methodName = "ListComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListComments(ctx, "\n", "\n", -1, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListComments(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListComments_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ListComments(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.ListComments(ctx, "%", "r", 1, nil) testURLParseError(t, err) } func TestIssuesService_GetComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -87,117 +116,165 @@ func TestIssuesService_GetComment(t *testing.T) { fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Issues.GetComment(context.Background(), "o", "r", 1) + ctx := t.Context() + comment, _, err := client.Issues.GetComment(ctx, "o", "r", 1) if err != nil { t.Errorf("Issues.GetComment returned error: %v", err) } - want := &IssueComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &IssueComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Issues.GetComment returned %+v, want %+v", comment, want) } + + const methodName = "GetComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.GetComment(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.GetComment(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_GetComment_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.GetComment(context.Background(), "%", "r", 1) + ctx := t.Context() + _, _, err := client.Issues.GetComment(ctx, "%", "r", 1) testURLParseError(t, err) } func TestIssuesService_CreateComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &IssueComment{Body: String("b")} + input := &IssueComment{Body: Ptr("b")} mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { - v := new(IssueComment) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Issues.CreateComment(context.Background(), "o", "r", 1, input) + ctx := t.Context() + comment, _, err := client.Issues.CreateComment(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Issues.CreateComment returned error: %v", err) } - want := &IssueComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &IssueComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Issues.CreateComment returned %+v, want %+v", comment, want) } + + const methodName = "CreateComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.CreateComment(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.CreateComment(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_CreateComment_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.CreateComment(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.CreateComment(ctx, "%", "r", 1, nil) testURLParseError(t, err) } func TestIssuesService_EditComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &IssueComment{Body: String("b")} + input := &IssueComment{Body: Ptr("b")} mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { - v := new(IssueComment) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Issues.EditComment(context.Background(), "o", "r", 1, input) + ctx := t.Context() + comment, _, err := client.Issues.EditComment(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Issues.EditComment returned error: %v", err) } - want := &IssueComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &IssueComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Issues.EditComment returned %+v, want %+v", comment, want) } + + const methodName = "EditComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.EditComment(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.EditComment(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_EditComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.EditComment(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.EditComment(ctx, "%", "r", 1, nil) testURLParseError(t, err) } func TestIssuesService_DeleteComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/issues/comments/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Issues.DeleteComment(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Issues.DeleteComment(ctx, "o", "r", 1) if err != nil { t.Errorf("Issues.DeleteComments returned error: %v", err) } + + const methodName = "DeleteComment" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.DeleteComment(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.DeleteComment(ctx, "o", "r", 1) + }) } func TestIssuesService_DeleteComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Issues.DeleteComment(context.Background(), "%", "r", 1) + ctx := t.Context() + _, err := client.Issues.DeleteComment(ctx, "%", "r", 1) testURLParseError(t, err) } diff --git a/github/issues_dependencies.go b/github/issues_dependencies.go new file mode 100644 index 00000000000..1a944aaa5d3 --- /dev/null +++ b/github/issues_dependencies.go @@ -0,0 +1,110 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// IssueDependencyRequest represents a request to add a dependency to an issue. +type IssueDependencyRequest struct { + IssueID int64 `json:"issue_id"` +} + +// ListBlockedBy lists the dependencies that block the specified issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/issue-dependencies?apiVersion=2022-11-28#list-dependencies-an-issue-is-blocked-by +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by +func (s *IssuesService) ListBlockedBy(ctx context.Context, owner, repo string, issueNumber int64, opts *ListOptions) ([]*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/dependencies/blocked_by", owner, repo, issueNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var issues []*Issue + resp, err := s.client.Do(req, &issues) + if err != nil { + return nil, resp, err + } + + return issues, resp, nil +} + +// AddBlockedBy adds a "blocked by" dependency to the specified issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/issue-dependencies?apiVersion=2022-11-28#add-a-dependency-an-issue-is-blocked-by +// +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by +func (s *IssuesService) AddBlockedBy(ctx context.Context, owner, repo string, issueNumber int64, body IssueDependencyRequest) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/dependencies/blocked_by", owner, repo, issueNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var issue *Issue + resp, err := s.client.Do(req, &issue) + if err != nil { + return nil, resp, err + } + + return issue, resp, nil +} + +// RemoveBlockedBy removes a "blocked by" dependency from the specified issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/issue-dependencies?apiVersion=2022-11-28#remove-dependency-an-issue-is-blocked-by +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by/{issue_id} +func (s *IssuesService) RemoveBlockedBy(ctx context.Context, owner, repo string, issueNumber, blockingIssueID int64) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/dependencies/blocked_by/%v", owner, repo, issueNumber, blockingIssueID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + var issue *Issue + resp, err := s.client.Do(req, &issue) + if err != nil { + return nil, resp, err + } + + return issue, resp, nil +} + +// ListBlocking lists the issues that the specified issue is blocking. +// +// GitHub API docs: https://docs.github.com/rest/issues/issue-dependencies?apiVersion=2022-11-28#list-dependencies-an-issue-is-blocking +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking +func (s *IssuesService) ListBlocking(ctx context.Context, owner, repo string, issueNumber int64, opts *ListOptions) ([]*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/dependencies/blocking", owner, repo, issueNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var issues []*Issue + resp, err := s.client.Do(req, &issues) + if err != nil { + return nil, resp, err + } + + return issues, resp, nil +} diff --git a/github/issues_dependencies_test.go b/github/issues_dependencies_test.go new file mode 100644 index 00000000000..34a45f8c7fa --- /dev/null +++ b/github/issues_dependencies_test.go @@ -0,0 +1,199 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestIssuesService_ListBlockedBy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/1/dependencies/blocked_by", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"number":1347,"title":"Found a bug"}]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + issues, _, err := client.Issues.ListBlockedBy(ctx, "o", "r", 1, opt) + if err != nil { + t.Errorf("Issues.ListBlockedBy returned error: %v", err) + } + + want := []*Issue{{Number: Ptr(1347), Title: Ptr("Found a bug")}} + if !cmp.Equal(issues, want) { + t.Errorf("Issues.ListBlockedBy returned %+v, want %+v", issues, want) + } + + const methodName = "ListBlockedBy" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListBlockedBy(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListBlockedBy(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssuesService_ListBlockedBy_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Issues.ListBlockedBy(ctx, "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestIssuesService_AddBlockedBy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := IssueDependencyRequest{IssueID: int64(42)} + + mux.HandleFunc("/repos/o/r/issues/1/dependencies/blocked_by", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"number":42,"title":"Dependency issue"}`) + }) + + ctx := t.Context() + issue, _, err := client.Issues.AddBlockedBy(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.AddBlockedBy returned error: %v", err) + } + + want := &Issue{Number: Ptr(42), Title: Ptr("Dependency issue")} + if !cmp.Equal(issue, want) { + t.Errorf("Issues.AddBlockedBy returned %+v, want %+v", issue, want) + } + + const methodName = "AddBlockedBy" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.AddBlockedBy(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.AddBlockedBy(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssuesService_AddBlockedBy_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Issues.AddBlockedBy(ctx, "%", "%", 1, IssueDependencyRequest{}) + testURLParseError(t, err) +} + +func TestIssuesService_RemoveBlockedBy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/1/dependencies/blocked_by/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `{"number":1,"title":"Original issue"}`) + }) + + ctx := t.Context() + issue, _, err := client.Issues.RemoveBlockedBy(ctx, "o", "r", 1, 42) + if err != nil { + t.Errorf("Issues.RemoveBlockedBy returned error: %v", err) + } + + want := &Issue{Number: Ptr(1), Title: Ptr("Original issue")} + if !cmp.Equal(issue, want) { + t.Errorf("Issues.RemoveBlockedBy returned %+v, want %+v", issue, want) + } + + const methodName = "RemoveBlockedBy" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.RemoveBlockedBy(ctx, "\n", "\n", -1, 42) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.RemoveBlockedBy(ctx, "o", "r", 1, 42) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssuesService_RemoveBlockedBy_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Issues.RemoveBlockedBy(ctx, "%", "%", 1, 42) + testURLParseError(t, err) +} + +func TestIssuesService_ListBlocking(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/1/dependencies/blocking", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"number":1348,"title":"Blocked issue"}]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + issues, _, err := client.Issues.ListBlocking(ctx, "o", "r", 1, opt) + if err != nil { + t.Errorf("Issues.ListBlocking returned error: %v", err) + } + + want := []*Issue{{Number: Ptr(1348), Title: Ptr("Blocked issue")}} + if !cmp.Equal(issues, want) { + t.Errorf("Issues.ListBlocking returned %+v, want %+v", issues, want) + } + + const methodName = "ListBlocking" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListBlocking(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListBlocking(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestIssuesService_ListBlocking_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Issues.ListBlocking(ctx, "%", "%", 1, nil) + testURLParseError(t, err) +} diff --git a/github/issues_events.go b/github/issues_events.go index 4456d67e5c3..82aa38f9a5e 100644 --- a/github/issues_events.go +++ b/github/issues_events.go @@ -8,8 +8,6 @@ package github import ( "context" "fmt" - "strings" - "time" ) // IssueEvent represents an event that occurred around an Issue or Pull Request. @@ -20,6 +18,9 @@ type IssueEvent struct { // The User that generated this event. Actor *User `json:"actor,omitempty"` + // The action corresponding to the event. + Action string `json:"action,omitempty"` + // Event identifies the actual type of Event that occurred. Possible // values are: // @@ -66,21 +67,29 @@ type IssueEvent struct { // review_dismissed // The review was dismissed and `DismissedReview` will be populated below. // + // review_requested, review_request_removed + // The Actor requested or removed the request for a review. + // RequestedReviewer or RequestedTeam, and ReviewRequester will be populated below. + // Event *string `json:"event,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` Issue *Issue `json:"issue,omitempty"` // Only present on certain events; see above. - Assignee *User `json:"assignee,omitempty"` - Assigner *User `json:"assigner,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - Label *Label `json:"label,omitempty"` - Rename *Rename `json:"rename,omitempty"` - LockReason *string `json:"lock_reason,omitempty"` - ProjectCard *ProjectCard `json:"project_card,omitempty"` - DismissedReview *DismissedReview `json:"dismissed_review,omitempty"` + Repository *Repository `json:"repository,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Assigner *User `json:"assigner,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + Label *Label `json:"label,omitempty"` + Rename *Rename `json:"rename,omitempty"` + LockReason *string `json:"lock_reason,omitempty"` + DismissedReview *DismissedReview `json:"dismissed_review,omitempty"` + RequestedReviewer *User `json:"requested_reviewer,omitempty"` + RequestedTeam *Team `json:"requested_team,omitempty"` + ReviewRequester *User `json:"review_requester,omitempty"` + PerformedViaGithubApp *App `json:"performed_via_github_app,omitempty"` } // DismissedReview represents details for 'dismissed_review' events. @@ -95,24 +104,25 @@ type DismissedReview struct { // ListIssueEvents lists events for the specified issue. // -// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue -func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*IssueEvent, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/events?apiVersion=2022-11-28#list-issue-events +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/events +func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*IssueEvent, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - acceptHeaders := []string{mediaTypeLockReasonPreview, mediaTypeProjectCardDetailsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeProjectCardDetailsPreview) var events []*IssueEvent - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -122,21 +132,23 @@ func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, // ListRepositoryEvents lists events for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository -func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/events?apiVersion=2022-11-28#list-issue-events-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/issues/events +func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo string, opts *ListOptions) ([]*IssueEvent, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var events []*IssueEvent - resp, err := s.client.Do(ctx, req, &events) + resp, err := s.client.Do(req, &events) if err != nil { return nil, resp, err } @@ -146,17 +158,19 @@ func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo st // GetEvent returns the specified issue event. // -// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event +// GitHub API docs: https://docs.github.com/rest/issues/events?apiVersion=2022-11-28#get-an-issue-event +// +//meta:operation GET /repos/{owner}/{repo}/issues/events/{event_id} func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int64) (*IssueEvent, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - event := new(IssueEvent) - resp, err := s.client.Do(ctx, req, event) + var event *IssueEvent + resp, err := s.client.Do(req, &event) if err != nil { return nil, resp, err } diff --git a/github/issues_events_test.go b/github/issues_events_test.go index 17d6ac3d52f..48b1e9afc6b 100644 --- a/github/issues_events_test.go +++ b/github/issues_events_test.go @@ -6,22 +6,20 @@ package github import ( - "context" "fmt" "net/http" - "reflect" - "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestIssuesService_ListIssueEvents(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeLockReasonPreview, mediaTypeProjectCardDetailsPreview} mux.HandleFunc("/repos/o/r/issues/1/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeProjectCardDetailsPreview) testFormValues(t, r, values{ "page": "1", "per_page": "2", @@ -30,20 +28,35 @@ func TestIssuesService_ListIssueEvents(t *testing.T) { }) opt := &ListOptions{Page: 1, PerPage: 2} - events, _, err := client.Issues.ListIssueEvents(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + events, _, err := client.Issues.ListIssueEvents(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Issues.ListIssueEvents returned error: %v", err) } - want := []*IssueEvent{{ID: Int64(1)}} - if !reflect.DeepEqual(events, want) { + want := []*IssueEvent{{ID: Ptr(int64(1))}} + if !cmp.Equal(events, want) { t.Errorf("Issues.ListIssueEvents returned %+v, want %+v", events, want) } + + const methodName = "ListIssueEvents" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListIssueEvents(ctx, "\n", "\n", -1, &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListIssueEvents(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListRepositoryEvents(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -55,33 +68,63 @@ func TestIssuesService_ListRepositoryEvents(t *testing.T) { }) opt := &ListOptions{Page: 1, PerPage: 2} - events, _, err := client.Issues.ListRepositoryEvents(context.Background(), "o", "r", opt) + ctx := t.Context() + events, _, err := client.Issues.ListRepositoryEvents(ctx, "o", "r", opt) if err != nil { t.Errorf("Issues.ListRepositoryEvents returned error: %v", err) } - want := []*IssueEvent{{ID: Int64(1)}} - if !reflect.DeepEqual(events, want) { + want := []*IssueEvent{{ID: Ptr(int64(1))}} + if !cmp.Equal(events, want) { t.Errorf("Issues.ListRepositoryEvents returned %+v, want %+v", events, want) } + + const methodName = "ListRepositoryEvents" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListRepositoryEvents(ctx, "\n", "\n", &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListRepositoryEvents(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_GetEvent(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/events/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - event, _, err := client.Issues.GetEvent(context.Background(), "o", "r", 1) + ctx := t.Context() + event, _, err := client.Issues.GetEvent(ctx, "o", "r", 1) if err != nil { t.Errorf("Issues.GetEvent returned error: %v", err) } - want := &IssueEvent{ID: Int64(1)} - if !reflect.DeepEqual(event, want) { + want := &IssueEvent{ID: Ptr(int64(1))} + if !cmp.Equal(event, want) { t.Errorf("Issues.GetEvent returned %+v, want %+v", event, want) } + + const methodName = "GetEvent" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.GetEvent(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.GetEvent(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/issues_labels.go b/github/issues_labels.go index adcbe06834a..b7798854ffc 100644 --- a/github/issues_labels.go +++ b/github/issues_labels.go @@ -10,41 +10,54 @@ import ( "fmt" ) -// Label represents a GitHub label on an Issue +// Label represents a GitHub label on an Issue. type Label struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - Name *string `json:"name,omitempty"` - Color *string `json:"color,omitempty"` - Description *string `json:"description,omitempty"` - Default *bool `json:"default,omitempty"` - NodeID *string `json:"node_id,omitempty"` + ID int64 `json:"id"` + URL string `json:"url"` + Name string `json:"name"` + Color string `json:"color"` + Description *string `json:"description"` + Default bool `json:"default"` + NodeID string `json:"node_id"` } func (l Label) String() string { return Stringify(l) } +// CreateIssueLabelRequest represents a request to create a label. +type CreateIssueLabelRequest struct { + Name string `json:"name"` + Color *string `json:"color,omitempty"` + Description *string `json:"description,omitempty"` +} + +// UpdateIssueLabelRequest represents a request to update a label. +type UpdateIssueLabelRequest struct { + NewName *string `json:"new_name,omitempty"` + Color *string `json:"color,omitempty"` + Description *string `json:"description,omitempty"` +} + // ListLabels lists all labels for a repository. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository -func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Label, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#list-labels-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/labels +func (s *IssuesService) ListLabels(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Label, *Response, error) { u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - var labels []*Label - resp, err := s.client.Do(ctx, req, &labels) + resp, err := s.client.Do(req, &labels) if err != nil { return nil, resp, err } @@ -54,19 +67,18 @@ func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo strin // GetLabel gets a single label. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-a-single-label -func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string, name string) (*Label, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#get-a-label +// +//meta:operation GET /repos/{owner}/{repo}/labels/{name} +func (s *IssuesService) GetLabel(ctx context.Context, owner, repo, name string) (*Label, *Response, error) { u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - - label := new(Label) - resp, err := s.client.Do(ctx, req, label) + var label *Label + resp, err := s.client.Do(req, &label) if err != nil { return nil, resp, err } @@ -76,19 +88,18 @@ func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string, // CreateLabel creates a new label on the specified repository. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#create-a-label -func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo string, label *Label) (*Label, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#create-a-label +// +//meta:operation POST /repos/{owner}/{repo}/labels +func (s *IssuesService) CreateLabel(ctx context.Context, owner, repo string, body CreateIssueLabelRequest) (*Label, *Response, error) { u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) - req, err := s.client.NewRequest("POST", u, label) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - - l := new(Label) - resp, err := s.client.Do(ctx, req, l) + var l *Label + resp, err := s.client.Do(req, &l) if err != nil { return nil, resp, err } @@ -96,21 +107,20 @@ func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo stri return l, resp, nil } -// EditLabel edits a label. +// UpdateLabel updates a label. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#update-a-label -func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string, name string, label *Label) (*Label, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#update-a-label +// +//meta:operation PATCH /repos/{owner}/{repo}/labels/{name} +func (s *IssuesService) UpdateLabel(ctx context.Context, owner, repo, name string, body UpdateIssueLabelRequest) (*Label, *Response, error) { u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) - req, err := s.client.NewRequest("PATCH", u, label) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - - l := new(Label) - resp, err := s.client.Do(ctx, req, l) + var l *Label + resp, err := s.client.Do(req, &l) if err != nil { return nil, resp, err } @@ -120,36 +130,37 @@ func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string // DeleteLabel deletes a label. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#delete-a-label -func (s *IssuesService) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#delete-a-label +// +//meta:operation DELETE /repos/{owner}/{repo}/labels/{name} +func (s *IssuesService) DeleteLabel(ctx context.Context, owner, repo, name string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListLabelsByIssue lists all labels for an issue. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue -func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#list-labels-for-an-issue +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/labels +func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/labels", owner, repo, number) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - var labels []*Label - resp, err := s.client.Do(ctx, req, &labels) + resp, err := s.client.Do(req, &labels) if err != nil { return nil, resp, err } @@ -159,19 +170,18 @@ func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, rep // AddLabelsToIssue adds labels to an issue. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue -func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - req, err := s.client.NewRequest("POST", u, labels) +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#add-labels-to-an-issue +// +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/labels +func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner, repo string, number int, body []string) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/labels", owner, repo, number) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - var l []*Label - resp, err := s.client.Do(ctx, req, &l) + resp, err := s.client.Do(req, &l) if err != nil { return nil, resp, err } @@ -181,35 +191,33 @@ func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo // RemoveLabelForIssue removes a label for an issue. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue -func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner string, repo string, number int, label string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#remove-a-label-from-an-issue +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name} +func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner, repo string, number int, label string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/labels/%v", owner, repo, number, label) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ReplaceLabelsForIssue replaces all labels for an issue. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue -func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - req, err := s.client.NewRequest("PUT", u, labels) +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#set-labels-for-an-issue +// +//meta:operation PUT /repos/{owner}/{repo}/issues/{issue_number}/labels +func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner, repo string, number int, body []string) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/labels", owner, repo, number) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - var l []*Label - resp, err := s.client.Do(ctx, req, &l) + resp, err := s.client.Do(req, &l) if err != nil { return nil, resp, err } @@ -219,40 +227,38 @@ func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string, // RemoveLabelsForIssue removes all labels for an issue. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue -func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#remove-all-labels-from-an-issue +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels +func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/labels", owner, repo, number) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListLabelsForMilestone lists labels for every issue in a milestone. // -// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone -func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/issues/labels?apiVersion=2022-11-28#list-labels-for-issues-in-a-milestone +// +//meta:operation GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels +func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*Label, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%v/labels", owner, repo, number) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - var labels []*Label - resp, err := s.client.Do(ctx, req, &labels) + resp, err := s.client.Do(req, &labels) if err != nil { return nil, resp, err } diff --git a/github/issues_labels_test.go b/github/issues_labels_test.go index 6437d8800fa..0b672218e2d 100644 --- a/github/issues_labels_test.go +++ b/github/issues_labels_test.go @@ -6,355 +6,484 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestIssuesService_ListLabels(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) }) opt := &ListOptions{Page: 2} - labels, _, err := client.Issues.ListLabels(context.Background(), "o", "r", opt) + ctx := t.Context() + labels, _, err := client.Issues.ListLabels(ctx, "o", "r", opt) if err != nil { t.Errorf("Issues.ListLabels returned error: %v", err) } - want := []*Label{{Name: String("a")}, {Name: String("b")}} - if !reflect.DeepEqual(labels, want) { + want := []*Label{{Name: "a"}, {Name: "b"}} + if !cmp.Equal(labels, want) { t.Errorf("Issues.ListLabels returned %+v, want %+v", labels, want) } + + const methodName = "ListLabels" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListLabels(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListLabels(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListLabels_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ListLabels(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Issues.ListLabels(ctx, "%", "%", nil) testURLParseError(t, err) } func TestIssuesService_GetLabel(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) fmt.Fprint(w, `{"url":"u", "name": "n", "color": "c", "description": "d"}`) }) - label, _, err := client.Issues.GetLabel(context.Background(), "o", "r", "n") + ctx := t.Context() + label, _, err := client.Issues.GetLabel(ctx, "o", "r", "n") if err != nil { t.Errorf("Issues.GetLabel returned error: %v", err) } - want := &Label{URL: String("u"), Name: String("n"), Color: String("c"), Description: String("d")} - if !reflect.DeepEqual(label, want) { + want := &Label{URL: "u", Name: "n", Color: "c", Description: Ptr("d")} + if !cmp.Equal(label, want) { t.Errorf("Issues.GetLabel returned %+v, want %+v", label, want) } + + const methodName = "GetLabel" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.GetLabel(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.GetLabel(ctx, "o", "r", "n") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_GetLabel_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.GetLabel(context.Background(), "%", "%", "%") + ctx := t.Context() + _, _, err := client.Issues.GetLabel(ctx, "%", "%", "%") testURLParseError(t, err) } func TestIssuesService_CreateLabel(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Label{Name: String("n")} + input := CreateIssueLabelRequest{Name: "n"} mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { - v := new(Label) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"url":"u"}`) }) - label, _, err := client.Issues.CreateLabel(context.Background(), "o", "r", input) + ctx := t.Context() + label, _, err := client.Issues.CreateLabel(ctx, "o", "r", input) if err != nil { t.Errorf("Issues.CreateLabel returned error: %v", err) } - want := &Label{URL: String("u")} - if !reflect.DeepEqual(label, want) { + want := &Label{URL: "u"} + if !cmp.Equal(label, want) { t.Errorf("Issues.CreateLabel returned %+v, want %+v", label, want) } + + const methodName = "CreateLabel" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.CreateLabel(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.CreateLabel(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_CreateLabel_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.CreateLabel(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Issues.CreateLabel(ctx, "%", "%", CreateIssueLabelRequest{}) testURLParseError(t, err) } -func TestIssuesService_EditLabel(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestIssuesService_UpdateLabel(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &Label{Name: String("z")} + input := UpdateIssueLabelRequest{NewName: Ptr("z")} mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { - v := new(Label) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testPlainBody(t, r, `{"new_name":"z"}`+"\n") fmt.Fprint(w, `{"url":"u"}`) }) - label, _, err := client.Issues.EditLabel(context.Background(), "o", "r", "n", input) + ctx := t.Context() + label, _, err := client.Issues.UpdateLabel(ctx, "o", "r", "n", input) if err != nil { - t.Errorf("Issues.EditLabel returned error: %v", err) + t.Errorf("Issues.UpdateLabel returned error: %v", err) } - want := &Label{URL: String("u")} - if !reflect.DeepEqual(label, want) { - t.Errorf("Issues.EditLabel returned %+v, want %+v", label, want) + want := &Label{URL: "u"} + if !cmp.Equal(label, want) { + t.Errorf("Issues.UpdateLabel returned %+v, want %+v", label, want) } + + const methodName = "UpdateLabel" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.UpdateLabel(ctx, "\n", "\n", "\n", UpdateIssueLabelRequest{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.UpdateLabel(ctx, "o", "r", "n", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestIssuesService_EditLabel_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestIssuesService_UpdateLabel_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.EditLabel(context.Background(), "%", "%", "%", nil) + ctx := t.Context() + _, _, err := client.Issues.UpdateLabel(ctx, "%", "%", "%", UpdateIssueLabelRequest{}) testURLParseError(t, err) } func TestIssuesService_DeleteLabel(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/labels/n", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Issues.DeleteLabel(context.Background(), "o", "r", "n") + ctx := t.Context() + _, err := client.Issues.DeleteLabel(ctx, "o", "r", "n") if err != nil { t.Errorf("Issues.DeleteLabel returned error: %v", err) } + + const methodName = "DeleteLabel" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.DeleteLabel(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.DeleteLabel(ctx, "o", "r", "n") + }) } func TestIssuesService_DeleteLabel_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Issues.DeleteLabel(context.Background(), "%", "%", "%") + ctx := t.Context() + _, err := client.Issues.DeleteLabel(ctx, "%", "%", "%") testURLParseError(t, err) } func TestIssuesService_ListLabelsByIssue(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"name":"a","id":1},{"name":"b","id":2}]`) }) opt := &ListOptions{Page: 2} - labels, _, err := client.Issues.ListLabelsByIssue(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + labels, _, err := client.Issues.ListLabelsByIssue(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Issues.ListLabelsByIssue returned error: %v", err) } want := []*Label{ - {Name: String("a"), ID: Int64(1)}, - {Name: String("b"), ID: Int64(2)}, + {Name: "a", ID: 1}, + {Name: "b", ID: 2}, } - if !reflect.DeepEqual(labels, want) { + if !cmp.Equal(labels, want) { t.Errorf("Issues.ListLabelsByIssue returned %+v, want %+v", labels, want) } + + const methodName = "ListLabelsByIssue" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListLabelsByIssue(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListLabelsByIssue(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListLabelsByIssue_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ListLabelsByIssue(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.ListLabelsByIssue(ctx, "%", "%", 1, nil) testURLParseError(t, err) } func TestIssuesService_AddLabelsToIssue(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := []string{"a", "b"} mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { - var v []string - json.NewDecoder(r.Body).Decode(&v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `[{"url":"u"}]`) }) - labels, _, err := client.Issues.AddLabelsToIssue(context.Background(), "o", "r", 1, input) + ctx := t.Context() + labels, _, err := client.Issues.AddLabelsToIssue(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Issues.AddLabelsToIssue returned error: %v", err) } - want := []*Label{{URL: String("u")}} - if !reflect.DeepEqual(labels, want) { + want := []*Label{{URL: "u"}} + if !cmp.Equal(labels, want) { t.Errorf("Issues.AddLabelsToIssue returned %+v, want %+v", labels, want) } + + const methodName = "AddLabelsToIssue" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.AddLabelsToIssue(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.AddLabelsToIssue(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_AddLabelsToIssue_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.AddLabelsToIssue(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.AddLabelsToIssue(ctx, "%", "%", 1, nil) testURLParseError(t, err) } func TestIssuesService_RemoveLabelForIssue(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/issues/1/labels/l", func(w http.ResponseWriter, r *http.Request) { - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) + mux.HandleFunc("/repos/o/r/issues/1/labels/l", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Issues.RemoveLabelForIssue(context.Background(), "o", "r", 1, "l") + ctx := t.Context() + _, err := client.Issues.RemoveLabelForIssue(ctx, "o", "r", 1, "l") if err != nil { t.Errorf("Issues.RemoveLabelForIssue returned error: %v", err) } + + const methodName = "RemoveLabelForIssue" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.RemoveLabelForIssue(ctx, "\n", "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.RemoveLabelForIssue(ctx, "o", "r", 1, "l") + }) } func TestIssuesService_RemoveLabelForIssue_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Issues.RemoveLabelForIssue(context.Background(), "%", "%", 1, "%") + ctx := t.Context() + _, err := client.Issues.RemoveLabelForIssue(ctx, "%", "%", 1, "%") testURLParseError(t, err) } func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := []string{"a", "b"} mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { - var v []string - json.NewDecoder(r.Body).Decode(&v) - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `[{"url":"u"}]`) }) - labels, _, err := client.Issues.ReplaceLabelsForIssue(context.Background(), "o", "r", 1, input) + ctx := t.Context() + labels, _, err := client.Issues.ReplaceLabelsForIssue(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Issues.ReplaceLabelsForIssue returned error: %v", err) } - want := []*Label{{URL: String("u")}} - if !reflect.DeepEqual(labels, want) { + want := []*Label{{URL: "u"}} + if !cmp.Equal(labels, want) { t.Errorf("Issues.ReplaceLabelsForIssue returned %+v, want %+v", labels, want) } + + const methodName = "ReplaceLabelsForIssue" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ReplaceLabelsForIssue(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ReplaceLabelsForIssue(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ReplaceLabelsForIssue_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ReplaceLabelsForIssue(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.ReplaceLabelsForIssue(ctx, "%", "%", 1, nil) testURLParseError(t, err) } func TestIssuesService_RemoveLabelsForIssue(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/issues/1/labels", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) }) - _, err := client.Issues.RemoveLabelsForIssue(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Issues.RemoveLabelsForIssue(ctx, "o", "r", 1) if err != nil { t.Errorf("Issues.RemoveLabelsForIssue returned error: %v", err) } + + const methodName = "RemoveLabelsForIssue" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.RemoveLabelsForIssue(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.RemoveLabelsForIssue(ctx, "o", "r", 1) + }) } func TestIssuesService_RemoveLabelsForIssue_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Issues.RemoveLabelsForIssue(context.Background(), "%", "%", 1) + ctx := t.Context() + _, err := client.Issues.RemoveLabelsForIssue(ctx, "%", "%", 1) testURLParseError(t, err) } func TestIssuesService_ListLabelsForMilestone(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/milestones/1/labels", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) }) opt := &ListOptions{Page: 2} - labels, _, err := client.Issues.ListLabelsForMilestone(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + labels, _, err := client.Issues.ListLabelsForMilestone(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Issues.ListLabelsForMilestone returned error: %v", err) } - want := []*Label{{Name: String("a")}, {Name: String("b")}} - if !reflect.DeepEqual(labels, want) { + want := []*Label{{Name: "a"}, {Name: "b"}} + if !cmp.Equal(labels, want) { t.Errorf("Issues.ListLabelsForMilestone returned %+v, want %+v", labels, want) } + + const methodName = "ListLabelsForMilestone" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListLabelsForMilestone(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListLabelsForMilestone(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListLabelsForMilestone_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ListLabelsForMilestone(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.ListLabelsForMilestone(ctx, "%", "%", 1, nil) testURLParseError(t, err) } diff --git a/github/issues_milestones.go b/github/issues_milestones.go index ffe9aae14ca..d863f5c6ad8 100644 --- a/github/issues_milestones.go +++ b/github/issues_milestones.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // Milestone represents a GitHub repository milestone. @@ -24,10 +23,10 @@ type Milestone struct { Creator *User `json:"creator,omitempty"` OpenIssues *int `json:"open_issues,omitempty"` ClosedIssues *int `json:"closed_issues,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - DueOn *time.Time `json:"due_on,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + DueOn *Timestamp `json:"due_on,omitempty"` NodeID *string `json:"node_id,omitempty"` } @@ -35,6 +34,22 @@ func (m Milestone) String() string { return Stringify(m) } +// CreateMilestoneRequest represents a request to create a milestone. +type CreateMilestoneRequest struct { + Title string `json:"title"` + State *string `json:"state,omitempty"` + Description *string `json:"description,omitempty"` + DueOn *Timestamp `json:"due_on,omitempty"` +} + +// UpdateMilestoneRequest represents a request to update a milestone. +type UpdateMilestoneRequest struct { + Title *string `json:"title,omitempty"` + State *string `json:"state,omitempty"` + Description *string `json:"description,omitempty"` + DueOn *Timestamp `json:"due_on,omitempty"` +} + // MilestoneListOptions specifies the optional parameters to the // IssuesService.ListMilestones method. type MilestoneListOptions struct { @@ -55,21 +70,23 @@ type MilestoneListOptions struct { // ListMilestones lists all milestones for a repository. // -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository -func (s *IssuesService) ListMilestones(ctx context.Context, owner string, repo string, opt *MilestoneListOptions) ([]*Milestone, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/milestones?apiVersion=2022-11-28#list-milestones +// +//meta:operation GET /repos/{owner}/{repo}/milestones +func (s *IssuesService) ListMilestones(ctx context.Context, owner, repo string, opts *MilestoneListOptions) ([]*Milestone, *Response, error) { u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var milestones []*Milestone - resp, err := s.client.Do(ctx, req, &milestones) + resp, err := s.client.Do(req, &milestones) if err != nil { return nil, resp, err } @@ -79,16 +96,18 @@ func (s *IssuesService) ListMilestones(ctx context.Context, owner string, repo s // GetMilestone gets a single milestone. // -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone -func (s *IssuesService) GetMilestone(ctx context.Context, owner string, repo string, number int) (*Milestone, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/milestones?apiVersion=2022-11-28#get-a-milestone +// +//meta:operation GET /repos/{owner}/{repo}/milestones/{milestone_number} +func (s *IssuesService) GetMilestone(ctx context.Context, owner, repo string, number int) (*Milestone, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - milestone := new(Milestone) - resp, err := s.client.Do(ctx, req, milestone) + var milestone *Milestone + resp, err := s.client.Do(req, &milestone) if err != nil { return nil, resp, err } @@ -98,16 +117,18 @@ func (s *IssuesService) GetMilestone(ctx context.Context, owner string, repo str // CreateMilestone creates a new milestone on the specified repository. // -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone -func (s *IssuesService) CreateMilestone(ctx context.Context, owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/milestones?apiVersion=2022-11-28#create-a-milestone +// +//meta:operation POST /repos/{owner}/{repo}/milestones +func (s *IssuesService) CreateMilestone(ctx context.Context, owner, repo string, body CreateMilestoneRequest) (*Milestone, *Response, error) { u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) - req, err := s.client.NewRequest("POST", u, milestone) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - m := new(Milestone) - resp, err := s.client.Do(ctx, req, m) + var m *Milestone + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -115,18 +136,20 @@ func (s *IssuesService) CreateMilestone(ctx context.Context, owner string, repo return m, resp, nil } -// EditMilestone edits a milestone. +// UpdateMilestone updates a milestone. // -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone -func (s *IssuesService) EditMilestone(ctx context.Context, owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) - req, err := s.client.NewRequest("PATCH", u, milestone) +// GitHub API docs: https://docs.github.com/rest/issues/milestones?apiVersion=2022-11-28#update-a-milestone +// +//meta:operation PATCH /repos/{owner}/{repo}/milestones/{milestone_number} +func (s *IssuesService) UpdateMilestone(ctx context.Context, owner, repo string, number int, body UpdateMilestoneRequest) (*Milestone, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - m := new(Milestone) - resp, err := s.client.Do(ctx, req, m) + var m *Milestone + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -136,13 +159,15 @@ func (s *IssuesService) EditMilestone(ctx context.Context, owner string, repo st // DeleteMilestone deletes a milestone. // -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone -func (s *IssuesService) DeleteMilestone(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/issues/milestones?apiVersion=2022-11-28#delete-a-milestone +// +//meta:operation DELETE /repos/{owner}/{repo}/milestones/{milestone_number} +func (s *IssuesService) DeleteMilestone(ctx context.Context, owner, repo string, number int) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/milestones/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/issues_milestones_test.go b/github/issues_milestones_test.go index 019f00a8b9b..233f4b59b5b 100644 --- a/github/issues_milestones_test.go +++ b/github/issues_milestones_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestIssuesService_ListMilestones(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -30,145 +29,209 @@ func TestIssuesService_ListMilestones(t *testing.T) { }) opt := &MilestoneListOptions{"closed", "due_date", "asc", ListOptions{Page: 2}} - milestones, _, err := client.Issues.ListMilestones(context.Background(), "o", "r", opt) + ctx := t.Context() + milestones, _, err := client.Issues.ListMilestones(ctx, "o", "r", opt) if err != nil { t.Errorf("IssuesService.ListMilestones returned error: %v", err) } - want := []*Milestone{{Number: Int(1)}} - if !reflect.DeepEqual(milestones, want) { + want := []*Milestone{{Number: Ptr(1)}} + if !cmp.Equal(milestones, want) { t.Errorf("IssuesService.ListMilestones returned %+v, want %+v", milestones, want) } + + const methodName = "ListMilestones" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListMilestones(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListMilestones(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.ListMilestones(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Issues.ListMilestones(ctx, "%", "r", nil) testURLParseError(t, err) } func TestIssuesService_GetMilestone(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"number":1}`) }) - milestone, _, err := client.Issues.GetMilestone(context.Background(), "o", "r", 1) + ctx := t.Context() + milestone, _, err := client.Issues.GetMilestone(ctx, "o", "r", 1) if err != nil { t.Errorf("IssuesService.GetMilestone returned error: %v", err) } - want := &Milestone{Number: Int(1)} - if !reflect.DeepEqual(milestone, want) { + want := &Milestone{Number: Ptr(1)} + if !cmp.Equal(milestone, want) { t.Errorf("IssuesService.GetMilestone returned %+v, want %+v", milestone, want) } + + const methodName = "GetMilestone" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.GetMilestone(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.GetMilestone(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.GetMilestone(context.Background(), "%", "r", 1) + ctx := t.Context() + _, _, err := client.Issues.GetMilestone(ctx, "%", "r", 1) testURLParseError(t, err) } func TestIssuesService_CreateMilestone(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Milestone{Title: String("t")} + input := CreateMilestoneRequest{Title: "t"} mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { - v := new(Milestone) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":1}`) }) - milestone, _, err := client.Issues.CreateMilestone(context.Background(), "o", "r", input) + ctx := t.Context() + milestone, _, err := client.Issues.CreateMilestone(ctx, "o", "r", input) if err != nil { t.Errorf("IssuesService.CreateMilestone returned error: %v", err) } - want := &Milestone{Number: Int(1)} - if !reflect.DeepEqual(milestone, want) { + want := &Milestone{Number: Ptr(1)} + if !cmp.Equal(milestone, want) { t.Errorf("IssuesService.CreateMilestone returned %+v, want %+v", milestone, want) } + + const methodName = "CreateMilestone" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.CreateMilestone(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.CreateMilestone(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.CreateMilestone(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Issues.CreateMilestone(ctx, "%", "r", CreateMilestoneRequest{}) testURLParseError(t, err) } -func TestIssuesService_EditMilestone(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestIssuesService_UpdateMilestone(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &Milestone{Title: String("t")} + input := UpdateMilestoneRequest{Title: Ptr("t")} mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { - v := new(Milestone) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":1}`) }) - milestone, _, err := client.Issues.EditMilestone(context.Background(), "o", "r", 1, input) + ctx := t.Context() + milestone, _, err := client.Issues.UpdateMilestone(ctx, "o", "r", 1, input) if err != nil { - t.Errorf("IssuesService.EditMilestone returned error: %v", err) + t.Errorf("IssuesService.UpdateMilestone returned error: %v", err) } - want := &Milestone{Number: Int(1)} - if !reflect.DeepEqual(milestone, want) { - t.Errorf("IssuesService.EditMilestone returned %+v, want %+v", milestone, want) + want := &Milestone{Number: Ptr(1)} + if !cmp.Equal(milestone, want) { + t.Errorf("IssuesService.UpdateMilestone returned %+v, want %+v", milestone, want) } + + const methodName = "UpdateMilestone" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.UpdateMilestone(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.UpdateMilestone(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestIssuesService_UpdateMilestone_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.EditMilestone(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.UpdateMilestone(ctx, "%", "r", 1, UpdateMilestoneRequest{}) testURLParseError(t, err) } func TestIssuesService_DeleteMilestone(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/milestones/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Issues.DeleteMilestone(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Issues.DeleteMilestone(ctx, "o", "r", 1) if err != nil { t.Errorf("IssuesService.DeleteMilestone returned error: %v", err) } + + const methodName = "DeleteMilestone" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.DeleteMilestone(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.DeleteMilestone(ctx, "o", "r", 1) + }) } func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Issues.DeleteMilestone(context.Background(), "%", "r", 1) + ctx := t.Context() + _, err := client.Issues.DeleteMilestone(ctx, "%", "r", 1) testURLParseError(t, err) } diff --git a/github/issues_test.go b/github/issues_test.go index 7edcf09b68a..9c5f90fda8c 100644 --- a/github/issues_test.go +++ b/github/issues_test.go @@ -6,113 +6,186 @@ package github import ( - "context" "encoding/json" "fmt" "net/http" - "reflect" - "strings" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) -func TestIssuesService_List_all(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestIssuesService_ListAllIssues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeReactionsPreview) testFormValues(t, r, values{ "filter": "all", "state": "closed", "labels": "a,b", "sort": "updated", "direction": "asc", - "since": "2002-02-10T15:30:00Z", - "page": "1", - "per_page": "2", + "since": referenceTimeRaw, + "collab": "true", + "orgs": "true", + "owned": "true", + "pulls": "true", + "page": "5", + "per_page": "10", }) fmt.Fprint(w, `[{"number":1}]`) }) - opt := &IssueListOptions{ - "all", "closed", []string{"a", "b"}, "updated", "asc", - time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), - ListOptions{Page: 1, PerPage: 2}, + opt := &ListAllIssuesOptions{ + Filter: "all", + State: "closed", + Labels: []string{"a", "b"}, + Sort: "updated", + Direction: "asc", + Since: referenceTime, + Collab: true, + Orgs: true, + Owned: true, + Pulls: true, + ListOptions: ListOptions{Page: 5, PerPage: 10}, } - issues, _, err := client.Issues.List(context.Background(), true, opt) + ctx := t.Context() + issues, _, err := client.Issues.ListAllIssues(ctx, opt) if err != nil { - t.Errorf("Issues.List returned error: %v", err) + t.Errorf("Issues.ListAllIssues returned error: %v", err) } - want := []*Issue{{Number: Int(1)}} - if !reflect.DeepEqual(issues, want) { - t.Errorf("Issues.List returned %+v, want %+v", issues, want) + want := []*Issue{{Number: Ptr(1)}} + if !cmp.Equal(issues, want) { + t.Errorf("Issues.ListAllIssues = %+v, want %+v", issues, want) } + + const methodName = "ListAllIssues" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListAllIssues(ctx, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestIssuesService_List_owned(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestIssuesService_ListUserIssues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{ + "filter": "all", + "state": "closed", + "labels": "a,b", + "sort": "updated", + "direction": "asc", + "since": referenceTimeRaw, + "per_page": "4", + "page": "2", + }) fmt.Fprint(w, `[{"number":1}]`) }) - issues, _, err := client.Issues.List(context.Background(), false, nil) + ctx := t.Context() + opts := &ListUserIssuesOptions{ + Filter: "all", + State: "closed", + Labels: []string{"a", "b"}, + Sort: "updated", + Direction: "asc", + Since: referenceTime, + ListOptions: ListOptions{Page: 2, PerPage: 4}, + } + issues, _, err := client.Issues.ListUserIssues(ctx, opts) if err != nil { - t.Errorf("Issues.List returned error: %v", err) + t.Errorf("Issues.ListUserIssues returned error: %v", err) } - want := []*Issue{{Number: Int(1)}} - if !reflect.DeepEqual(issues, want) { - t.Errorf("Issues.List returned %+v, want %+v", issues, want) + want := []*Issue{{Number: Ptr(1)}} + if !cmp.Equal(issues, want) { + t.Errorf("Issues.ListUserIssues = %+v, want %+v", issues, want) } + + const methodName = "ListUserIssues" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListUserIssues(ctx, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, nil) + } + return resp, err + }) } func TestIssuesService_ListByOrg(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testFormValues(t, r, values{ + "filter": "all", + "state": "closed", + "labels": "a,b", + "type": "bug", + "sort": "updated", + "direction": "asc", + "since": referenceTimeRaw, + "per_page": "4", + "page": "2", + }) + testHeader(t, r, "Accept", mediaTypeReactionsPreview) fmt.Fprint(w, `[{"number":1}]`) }) - issues, _, err := client.Issues.ListByOrg(context.Background(), "o", nil) + ctx := t.Context() + opts := &IssueListByOrgOptions{ + Filter: "all", + State: "closed", + Labels: []string{"a", "b"}, + Type: "bug", + Sort: "updated", + Direction: "asc", + Since: referenceTime, + ListOptions: ListOptions{Page: 2, PerPage: 4}, + } + issues, _, err := client.Issues.ListByOrg(ctx, "o", opts) if err != nil { t.Errorf("Issues.ListByOrg returned error: %v", err) } - want := []*Issue{{Number: Int(1)}} - if !reflect.DeepEqual(issues, want) { - t.Errorf("Issues.List returned %+v, want %+v", issues, want) + want := []*Issue{{Number: Ptr(1)}} + if !cmp.Equal(issues, want) { + t.Errorf("Issues.ListByOrg returned %+v, want %+v", issues, want) } -} -func TestIssuesService_ListByOrg_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + const methodName = "ListByOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListByOrg(ctx, "\n", opts) + return err + }) - _, _, err := client.Issues.ListByOrg(context.Background(), "%", nil) - testURLParseError(t, err) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListByOrg(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_ListByRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview} mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeReactionsPreview) testFormValues(t, r, values{ "milestone": "*", "state": "closed", @@ -122,156 +195,286 @@ func TestIssuesService_ListByRepo(t *testing.T) { "labels": "a,b", "sort": "updated", "direction": "asc", - "since": "2002-02-10T15:30:00Z", + "since": referenceTimeRaw, + "per_page": "1", }) fmt.Fprint(w, `[{"number":1}]`) }) + // IssueListByRepoOptions uses standard strings (not pointers) and ListCursorOptions opt := &IssueListByRepoOptions{ - "*", "closed", "a", "c", "m", []string{"a", "b"}, "updated", "asc", - time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), - ListOptions{0, 0}, + Milestone: "*", + State: "closed", + Assignee: "a", + Creator: "c", + Mentioned: "m", + Labels: []string{"a", "b"}, + Sort: "updated", + Direction: "asc", + Since: referenceTime, + ListCursorOptions: ListCursorOptions{PerPage: 1}, } - issues, _, err := client.Issues.ListByRepo(context.Background(), "o", "r", opt) + + ctx := t.Context() + issues, _, err := client.Issues.ListByRepo(ctx, "o", "r", opt) if err != nil { - t.Errorf("Issues.ListByOrg returned error: %v", err) + t.Errorf("Issues.ListByRepo returned error: %v", err) } - want := []*Issue{{Number: Int(1)}} - if !reflect.DeepEqual(issues, want) { - t.Errorf("Issues.List returned %+v, want %+v", issues, want) + want := []*Issue{{Number: Ptr(1)}} + if !cmp.Equal(issues, want) { + t.Errorf("Issues.ListByRepo returned %+v, want %+v", issues, want) } -} -func TestIssuesService_ListByRepo_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + const methodName = "ListByRepo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListByRepo(ctx, "\n", "\n", opt) + return err + }) - _, _, err := client.Issues.ListByRepo(context.Background(), "%", "r", nil) - testURLParseError(t, err) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListByRepo(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - fmt.Fprint(w, `{"number":1, "labels": [{"url": "u", "name": "n", "color": "c"}]}`) + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + fmt.Fprint(w, `{"number":1, "author_association": "MEMBER","labels": [{"url": "u", "name": "n", "color": "c"}]}`) }) - issue, _, err := client.Issues.Get(context.Background(), "o", "r", 1) + ctx := t.Context() + issue, _, err := client.Issues.Get(ctx, "o", "r", 1) if err != nil { t.Errorf("Issues.Get returned error: %v", err) } want := &Issue{ - Number: Int(1), - Labels: []Label{{ - URL: String("u"), - Name: String("n"), - Color: String("c"), + Number: Ptr(1), + AuthorAssociation: Ptr("MEMBER"), + Labels: []*Label{{ + URL: "u", + Name: "n", + Color: "c", }}, } - if !reflect.DeepEqual(issue, want) { + if !cmp.Equal(issue, want) { t.Errorf("Issues.Get returned %+v, want %+v", issue, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.Get(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.Get(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_Get_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.Get(context.Background(), "%", "r", 1) + ctx := t.Context() + _, _, err := client.Issues.Get(ctx, "%", "r", 1) testURLParseError(t, err) } func TestIssuesService_Create(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &IssueRequest{ - Title: String("t"), - Body: String("b"), - Assignee: String("a"), - Labels: &[]string{"l1", "l2"}, + t.Parallel() + client, mux, _ := setup(t) + + input := CreateIssueRequest{ + Title: "t", + Body: Ptr("b"), + Assignee: Ptr("a"), + Labels: []string{"l1", "l2"}, } mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { - v := new(IssueRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":1}`) }) - issue, _, err := client.Issues.Create(context.Background(), "o", "r", input) + ctx := t.Context() + issue, _, err := client.Issues.Create(ctx, "o", "r", input) if err != nil { t.Errorf("Issues.Create returned error: %v", err) } - want := &Issue{Number: Int(1)} - if !reflect.DeepEqual(issue, want) { + want := &Issue{Number: Ptr(1)} + if !cmp.Equal(issue, want) { t.Errorf("Issues.Create returned %+v, want %+v", issue, want) } + + const methodName = "Create" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.Create(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.Create(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestIssuesService_Create_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.Create(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Issues.Create(ctx, "%", "r", CreateIssueRequest{}) testURLParseError(t, err) } -func TestIssuesService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestIssuesService_Update(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &IssueRequest{Title: String("t")} + input := UpdateIssueRequest{Title: Ptr("t"), Type: Ptr("bug")} mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { - v := new(IssueRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + testJSONBody(t, r, input) + fmt.Fprint(w, `{"number":1, "type": {"name": "bug"}}`) + }) + + ctx := t.Context() + issue, _, err := client.Issues.Update(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("Issues.Update returned error: %v", err) + } + + want := &Issue{Number: Ptr(1), Type: &IssueType{Name: Ptr("bug")}} + if !cmp.Equal(issue, want) { + t.Errorf("Issues.Update returned %+v, want %+v", issue, want) + } + + const methodName = "Update" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.Update(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.Update(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} +func TestIssuesService_RemoveMilestone(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") fmt.Fprint(w, `{"number":1}`) }) - issue, _, err := client.Issues.Edit(context.Background(), "o", "r", 1, input) + ctx := t.Context() + issue, _, err := client.Issues.RemoveMilestone(ctx, "o", "r", 1) if err != nil { - t.Errorf("Issues.Edit returned error: %v", err) + t.Errorf("Issues.RemoveMilestone returned error: %v", err) } - want := &Issue{Number: Int(1)} - if !reflect.DeepEqual(issue, want) { - t.Errorf("Issues.Edit returned %+v, want %+v", issue, want) + want := &Issue{Number: Ptr(1)} + if !cmp.Equal(issue, want) { + t.Errorf("Issues.RemoveMilestone returned %+v, want %+v", issue, want) } + + const methodName = "RemoveMilestone" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.RemoveMilestone(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.RemoveMilestone(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestIssuesService_Edit_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestIssuesService_Update_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Issues.Edit(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.Issues.Update(ctx, "%", "r", 1, UpdateIssueRequest{}) testURLParseError(t, err) } +// TestIssueRequest_Marshal_LabelsAndAssignees verifies the omitzero behavior of +// the Labels and Assignees fields: a nil slice is omitted from the request body +// (leaving the existing values unchanged), whereas a non-nil slice — including +// an explicit empty slice — is sent (clearing the values when empty). +func TestIssueRequest_Marshal_LabelsAndAssignees(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input UpdateIssueRequest + want string + }{ + { + name: "nil labels and assignees are omitted", + input: UpdateIssueRequest{}, + want: `{}`, + }, + { + name: "empty non-nil labels and assignees are sent to clear them", + input: UpdateIssueRequest{Labels: []string{}, Assignees: []string{}}, + want: `{"labels":[],"assignees":[]}`, + }, + { + name: "populated labels and assignees are sent", + input: UpdateIssueRequest{Labels: []string{"bug"}, Assignees: []string{"octocat"}}, + want: `{"labels":["bug"],"assignees":["octocat"]}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + b, err := json.Marshal(tt.input) + if err != nil { + t.Fatalf("json.Marshal(%#v) returned error: %v", tt.input, err) + } + if got := string(b); got != tt.want { + t.Errorf("json.Marshal(%#v) = %v, want %v", tt.input, got, tt.want) + } + }) + } +} + func TestIssuesService_Lock(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -279,31 +482,42 @@ func TestIssuesService_Lock(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Issues.Lock(context.Background(), "o", "r", 1, nil); err != nil { + ctx := t.Context() + if _, err := client.Issues.Lock(ctx, "o", "r", 1, nil); err != nil { t.Errorf("Issues.Lock returned error: %v", err) } + + const methodName = "Lock" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.Lock(ctx, "\n", "\n", -1, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.Lock(ctx, "o", "r", 1, nil) + }) } func TestIssuesService_LockWithReason(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeLockReasonPreview) w.WriteHeader(http.StatusNoContent) }) opt := &LockIssueOptions{LockReason: "off-topic"} - if _, err := client.Issues.Lock(context.Background(), "o", "r", 1, opt); err != nil { + ctx := t.Context() + if _, err := client.Issues.Lock(ctx, "o", "r", 1, opt); err != nil { t.Errorf("Issues.Lock returned error: %v", err) } } func TestIssuesService_Unlock(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -311,18 +525,30 @@ func TestIssuesService_Unlock(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Issues.Unlock(context.Background(), "o", "r", 1); err != nil { + ctx := t.Context() + if _, err := client.Issues.Unlock(ctx, "o", "r", 1); err != nil { t.Errorf("Issues.Unlock returned error: %v", err) } + + const methodName = "Unlock" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Issues.Unlock(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Issues.Unlock(ctx, "o", "r", 1) + }) } func TestIsPullRequest(t *testing.T) { - i := new(Issue) - if i.IsPullRequest() == true { + t.Parallel() + var i Issue + if i.IsPullRequest() { t.Errorf("expected i.IsPullRequest (%v) to return false, got true", i) } - i.PullRequestLinks = &PullRequestLinks{URL: String("http://example.com")} - if i.IsPullRequest() == false { + i.PullRequestLinks = &PullRequestLinks{URL: Ptr("http://example.com")} + if !i.IsPullRequest() { t.Errorf("expected i.IsPullRequest (%v) to return true, got false", i) } } diff --git a/github/issues_timeline.go b/github/issues_timeline.go index d0e4a3a9428..7cebc47c73a 100644 --- a/github/issues_timeline.go +++ b/github/issues_timeline.go @@ -9,13 +9,12 @@ import ( "context" "fmt" "strings" - "time" ) // Timeline represents an event that occurred around an Issue or Pull Request. // // It is similar to an IssueEvent but may contain more information. -// GitHub API docs: https://developer.github.com/v3/issues/timeline/ +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/events/issue-event-types type Timeline struct { ID *int64 `json:"id,omitempty"` URL *string `json:"url,omitempty"` @@ -24,6 +23,20 @@ type Timeline struct { // The User object that generated the event. Actor *User `json:"actor,omitempty"` + // The person who commented on the issue. + User *User `json:"user,omitempty"` + + // The person who authored the commit. + Author *CommitAuthor `json:"author,omitempty"` + // The person who committed the commit on behalf of the author. + Committer *CommitAuthor `json:"committer,omitempty"` + // The SHA of the commit in the pull request. + SHA *string `json:"sha,omitempty"` + // The commit message. + Message *string `json:"message,omitempty"` + // A list of parent commits. + Parents []*Commit `json:"parents,omitempty"` + // Event identifies the actual type of Event that occurred. Possible values // are: // @@ -81,6 +94,17 @@ type Timeline struct { // reopened // The issue was reopened by the actor. // + // reviewed + // The pull request was reviewed. + // + // review_requested + // The actor requested a review from a user or team. + // Reviewer and Requester/RequestedTeam will be populated. + // + // review_request_removed + // The actor removed a review request from a user or team. + // Reviewer and Requester/RequestedTeam will be populated. + // // subscribed // The actor subscribed to receive notifications for an issue. // @@ -101,13 +125,15 @@ type Timeline struct { // The string SHA of a commit that referenced this Issue or Pull Request. CommitID *string `json:"commit_id,omitempty"` // The timestamp indicating when the event occurred. - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` // The Label object including `name` and `color` attributes. Only provided for // 'labeled' and 'unlabeled' events. Label *Label `json:"label,omitempty"` // The User object which was assigned to (or unassigned from) this Issue or // Pull Request. Only provided for 'assigned' and 'unassigned' events. Assignee *User `json:"assignee,omitempty"` + Assigner *User `json:"assigner,omitempty"` + // The Milestone object including a 'title' attribute. // Only provided for 'milestoned' and 'demilestoned' events. Milestone *Milestone `json:"milestone,omitempty"` @@ -116,8 +142,24 @@ type Timeline struct { Source *Source `json:"source,omitempty"` // An object containing rename details including 'from' and 'to' attributes. // Only provided for 'renamed' events. - Rename *Rename `json:"rename,omitempty"` - ProjectCard *ProjectCard `json:"project_card,omitempty"` + Rename *Rename `json:"rename,omitempty"` + // The state of a submitted review. Can be one of: 'commented', + // 'changes_requested' or 'approved'. + // Only provided for 'reviewed' events. + State *string `json:"state,omitempty"` + + // The person requested to review the pull request. + Reviewer *User `json:"requested_reviewer,omitempty"` + // RequestedTeam contains the team requested to review the pull request. + RequestedTeam *Team `json:"requested_team,omitempty"` + // The person who requested a review. + Requester *User `json:"review_requester,omitempty"` + + // The review summary text. + Body *string `json:"body,omitempty"` + SubmittedAt *Timestamp `json:"submitted_at,omitempty"` + + PerformedViaGithubApp *App `json:"performed_via_github_app,omitempty"` } // Source represents a reference's source. @@ -131,24 +173,29 @@ type Source struct { // ListIssueTimeline lists events for the specified issue. // -// GitHub API docs: https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue -func (s *IssuesService) ListIssueTimeline(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Timeline, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/issues/timeline?apiVersion=2022-11-28#list-timeline-events-for-an-issue +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/timeline +func (s *IssuesService) ListIssueTimeline(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*Timeline, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/%v/timeline", owner, repo, number) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. acceptHeaders := []string{mediaTypeTimelinePreview, mediaTypeProjectCardDetailsPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var events []*Timeline - resp, err := s.client.Do(ctx, req, &events) - return events, resp, err + resp, err := s.client.Do(req, &events) + if err != nil { + return nil, resp, err + } + + return events, resp, nil } diff --git a/github/issues_timeline_test.go b/github/issues_timeline_test.go index 4d0a41bb94d..81cc6e5b0ae 100644 --- a/github/issues_timeline_test.go +++ b/github/issues_timeline_test.go @@ -6,17 +6,17 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestIssuesService_ListIssueTimeline(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) wantAcceptHeaders := []string{mediaTypeTimelinePreview, mediaTypeProjectCardDetailsPreview} mux.HandleFunc("/repos/o/r/issues/1/timeline", func(w http.ResponseWriter, r *http.Request) { @@ -30,13 +30,116 @@ func TestIssuesService_ListIssueTimeline(t *testing.T) { }) opt := &ListOptions{Page: 1, PerPage: 2} - events, _, err := client.Issues.ListIssueTimeline(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + events, _, err := client.Issues.ListIssueTimeline(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Issues.ListIssueTimeline returned error: %v", err) } - want := []*Timeline{{ID: Int64(1)}} - if !reflect.DeepEqual(events, want) { + want := []*Timeline{{ID: Ptr(int64(1))}} + if !cmp.Equal(events, want) { t.Errorf("Issues.ListIssueTimeline = %+v, want %+v", events, want) } + + const methodName = "ListIssueTimeline" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Issues.ListIssueTimeline(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Issues.ListIssueTimeline(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTimeline_ReviewRequests(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/example-org/example-repo/issues/3/timeline", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "id": 1234567890, + "url": "http://example.com/timeline/1", + "actor": { + "login": "actor-user", + "id": 1 + }, + "event": "review_requested", + "created_at": "2006-01-02T15:04:05Z", + "requested_reviewer": { + "login": "reviewer-user", + "id": 2 + }, + "review_requester": { + "login": "requester-user", + "id": 1 + } + }, + { + "id": 1234567891, + "url": "http://example.com/timeline/2", + "actor": { + "login": "actor-user", + "id": 1 + }, + "event": "review_request_removed", + "created_at": "2006-01-02T15:04:05Z", + "requested_reviewer": { + "login": "reviewer-user", + "id": 2 + } + }]`) + }) + + ctx := t.Context() + events, _, err := client.Issues.ListIssueTimeline(ctx, "example-org", "example-repo", 3, nil) + if err != nil { + t.Errorf("Issues.ListIssueTimeline returned error: %v", err) + } + + want := []*Timeline{ + { + ID: Ptr(int64(1234567890)), + URL: Ptr("http://example.com/timeline/1"), + Actor: &User{ + Login: Ptr("actor-user"), + ID: Ptr(int64(1)), + }, + Event: Ptr("review_requested"), + CreatedAt: &Timestamp{referenceTime}, + Reviewer: &User{ + Login: Ptr("reviewer-user"), + ID: Ptr(int64(2)), + }, + Requester: &User{ + Login: Ptr("requester-user"), + ID: Ptr(int64(1)), + }, + }, + { + ID: Ptr(int64(1234567891)), + URL: Ptr("http://example.com/timeline/2"), + Actor: &User{ + Login: Ptr("actor-user"), + ID: Ptr(int64(1)), + }, + Event: Ptr("review_request_removed"), + CreatedAt: &Timestamp{referenceTime}, + Reviewer: &User{ + Login: Ptr("reviewer-user"), + ID: Ptr(int64(2)), + }, + }, + } + + if !cmp.Equal(events, want) { + t.Errorf("Issues.ListIssueTimeline review request events = %+v, want %+v", events, want) + diff := cmp.Diff(events, want) + t.Errorf("Difference: %v", diff) + } } diff --git a/github/licenses.go b/github/licenses.go index 1176d3a8bf7..8d34f6feb47 100644 --- a/github/licenses.go +++ b/github/licenses.go @@ -13,7 +13,7 @@ import ( // LicensesService handles communication with the license related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/licenses/ +// GitHub API docs: https://docs.github.com/rest/licenses?apiVersion=2022-11-28 type LicensesService service // RepositoryLicense represents the license for a repository. @@ -58,17 +58,32 @@ func (l License) String() string { return Stringify(l) } +// ListLicensesOptions specifies the optional parameters to the LicensesService.List method. +type ListLicensesOptions struct { + Featured *bool `url:"featured,omitempty"` + + ListOptions +} + // List popular open source licenses. // -// GitHub API docs: https://developer.github.com/v3/licenses/#list-all-licenses -func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, error) { - req, err := s.client.NewRequest("GET", "licenses", nil) +// GitHub API docs: https://docs.github.com/rest/licenses/licenses?apiVersion=2022-11-28#get-all-commonly-used-licenses +// +//meta:operation GET /licenses +func (s *LicensesService) List(ctx context.Context, opts *ListLicensesOptions) ([]*License, *Response, error) { + u := "licenses" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var licenses []*License - resp, err := s.client.Do(ctx, req, &licenses) + resp, err := s.client.Do(req, &licenses) if err != nil { return nil, resp, err } @@ -78,17 +93,19 @@ func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, erro // Get extended metadata for one license. // -// GitHub API docs: https://developer.github.com/v3/licenses/#get-an-individual-license +// GitHub API docs: https://docs.github.com/rest/licenses/licenses?apiVersion=2022-11-28#get-a-license +// +//meta:operation GET /licenses/{license} func (s *LicensesService) Get(ctx context.Context, licenseName string) (*License, *Response, error) { - u := fmt.Sprintf("licenses/%s", licenseName) + u := fmt.Sprintf("licenses/%v", licenseName) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - license := new(License) - resp, err := s.client.Do(ctx, req, license) + var license *License + resp, err := s.client.Do(req, &license) if err != nil { return nil, resp, err } diff --git a/github/licenses_test.go b/github/licenses_test.go index a71f6a4efba..521e3712bcf 100644 --- a/github/licenses_test.go +++ b/github/licenses_test.go @@ -6,63 +6,91 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestLicensesService_List(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/licenses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") + testFormValues(t, r, values{"featured": "true", "page": "2", "per_page": "20"}) fmt.Fprint(w, `[{"key":"mit","name":"MIT","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}]`) }) - licenses, _, err := client.Licenses.List(context.Background()) + opts := &ListLicensesOptions{Featured: Ptr(true), ListOptions: ListOptions{Page: 2, PerPage: 20}} + ctx := t.Context() + licenses, _, err := client.Licenses.List(ctx, opts) if err != nil { t.Errorf("Licenses.List returned error: %v", err) } want := []*License{{ - Key: String("mit"), - Name: String("MIT"), - SPDXID: String("MIT"), - URL: String("https://api.github.com/licenses/mit"), - Featured: Bool(true), + Key: Ptr("mit"), + Name: Ptr("MIT"), + SPDXID: Ptr("MIT"), + URL: Ptr("https://api.github.com/licenses/mit"), + Featured: Ptr(true), }} - if !reflect.DeepEqual(licenses, want) { + if !cmp.Equal(licenses, want) { t.Errorf("Licenses.List returned %+v, want %+v", licenses, want) } + + const methodName = "List" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Licenses.List(ctx, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestLicensesService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/licenses/mit", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"key":"mit","name":"MIT"}`) }) - license, _, err := client.Licenses.Get(context.Background(), "mit") + ctx := t.Context() + license, _, err := client.Licenses.Get(ctx, "mit") if err != nil { t.Errorf("Licenses.Get returned error: %v", err) } - want := &License{Key: String("mit"), Name: String("MIT")} - if !reflect.DeepEqual(license, want) { + want := &License{Key: Ptr("mit"), Name: Ptr("MIT")} + if !cmp.Equal(license, want) { t.Errorf("Licenses.Get returned %+v, want %+v", license, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Licenses.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Licenses.Get(ctx, "mit") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestLicensesService_Get_invalidTemplate(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Licenses.Get(context.Background(), "%") + ctx := t.Context() + _, _, err := client.Licenses.Get(ctx, "%") testURLParseError(t, err) } diff --git a/github/markdown.go b/github/markdown.go new file mode 100644 index 00000000000..60e4660cd46 --- /dev/null +++ b/github/markdown.go @@ -0,0 +1,69 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" +) + +// MarkdownService provides access to markdown-related functions in the GitHub API. +type MarkdownService service + +// MarkdownOptions specifies optional parameters to the Render method. +type MarkdownOptions struct { + // Mode identifies the rendering mode. Possible values are: + // markdown - render a document as plain Render, just like + // README files are rendered. + // + // gfm - to render a document as user-content, e.g. like user + // comments or issues are rendered. In GFM mode, hard line breaks are + // always taken into account, and issue and user mentions are linked + // accordingly. + // + // Default is "markdown". + Mode string + + // Context identifies the repository context. Only taken into account + // when rendering as "gfm". + Context string +} + +type markdownRenderRequest struct { + Text *string `json:"text,omitempty"` + Mode *string `json:"mode,omitempty"` + Context *string `json:"context,omitempty"` +} + +// Render renders an arbitrary Render document. +// +// GitHub API docs: https://docs.github.com/rest/markdown/markdown?apiVersion=2022-11-28#render-a-markdown-document +// +//meta:operation POST /markdown +func (s *MarkdownService) Render(ctx context.Context, text string, opts *MarkdownOptions) (string, *Response, error) { + request := &markdownRenderRequest{Text: &text} + if opts != nil { + if opts.Mode != "" { + request.Mode = &opts.Mode + } + if opts.Context != "" { + request.Context = &opts.Context + } + } + + req, err := s.client.NewRequest(ctx, "POST", "markdown", request) + if err != nil { + return "", nil, err + } + + var buf bytes.Buffer + resp, err := s.client.Do(req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} diff --git a/github/markdown_test.go b/github/markdown_test.go new file mode 100644 index 00000000000..37eda279592 --- /dev/null +++ b/github/markdown_test.go @@ -0,0 +1,53 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" +) + +func TestMarkdownService_Markdown(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &markdownRenderRequest{ + Text: Ptr("# text #"), + Mode: Ptr("gfm"), + Context: Ptr("google/go-github"), + } + mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `

text

`) + }) + + ctx := t.Context() + md, _, err := client.Markdown.Render(ctx, "# text #", &MarkdownOptions{ + Mode: "gfm", + Context: "google/go-github", + }) + if err != nil { + t.Errorf("Render returned error: %v", err) + } + + if want := "

text

"; want != md { + t.Errorf("Render returned %+v, want %+v", md, want) + } + + const methodName = "Render" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Markdown.Render(ctx, "# text #", &MarkdownOptions{ + Mode: "gfm", + Context: "google/go-github", + }) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/messages.go b/github/messages.go index ac7c17f3ca8..9bb43c75df7 100644 --- a/github/messages.go +++ b/github/messages.go @@ -18,9 +18,13 @@ import ( "errors" "fmt" "hash" - "io/ioutil" + "io" + "maps" + "mime" "net/http" "net/url" + "reflect" + "slices" "strings" ) @@ -30,60 +34,108 @@ const ( // sha256Prefix and sha512Prefix are provided for future compatibility. sha256Prefix = "sha256" sha512Prefix = "sha512" - // signatureHeader is the GitHub header key used to pass the HMAC hexdigest. - signatureHeader = "X-Hub-Signature" - // eventTypeHeader is the GitHub header key used to pass the event type. - eventTypeHeader = "X-Github-Event" - // deliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. - deliveryIDHeader = "X-Github-Delivery" + // SHA1SignatureHeader is the GitHub header key used to pass the HMAC-SHA1 hexdigest. + SHA1SignatureHeader = "X-Hub-Signature" + // SHA256SignatureHeader is the GitHub header key used to pass the HMAC-SHA256 hexdigest. + SHA256SignatureHeader = "X-Hub-Signature-256" + // EventTypeHeader is the GitHub header key used to pass the event type. + EventTypeHeader = "X-Github-Event" + // DeliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. + DeliveryIDHeader = "X-Github-Delivery" + + // maxPayloadSize is the maximum size of a GitHub webhook payload. + // GitHub documents a 25 MB limit for webhook payloads. + maxPayloadSize = 25 * 1024 * 1024 ) var ( // eventTypeMapping maps webhooks types to their corresponding go-github struct types. - eventTypeMapping = map[string]string{ - "check_run": "CheckRunEvent", - "check_suite": "CheckSuiteEvent", - "commit_comment": "CommitCommentEvent", - "create": "CreateEvent", - "delete": "DeleteEvent", - "deploy_key": "DeployKeyEvent", - "deployment": "DeploymentEvent", - "deployment_status": "DeploymentStatusEvent", - "fork": "ForkEvent", - "gollum": "GollumEvent", - "installation": "InstallationEvent", - "installation_repositories": "InstallationRepositoriesEvent", - "issue_comment": "IssueCommentEvent", - "issues": "IssuesEvent", - "label": "LabelEvent", - "marketplace_purchase": "MarketplacePurchaseEvent", - "member": "MemberEvent", - "membership": "MembershipEvent", - "meta": "MetaEvent", - "milestone": "MilestoneEvent", - "organization": "OrganizationEvent", - "org_block": "OrgBlockEvent", - "page_build": "PageBuildEvent", - "ping": "PingEvent", - "project": "ProjectEvent", - "project_card": "ProjectCardEvent", - "project_column": "ProjectColumnEvent", - "public": "PublicEvent", - "pull_request_review": "PullRequestReviewEvent", - "pull_request_review_comment": "PullRequestReviewCommentEvent", - "pull_request": "PullRequestEvent", - "push": "PushEvent", - "repository": "RepositoryEvent", - "repository_vulnerability_alert": "RepositoryVulnerabilityAlertEvent", - "release": "ReleaseEvent", - "star": "StarEvent", - "status": "StatusEvent", - "team": "TeamEvent", - "team_add": "TeamAddEvent", - "watch": "WatchEvent", + eventTypeMapping = map[string]any{ + "branch_protection_configuration": &BranchProtectionConfigurationEvent{}, + "branch_protection_rule": &BranchProtectionRuleEvent{}, + "check_run": &CheckRunEvent{}, + "check_suite": &CheckSuiteEvent{}, + "code_scanning_alert": &CodeScanningAlertEvent{}, + "commit_comment": &CommitCommentEvent{}, + "content_reference": &ContentReferenceEvent{}, + "create": &CreateEvent{}, + "custom_property": &CustomPropertyEvent{}, + "custom_property_values": &CustomPropertyValuesEvent{}, + "delete": &DeleteEvent{}, + "dependabot_alert": &DependabotAlertEvent{}, + "deploy_key": &DeployKeyEvent{}, + "deployment": &DeploymentEvent{}, + "deployment_review": &DeploymentReviewEvent{}, + "deployment_status": &DeploymentStatusEvent{}, + "deployment_protection_rule": &DeploymentProtectionRuleEvent{}, + "discussion": &DiscussionEvent{}, + "discussion_comment": &DiscussionCommentEvent{}, + "fork": &ForkEvent{}, + "github_app_authorization": &GitHubAppAuthorizationEvent{}, + "gollum": &GollumEvent{}, + "installation": &InstallationEvent{}, + "installation_repositories": &InstallationRepositoriesEvent{}, + "installation_target": &InstallationTargetEvent{}, + "issue_comment": &IssueCommentEvent{}, + "issues": &IssuesEvent{}, + "label": &LabelEvent{}, + "marketplace_purchase": &MarketplacePurchaseEvent{}, + "member": &MemberEvent{}, + "membership": &MembershipEvent{}, + "merge_group": &MergeGroupEvent{}, + "meta": &MetaEvent{}, + "milestone": &MilestoneEvent{}, + "organization": &OrganizationEvent{}, + "org_block": &OrgBlockEvent{}, + "package": &PackageEvent{}, + "page_build": &PageBuildEvent{}, + "personal_access_token_request": &PersonalAccessTokenRequestEvent{}, + "ping": &PingEvent{}, + "projects_v2": &ProjectV2Event{}, + "projects_v2_item": &ProjectV2ItemEvent{}, + "public": &PublicEvent{}, + "pull_request": &PullRequestEvent{}, + "pull_request_review": &PullRequestReviewEvent{}, + "pull_request_review_comment": &PullRequestReviewCommentEvent{}, + "pull_request_review_thread": &PullRequestReviewThreadEvent{}, + "pull_request_target": &PullRequestTargetEvent{}, + "push": &PushEvent{}, + "registry_package": &RegistryPackageEvent{}, + "repository": &RepositoryEvent{}, + "repository_dispatch": &RepositoryDispatchEvent{}, + "repository_import": &RepositoryImportEvent{}, + "repository_ruleset": &RepositoryRulesetEvent{}, + "repository_vulnerability_alert": &RepositoryVulnerabilityAlertEvent{}, + "release": &ReleaseEvent{}, + "secret_scanning_alert": &SecretScanningAlertEvent{}, + "secret_scanning_alert_location": &SecretScanningAlertLocationEvent{}, + "security_advisory": &SecurityAdvisoryEvent{}, + "security_and_analysis": &SecurityAndAnalysisEvent{}, + "sponsorship": &SponsorshipEvent{}, + "star": &StarEvent{}, + "status": &StatusEvent{}, + "team": &TeamEvent{}, + "team_add": &TeamAddEvent{}, + "user": &UserEvent{}, + "watch": &WatchEvent{}, + "workflow_dispatch": &WorkflowDispatchEvent{}, + "workflow_job": &WorkflowJobEvent{}, + "workflow_run": &WorkflowRunEvent{}, } + // Forward mapping of event types to the string names of the structs. + messageToTypeName = make(map[string]string, len(eventTypeMapping)) + // Inverse map of the above. + typeToMessageMapping = make(map[string]string, len(eventTypeMapping)) ) +func init() { + for k, v := range eventTypeMapping { + typename := reflect.TypeOf(v).Elem().Name() + messageToTypeName[k] = typename + typeToMessageMapping[typename] = k + } +} + // genMAC generates the HMAC signature for a message provided the secret key // and hashFunc. func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte { @@ -98,8 +150,19 @@ func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool { return hmac.Equal(messageMAC, expectedMAC) } -// messageMAC returns the hex-decoded HMAC tag from the signature and its -// corresponding hash function. +// readPayloadBody reads the body from readable, enforcing maxPayloadSize. +func readPayloadBody(readable io.Reader) ([]byte, error) { + body, err := io.ReadAll(io.LimitReader(readable, maxPayloadSize+1)) + if err != nil { + return nil, err + } + if len(body) > maxPayloadSize { + return nil, errors.New("webhook payload exceeds maximum allowed size") + } + return body, nil +} + +// messageMAC returns the MAC method and the corresponding hash function. func messageMAC(signature string) ([]byte, func() hash.Hash, error) { if signature == "" { return nil, nil, errors.New("missing signature") @@ -128,29 +191,30 @@ func messageMAC(signature string) ([]byte, func() hash.Hash, error) { return buf, hashFunc, nil } -// ValidatePayload validates an incoming GitHub Webhook event request +// ValidatePayloadFromBody validates an incoming GitHub Webhook event request body // and returns the (JSON) payload. // The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". // If the Content-Type is neither then an error is returned. // secretToken is the GitHub Webhook secret token. -// If your webhook does not contain a secret token, you can pass nil or an empty slice. -// This is intended for local development purposes only and all webhooks should ideally set up a secret token. +// If your webhook does not contain a secret token, you can pass an empty secretToken. +// Webhooks without a secret token are not secure and should be avoided. // // Example usage: // -// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { -// payload, err := github.ValidatePayload(r, s.webhookSecretKey) -// if err != nil { ... } -// // Process payload... -// } -// -func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err error) { +// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { +// // read signature from request +// signature := "" +// payload, err := github.ValidatePayloadFromBody(r.Header.Get("Content-Type"), r.Body, signature, s.webhookSecretKey) +// if err != nil { ... } +// // Process payload... +// } +func ValidatePayloadFromBody(contentType string, readable io.Reader, signature string, secretToken []byte) (payload []byte, err error) { var body []byte // Raw body that GitHub uses to calculate the signature. - switch ct := r.Header.Get("Content-Type"); ct { + switch contentType { case "application/json": var err error - if body, err = ioutil.ReadAll(r.Body); err != nil { + if body, err = readPayloadBody(readable); err != nil { return nil, err } @@ -164,7 +228,7 @@ func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err e const payloadFormParam = "payload" var err error - if body, err = ioutil.ReadAll(r.Body); err != nil { + if body, err = readPayloadBody(readable); err != nil { return nil, err } @@ -177,14 +241,12 @@ func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err e payload = []byte(form.Get(payloadFormParam)) default: - return nil, fmt.Errorf("Webhook request has unsupported Content-Type %q", ct) + return nil, fmt.Errorf("webhook request has unsupported Content-Type %q", contentType) } - // Only validate the signature if a secret token exists. This is intended for - // local development only and all webhooks should ideally set up a secret token. - if len(secretToken) > 0 { - sig := r.Header.Get(signatureHeader) - if err := ValidateSignature(sig, body, secretToken); err != nil { + // Validate the signature if present or if one is expected (secretToken is non-empty). + if len(secretToken) > 0 || len(signature) > 0 { + if err := ValidateSignature(signature, body, secretToken); err != nil { return nil, err } } @@ -192,6 +254,35 @@ func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err e return payload, nil } +// ValidatePayload validates an incoming GitHub Webhook event request +// and returns the (JSON) payload. +// The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". +// If the Content-Type is neither then an error is returned. +// secretToken is the GitHub Webhook secret token. +// If your webhook does not contain a secret token, you can pass nil or an empty slice. +// This is intended for local development purposes only and all webhooks should ideally set up a secret token. +// +// Example usage: +// +// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { +// payload, err := github.ValidatePayload(r, s.webhookSecretKey) +// if err != nil { ... } +// // Process payload... +// } +func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err error) { + signature := r.Header.Get(SHA256SignatureHeader) + if signature == "" { + signature = r.Header.Get(SHA1SignatureHeader) + } + + contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return nil, err + } + + return ValidatePayloadFromBody(contentType, r.Body, signature, secretToken) +} + // ValidateSignature validates the signature for the given payload. // signature is the GitHub hash signature delivered in the X-Hub-Signature header. // payload is the JSON payload sent by GitHub Webhooks. @@ -211,41 +302,40 @@ func ValidateSignature(signature string, payload, secretToken []byte) error { // WebHookType returns the event type of webhook request r. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/events/github-event-types func WebHookType(r *http.Request) string { - return r.Header.Get(eventTypeHeader) + return r.Header.Get(EventTypeHeader) } // DeliveryID returns the unique delivery ID of webhook request r. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers +// GitHub API docs: https://docs.github.com/developers/webhooks-and-events/events/github-event-types func DeliveryID(r *http.Request) string { - return r.Header.Get(deliveryIDHeader) + return r.Header.Get(DeliveryIDHeader) } // ParseWebHook parses the event payload. For recognized event types, a // value of the corresponding struct type will be returned (as returned -// by Event.ParsePayload()). An error will be returned for unrecognized event +// by [Event.ParsePayload]). An error will be returned for unrecognized event // types. // // Example usage: // -// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { -// payload, err := github.ValidatePayload(r, s.webhookSecretKey) -// if err != nil { ... } -// event, err := github.ParseWebHook(github.WebHookType(r), payload) -// if err != nil { ... } -// switch event := event.(type) { -// case *github.CommitCommentEvent: -// processCommitCommentEvent(event) -// case *github.CreateEvent: -// processCreateEvent(event) -// ... -// } -// } -// -func ParseWebHook(messageType string, payload []byte) (interface{}, error) { - eventType, ok := eventTypeMapping[messageType] +// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { +// payload, err := github.ValidatePayload(r, s.webhookSecretKey) +// if err != nil { ... } +// event, err := github.ParseWebHook(github.WebHookType(r), payload) +// if err != nil { ... } +// switch event := event.(type) { +// case *github.CommitCommentEvent: +// processCommitCommentEvent(event) +// case *github.CreateEvent: +// processCreateEvent(event) +// ... +// } +// } +func ParseWebHook(messageType string, payload []byte) (any, error) { + eventType, ok := messageToTypeName[messageType] if !ok { return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType) } @@ -256,3 +346,23 @@ func ParseWebHook(messageType string, payload []byte) (interface{}, error) { } return event.ParsePayload() } + +// MessageTypes returns a sorted list of all the known GitHub event type strings +// supported by go-github. +func MessageTypes() []string { + return slices.Sorted(maps.Keys(eventTypeMapping)) +} + +// EventForType returns an empty struct matching the specified GitHub event type. +// If messageType does not match any known event types, it returns nil. +func EventForType(messageType string) any { + prototype := eventTypeMapping[messageType] + if prototype == nil { + return nil + } + // return a _copy_ of the pointed-to-object. Unfortunately, for this we + // need to use reflection. If we store the actual objects in the map, + // we still need to use reflection to convert from `any` to the actual + // type, so this was deemed the lesser of two evils. (#2865) + return reflect.New(reflect.TypeOf(prototype).Elem()).Interface() +} diff --git a/github/messages_test.go b/github/messages_test.go index 105904c665c..62bd47c6e1d 100644 --- a/github/messages_test.go +++ b/github/messages_test.go @@ -8,55 +8,67 @@ package github import ( "bytes" "encoding/json" + "errors" + "fmt" + "io" "net/http" "net/url" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) +func TestMessageMAC_BadHashTypePrefix(t *testing.T) { + t.Parallel() + const signature = "bogus1=1234567" + if _, _, err := messageMAC(signature); err == nil { + t.Fatal("messageMAC returned nil; wanted error") + } +} + func TestValidatePayload(t *testing.T) { + t.Parallel() const defaultBody = `{"yo":true}` // All tests below use the default request body and signature. const defaultSignature = "sha1=126f2c800419c60137ce748d7672e77b65cf16d6" secretKey := []byte("0123456789abcdef") tests := []struct { - signature string - eventID string - event string - wantEventID string - wantEvent string - wantPayload string + secretKey []byte + signature string + signatureHeader string + wantPayload string }{ // The following tests generate expected errors: - {}, // Missing signature - {signature: "yo"}, // Missing signature prefix - {signature: "sha1=yo"}, // Signature not hex string - {signature: "sha1=012345"}, // Invalid signature + {secretKey: secretKey}, // Missing signature + {secretKey: secretKey, signature: "yo"}, // Missing signature prefix + {secretKey: secretKey, signature: "sha1=yo"}, // Signature not hex string + {secretKey: secretKey, signature: "sha1=012345"}, // Invalid signature + {signature: defaultSignature}, // signature without secretKey + // The following tests expect err=nil: { - signature: defaultSignature, - eventID: "dead-beef", - event: "ping", - wantEventID: "dead-beef", - wantEvent: "ping", + // no secretKey and no signature still passes validation wantPayload: defaultBody, }, { + secretKey: secretKey, signature: defaultSignature, - event: "ping", - wantEvent: "ping", wantPayload: defaultBody, }, { + secretKey: secretKey, signature: "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b", - event: "ping", - wantEvent: "ping", wantPayload: defaultBody, }, { + secretKey: secretKey, + signature: "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b", + signatureHeader: SHA256SignatureHeader, + wantPayload: defaultBody, + }, + { + secretKey: secretKey, signature: "sha512=8456767023c1195682e182a23b3f5d19150ecea598fde8cb85918f7281b16079471b1329f92b912c4d8bd7455cb159777db8f29608b20c7c87323ba65ae62e1f", - event: "ping", - wantEvent: "ping", wantPayload: defaultBody, }, } @@ -68,11 +80,15 @@ func TestValidatePayload(t *testing.T) { t.Fatalf("NewRequest: %v", err) } if test.signature != "" { - req.Header.Set(signatureHeader, test.signature) + if test.signatureHeader != "" { + req.Header.Set(test.signatureHeader, test.signature) + } else { + req.Header.Set(SHA1SignatureHeader, test.signature) + } } req.Header.Set("Content-Type", "application/json") - got, err := ValidatePayload(req, secretKey) + got, err := ValidatePayload(req, test.secretKey) if err != nil { if test.wantPayload != "" { t.Errorf("ValidatePayload(%#v): err = %v, want nil", test, err) @@ -86,6 +102,7 @@ func TestValidatePayload(t *testing.T) { } func TestValidatePayload_FormGet(t *testing.T) { + t.Parallel() payload := `{"yo":true}` signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368" secretKey := []byte("0123456789abcdef") @@ -98,7 +115,7 @@ func TestValidatePayload_FormGet(t *testing.T) { } req.PostForm = form req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set(signatureHeader, signature) + req.Header.Set(SHA1SignatureHeader, signature) got, err := ValidatePayload(req, secretKey) if err != nil { @@ -109,13 +126,14 @@ func TestValidatePayload_FormGet(t *testing.T) { } // check that if payload is invalid we get error - req.Header.Set(signatureHeader, "invalid signature") + req.Header.Set(SHA1SignatureHeader, "invalid signature") if _, err = ValidatePayload(req, []byte{0}); err == nil { t.Error("ValidatePayload = nil, want err") } } func TestValidatePayload_FormPost(t *testing.T) { + t.Parallel() payload := `{"yo":true}` signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368" secretKey := []byte("0123456789abcdef") @@ -128,7 +146,7 @@ func TestValidatePayload_FormPost(t *testing.T) { t.Fatalf("NewRequest: %v", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set(signatureHeader, signature) + req.Header.Set(SHA1SignatureHeader, signature) got, err := ValidatePayload(req, secretKey) if err != nil { @@ -139,13 +157,14 @@ func TestValidatePayload_FormPost(t *testing.T) { } // check that if payload is invalid we get error - req.Header.Set(signatureHeader, "invalid signature") + req.Header.Set(SHA1SignatureHeader, "invalid signature") if _, err = ValidatePayload(req, []byte{0}); err == nil { t.Error("ValidatePayload = nil, want err") } } func TestValidatePayload_InvalidContentType(t *testing.T) { + t.Parallel() req, err := http.NewRequest("POST", "http://localhost/event", nil) if err != nil { t.Fatalf("NewRequest: %v", err) @@ -157,6 +176,7 @@ func TestValidatePayload_InvalidContentType(t *testing.T) { } func TestValidatePayload_NoSecretKey(t *testing.T) { + t.Parallel() payload := `{"yo":true}` form := url.Values{} @@ -177,11 +197,119 @@ func TestValidatePayload_NoSecretKey(t *testing.T) { } } +// badReader satisfies io.Reader but always returns an error. +type badReader struct{} + +func (b *badReader) Read([]byte) (int, error) { + return 0, errors.New("bad reader") +} + +func (b *badReader) Close() error { return errors.New("bad reader") } + +type readerFunc func([]byte) (int, error) + +func (f readerFunc) Read(p []byte) (int, error) { return f(p) } + +func TestValidatePayload_BadRequestBody(t *testing.T) { + t.Parallel() + tests := []struct { + contentType string + }{ + {contentType: "application/json"}, + {contentType: "application/x-www-form-urlencoded"}, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("test #%v", i), func(t *testing.T) { + t.Parallel() + req := &http.Request{ + Header: http.Header{"Content-Type": []string{tt.contentType}}, + Body: &badReader{}, + } + if _, err := ValidatePayload(req, nil); err == nil { + t.Fatal("ValidatePayload returned nil; want error") + } + }) + } +} + +func TestValidatePayload_OversizedBody(t *testing.T) { + t.Parallel() + tests := []struct { + contentType string + }{ + {contentType: "application/json"}, + {contentType: "application/x-www-form-urlencoded"}, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("test #%v", i), func(t *testing.T) { + t.Parallel() + // Simulate a reader that reports more than maxPayloadSize bytes. + oversized := io.LimitReader(readerFunc(func(p []byte) (int, error) { + for i := range p { + p[i] = 0 + } + return len(p), nil + }), maxPayloadSize+1) + req := &http.Request{ + Header: http.Header{"Content-Type": []string{tt.contentType}}, + Body: io.NopCloser(oversized), + } + _, err := ValidatePayload(req, nil) + if err == nil { + t.Fatal("ValidatePayload returned nil; want error for oversized body") + } + if want := "webhook payload exceeds maximum allowed size"; err.Error() != want { + t.Errorf("ValidatePayload error = %q, want %q", err.Error(), want) + } + }) + } +} + +func TestValidatePayload_InvalidContentTypeParams(t *testing.T) { + t.Parallel() + req, err := http.NewRequest("POST", "http://localhost/event", nil) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + req.Header.Set("Content-Type", "application/json; charset=") + if _, err = ValidatePayload(req, nil); err == nil { + t.Error("ValidatePayload = nil, want err") + } +} + +func TestValidatePayload_ValidContentTypeParams(t *testing.T) { + t.Parallel() + requestBody := `{"yo":true}` + buf := bytes.NewBufferString(requestBody) + + req, err := http.NewRequest("POST", "http://localhost/event", buf) + if err != nil { + t.Fatalf("NewRequest: %v", err) + } + req.Header.Set("Content-Type", "application/json; charset=UTF-8") + + _, err = ValidatePayload(req, nil) + if err != nil { + t.Error("ValidatePayload = nil, want err") + } +} + func TestParseWebHook(t *testing.T) { + t.Parallel() tests := []struct { - payload interface{} + payload any messageType string }{ + { + payload: &BranchProtectionConfigurationEvent{}, + messageType: "branch_protection_configuration", + }, + { + payload: &BranchProtectionRuleEvent{}, + messageType: "branch_protection_rule", + }, { payload: &CheckRunEvent{}, messageType: "check_run", @@ -190,18 +318,38 @@ func TestParseWebHook(t *testing.T) { payload: &CheckSuiteEvent{}, messageType: "check_suite", }, + { + payload: &CodeScanningAlertEvent{}, + messageType: "code_scanning_alert", + }, { payload: &CommitCommentEvent{}, messageType: "commit_comment", }, + { + payload: &ContentReferenceEvent{}, + messageType: "content_reference", + }, { payload: &CreateEvent{}, messageType: "create", }, + { + payload: &CustomPropertyEvent{}, + messageType: "custom_property", + }, + { + payload: &CustomPropertyValuesEvent{}, + messageType: "custom_property_values", + }, { payload: &DeleteEvent{}, messageType: "delete", }, + { + payload: &DependabotAlertEvent{}, + messageType: "dependabot_alert", + }, { payload: &DeployKeyEvent{}, messageType: "deploy_key", @@ -210,15 +358,34 @@ func TestParseWebHook(t *testing.T) { payload: &DeploymentEvent{}, messageType: "deployment", }, - + { + payload: &DeploymentProtectionRuleEvent{}, + messageType: "deployment_protection_rule", + }, + { + payload: &DeploymentReviewEvent{}, + messageType: "deployment_review", + }, { payload: &DeploymentStatusEvent{}, messageType: "deployment_status", }, + { + payload: &DiscussionCommentEvent{}, + messageType: "discussion_comment", + }, + { + payload: &DiscussionEvent{}, + messageType: "discussion", + }, { payload: &ForkEvent{}, messageType: "fork", }, + { + payload: &GitHubAppAuthorizationEvent{}, + messageType: "github_app_authorization", + }, { payload: &GollumEvent{}, messageType: "gollum", @@ -231,6 +398,10 @@ func TestParseWebHook(t *testing.T) { payload: &InstallationRepositoriesEvent{}, messageType: "installation_repositories", }, + { + payload: &InstallationTargetEvent{}, + messageType: "installation_target", + }, { payload: &IssueCommentEvent{}, messageType: "issue_comment", @@ -255,6 +426,10 @@ func TestParseWebHook(t *testing.T) { payload: &MembershipEvent{}, messageType: "membership", }, + { + payload: &MergeGroupEvent{}, + messageType: "merge_group", + }, { payload: &MetaEvent{}, messageType: "meta", @@ -271,25 +446,29 @@ func TestParseWebHook(t *testing.T) { payload: &OrgBlockEvent{}, messageType: "org_block", }, + { + payload: &PackageEvent{}, + messageType: "package", + }, { payload: &PageBuildEvent{}, messageType: "page_build", }, { - payload: &PingEvent{}, - messageType: "ping", + payload: &PersonalAccessTokenRequestEvent{}, + messageType: "personal_access_token_request", }, { - payload: &ProjectEvent{}, - messageType: "project", + payload: &PingEvent{}, + messageType: "ping", }, { - payload: &ProjectCardEvent{}, - messageType: "project_card", + payload: &ProjectV2Event{}, + messageType: "projects_v2", }, { - payload: &ProjectColumnEvent{}, - messageType: "project_column", + payload: &ProjectV2ItemEvent{}, + messageType: "projects_v2_item", }, { payload: &PublicEvent{}, @@ -307,10 +486,22 @@ func TestParseWebHook(t *testing.T) { payload: &PullRequestReviewCommentEvent{}, messageType: "pull_request_review_comment", }, + { + payload: &PullRequestReviewThreadEvent{}, + messageType: "pull_request_review_thread", + }, + { + payload: &PullRequestTargetEvent{}, + messageType: "pull_request_target", + }, { payload: &PushEvent{}, messageType: "push", }, + { + payload: &RegistryPackageEvent{}, + messageType: "registry_package", + }, { payload: &ReleaseEvent{}, messageType: "release", @@ -319,10 +510,34 @@ func TestParseWebHook(t *testing.T) { payload: &RepositoryEvent{}, messageType: "repository", }, + { + payload: &RepositoryRulesetEvent{}, + messageType: "repository_ruleset", + }, { payload: &RepositoryVulnerabilityAlertEvent{}, messageType: "repository_vulnerability_alert", }, + { + payload: &SecretScanningAlertEvent{}, + messageType: "secret_scanning_alert", + }, + { + payload: &SecretScanningAlertLocationEvent{}, + messageType: "secret_scanning_alert_location", + }, + { + payload: &SecurityAdvisoryEvent{}, + messageType: "security_advisory", + }, + { + payload: &SecurityAndAnalysisEvent{}, + messageType: "security_and_analysis", + }, + { + payload: &SponsorshipEvent{}, + messageType: "sponsorship", + }, { payload: &StarEvent{}, messageType: "star", @@ -339,10 +554,34 @@ func TestParseWebHook(t *testing.T) { payload: &TeamAddEvent{}, messageType: "team_add", }, + { + payload: &UserEvent{}, + messageType: "user", + }, { payload: &WatchEvent{}, messageType: "watch", }, + { + payload: &RepositoryImportEvent{}, + messageType: "repository_import", + }, + { + payload: &RepositoryDispatchEvent{}, + messageType: "repository_dispatch", + }, + { + payload: &WorkflowDispatchEvent{}, + messageType: "workflow_dispatch", + }, + { + payload: &WorkflowJobEvent{}, + messageType: "workflow_job", + }, + { + payload: &WorkflowRunEvent{}, + messageType: "workflow_run", + }, } for _, test := range tests { @@ -354,13 +593,54 @@ func TestParseWebHook(t *testing.T) { if err != nil { t.Fatalf("ParseWebHook: %v", err) } - if want := test.payload; !reflect.DeepEqual(got, want) { + if want := test.payload; !cmp.Equal(got, want) { t.Errorf("ParseWebHook(%#v, %#v) = %#v, want %#v", test.messageType, p, got, want) } } } +func TestAllMessageTypesMapped(t *testing.T) { + t.Parallel() + for _, mt := range MessageTypes() { + if obj := EventForType(mt); obj == nil { + t.Errorf("messageMap missing message type %q", mt) + } + } +} + +func TestUnknownMessageType(t *testing.T) { + t.Parallel() + if obj := EventForType("unknown"); obj != nil { + t.Errorf("EventForType(unknown) = %#v, want nil", obj) + } + if obj := EventForType(""); obj != nil { + t.Errorf(`EventForType("") = %#v, want nil`, obj) + } +} + +func TestParseWebHook_BadMessageType(t *testing.T) { + t.Parallel() + if _, err := ParseWebHook("bogus message type", []byte("{}")); err == nil { + t.Fatal("ParseWebHook returned nil; wanted error") + } +} + +func TestValidatePayloadFromBody_UnableToParseBody(t *testing.T) { + t.Parallel() + if _, err := ValidatePayloadFromBody("application/x-www-form-urlencoded", bytes.NewReader([]byte(`%`)), "sha1=", []byte{}); err == nil { + t.Error("ValidatePayloadFromBody returned nil; wanted error") + } +} + +func TestValidatePayloadFromBody_UnsupportedContentType(t *testing.T) { + t.Parallel() + if _, err := ValidatePayloadFromBody("invalid", bytes.NewReader([]byte(`{}`)), "sha1=", []byte{}); err == nil { + t.Error("ValidatePayloadFromBody returned nil; wanted error") + } +} + func TestDeliveryID(t *testing.T) { + t.Parallel() id := "8970a780-244e-11e7-91ca-da3aabcb9793" req, err := http.NewRequest("POST", "http://localhost", nil) if err != nil { @@ -373,3 +653,14 @@ func TestDeliveryID(t *testing.T) { t.Errorf("DeliveryID(%#v) = %q, want %q", req, got, id) } } + +func TestWebHookType(t *testing.T) { + t.Parallel() + want := "yo" + req := &http.Request{ + Header: http.Header{EventTypeHeader: []string{want}}, + } + if got := WebHookType(req); got != want { + t.Errorf("WebHookType = %q, want %q", got, want) + } +} diff --git a/github/meta.go b/github/meta.go new file mode 100644 index 00000000000..7d663a39bb2 --- /dev/null +++ b/github/meta.go @@ -0,0 +1,224 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "bytes" + "context" + "fmt" + "net/url" +) + +// MetaService provides access to functions in the GitHub API that GitHub categorizes as "meta". +type MetaService service + +// APIMeta represents metadata about the GitHub API. +type APIMeta struct { + // An array of IP addresses in CIDR format specifying the addresses + // that incoming service hooks will originate from on GitHub.com. + Hooks []string `json:"hooks,omitempty"` + + // An array of IP addresses in CIDR format specifying the Git servers + // for GitHub.com. + Git []string `json:"git,omitempty"` + + // Whether authentication with username and password is supported. + // (GitHub Enterprise instances using CAS or OAuth for authentication + // will return false. Features like Basic Authentication with a + // username and password, sudo mode, and two-factor authentication are + // not supported on these servers.) + VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub Packages. + Packages []string `json:"packages,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub Pages websites. + Pages []string `json:"pages,omitempty"` + + // An array of IP addresses specifying the addresses that source imports + // will originate from on GitHub.com. + Importer []string `json:"importer,omitempty"` + + // An array of IP addresses specifying the addresses that source imports + // will originate from on GitHub Enterprise Cloud. + GithubEnterpriseImporter []string `json:"github_enterprise_importer,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // GitHub Actions will originate from. + Actions []string `json:"actions,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // GitHub Action macOS runner will originate from. + ActionsMacos []string `json:"actions_macos,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // GitHub Codespaces will originate from. + Codespaces []string `json:"codespaces,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // GitHub Copilot will originate from. + Copilot []string `json:"copilot,omitempty"` + + // An array of IP addresses in CIDR format specifying the IP addresses + // Dependabot will originate from. + Dependabot []string `json:"dependabot,omitempty"` + + // A map of algorithms to SSH key fingerprints. + SSHKeyFingerprints map[string]string `json:"ssh_key_fingerprints,omitempty"` + + // An array of SSH keys. + SSHKeys []string `json:"ssh_keys,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub websites. + Web []string `json:"web,omitempty"` + + // An array of IP addresses in CIDR format specifying the addresses + // which serve GitHub APIs. + API []string `json:"api,omitempty"` + + // GitHub services and their associated domains. Note that many of these domains + // are represented as wildcards (e.g. "*.github.com"). + Domains *APIMetaDomains `json:"domains,omitempty"` +} + +// APIMetaDomains represents the domains associated with GitHub services. +type APIMetaDomains struct { + Website []string `json:"website,omitempty"` + Codespaces []string `json:"codespaces,omitempty"` + Copilot []string `json:"copilot,omitempty"` + Packages []string `json:"packages,omitempty"` + Actions []string `json:"actions,omitempty"` + ActionsInbound *ActionsInboundDomains `json:"actions_inbound,omitempty"` + ArtifactAttestations *APIMetaArtifactAttestations `json:"artifact_attestations,omitempty"` +} + +// ActionsInboundDomains represents the domains associated with GitHub Actions inbound traffic. +type ActionsInboundDomains struct { + FullDomains []string `json:"full_domains,omitempty"` + WildcardDomains []string `json:"wildcard_domains,omitempty"` +} + +// APIMetaArtifactAttestations represents the artifact attestation services domains. +type APIMetaArtifactAttestations struct { + TrustDomain string `json:"trust_domain,omitempty"` + Services []string `json:"services,omitempty"` +} + +// Get returns information about GitHub.com, the service. Or, if you access +// this endpoint on your organization’s GitHub Enterprise installation, this +// endpoint provides information about that installation. +// +// GitHub API docs: https://docs.github.com/rest/meta/meta?apiVersion=2022-11-28#get-github-meta-information +// +//meta:operation GET /meta +func (s *MetaService) Get(ctx context.Context) (*APIMeta, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "meta", nil) + if err != nil { + return nil, nil, err + } + + var meta *APIMeta + resp, err := s.client.Do(req, &meta) + if err != nil { + return nil, resp, err + } + + return meta, resp, nil +} + +// APIMeta returns information about GitHub.com. +// +// Deprecated: Use MetaService.Get instead. +func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { + return c.Meta.Get(ctx) +} + +// ListAPIVersions returns the API versions supported by the GitHub REST API, +// as dates in the YYYY-MM-DD format. +// +// GitHub API docs: https://docs.github.com/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions +// +//meta:operation GET /versions +func (s *MetaService) ListAPIVersions(ctx context.Context) ([]string, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "versions", nil) + if err != nil { + return nil, nil, err + } + + var versions []string + resp, err := s.client.Do(req, &versions) + if err != nil { + return nil, resp, err + } + + return versions, resp, nil +} + +// Octocat returns an ASCII art octocat with the specified message in a speech +// bubble. If message is empty, a random zen phrase is used. +// +// GitHub API docs: https://docs.github.com/rest/meta/meta?apiVersion=2022-11-28#get-octocat +// +//meta:operation GET /octocat +func (s *MetaService) Octocat(ctx context.Context, message string) (string, *Response, error) { + u := "octocat" + if message != "" { + u = fmt.Sprintf("%v?s=%v", u, url.QueryEscape(message)) + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return "", nil, err + } + + var buf bytes.Buffer + resp, err := s.client.Do(req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// Octocat returns an ASCII art octocat with the specified message in a speech +// bubble. If message is empty, a random zen phrase is used. +// +// Deprecated: Use MetaService.Octocat instead. +func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { + return c.Meta.Octocat(ctx, message) +} + +// Zen returns a random line from The Zen of GitHub. +// +// See also: https://warpspire.com/posts/taste/ +// +// GitHub API docs: https://docs.github.com/rest/meta/meta?apiVersion=2022-11-28#get-the-zen-of-github +// +//meta:operation GET /zen +func (s *MetaService) Zen(ctx context.Context) (string, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", "zen", nil) + if err != nil { + return "", nil, err + } + + var buf bytes.Buffer + resp, err := s.client.Do(req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + +// Zen returns a random line from The Zen of GitHub. +// +// Deprecated: Use MetaService.Zen instead. +func (c *Client) Zen(ctx context.Context) (string, *Response, error) { + return c.Meta.Zen(ctx) +} diff --git a/github/meta_test.go b/github/meta_test.go new file mode 100644 index 00000000000..443f2e75fd1 --- /dev/null +++ b/github/meta_test.go @@ -0,0 +1,177 @@ +// Copyright 2014 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestMetaService_Get(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"web":["w"],"api":["a"],"hooks":["h"], "git":["g"], "pages":["p"], "importer":["i"], "github_enterprise_importer": ["gei"], "actions":["a"], "actions_macos": ["example.com/1", "example.com/2"], "codespaces": ["cs"], "copilot": ["c"], "dependabot":["d"], "verifiable_password_authentication": true, "domains":{"actions_inbound": { "full_domains": ["github.com"], "wildcard_domains": ["*.github.com"]},"website":["*.github.com","*.github.dev","*.github.io","*.example.com/assets","*.example.com"],"artifact_attestations":{"trust_domain":"","services":["*.actions.github.com","tuf-repo.github.com","fulcio.github.com","timestamp.github.com"]}}}`) + }) + + ctx := t.Context() + meta, _, err := client.Meta.Get(ctx) + if err != nil { + t.Errorf("Get returned error: %v", err) + } + + want := &APIMeta{ + Hooks: []string{"h"}, + Git: []string{"g"}, + Pages: []string{"p"}, + Importer: []string{"i"}, + GithubEnterpriseImporter: []string{"gei"}, + Actions: []string{"a"}, + ActionsMacos: []string{"example.com/1", "example.com/2"}, + Codespaces: []string{"cs"}, + Copilot: []string{"c"}, + Dependabot: []string{"d"}, + API: []string{"a"}, + Web: []string{"w"}, + Domains: &APIMetaDomains{ + Website: []string{ + "*.github.com", + "*.github.dev", + "*.github.io", + "*.example.com/assets", + "*.example.com", + }, + ArtifactAttestations: &APIMetaArtifactAttestations{ + TrustDomain: "", + Services: []string{ + "*.actions.github.com", + "tuf-repo.github.com", + "fulcio.github.com", + "timestamp.github.com", + }, + }, + ActionsInbound: &ActionsInboundDomains{ + FullDomains: []string{"github.com"}, + WildcardDomains: []string{"*.github.com"}, + }, + }, + + VerifiablePasswordAuthentication: Ptr(true), + } + if !cmp.Equal(want, meta) { + t.Errorf("Get returned %+v, want %+v", meta, want) + } + + const methodName = "Get" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.Get(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestMetaService_ListAPIVersions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/versions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `["2021-01-01","2022-11-28"]`) + }) + + ctx := t.Context() + versions, _, err := client.Meta.ListAPIVersions(ctx) + if err != nil { + t.Errorf("ListAPIVersions returned error: %v", err) + } + + want := []string{"2021-01-01", "2022-11-28"} + if !cmp.Equal(want, versions) { + t.Errorf("ListAPIVersions returned %+v, want %+v", versions, want) + } + + const methodName = "ListAPIVersions" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.ListAPIVersions(ctx) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestMetaService_Octocat(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := "input" + output := "sample text" + + mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"s": input}) + w.Header().Set("Content-Type", "application/octocat-stream") + fmt.Fprint(w, output) + }) + + ctx := t.Context() + got, _, err := client.Meta.Octocat(ctx, input) + if err != nil { + t.Errorf("Octocat returned error: %v", err) + } + + if want := output; got != want { + t.Errorf("Octocat returned %+v, want %+v", got, want) + } + + const methodName = "Octocat" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.Octocat(ctx, input) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestMetaService_Zen(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + output := "sample text" + + mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.Header().Set("Content-Type", "text/plain;charset=utf-8") + fmt.Fprint(w, output) + }) + + ctx := t.Context() + got, _, err := client.Meta.Zen(ctx) + if err != nil { + t.Errorf("Zen returned error: %v", err) + } + + if want := output; got != want { + t.Errorf("Zen returned %+v, want %+v", got, want) + } + + const methodName = "Zen" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Meta.Zen(ctx) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/migrations.go b/github/migrations.go index 90cc1fae85f..0ebf8b12211 100644 --- a/github/migrations.go +++ b/github/migrations.go @@ -9,14 +9,12 @@ import ( "context" "errors" "fmt" - "net/http" - "strings" ) // MigrationService provides access to the migration related functions // in the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/migration/ +// GitHub API docs: https://docs.github.com/rest/migrations?apiVersion=2022-11-28 type MigrationService service // Migration represents a GitHub migration (archival). @@ -55,6 +53,14 @@ type MigrationOptions struct { // ExcludeAttachments indicates whether attachments should be excluded from // the migration (to reduce migration archive file size). ExcludeAttachments bool + + // ExcludeReleases indicates whether releases should be excluded from + // the migration (to reduce migration archive file size). + ExcludeReleases bool + + // Exclude is a slice of related items to exclude from the response in order + // to improve performance of the request. Supported values are: "repositories" + Exclude []string } // startMigration represents the body of a StartMigration request. @@ -69,31 +75,42 @@ type startMigration struct { // ExcludeAttachments indicates whether attachments should be excluded from // the migration (to reduce migration archive file size). ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` + + // ExcludeReleases indicates whether releases should be excluded from + // the migration (to reduce migration archive file size). + ExcludeReleases *bool `json:"exclude_releases,omitempty"` + + // Exclude is a slice of related items to exclude from the response in order + // to improve performance of the request. Supported values are: "repositories" + Exclude []string `json:"exclude,omitempty"` } // StartMigration starts the generation of a migration archive. // repos is a slice of repository names to migrate. // -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#start-a-migration -func (s *MigrationService) StartMigration(ctx context.Context, org string, repos []string, opt *MigrationOptions) (*Migration, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/migrations/orgs?apiVersion=2022-11-28#start-an-organization-migration +// +//meta:operation POST /orgs/{org}/migrations +func (s *MigrationService) StartMigration(ctx context.Context, org string, repos []string, opts *MigrationOptions) (*Migration, *Response, error) { u := fmt.Sprintf("orgs/%v/migrations", org) body := &startMigration{Repositories: repos} - if opt != nil { - body.LockRepositories = Bool(opt.LockRepositories) - body.ExcludeAttachments = Bool(opt.ExcludeAttachments) + if opts != nil { + body.LockRepositories = &opts.LockRepositories + body.ExcludeAttachments = &opts.ExcludeAttachments + body.ExcludeReleases = &opts.ExcludeReleases + body.Exclude = append(body.Exclude, opts.Exclude...) } - req, err := s.client.NewRequest("POST", u, body) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - m := &Migration{} - resp, err := s.client.Do(ctx, req, m) + var m *Migration + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -103,20 +120,25 @@ func (s *MigrationService) StartMigration(ctx context.Context, org string, repos // ListMigrations lists the most recent migrations. // -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-a-list-of-migrations -func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*Migration, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/migrations/orgs?apiVersion=2022-11-28#list-organization-migrations +// +//meta:operation GET /orgs/{org}/migrations +func (s *MigrationService) ListMigrations(ctx context.Context, org string, opts *ListOptions) ([]*Migration, *Response, error) { u := fmt.Sprintf("orgs/%v/migrations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) var m []*Migration - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -127,20 +149,21 @@ func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*M // MigrationStatus gets the status of a specific migration archive. // id is the migration ID. // -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration +// GitHub API docs: https://docs.github.com/rest/migrations/orgs?apiVersion=2022-11-28#get-an-organization-migration-status +// +//meta:operation GET /orgs/{org}/migrations/{migration_id} func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int64) (*Migration, *Response, error) { u := fmt.Sprintf("orgs/%v/migrations/%v", org, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - m := &Migration{} - resp, err := s.client.Do(ctx, req, m) + var m *Migration + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -151,56 +174,47 @@ func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id i // MigrationArchiveURL fetches a migration archive URL. // id is the migration ID. // -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive +// GitHub API docs: https://docs.github.com/rest/migrations/orgs?apiVersion=2022-11-28#download-an-organization-migration-archive +// +//meta:operation GET /orgs/{org}/migrations/{migration_id}/archive func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int64) (url string, err error) { u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return "", err } - - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - s.client.clientMu.Lock() - defer s.client.clientMu.Unlock() - - // Disable the redirect mechanism because AWS fails if the GitHub auth token is provided. - var loc string - saveRedirect := s.client.client.CheckRedirect - s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - loc = req.URL.String() - return errors.New("disable redirect") + loc, _, err := s.client.bareDoUntilFound(req, 10) + if err != nil { + return "", err } - defer func() { s.client.client.CheckRedirect = saveRedirect }() - _, err = s.client.Do(ctx, req, nil) // expect error from disable redirect - if err == nil { + if loc == nil { return "", errors.New("expected redirect, none provided") } - if !strings.Contains(err.Error(), "disable redirect") { - return "", err - } - return loc, nil + + return loc.String(), nil } // DeleteMigration deletes a previous migration archive. // id is the migration ID. // -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive +// GitHub API docs: https://docs.github.com/rest/migrations/orgs?apiVersion=2022-11-28#delete-an-organization-migration-archive +// +//meta:operation DELETE /orgs/{org}/migrations/{migration_id}/archive func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int64) (*Response, error) { u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UnlockRepo unlocks a repository that was locked for migration. @@ -208,17 +222,18 @@ func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id i // You should unlock each migrated repository and delete them when the migration // is complete and you no longer need the source data. // -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository +// GitHub API docs: https://docs.github.com/rest/migrations/orgs?apiVersion=2022-11-28#unlock-an-organization-repository +// +//meta:operation DELETE /orgs/{org}/migrations/{migration_id}/repos/{repo_name}/lock func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int64, repo string) (*Response, error) { u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/migrations_source_import.go b/github/migrations_source_import.go index fd45e780065..4454d6cc610 100644 --- a/github/migrations_source_import.go +++ b/github/migrations_source_import.go @@ -76,7 +76,7 @@ type Import struct { // Contact GitHub support for more information. // detection_needs_auth - the importer requires authentication for // the originating repository to continue detection. Make an - // UpdatImport request, and include VCSUsername and + // UpdateImport request, and include VCSUsername and // VCSPassword. // detection_found_nothing - the importer didn't recognize any // source control at the URL. @@ -106,7 +106,7 @@ type Import struct { // When the importer finds several projects or repositories at the // provided URLs, this will identify the available choices. Call // UpdateImport with the selected Import value. - ProjectChoices []Import `json:"project_choices,omitempty"` + ProjectChoices []*Import `json:"project_choices,omitempty"` } func (i Import) String() string { @@ -115,7 +115,7 @@ func (i Import) String() string { // SourceImportAuthor identifies an author imported from a source repository. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#get-commit-authors type SourceImportAuthor struct { ID *int64 `json:"id,omitempty"` RemoteID *string `json:"remote_id,omitempty"` @@ -132,7 +132,7 @@ func (a SourceImportAuthor) String() string { // LargeFile identifies a file larger than 100MB found during a repository import. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#get-large-files type LargeFile struct { RefName *string `json:"ref_name,omitempty"` Path *string `json:"path,omitempty"` @@ -146,19 +146,20 @@ func (f LargeFile) String() string { // StartImport initiates a repository import. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#start-an-import -func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#start-an-import +// +//meta:operation PUT /repos/{owner}/{repo}/import +func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, body *Import) (*Import, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("PUT", u, in) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) + var out *Import + resp, err := s.client.Do(req, &out) if err != nil { return nil, resp, err } @@ -168,19 +169,20 @@ func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, // ImportProgress queries for the status and progress of an ongoing repository import. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-import-progress +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#get-an-import-status +// +//meta:operation GET /repos/{owner}/{repo}/import func (s *MigrationService) ImportProgress(ctx context.Context, owner, repo string) (*Import, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) + var out *Import + resp, err := s.client.Do(req, &out) if err != nil { return nil, resp, err } @@ -190,19 +192,20 @@ func (s *MigrationService) ImportProgress(ctx context.Context, owner, repo strin // UpdateImport initiates a repository import. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#update-existing-import -func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#update-an-import +// +//meta:operation PATCH /repos/{owner}/{repo}/import +func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, body *Import) (*Import, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("PATCH", u, in) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) + var out *Import + resp, err := s.client.Do(req, &out) if err != nil { return nil, resp, err } @@ -222,19 +225,20 @@ func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, // This method and MapCommitAuthor allow you to provide correct Git author // information. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#get-commit-authors +// +//meta:operation GET /repos/{owner}/{repo}/import/authors func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string) ([]*SourceImportAuthor, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import/authors", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - var authors []*SourceImportAuthor - resp, err := s.client.Do(ctx, req, &authors) + resp, err := s.client.Do(req, &authors) if err != nil { return nil, resp, err } @@ -246,19 +250,20 @@ func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string // application can continue updating authors any time before you push new // commits to the repository. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author -func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int64, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) { +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#map-a-commit-author +// +//meta:operation PATCH /repos/{owner}/{repo}/import/authors/{author_id} +func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int64, body *SourceImportAuthor) (*SourceImportAuthor, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, author) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(SourceImportAuthor) - resp, err := s.client.Do(ctx, req, out) + var out *SourceImportAuthor + resp, err := s.client.Do(req, &out) if err != nil { return nil, resp, err } @@ -270,19 +275,20 @@ func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo stri // files larger than 100MB. Only the UseLFS field on the provided Import is // used. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference -func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#update-git-lfs-preference +// +//meta:operation PATCH /repos/{owner}/{repo}/import/lfs +func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo string, body *Import) (*Import, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import/lfs", owner, repo) - req, err := s.client.NewRequest("PATCH", u, in) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) + var out *Import + resp, err := s.client.Do(req, &out) if err != nil { return nil, resp, err } @@ -292,19 +298,20 @@ func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo str // LargeFiles lists files larger than 100MB found during the import. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#get-large-files +// +//meta:operation GET /repos/{owner}/{repo}/import/large_files func (s *MigrationService) LargeFiles(ctx context.Context, owner, repo string) ([]*LargeFile, *Response, error) { u := fmt.Sprintf("repos/%v/%v/import/large_files", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - var files []*LargeFile - resp, err := s.client.Do(ctx, req, &files) + resp, err := s.client.Do(req, &files) if err != nil { return nil, resp, err } @@ -314,16 +321,17 @@ func (s *MigrationService) LargeFiles(ctx context.Context, owner, repo string) ( // CancelImport stops an import for a repository. // -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#cancel-an-import +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/rest/migrations/source-imports?apiVersion=2022-11-28#cancel-an-import +// +//meta:operation DELETE /repos/{owner}/{repo}/import func (s *MigrationService) CancelImport(ctx context.Context, owner, repo string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/migrations_source_import_test.go b/github/migrations_source_import_test.go index e9e9be062be..ae1fcce69ef 100644 --- a/github/migrations_source_import_test.go +++ b/github/migrations_source_import_test.go @@ -6,221 +6,304 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestMigrationService_StartImport(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := &Import{ - VCS: String("git"), - VCSURL: String("url"), - VCSUsername: String("u"), - VCSPassword: String("p"), + VCS: Ptr("git"), + VCSURL: Ptr("url"), + VCSUsername: Ptr("u"), + VCSPassword: Ptr("p"), } mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { - v := new(Import) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeImportPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{"status":"importing"}`) }) - got, _, err := client.Migrations.StartImport(context.Background(), "o", "r", input) + ctx := t.Context() + got, _, err := client.Migrations.StartImport(ctx, "o", "r", input) if err != nil { t.Errorf("StartImport returned error: %v", err) } - want := &Import{Status: String("importing")} - if !reflect.DeepEqual(got, want) { + want := &Import{Status: Ptr("importing")} + if !cmp.Equal(got, want) { t.Errorf("StartImport = %+v, want %+v", got, want) } + + const methodName = "StartImport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.StartImport(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.StartImport(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_ImportProgress(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeImportPreview) fmt.Fprint(w, `{"status":"complete"}`) }) - got, _, err := client.Migrations.ImportProgress(context.Background(), "o", "r") + ctx := t.Context() + got, _, err := client.Migrations.ImportProgress(ctx, "o", "r") if err != nil { t.Errorf("ImportProgress returned error: %v", err) } - want := &Import{Status: String("complete")} - if !reflect.DeepEqual(got, want) { + want := &Import{Status: Ptr("complete")} + if !cmp.Equal(got, want) { t.Errorf("ImportProgress = %+v, want %+v", got, want) } + + const methodName = "ImportProgress" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.ImportProgress(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.ImportProgress(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_UpdateImport(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := &Import{ - VCS: String("git"), - VCSURL: String("url"), - VCSUsername: String("u"), - VCSPassword: String("p"), + VCS: Ptr("git"), + VCSURL: Ptr("url"), + VCSUsername: Ptr("u"), + VCSPassword: Ptr("p"), } mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { - v := new(Import) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeImportPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{"status":"importing"}`) }) - got, _, err := client.Migrations.UpdateImport(context.Background(), "o", "r", input) + ctx := t.Context() + got, _, err := client.Migrations.UpdateImport(ctx, "o", "r", input) if err != nil { t.Errorf("UpdateImport returned error: %v", err) } - want := &Import{Status: String("importing")} - if !reflect.DeepEqual(got, want) { + want := &Import{Status: Ptr("importing")} + if !cmp.Equal(got, want) { t.Errorf("UpdateImport = %+v, want %+v", got, want) } + + const methodName = "UpdateImport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.UpdateImport(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.UpdateImport(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_CommitAuthors(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/import/authors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeImportPreview) fmt.Fprint(w, `[{"id":1,"name":"a"},{"id":2,"name":"b"}]`) }) - got, _, err := client.Migrations.CommitAuthors(context.Background(), "o", "r") + ctx := t.Context() + got, _, err := client.Migrations.CommitAuthors(ctx, "o", "r") if err != nil { t.Errorf("CommitAuthors returned error: %v", err) } want := []*SourceImportAuthor{ - {ID: Int64(1), Name: String("a")}, - {ID: Int64(2), Name: String("b")}, + {ID: Ptr(int64(1)), Name: Ptr("a")}, + {ID: Ptr(int64(2)), Name: Ptr("b")}, } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("CommitAuthors = %+v, want %+v", got, want) } + + const methodName = "CommitAuthors" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.CommitAuthors(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.CommitAuthors(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_MapCommitAuthor(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &SourceImportAuthor{Name: String("n"), Email: String("e")} + input := &SourceImportAuthor{Name: Ptr("n"), Email: Ptr("e")} mux.HandleFunc("/repos/o/r/import/authors/1", func(w http.ResponseWriter, r *http.Request) { - v := new(SourceImportAuthor) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeImportPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id": 1}`) }) - got, _, err := client.Migrations.MapCommitAuthor(context.Background(), "o", "r", 1, input) + ctx := t.Context() + got, _, err := client.Migrations.MapCommitAuthor(ctx, "o", "r", 1, input) if err != nil { t.Errorf("MapCommitAuthor returned error: %v", err) } - want := &SourceImportAuthor{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { + want := &SourceImportAuthor{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { t.Errorf("MapCommitAuthor = %+v, want %+v", got, want) } + + const methodName = "MapCommitAuthor" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.MapCommitAuthor(ctx, "\n", "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.MapCommitAuthor(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_SetLFSPreference(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Import{UseLFS: String("opt_in")} + input := &Import{UseLFS: Ptr("opt_in")} mux.HandleFunc("/repos/o/r/import/lfs", func(w http.ResponseWriter, r *http.Request) { - v := new(Import) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeImportPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) w.WriteHeader(http.StatusCreated) fmt.Fprint(w, `{"status":"importing"}`) }) - got, _, err := client.Migrations.SetLFSPreference(context.Background(), "o", "r", input) + ctx := t.Context() + got, _, err := client.Migrations.SetLFSPreference(ctx, "o", "r", input) if err != nil { t.Errorf("SetLFSPreference returned error: %v", err) } - want := &Import{Status: String("importing")} - if !reflect.DeepEqual(got, want) { + want := &Import{Status: Ptr("importing")} + if !cmp.Equal(got, want) { t.Errorf("SetLFSPreference = %+v, want %+v", got, want) } + + const methodName = "SetLFSPreference" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.SetLFSPreference(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.SetLFSPreference(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_LargeFiles(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/import/large_files", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeImportPreview) fmt.Fprint(w, `[{"oid":"a"},{"oid":"b"}]`) }) - got, _, err := client.Migrations.LargeFiles(context.Background(), "o", "r") + ctx := t.Context() + got, _, err := client.Migrations.LargeFiles(ctx, "o", "r") if err != nil { t.Errorf("LargeFiles returned error: %v", err) } want := []*LargeFile{ - {OID: String("a")}, - {OID: String("b")}, + {OID: Ptr("a")}, + {OID: Ptr("b")}, } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("LargeFiles = %+v, want %+v", got, want) } + + const methodName = "LargeFiles" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.LargeFiles(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.LargeFiles(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_CancelImport(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeImportPreview) w.WriteHeader(http.StatusNoContent) }) - _, err := client.Migrations.CancelImport(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Migrations.CancelImport(ctx, "o", "r") if err != nil { t.Errorf("CancelImport returned error: %v", err) } + + const methodName = "CancelImport" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Migrations.CancelImport(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Migrations.CancelImport(ctx, "o", "r") + }) } diff --git a/github/migrations_test.go b/github/migrations_test.go index 687f89a353b..73d34ec46f2 100644 --- a/github/migrations_test.go +++ b/github/migrations_test.go @@ -6,84 +6,131 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestMigrationService_StartMigration(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeMigrationsPreview) w.WriteHeader(http.StatusCreated) - w.Write(migrationJSON) + assertWrite(t, w, migrationJSON) }) opt := &MigrationOptions{ LockRepositories: true, ExcludeAttachments: false, + ExcludeReleases: true, + Exclude: []string{"repositories"}, } - got, _, err := client.Migrations.StartMigration(context.Background(), "o", []string{"r"}, opt) + ctx := t.Context() + got, _, err := client.Migrations.StartMigration(ctx, "o", []string{"r"}, opt) if err != nil { t.Errorf("StartMigration returned error: %v", err) } - if want := wantMigration; !reflect.DeepEqual(got, want) { + if want := wantMigration; !cmp.Equal(got, want) { t.Errorf("StartMigration = %+v, want %+v", got, want) } + + const methodName = "StartMigration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.StartMigration(ctx, "\n", []string{"\n"}, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.StartMigration(ctx, "o", []string{"r"}, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_ListMigrations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeMigrationsPreview) w.WriteHeader(http.StatusOK) - w.Write([]byte(fmt.Sprintf("[%s]", migrationJSON))) + assertWrite(t, w, fmt.Appendf(nil, "[%s]", migrationJSON)) }) - got, _, err := client.Migrations.ListMigrations(context.Background(), "o") + ctx := t.Context() + got, _, err := client.Migrations.ListMigrations(ctx, "o", &ListOptions{Page: 1, PerPage: 2}) if err != nil { t.Errorf("ListMigrations returned error: %v", err) } - if want := []*Migration{wantMigration}; !reflect.DeepEqual(got, want) { + if want := []*Migration{wantMigration}; !cmp.Equal(got, want) { t.Errorf("ListMigrations = %+v, want %+v", got, want) } + + const methodName = "ListMigrations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.ListMigrations(ctx, "\n", &ListOptions{Page: 1, PerPage: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.ListMigrations(ctx, "o", &ListOptions{Page: 1, PerPage: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_MigrationStatus(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/migrations/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeMigrationsPreview) w.WriteHeader(http.StatusOK) - w.Write(migrationJSON) + assertWrite(t, w, migrationJSON) }) - got, _, err := client.Migrations.MigrationStatus(context.Background(), "o", 1) + ctx := t.Context() + got, _, err := client.Migrations.MigrationStatus(ctx, "o", 1) if err != nil { - t.Errorf("MigrationStatus returned error: %v", err) + t.Errorf("Migrations.MigrationStatus returned error: %v", err) } - if want := wantMigration; !reflect.DeepEqual(got, want) { + if want := wantMigration; !cmp.Equal(got, want) { t.Errorf("MigrationStatus = %+v, want %+v", got, want) } + + const methodName = "MigrationStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Migrations.MigrationStatus(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.MigrationStatus(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestMigrationService_MigrationArchiveURL(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestMigrationService_MigrationArchiveURL_Redirect(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -95,21 +142,50 @@ func TestMigrationService_MigrationArchiveURL(t *testing.T) { testMethod(t, r, "GET") w.WriteHeader(http.StatusOK) - w.Write([]byte("0123456789abcdef")) + assertWrite(t, w, []byte("0123456789abcdef")) }) - got, err := client.Migrations.MigrationArchiveURL(context.Background(), "o", 1) + ctx := t.Context() + got, err := client.Migrations.MigrationArchiveURL(ctx, "o", 1) if err != nil { - t.Errorf("MigrationStatus returned error: %v", err) + t.Errorf("Migrations.MigrationArchiveURL returned error: %v", err) } if want := "/yo"; !strings.HasSuffix(got, want) { t.Errorf("MigrationArchiveURL = %+v, want %+v", got, want) } + + const methodName = "MigrationArchiveURL" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Migrations.MigrationArchiveURL(ctx, "\n", -1) + return err + }) +} + +func TestMigrationService_MigrationArchiveURL_NoRedirect(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMigrationsPreview) + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte("0123456789abcdef")) + }) + + ctx := t.Context() + got, err := client.Migrations.MigrationArchiveURL(ctx, "o", 1) + if err == nil { + t.Error("Migrations.MigrationArchiveURL did not return expected error") + } + if got != "" { + t.Errorf("MigrationArchiveURL = %+v, want %+v", got, "") + } } func TestMigrationService_DeleteMigration(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -118,14 +194,25 @@ func TestMigrationService_DeleteMigration(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Migrations.DeleteMigration(context.Background(), "o", 1); err != nil { + ctx := t.Context() + if _, err := client.Migrations.DeleteMigration(ctx, "o", 1); err != nil { t.Errorf("DeleteMigration returned error: %v", err) } + + const methodName = "DeleteMigration" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Migrations.DeleteMigration(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Migrations.DeleteMigration(ctx, "o", 1) + }) } func TestMigrationService_UnlockRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/migrations/1/repos/r/lock", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -134,9 +221,20 @@ func TestMigrationService_UnlockRepo(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Migrations.UnlockRepo(context.Background(), "o", 1, "r"); err != nil { + ctx := t.Context() + if _, err := client.Migrations.UnlockRepo(ctx, "o", 1, "r"); err != nil { t.Errorf("UnlockRepo returned error: %v", err) } + + const methodName = "UnlockRepo" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Migrations.UnlockRepo(ctx, "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Migrations.UnlockRepo(ctx, "o", 1, "r") + }) } var migrationJSON = []byte(`{ @@ -159,20 +257,20 @@ var migrationJSON = []byte(`{ }`) var wantMigration = &Migration{ - ID: Int64(79), - GUID: String("0b989ba4-242f-11e5-81e1-c7b6966d2516"), - State: String("pending"), - LockRepositories: Bool(true), - ExcludeAttachments: Bool(false), - URL: String("https://api.github.com/orgs/octo-org/migrations/79"), - CreatedAt: String("2015-07-06T15:33:38-07:00"), - UpdatedAt: String("2015-07-06T15:33:38-07:00"), + ID: Ptr(int64(79)), + GUID: Ptr("0b989ba4-242f-11e5-81e1-c7b6966d2516"), + State: Ptr("pending"), + LockRepositories: Ptr(true), + ExcludeAttachments: Ptr(false), + URL: Ptr("https://api.github.com/orgs/octo-org/migrations/79"), + CreatedAt: Ptr("2015-07-06T15:33:38-07:00"), + UpdatedAt: Ptr("2015-07-06T15:33:38-07:00"), Repositories: []*Repository{ { - ID: Int64(1296269), - Name: String("Hello-World"), - FullName: String("octocat/Hello-World"), - Description: String("This your first repo!"), + ID: Ptr(int64(1296269)), + Name: Ptr("Hello-World"), + FullName: Ptr("octocat/Hello-World"), + Description: Ptr("This your first repo!"), }, }, } diff --git a/github/migrations_user.go b/github/migrations_user.go index d45555f216b..6387d7f1590 100644 --- a/github/migrations_user.go +++ b/github/migrations_user.go @@ -67,26 +67,27 @@ type startUserMigration struct { // StartUserMigration starts the generation of a migration archive. // repos is a slice of repository names to migrate. // -// GitHub API docs: https://developer.github.com/v3/migrations/users/#start-a-user-migration -func (s *MigrationService) StartUserMigration(ctx context.Context, repos []string, opt *UserMigrationOptions) (*UserMigration, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/migrations/users?apiVersion=2022-11-28#start-a-user-migration +// +//meta:operation POST /user/migrations +func (s *MigrationService) StartUserMigration(ctx context.Context, repos []string, opts *UserMigrationOptions) (*UserMigration, *Response, error) { u := "user/migrations" body := &startUserMigration{Repositories: repos} - if opt != nil { - body.LockRepositories = Bool(opt.LockRepositories) - body.ExcludeAttachments = Bool(opt.ExcludeAttachments) + if opts != nil { + body.LockRepositories = &opts.LockRepositories + body.ExcludeAttachments = &opts.ExcludeAttachments } - req, err := s.client.NewRequest("POST", u, body) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - m := &UserMigration{} - resp, err := s.client.Do(ctx, req, m) + var m *UserMigration + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -96,20 +97,25 @@ func (s *MigrationService) StartUserMigration(ctx context.Context, repos []strin // ListUserMigrations lists the most recent migrations. // -// GitHub API docs: https://developer.github.com/v3/migrations/users/#get-a-list-of-user-migrations -func (s *MigrationService) ListUserMigrations(ctx context.Context) ([]*UserMigration, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/migrations/users?apiVersion=2022-11-28#list-user-migrations +// +//meta:operation GET /user/migrations +func (s *MigrationService) ListUserMigrations(ctx context.Context, opts *ListOptions) ([]*UserMigration, *Response, error) { u := "user/migrations" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) var m []*UserMigration - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -120,20 +126,21 @@ func (s *MigrationService) ListUserMigrations(ctx context.Context) ([]*UserMigra // UserMigrationStatus gets the status of a specific migration archive. // id is the migration ID. // -// GitHub API docs: https://developer.github.com/v3/migrations/users/#get-the-status-of-a-user-migration +// GitHub API docs: https://docs.github.com/rest/migrations/users?apiVersion=2022-11-28#get-a-user-migration-status +// +//meta:operation GET /user/migrations/{migration_id} func (s *MigrationService) UserMigrationStatus(ctx context.Context, id int64) (*UserMigration, *Response, error) { u := fmt.Sprintf("user/migrations/%v", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - m := &UserMigration{} - resp, err := s.client.Do(ctx, req, m) + var m *UserMigration + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -144,33 +151,35 @@ func (s *MigrationService) UserMigrationStatus(ctx context.Context, id int64) (* // UserMigrationArchiveURL gets the URL for a specific migration archive. // id is the migration ID. // -// GitHub API docs: https://developer.github.com/v3/migrations/users/#download-a-user-migration-archive +// GitHub API docs: https://docs.github.com/rest/migrations/users?apiVersion=2022-11-28#download-a-user-migration-archive +// +//meta:operation GET /user/migrations/{migration_id}/archive func (s *MigrationService) UserMigrationArchiveURL(ctx context.Context, id int64) (string, error) { url := fmt.Sprintf("user/migrations/%v/archive", id) - req, err := s.client.NewRequest("GET", url, nil) + req, err := s.client.NewRequest(ctx, "GET", url, nil) if err != nil { return "", err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - m := &UserMigration{} - var loc string originalRedirect := s.client.client.CheckRedirect - s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + s.client.client.CheckRedirect = func(req *http.Request, _ []*http.Request) error { loc = req.URL.String() return http.ErrUseLastResponse } defer func() { s.client.client.CheckRedirect = originalRedirect }() - resp, err := s.client.Do(ctx, req, m) + + var m *UserMigration + resp, err := s.client.Do(req, &m) if err == nil { return "", errors.New("expected redirect, none provided") } + loc = resp.Header.Get("Location") return loc, nil } @@ -178,19 +187,20 @@ func (s *MigrationService) UserMigrationArchiveURL(ctx context.Context, id int64 // DeleteUserMigration will delete a previous migration archive. // id is the migration ID. // -// GitHub API docs: https://developer.github.com/v3/migrations/users/#delete-a-user-migration-archive +// GitHub API docs: https://docs.github.com/rest/migrations/users?apiVersion=2022-11-28#delete-a-user-migration-archive +// +//meta:operation DELETE /user/migrations/{migration_id}/archive func (s *MigrationService) DeleteUserMigration(ctx context.Context, id int64) (*Response, error) { url := fmt.Sprintf("user/migrations/%v/archive", id) - req, err := s.client.NewRequest("DELETE", url, nil) + req, err := s.client.NewRequest(ctx, "DELETE", url, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UnlockUserRepo will unlock a repo that was locked for migration. @@ -198,17 +208,18 @@ func (s *MigrationService) DeleteUserMigration(ctx context.Context, id int64) (* // You should unlock each migrated repository and delete them when the migration // is complete and you no longer need the source data. // -// GitHub API docs: https://developer.github.com/v3/migrations/users/#unlock-a-user-repository +// GitHub API docs: https://docs.github.com/rest/migrations/users?apiVersion=2022-11-28#unlock-a-user-repository +// +//meta:operation DELETE /user/migrations/{migration_id}/repos/{repo_name}/lock func (s *MigrationService) UnlockUserRepo(ctx context.Context, id int64, repo string) (*Response, error) { url := fmt.Sprintf("user/migrations/%v/repos/%v/lock", id, repo) - req, err := s.client.NewRequest("DELETE", url, nil) + req, err := s.client.NewRequest(ctx, "DELETE", url, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMigrationsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/migrations_user_test.go b/github/migrations_user_test.go index c271e02a811..7ffdc0935ea 100644 --- a/github/migrations_user_test.go +++ b/github/migrations_user_test.go @@ -6,24 +6,24 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestMigrationService_StartUserMigration(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/migrations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeMigrationsPreview) w.WriteHeader(http.StatusCreated) - w.Write(userMigrationJSON) + assertWrite(t, w, userMigrationJSON) }) opt := &UserMigrationOptions{ @@ -31,66 +31,96 @@ func TestMigrationService_StartUserMigration(t *testing.T) { ExcludeAttachments: false, } - got, _, err := client.Migrations.StartUserMigration(context.Background(), []string{"r"}, opt) + ctx := t.Context() + got, _, err := client.Migrations.StartUserMigration(ctx, []string{"r"}, opt) if err != nil { t.Errorf("StartUserMigration returned error: %v", err) } want := wantUserMigration - if !reflect.DeepEqual(want, got) { + if !cmp.Equal(want, got) { t.Errorf("StartUserMigration = %v, want = %v", got, want) } + + const methodName = "StartUserMigration" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.StartUserMigration(ctx, []string{"r"}, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_ListUserMigrations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/migrations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeMigrationsPreview) w.WriteHeader(http.StatusOK) - w.Write([]byte(fmt.Sprintf("[%s]", userMigrationJSON))) + assertWrite(t, w, fmt.Appendf(nil, "[%s]", userMigrationJSON)) }) - got, _, err := client.Migrations.ListUserMigrations(context.Background()) + ctx := t.Context() + got, _, err := client.Migrations.ListUserMigrations(ctx, &ListOptions{Page: 1, PerPage: 2}) if err != nil { t.Errorf("ListUserMigrations returned error %v", err) } want := []*UserMigration{wantUserMigration} - if !reflect.DeepEqual(want, got) { + if !cmp.Equal(want, got) { t.Errorf("ListUserMigrations = %v, want = %v", got, want) } + + const methodName = "ListUserMigrations" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.ListUserMigrations(ctx, &ListOptions{Page: 1, PerPage: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_UserMigrationStatus(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/migrations/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeMigrationsPreview) w.WriteHeader(http.StatusOK) - w.Write(userMigrationJSON) + assertWrite(t, w, userMigrationJSON) }) - got, _, err := client.Migrations.UserMigrationStatus(context.Background(), 1) + ctx := t.Context() + got, _, err := client.Migrations.UserMigrationStatus(ctx, 1) if err != nil { t.Errorf("UserMigrationStatus returned error %v", err) } want := wantUserMigration - if !reflect.DeepEqual(want, got) { + if !cmp.Equal(want, got) { t.Errorf("UserMigrationStatus = %v, want = %v", got, want) } + + const methodName = "UserMigrationStatus" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Migrations.UserMigrationStatus(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestMigrationService_UserMigrationArchiveURL(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -105,7 +135,8 @@ func TestMigrationService_UserMigrationArchiveURL(t *testing.T) { w.WriteHeader(http.StatusOK) }) - got, err := client.Migrations.UserMigrationArchiveURL(context.Background(), 1) + ctx := t.Context() + got, err := client.Migrations.UserMigrationArchiveURL(ctx, 1) if err != nil { t.Errorf("UserMigrationArchiveURL returned error %v", err) } @@ -117,8 +148,8 @@ func TestMigrationService_UserMigrationArchiveURL(t *testing.T) { } func TestMigrationService_DeleteUserMigration(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -127,7 +158,8 @@ func TestMigrationService_DeleteUserMigration(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - got, err := client.Migrations.DeleteUserMigration(context.Background(), 1) + ctx := t.Context() + got, err := client.Migrations.DeleteUserMigration(ctx, 1) if err != nil { t.Errorf("DeleteUserMigration returned error %v", err) } @@ -135,11 +167,21 @@ func TestMigrationService_DeleteUserMigration(t *testing.T) { if got.StatusCode != http.StatusNoContent { t.Errorf("DeleteUserMigration returned status = %v, want = %v", got.StatusCode, http.StatusNoContent) } + + const methodName = "DeleteUserMigration" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Migrations.DeleteUserMigration(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Migrations.DeleteUserMigration(ctx, 1) + }) } func TestMigrationService_UnlockUserRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/migrations/1/repos/r/lock", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -148,7 +190,8 @@ func TestMigrationService_UnlockUserRepo(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - got, err := client.Migrations.UnlockUserRepo(context.Background(), 1, "r") + ctx := t.Context() + got, err := client.Migrations.UnlockUserRepo(ctx, 1, "r") if err != nil { t.Errorf("UnlockUserRepo returned error %v", err) } @@ -156,6 +199,16 @@ func TestMigrationService_UnlockUserRepo(t *testing.T) { if got.StatusCode != http.StatusNoContent { t.Errorf("UnlockUserRepo returned status = %v, want = %v", got.StatusCode, http.StatusNoContent) } + + const methodName = "UnlockUserRepo" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Migrations.UnlockUserRepo(ctx, -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Migrations.UnlockUserRepo(ctx, 1, "r") + }) } var userMigrationJSON = []byte(`{ @@ -178,20 +231,20 @@ var userMigrationJSON = []byte(`{ }`) var wantUserMigration = &UserMigration{ - ID: Int64(79), - GUID: String("0b989ba4-242f-11e5-81e1-c7b6966d2516"), - State: String("pending"), - LockRepositories: Bool(true), - ExcludeAttachments: Bool(false), - URL: String("https://api.github.com/orgs/octo-org/migrations/79"), - CreatedAt: String("2015-07-06T15:33:38-07:00"), - UpdatedAt: String("2015-07-06T15:33:38-07:00"), + ID: Ptr(int64(79)), + GUID: Ptr("0b989ba4-242f-11e5-81e1-c7b6966d2516"), + State: Ptr("pending"), + LockRepositories: Ptr(true), + ExcludeAttachments: Ptr(false), + URL: Ptr("https://api.github.com/orgs/octo-org/migrations/79"), + CreatedAt: Ptr("2015-07-06T15:33:38-07:00"), + UpdatedAt: Ptr("2015-07-06T15:33:38-07:00"), Repositories: []*Repository{ { - ID: Int64(1296269), - Name: String("Hello-World"), - FullName: String("octocat/Hello-World"), - Description: String("This your first repo!"), + ID: Ptr(int64(1296269)), + Name: Ptr("Hello-World"), + FullName: Ptr("octocat/Hello-World"), + Description: Ptr("This your first repo!"), }, }, } diff --git a/github/misc.go b/github/misc.go deleted file mode 100644 index e9b0ea22a62..00000000000 --- a/github/misc.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2014 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "bytes" - "context" - "fmt" - "net/url" -) - -// MarkdownOptions specifies optional parameters to the Markdown method. -type MarkdownOptions struct { - // Mode identifies the rendering mode. Possible values are: - // markdown - render a document as plain Markdown, just like - // README files are rendered. - // - // gfm - to render a document as user-content, e.g. like user - // comments or issues are rendered. In GFM mode, hard line breaks are - // always taken into account, and issue and user mentions are linked - // accordingly. - // - // Default is "markdown". - Mode string - - // Context identifies the repository context. Only taken into account - // when rendering as "gfm". - Context string -} - -type markdownRequest struct { - Text *string `json:"text,omitempty"` - Mode *string `json:"mode,omitempty"` - Context *string `json:"context,omitempty"` -} - -// Markdown renders an arbitrary Markdown document. -// -// GitHub API docs: https://developer.github.com/v3/markdown/ -func (c *Client) Markdown(ctx context.Context, text string, opt *MarkdownOptions) (string, *Response, error) { - request := &markdownRequest{Text: String(text)} - if opt != nil { - if opt.Mode != "" { - request.Mode = String(opt.Mode) - } - if opt.Context != "" { - request.Context = String(opt.Context) - } - } - - req, err := c.NewRequest("POST", "markdown", request) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// ListEmojis returns the emojis available to use on GitHub. -// -// GitHub API docs: https://developer.github.com/v3/emojis/ -func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { - req, err := c.NewRequest("GET", "emojis", nil) - if err != nil { - return nil, nil, err - } - - var emoji map[string]string - resp, err := c.Do(ctx, req, &emoji) - if err != nil { - return nil, resp, err - } - - return emoji, resp, nil -} - -// CodeOfConduct represents a code of conduct. -type CodeOfConduct struct { - Name *string `json:"name,omitempty"` - Key *string `json:"key,omitempty"` - URL *string `json:"url,omitempty"` - Body *string `json:"body,omitempty"` -} - -func (c *CodeOfConduct) String() string { - return Stringify(c) -} - -// ListCodesOfConduct returns all codes of conduct. -// -// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#list-all-codes-of-conduct -func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { - req, err := c.NewRequest("GET", "codes_of_conduct", nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - var cs []*CodeOfConduct - resp, err := c.Do(ctx, req, &cs) - if err != nil { - return nil, resp, err - } - - return cs, resp, nil -} - -// GetCodeOfConduct returns an individual code of conduct. -// -// https://developer.github.com/v3/codes_of_conduct/#get-an-individual-code-of-conduct -func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { - u := fmt.Sprintf("codes_of_conduct/%s", key) - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - coc := new(CodeOfConduct) - resp, err := c.Do(ctx, req, coc) - if err != nil { - return nil, resp, err - } - - return coc, resp, nil -} - -// APIMeta represents metadata about the GitHub API. -type APIMeta struct { - // An Array of IP addresses in CIDR format specifying the addresses - // that incoming service hooks will originate from on GitHub.com. - Hooks []string `json:"hooks,omitempty"` - - // An Array of IP addresses in CIDR format specifying the Git servers - // for GitHub.com. - Git []string `json:"git,omitempty"` - - // Whether authentication with username and password is supported. - // (GitHub Enterprise instances using CAS or OAuth for authentication - // will return false. Features like Basic Authentication with a - // username and password, sudo mode, and two-factor authentication are - // not supported on these servers.) - VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` - - // An array of IP addresses in CIDR format specifying the addresses - // which serve GitHub Pages websites. - Pages []string `json:"pages,omitempty"` - - // An Array of IP addresses specifying the addresses that source imports - // will originate from on GitHub.com. - Importer []string `json:"importer,omitempty"` -} - -// APIMeta returns information about GitHub.com, the service. Or, if you access -// this endpoint on your organization’s GitHub Enterprise installation, this -// endpoint provides information about that installation. -// -// GitHub API docs: https://developer.github.com/v3/meta/ -func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { - req, err := c.NewRequest("GET", "meta", nil) - if err != nil { - return nil, nil, err - } - - meta := new(APIMeta) - resp, err := c.Do(ctx, req, meta) - if err != nil { - return nil, resp, err - } - - return meta, resp, nil -} - -// Octocat returns an ASCII art octocat with the specified message in a speech -// bubble. If message is empty, a random zen phrase is used. -func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { - u := "octocat" - if message != "" { - u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) - } - - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// Zen returns a random line from The Zen of GitHub. -// -// see also: http://warpspire.com/posts/taste/ -func (c *Client) Zen(ctx context.Context) (string, *Response, error) { - req, err := c.NewRequest("GET", "zen", nil) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// ServiceHook represents a hook that has configuration settings, a list of -// available events, and default events. -type ServiceHook struct { - Name *string `json:"name,omitempty"` - Events []string `json:"events,omitempty"` - SupportedEvents []string `json:"supported_events,omitempty"` - Schema [][]string `json:"schema,omitempty"` -} - -func (s *ServiceHook) String() string { - return Stringify(s) -} - -// ListServiceHooks lists all of the available service hooks. -// -// GitHub API docs: https://developer.github.com/webhooks/#services -func (c *Client) ListServiceHooks(ctx context.Context) ([]*ServiceHook, *Response, error) { - u := "hooks" - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var hooks []*ServiceHook - resp, err := c.Do(ctx, req, &hooks) - if err != nil { - return nil, resp, err - } - - return hooks, resp, nil -} diff --git a/github/misc_test.go b/github/misc_test.go deleted file mode 100644 index b7144f9091e..00000000000 --- a/github/misc_test.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2014 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "reflect" - "testing" -) - -func TestMarkdown(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &markdownRequest{ - Text: String("# text #"), - Mode: String("gfm"), - Context: String("google/go-github"), - } - mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { - v := new(markdownRequest) - json.NewDecoder(r.Body).Decode(v) - - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - fmt.Fprint(w, `

text

`) - }) - - md, _, err := client.Markdown(context.Background(), "# text #", &MarkdownOptions{ - Mode: "gfm", - Context: "google/go-github", - }) - if err != nil { - t.Errorf("Markdown returned error: %v", err) - } - - if want := "

text

"; want != md { - t.Errorf("Markdown returned %+v, want %+v", md, want) - } -} - -func TestListEmojis(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"+1": "+1.png"}`) - }) - - emoji, _, err := client.ListEmojis(context.Background()) - if err != nil { - t.Errorf("ListEmojis returned error: %v", err) - } - - want := map[string]string{"+1": "+1.png"} - if !reflect.DeepEqual(want, emoji) { - t.Errorf("ListEmojis returned %+v, want %+v", emoji, want) - } -} - -func TestListCodesOfConduct(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/codes_of_conduct", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) - fmt.Fprint(w, `[{ - "key": "key", - "name": "name", - "url": "url"} - ]`) - }) - - cs, _, err := client.ListCodesOfConduct(context.Background()) - if err != nil { - t.Errorf("ListCodesOfConduct returned error: %v", err) - } - - want := []*CodeOfConduct{ - { - Key: String("key"), - Name: String("name"), - URL: String("url"), - }} - if !reflect.DeepEqual(want, cs) { - t.Errorf("ListCodesOfConduct returned %+v, want %+v", cs, want) - } -} - -func TestGetCodeOfConduct(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/codes_of_conduct/k", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) - fmt.Fprint(w, `{ - "key": "key", - "name": "name", - "url": "url", - "body": "body"}`, - ) - }) - - coc, _, err := client.GetCodeOfConduct(context.Background(), "k") - if err != nil { - t.Errorf("ListCodesOfConduct returned error: %v", err) - } - - want := &CodeOfConduct{ - Key: String("key"), - Name: String("name"), - URL: String("url"), - Body: String("body"), - } - if !reflect.DeepEqual(want, coc) { - t.Errorf("GetCodeOfConductByKey returned %+v, want %+v", coc, want) - } -} - -func TestAPIMeta(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"hooks":["h"], "git":["g"], "pages":["p"], "importer":["i"], "verifiable_password_authentication": true}`) - }) - - meta, _, err := client.APIMeta(context.Background()) - if err != nil { - t.Errorf("APIMeta returned error: %v", err) - } - - want := &APIMeta{ - Hooks: []string{"h"}, - Git: []string{"g"}, - Pages: []string{"p"}, - Importer: []string{"i"}, - - VerifiablePasswordAuthentication: Bool(true), - } - if !reflect.DeepEqual(want, meta) { - t.Errorf("APIMeta returned %+v, want %+v", meta, want) - } -} - -func TestOctocat(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := "input" - output := "sample text" - - mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{"s": input}) - w.Header().Set("Content-Type", "application/octocat-stream") - fmt.Fprint(w, output) - }) - - got, _, err := client.Octocat(context.Background(), input) - if err != nil { - t.Errorf("Octocat returned error: %v", err) - } - - if want := output; got != want { - t.Errorf("Octocat returned %+v, want %+v", got, want) - } -} - -func TestZen(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - output := "sample text" - - mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - w.Header().Set("Content-Type", "text/plain;charset=utf-8") - fmt.Fprint(w, output) - }) - - got, _, err := client.Zen(context.Background()) - if err != nil { - t.Errorf("Zen returned error: %v", err) - } - - if want := output; got != want { - t.Errorf("Zen returned %+v, want %+v", got, want) - } -} - -func TestListServiceHooks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/hooks", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `[{ - "name":"n", - "events":["e"], - "supported_events":["s"], - "schema":[ - ["a", "b"] - ] - }]`) - }) - - hooks, _, err := client.ListServiceHooks(context.Background()) - if err != nil { - t.Errorf("ListServiceHooks returned error: %v", err) - } - - want := []*ServiceHook{{ - Name: String("n"), - Events: []string{"e"}, - SupportedEvents: []string{"s"}, - Schema: [][]string{{"a", "b"}}, - }} - if !reflect.DeepEqual(hooks, want) { - t.Errorf("ListServiceHooks returned %+v, want %+v", hooks, want) - } -} diff --git a/github/orgs.go b/github/orgs.go index e1aa20db0b1..d4be778e906 100644 --- a/github/orgs.go +++ b/github/orgs.go @@ -8,13 +8,12 @@ package github import ( "context" "fmt" - "time" ) // OrganizationsService provides access to the organization related functions // in the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/orgs/ +// GitHub API docs: https://docs.github.com/rest/orgs?apiVersion=2022-11-28 type OrganizationsService service // Organization represents a GitHub organization account. @@ -29,15 +28,17 @@ type Organization struct { Blog *string `json:"blog,omitempty"` Location *string `json:"location,omitempty"` Email *string `json:"email,omitempty"` + TwitterUsername *string `json:"twitter_username,omitempty"` Description *string `json:"description,omitempty"` PublicRepos *int `json:"public_repos,omitempty"` PublicGists *int `json:"public_gists,omitempty"` Followers *int `json:"followers,omitempty"` Following *int `json:"following,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - TotalPrivateRepos *int `json:"total_private_repos,omitempty"` - OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ArchivedAt *Timestamp `json:"archived_at,omitempty"` + TotalPrivateRepos *int64 `json:"total_private_repos,omitempty"` + OwnedPrivateRepos *int64 `json:"owned_private_repos,omitempty"` PrivateGists *int `json:"private_gists,omitempty"` DiskUsage *int `json:"disk_usage,omitempty"` Collaborators *int `json:"collaborators,omitempty"` @@ -45,6 +46,9 @@ type Organization struct { Type *string `json:"type,omitempty"` Plan *Plan `json:"plan,omitempty"` TwoFactorRequirementEnabled *bool `json:"two_factor_requirement_enabled,omitempty"` + IsVerified *bool `json:"is_verified,omitempty"` + HasOrganizationProjects *bool `json:"has_organization_projects,omitempty"` + HasRepositoryProjects *bool `json:"has_repository_projects,omitempty"` // DefaultRepoPermission can be one of: "read", "write", "admin", or "none". (Default: "read"). // It is only used in OrganizationsService.Edit. @@ -56,10 +60,70 @@ type Organization struct { // MembersCanCreateRepos default value is true and is only used in Organizations.Edit. MembersCanCreateRepos *bool `json:"members_can_create_repositories,omitempty"` + // https://developer.github.com/changes/2019-12-03-internal-visibility-changes/#rest-v3-api + MembersCanCreatePublicRepos *bool `json:"members_can_create_public_repositories,omitempty"` + MembersCanCreatePrivateRepos *bool `json:"members_can_create_private_repositories,omitempty"` + MembersCanCreateInternalRepos *bool `json:"members_can_create_internal_repositories,omitempty"` + + // MembersCanForkPrivateRepos toggles whether organization members can fork private organization repositories. + MembersCanForkPrivateRepos *bool `json:"members_can_fork_private_repositories,omitempty"` + + // DeployKeysEnabledForRepositories toggles whether deploy keys may be added and used for repositories in the organization. + DeployKeysEnabledForRepositories *bool `json:"deploy_keys_enabled_for_repositories,omitempty"` + // MembersAllowedRepositoryCreationType denotes if organization members can create repositories // and the type of repositories they can create. Possible values are: "all", "private", or "none". + // + // Deprecated: Use MembersCanCreatePublicRepos, MembersCanCreatePrivateRepos, MembersCanCreateInternalRepos + // instead. The new fields overrides the existing MembersAllowedRepositoryCreationType during 'edit' + // operation and does not consider 'internal' repositories during 'get' operation MembersAllowedRepositoryCreationType *string `json:"members_allowed_repository_creation_type,omitempty"` + // MembersCanCreatePages toggles whether organization members can create GitHub Pages sites. + MembersCanCreatePages *bool `json:"members_can_create_pages,omitempty"` + // MembersCanCreatePublicPages toggles whether organization members can create public GitHub Pages sites. + MembersCanCreatePublicPages *bool `json:"members_can_create_public_pages,omitempty"` + // MembersCanCreatePrivatePages toggles whether organization members can create private GitHub Pages sites. + MembersCanCreatePrivatePages *bool `json:"members_can_create_private_pages,omitempty"` + // WebCommitSignoffRequire toggles + WebCommitSignoffRequired *bool `json:"web_commit_signoff_required,omitempty"` + // AdvancedSecurityAuditLogEnabled toggles whether the advanced security audit log is enabled. + AdvancedSecurityEnabledForNewRepos *bool `json:"advanced_security_enabled_for_new_repositories,omitempty"` + // DependabotAlertsEnabled toggles whether dependabot alerts are enabled. + DependabotAlertsEnabledForNewRepos *bool `json:"dependabot_alerts_enabled_for_new_repositories,omitempty"` + // DependabotSecurityUpdatesEnabled toggles whether dependabot security updates are enabled. + DependabotSecurityUpdatesEnabledForNewRepos *bool `json:"dependabot_security_updates_enabled_for_new_repositories,omitempty"` + // DependabotGraphEnabledForNewRepos toggles whether dependabot graph is enabled on new repositories. + DependencyGraphEnabledForNewRepos *bool `json:"dependency_graph_enabled_for_new_repositories,omitempty"` + // SecretScanningEnabled toggles whether secret scanning is enabled on new repositories. + SecretScanningEnabledForNewRepos *bool `json:"secret_scanning_enabled_for_new_repositories,omitempty"` + // SecretScanningPushProtectionEnabledForNewRepos toggles whether secret scanning push protection is enabled on new repositories. + SecretScanningPushProtectionEnabledForNewRepos *bool `json:"secret_scanning_push_protection_enabled_for_new_repositories,omitempty"` + // SecretScanningValidityChecksEnabled toggles whether secret scanning validity check is enabled. + SecretScanningValidityChecksEnabled *bool `json:"secret_scanning_validity_checks_enabled,omitempty"` + // SecretScanningPushProtectionCustomLinkEnabled toggles whether a custom link is shown to contributors blocked by secret scanning push protection. + SecretScanningPushProtectionCustomLinkEnabled *bool `json:"secret_scanning_push_protection_custom_link_enabled,omitempty"` + // SecretScanningPushProtectionCustomLink is the URL displayed to contributors blocked by secret scanning push protection. + SecretScanningPushProtectionCustomLink *string `json:"secret_scanning_push_protection_custom_link,omitempty"` + // MembersCanDeleteRepositories toggles whether members with admin permissions can delete a repository. + MembersCanDeleteRepositories *bool `json:"members_can_delete_repositories,omitempty"` + // MembersCanChangeRepoVisibility toggles whether members with admin permissions can change the visibility for a repository. + MembersCanChangeRepoVisibility *bool `json:"members_can_change_repo_visibility,omitempty"` + // MembersCanInviteOutsideCollaborators toggles whether members with admin permissions can invite outside collaborators. + MembersCanInviteOutsideCollaborators *bool `json:"members_can_invite_outside_collaborators,omitempty"` + // MembersCanDeleteIssues toggles whether members with admin permissions can delete issues. + MembersCanDeleteIssues *bool `json:"members_can_delete_issues,omitempty"` + // DisplayCommenterFullNameSettingEnabled toggles whether members can see the comment author's profile name in private repositories. + DisplayCommenterFullNameSettingEnabled *bool `json:"display_commenter_full_name_setting_enabled,omitempty"` + // ReadersCanCreateDiscussions toggles whether users with read access can create and comment on discussions. + ReadersCanCreateDiscussions *bool `json:"readers_can_create_discussions,omitempty"` + // MembersCanCreateTeams toggles whether members of an organization can create new teams. + MembersCanCreateTeams *bool `json:"members_can_create_teams,omitempty"` + // MembersCanViewDependencyInsights toggles whether members may view dependency insights. + MembersCanViewDependencyInsights *bool `json:"members_can_view_dependency_insights,omitempty"` + // DefaultRepositoryBranch is the default branch for new repositories in the organization. + DefaultRepositoryBranch *string `json:"default_repository_branch,omitempty"` + // API URLs URL *string `json:"url,omitempty"` EventsURL *string `json:"events_url,omitempty"` @@ -70,6 +134,12 @@ type Organization struct { ReposURL *string `json:"repos_url,omitempty"` } +// OrganizationInstallations represents GitHub app installations for an organization. +type OrganizationInstallations struct { + TotalCount *int `json:"total_count,omitempty"` + Installations []*Installation `json:"installations,omitempty"` +} + func (o Organization) String() string { return Stringify(o) } @@ -79,7 +149,9 @@ type Plan struct { Name *string `json:"name,omitempty"` Space *int `json:"space,omitempty"` Collaborators *int `json:"collaborators,omitempty"` - PrivateRepos *int `json:"private_repos,omitempty"` + PrivateRepos *int64 `json:"private_repos,omitempty"` + FilledSeats *int `json:"filled_seats,omitempty"` + Seats *int `json:"seats,omitempty"` } func (p Plan) String() string { @@ -89,13 +161,11 @@ func (p Plan) String() string { // OrganizationsListOptions specifies the optional parameters to the // OrganizationsService.ListAll method. type OrganizationsListOptions struct { - // Since filters Organizations by ID. + // An organization ID. Only return organizations with an ID greater than this ID. Since int64 `url:"since,omitempty"` - // Note: Pagination is powered exclusively by the Since parameter, - // ListOptions.Page has no effect. - // ListOptions.PerPage controls an undocumented GitHub API parameter. - ListOptions + // The number of results per page (max 100). + PerPage int `url:"per_page,omitempty"` } // ListAll lists all organizations, in the order that they were created on GitHub. @@ -104,20 +174,22 @@ type OrganizationsListOptions struct { // listing the next set of organizations, use the ID of the last-returned organization // as the opts.Since parameter for the next call. // -// GitHub API docs: https://developer.github.com/v3/orgs/#list-all-organizations -func (s *OrganizationsService) ListAll(ctx context.Context, opt *OrganizationsListOptions) ([]*Organization, *Response, error) { - u, err := addOptions("organizations", opt) +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations +// +//meta:operation GET /organizations +func (s *OrganizationsService) ListAll(ctx context.Context, opts *OrganizationsListOptions) ([]*Organization, *Response, error) { + u, err := addOptions("organizations", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } orgs := []*Organization{} - resp, err := s.client.Do(ctx, req, &orgs) + resp, err := s.client.Do(req, &orgs) if err != nil { return nil, resp, err } @@ -127,26 +199,31 @@ func (s *OrganizationsService) ListAll(ctx context.Context, opt *OrganizationsLi // List the organizations for a user. Passing the empty string will list // organizations for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/orgs/#list-user-organizations -func (s *OrganizationsService) List(ctx context.Context, user string, opt *ListOptions) ([]*Organization, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations-for-the-authenticated-user +// +//meta:operation GET /user/orgs +//meta:operation GET /users/{username}/orgs +func (s *OrganizationsService) List(ctx context.Context, user string, opts *ListOptions) ([]*Organization, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/orgs", user) } else { u = "user/orgs" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var orgs []*Organization - resp, err := s.client.Do(ctx, req, &orgs) + resp, err := s.client.Do(req, &orgs) if err != nil { return nil, resp, err } @@ -156,60 +233,109 @@ func (s *OrganizationsService) List(ctx context.Context, user string, opt *ListO // Get fetches an organization by name. // -// GitHub API docs: https://developer.github.com/v3/orgs/#get-an-organization +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#get-an-organization +// +//meta:operation GET /orgs/{org} func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organization, *Response, error) { u := fmt.Sprintf("orgs/%v", org) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeMemberAllowedRepoCreationTypePreview) - organization := new(Organization) - resp, err := s.client.Do(ctx, req, organization) + var o *Organization + resp, err := s.client.Do(req, &o) if err != nil { return nil, resp, err } - return organization, resp, nil + return o, resp, nil } // GetByID fetches an organization. // -// Note: GetByID uses the undocumented GitHub API endpoint /organizations/:id. +// Note: GetByID uses the undocumented GitHub API endpoint "GET /organizations/{organization_id}". +// +//meta:operation GET /organizations/{organization_id} func (s *OrganizationsService) GetByID(ctx context.Context, id int64) (*Organization, *Response, error) { - u := fmt.Sprintf("organizations/%d", id) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("organizations/%v", id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - organization := new(Organization) - resp, err := s.client.Do(ctx, req, organization) + var org *Organization + resp, err := s.client.Do(req, &org) if err != nil { return nil, resp, err } - return organization, resp, nil + return org, resp, nil } // Edit an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/#edit-an-organization -func (s *OrganizationsService) Edit(ctx context.Context, name string, org *Organization) (*Organization, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#update-an-organization +// +//meta:operation PATCH /orgs/{org} +func (s *OrganizationsService) Edit(ctx context.Context, name string, body *Organization) (*Organization, *Response, error) { u := fmt.Sprintf("orgs/%v", name) - req, err := s.client.NewRequest("PATCH", u, org) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - o := new(Organization) - resp, err := s.client.Do(ctx, req, o) + req.Header.Set("Accept", mediaTypeMemberAllowedRepoCreationTypePreview) + + var o *Organization + resp, err := s.client.Do(req, &o) if err != nil { return nil, resp, err } return o, resp, nil } + +// Delete an organization by name. +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#delete-an-organization +// +//meta:operation DELETE /orgs/{org} +func (s *OrganizationsService) Delete(ctx context.Context, org string) (*Response, error) { + u := fmt.Sprintf("orgs/%v", org) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListInstallations lists installations for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#list-app-installations-for-an-organization +// +//meta:operation GET /orgs/{org}/installations +func (s *OrganizationsService) ListInstallations(ctx context.Context, org string, opts *ListOptions) (*OrganizationInstallations, *Response, error) { + u := fmt.Sprintf("orgs/%v/installations", org) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var result *OrganizationInstallations + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} diff --git a/github/orgs_actions_allowed.go b/github/orgs_actions_allowed.go new file mode 100644 index 00000000000..fdec0c73aee --- /dev/null +++ b/github/orgs_actions_allowed.go @@ -0,0 +1,34 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// GetActionsAllowed gets the actions that are allowed in an organization. +// +// Deprecated: please use `client.Actions.GetActionsAllowed` instead. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-allowed-actions-and-reusable-workflows-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions/selected-actions +func (s *OrganizationsService) GetActionsAllowed(ctx context.Context, org string) (*ActionsAllowed, *Response, error) { + s2 := (*ActionsService)(s) + return s2.GetActionsAllowed(ctx, org) +} + +// UpdateActionsAllowed sets the actions that are allowed in an organization. +// +// Deprecated: please use `client.Actions.UpdateActionsAllowed` instead. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-allowed-actions-and-reusable-workflows-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions/selected-actions +func (s *OrganizationsService) UpdateActionsAllowed(ctx context.Context, org string, actionsAllowed ActionsAllowed) (*ActionsAllowed, *Response, error) { + s2 := (*ActionsService)(s) + return s2.UpdateActionsAllowed(ctx, org, actionsAllowed) +} diff --git a/github/orgs_actions_allowed_test.go b/github/orgs_actions_allowed_test.go new file mode 100644 index 00000000000..4eb5e9496ed --- /dev/null +++ b/github/orgs_actions_allowed_test.go @@ -0,0 +1,86 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_GetActionsAllowed(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Organizations.GetActionsAllowed(ctx, "o") + if err != nil { + t.Errorf("Organizations.GetActionsAllowed returned error: %v", err) + } + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Organizations.GetActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetActionsAllowed(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetActionsAllowed(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateActionsAllowed(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + + mux.HandleFunc("/orgs/o/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Organizations.UpdateActionsAllowed(ctx, "o", *input) + if err != nil { + t.Errorf("Organizations.UpdateActionsAllowed returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Organizations.UpdateActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "UpdateActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateActionsAllowed(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateActionsAllowed(ctx, "o", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_actions_permissions.go b/github/orgs_actions_permissions.go new file mode 100644 index 00000000000..8f25318865f --- /dev/null +++ b/github/orgs_actions_permissions.go @@ -0,0 +1,34 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" +) + +// GetActionsPermissions gets the GitHub Actions permissions policy for repositories and allowed actions in an organization. +// +// Deprecated: please use `client.Actions.GetActionsPermissions` instead. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-github-actions-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/permissions +func (s *OrganizationsService) GetActionsPermissions(ctx context.Context, org string) (*ActionsPermissions, *Response, error) { + s2 := (*ActionsService)(s) + return s2.GetActionsPermissions(ctx, org) +} + +// UpdateActionsPermissions sets the permissions policy for repositories and allowed actions in an organization. +// +// Deprecated: please use `client.Actions.UpdateActionsPermissions` instead. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-github-actions-permissions-for-an-organization +// +//meta:operation PUT /orgs/{org}/actions/permissions +func (s *OrganizationsService) UpdateActionsPermissions(ctx context.Context, org string, actionsPermissions ActionsPermissions) (*ActionsPermissions, *Response, error) { + s2 := (*ActionsService)(s) + return s2.UpdateActionsPermissions(ctx, org, actionsPermissions) +} diff --git a/github/orgs_actions_permissions_test.go b/github/orgs_actions_permissions_test.go new file mode 100644 index 00000000000..c97f783fe35 --- /dev/null +++ b/github/orgs_actions_permissions_test.go @@ -0,0 +1,86 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_GetActionsPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled_repositories": "all", "allowed_actions": "all"}`) + }) + + ctx := t.Context() + org, _, err := client.Organizations.GetActionsPermissions(ctx, "o") + if err != nil { + t.Errorf("Organizations.GetActionsPermissions returned error: %v", err) + } + want := &ActionsPermissions{EnabledRepositories: Ptr("all"), AllowedActions: Ptr("all")} + if !cmp.Equal(org, want) { + t.Errorf("Organizations.GetActionsPermissions returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetActionsPermissions(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetActionsPermissions(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateActionsPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsPermissions{EnabledRepositories: Ptr("all"), AllowedActions: Ptr("selected")} + + mux.HandleFunc("/orgs/o/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"enabled_repositories": "all", "allowed_actions": "selected"}`) + }) + + ctx := t.Context() + org, _, err := client.Organizations.UpdateActionsPermissions(ctx, "o", *input) + if err != nil { + t.Errorf("Organizations.UpdateActionsPermissions returned error: %v", err) + } + + want := &ActionsPermissions{EnabledRepositories: Ptr("all"), AllowedActions: Ptr("selected")} + if !cmp.Equal(org, want) { + t.Errorf("Organizations.UpdateActionsPermissions returned %+v, want %+v", org, want) + } + + const methodName = "UpdateActionsPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateActionsPermissions(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateActionsPermissions(ctx, "o", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_artifacts.go b/github/orgs_artifacts.go new file mode 100644 index 00000000000..b9f7b50627f --- /dev/null +++ b/github/orgs_artifacts.go @@ -0,0 +1,219 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// DeploymentRuntimeRisk represents the runtime risk of a deployment. +type DeploymentRuntimeRisk string + +const ( + DeploymentRuntimeRiskCriticalResource DeploymentRuntimeRisk = "critical-resource" + DeploymentRuntimeRiskInternetExposed DeploymentRuntimeRisk = "internet-exposed" + DeploymentRuntimeRiskLateralMovement DeploymentRuntimeRisk = "lateral-movement" + DeploymentRuntimeRiskSensitiveData DeploymentRuntimeRisk = "sensitive-data" +) + +// ArtifactDeploymentRecord represents a GitHub artifact deployment record. +type ArtifactDeploymentRecord struct { + ID *int64 `json:"id,omitempty"` + Digest *string `json:"digest,omitempty"` + LogicalEnvironment *string `json:"logical_environment,omitempty"` + PhysicalEnvironment *string `json:"physical_environment,omitempty"` + Cluster *string `json:"cluster,omitempty"` + DeploymentName *string `json:"deployment_name,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + RuntimeRisks []DeploymentRuntimeRisk `json:"runtime_risks,omitempty"` + AttestationID *int64 `json:"attestation_id,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// CreateArtifactDeploymentRequest represents the request body for creating a deployment record. +type CreateArtifactDeploymentRequest struct { + Name string `json:"name"` + Digest string `json:"digest"` + Version *string `json:"version,omitempty"` + Status string `json:"status"` + LogicalEnvironment string `json:"logical_environment"` + PhysicalEnvironment *string `json:"physical_environment,omitempty"` + Cluster *string `json:"cluster,omitempty"` + DeploymentName string `json:"deployment_name"` + Tags map[string]string `json:"tags,omitempty"` + RuntimeRisks []DeploymentRuntimeRisk `json:"runtime_risks,omitempty"` + GithubRepository *string `json:"github_repository,omitempty"` +} + +// ArtifactDeploymentResponse represents the response for deployment records. +type ArtifactDeploymentResponse struct { + TotalCount *int `json:"total_count,omitempty"` + DeploymentRecords []*ArtifactDeploymentRecord `json:"deployment_records,omitempty"` +} + +// ClusterArtifactDeployment represents a deployment within a cluster record request. +type ClusterArtifactDeployment struct { + Name string `json:"name"` + Digest string `json:"digest"` + Version *string `json:"version,omitempty"` + Status string `json:"status"` + DeploymentName string `json:"deployment_name"` + Tags map[string]string `json:"tags,omitempty"` + RuntimeRisks []DeploymentRuntimeRisk `json:"runtime_risks,omitempty"` + GithubRepository *string `json:"github_repository,omitempty"` +} + +// ClusterDeploymentRecordsRequest represents the request body for setting cluster deployment records. +type ClusterDeploymentRecordsRequest struct { + LogicalEnvironment string `json:"logical_environment"` + PhysicalEnvironment *string `json:"physical_environment,omitempty"` + Deployments []*ClusterArtifactDeployment `json:"deployments"` +} + +// ArtifactStorageRecord represents a GitHub artifact storage record. +type ArtifactStorageRecord struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Digest *string `json:"digest,omitempty"` + ArtifactURL *string `json:"artifact_url,omitempty"` + RegistryURL *string `json:"registry_url,omitempty"` + Repository *string `json:"repository,omitempty"` + Status *string `json:"status,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// CreateArtifactStorageRequest represents the request body for creating a storage record. +type CreateArtifactStorageRequest struct { + Name string `json:"name"` + Digest string `json:"digest"` + Version *string `json:"version,omitempty"` + ArtifactURL *string `json:"artifact_url,omitempty"` + Path *string `json:"path,omitempty"` + RegistryURL string `json:"registry_url"` + Repository *string `json:"repository,omitempty"` + Status *string `json:"status,omitempty"` + GithubRepository *string `json:"github_repository,omitempty"` +} + +// ArtifactStorageResponse represents the response for storage records. +type ArtifactStorageResponse struct { + TotalCount *int `json:"total_count,omitempty"` + StorageRecords []*ArtifactStorageRecord `json:"storage_records,omitempty"` +} + +// CreateArtifactDeploymentRecord creates or updates deployment records for an artifact associated with an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/artifact-metadata?apiVersion=2022-11-28#create-an-artifact-deployment-record +// +//meta:operation POST /orgs/{org}/artifacts/metadata/deployment-record +func (s *OrganizationsService) CreateArtifactDeploymentRecord(ctx context.Context, org string, body CreateArtifactDeploymentRequest) (*ArtifactDeploymentResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/artifacts/metadata/deployment-record", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var v *ArtifactDeploymentResponse + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, nil +} + +// SetClusterDeploymentRecords sets deployment records for a given cluster. +// +// GitHub API docs: https://docs.github.com/rest/orgs/artifact-metadata?apiVersion=2022-11-28#set-cluster-deployment-records +// +//meta:operation POST /orgs/{org}/artifacts/metadata/deployment-record/cluster/{cluster} +func (s *OrganizationsService) SetClusterDeploymentRecords(ctx context.Context, org, cluster string, body ClusterDeploymentRecordsRequest) (*ArtifactDeploymentResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/artifacts/metadata/deployment-record/cluster/%v", org, cluster) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var v *ArtifactDeploymentResponse + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, nil +} + +// CreateArtifactStorageRecord creates metadata storage records for artifacts. +// +// GitHub API docs: https://docs.github.com/rest/orgs/artifact-metadata?apiVersion=2022-11-28#create-artifact-metadata-storage-record +// +//meta:operation POST /orgs/{org}/artifacts/metadata/storage-record +func (s *OrganizationsService) CreateArtifactStorageRecord(ctx context.Context, org string, body CreateArtifactStorageRequest) (*ArtifactStorageResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/artifacts/metadata/storage-record", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var v *ArtifactStorageResponse + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, nil +} + +// ListArtifactDeploymentRecords lists deployment records for an artifact metadata. +// +// subjectDigest is SHA256 digest of the artifact, in the form sha256:HEX_DIGEST. +// +// GitHub API docs: https://docs.github.com/rest/orgs/artifact-metadata?apiVersion=2022-11-28#list-artifact-deployment-records +// +//meta:operation GET /orgs/{org}/artifacts/{subject_digest}/metadata/deployment-records +func (s *OrganizationsService) ListArtifactDeploymentRecords(ctx context.Context, org, subjectDigest string) (*ArtifactDeploymentResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/artifacts/%v/metadata/deployment-records", org, subjectDigest) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var v *ArtifactDeploymentResponse + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, nil +} + +// ListArtifactStorageRecords lists artifact storage records with a given subject digest. +// +// subjectDigest is SHA256 digest of the artifact, in the form sha256:HEX_DIGEST. +// +// GitHub API docs: https://docs.github.com/rest/orgs/artifact-metadata?apiVersion=2022-11-28#list-artifact-storage-records +// +//meta:operation GET /orgs/{org}/artifacts/{subject_digest}/metadata/storage-records +func (s *OrganizationsService) ListArtifactStorageRecords(ctx context.Context, org, subjectDigest string) (*ArtifactStorageResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/artifacts/%v/metadata/storage-records", org, subjectDigest) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var v *ArtifactStorageResponse + resp, err := s.client.Do(req, &v) + if err != nil { + return nil, resp, err + } + + return v, resp, nil +} diff --git a/github/orgs_artifacts_test.go b/github/orgs_artifacts_test.go new file mode 100644 index 00000000000..8ebfbc3df08 --- /dev/null +++ b/github/orgs_artifacts_test.go @@ -0,0 +1,251 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_CreateArtifactDeploymentRecord(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateArtifactDeploymentRequest{ + Name: "test-n", + Digest: "sha256:123", + Version: Ptr("v1.0.0"), + Status: "deployed", + LogicalEnvironment: "prod", + DeploymentName: "dep-1", + RuntimeRisks: []DeploymentRuntimeRisk{DeploymentRuntimeRiskCriticalResource, DeploymentRuntimeRiskInternetExposed}, + GithubRepository: Ptr("octo-org/octo-repo"), + Tags: map[string]string{ + "data-access": "sensitive", + }, + } + + mux.HandleFunc("/orgs/o/artifacts/metadata/deployment-record", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"total_count":1,"deployment_records":[{"id":1}]}`) + }) + + ctx := t.Context() + got, _, err := client.Organizations.CreateArtifactDeploymentRecord(ctx, "o", input) + if err != nil { + t.Errorf("CreateArtifactDeploymentRecord returned error: %v", err) + } + + want := &ArtifactDeploymentResponse{ + TotalCount: Ptr(1), + DeploymentRecords: []*ArtifactDeploymentRecord{{ID: Ptr(int64(1))}}, + } + + if !cmp.Equal(got, want) { + t.Errorf("CreateArtifactDeploymentRecord returned %+v, want %+v", got, want) + } + + const methodName = "CreateArtifactDeploymentRecord" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateArtifactDeploymentRecord(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateArtifactDeploymentRecord(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_SetClusterDeploymentRecords(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ClusterDeploymentRecordsRequest{ + LogicalEnvironment: "prod", + PhysicalEnvironment: Ptr("pacific-east"), + Deployments: []*ClusterArtifactDeployment{ + { + Name: "awesome-image", + Digest: "sha256:abc", + DeploymentName: "dep-1", + Version: Ptr("v2.0"), + Status: "deployed", + }, + }, + } + + mux.HandleFunc("/orgs/o/artifacts/metadata/deployment-record/cluster/c1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"total_count":1,"deployment_records":[{"id":2}]}`) + }) + + ctx := t.Context() + got, _, err := client.Organizations.SetClusterDeploymentRecords(ctx, "o", "c1", input) + if err != nil { + t.Errorf("SetClusterDeploymentRecords returned error: %v", err) + } + + want := &ArtifactDeploymentResponse{ + TotalCount: Ptr(1), + DeploymentRecords: []*ArtifactDeploymentRecord{{ID: Ptr(int64(2))}}, + } + if !cmp.Equal(got, want) { + t.Errorf("SetClusterDeploymentRecords returned %+v, want %+v", got, want) + } + + const methodName = "SetClusterDeploymentRecords" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.SetClusterDeploymentRecords(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.SetClusterDeploymentRecords(ctx, "o", "c1", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateArtifactStorageRecord(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateArtifactStorageRequest{ + Name: "libfoo", + Digest: "sha256:123", + Version: Ptr("v1.2.3"), + Path: Ptr("target/libs"), + GithubRepository: Ptr("org/repo"), + RegistryURL: "https://reg.example.com", + Status: Ptr("active"), + } + + mux.HandleFunc("/orgs/o/artifacts/metadata/storage-record", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"total_count":1,"storage_records":[{"name":"libfoo"}]}`) + }) + + ctx := t.Context() + got, _, err := client.Organizations.CreateArtifactStorageRecord(ctx, "o", input) + if err != nil { + t.Errorf("CreateArtifactStorageRecord returned error: %v", err) + } + + want := &ArtifactStorageResponse{ + TotalCount: Ptr(1), + StorageRecords: []*ArtifactStorageRecord{{Name: Ptr("libfoo")}}, + } + + if !cmp.Equal(got, want) { + t.Errorf("CreateArtifactStorageRecord returned %+v, want %+v", got, want) + } + + const methodName = "CreateArtifactStorageRecord" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateArtifactStorageRecord(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateArtifactStorageRecord(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListArtifactDeploymentRecords(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/artifacts/sha256:abc/metadata/deployment-records", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{}) + fmt.Fprint(w, `{"total_count":1,"deployment_records":[{"id":1, "runtime_risks": ["sensitive-data"]}]}`) + }) + + ctx := t.Context() + got, _, err := client.Organizations.ListArtifactDeploymentRecords(ctx, "o", "sha256:abc") + if err != nil { + t.Errorf("ListArtifactDeploymentRecords returned error: %v", err) + } + + want := &ArtifactDeploymentResponse{ + TotalCount: Ptr(1), + DeploymentRecords: []*ArtifactDeploymentRecord{ + {ID: Ptr(int64(1)), RuntimeRisks: []DeploymentRuntimeRisk{DeploymentRuntimeRiskSensitiveData}}, + }, + } + if !cmp.Equal(got, want) { + t.Errorf("ListArtifactDeploymentRecords returned %+v, want %+v", got, want) + } + + const methodName = "ListArtifactDeploymentRecords" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListArtifactDeploymentRecords(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListArtifactDeploymentRecords(ctx, "o", "sha256:abc") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListArtifactStorageRecords(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/artifacts/sha256:abc/metadata/storage-records", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{}) + fmt.Fprint(w, `{"total_count":1,"storage_records":[{"name":"libfoo"}]}`) + }) + + ctx := t.Context() + got, _, err := client.Organizations.ListArtifactStorageRecords(ctx, "o", "sha256:abc") + if err != nil { + t.Errorf("ListArtifactStorageRecords returned error: %v", err) + } + + want := &ArtifactStorageResponse{ + TotalCount: Ptr(1), + StorageRecords: []*ArtifactStorageRecord{{Name: Ptr("libfoo")}}, + } + if !cmp.Equal(got, want) { + t.Errorf("ListArtifactStorageRecords returned %+v, want %+v", got, want) + } + + const methodName = "ListArtifactStorageRecords" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListArtifactStorageRecords(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListArtifactStorageRecords(ctx, "o", "sha256:abc") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_attestations.go b/github/orgs_attestations.go new file mode 100644 index 00000000000..bfc4f61c755 --- /dev/null +++ b/github/orgs_attestations.go @@ -0,0 +1,40 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListAttestations returns a collection of artifact attestations +// with a given subject digest that are associated with repositories +// owned by an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/attestations?apiVersion=2022-11-28#list-attestations +// +//meta:operation GET /orgs/{org}/attestations/{subject_digest} +func (s *OrganizationsService) ListAttestations(ctx context.Context, org, subjectDigest string, opts *ListOptions) (*AttestationsResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/attestations/%v", org, subjectDigest) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var attestations *AttestationsResponse + resp, err := s.client.Do(req, &attestations) + if err != nil { + return nil, resp, err + } + + return attestations, resp, nil +} diff --git a/github/orgs_attestations_test.go b/github/orgs_attestations_test.go new file mode 100644 index 00000000000..a0ed59c9f0e --- /dev/null +++ b/github/orgs_attestations_test.go @@ -0,0 +1,71 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListAttestations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/attestations/digest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "attestations": [ + { + "repository_id": 1, + "bundle": {} + }, + { + "repository_id": 2, + "bundle": {} + } + ] + }`) + }) + ctx := t.Context() + attestations, _, err := client.Organizations.ListAttestations(ctx, "o", "digest", &ListOptions{}) + if err != nil { + t.Errorf("Organizations.ListAttestations returned error: %v", err) + } + + want := &AttestationsResponse{ + Attestations: []*Attestation{ + { + RepositoryID: 1, + Bundle: []byte(`{}`), + }, + { + RepositoryID: 2, + Bundle: []byte(`{}`), + }, + }, + } + + if !cmp.Equal(attestations, want) { + t.Errorf("Organizations.ListAttestations = %+v, want %+v", attestations, want) + } + const methodName = "ListAttestations" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListAttestations(ctx, "\n", "\n", &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListAttestations(ctx, "o", "digest", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_audit_log.go b/github/orgs_audit_log.go new file mode 100644 index 00000000000..65e65e893b0 --- /dev/null +++ b/github/orgs_audit_log.go @@ -0,0 +1,206 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "strings" +) + +// GetAuditLogOptions sets up optional parameters to query audit-log endpoint. +type GetAuditLogOptions struct { + Phrase *string `url:"phrase,omitempty"` // A search phrase. (Optional.) + Include *string `url:"include,omitempty"` // Event type includes. Can be one of "web", "git", "all". Default: "web". (Optional.) + Order *string `url:"order,omitempty"` // The order of audit log events. Can be one of "asc" or "desc". Default: "desc". (Optional.) + + ListCursorOptions +} + +// ActorLocation contains information about reported location for an actor. +type ActorLocation struct { + CountryCode *string `json:"country_code,omitempty"` +} + +// AuditEntry describes the fields that may be represented by various audit-log "action" entries. +// There are many other fields that may be present depending on the action. You can access those +// in AdditionalFields. +// For a list of actions see - https://docs.github.com/github/setting-up-and-managing-organizations-and-teams/reviewing-the-audit-log-for-your-organization#audit-log-actions +type AuditEntry struct { + Action *string `json:"action,omitempty"` // The name of the action that was performed, for example `user.login` or `repo.create`. + Actor *string `json:"actor,omitempty"` // The actor who performed the action. + ActorID *int64 `json:"actor_id,omitempty"` + ActorLocation *ActorLocation `json:"actor_location,omitempty"` + Business *string `json:"business,omitempty"` + BusinessID *int64 `json:"business_id,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + DocumentID *string `json:"_document_id,omitempty"` + ExternalIdentityNameID *string `json:"external_identity_nameid,omitempty"` + ExternalIdentityUsername *string `json:"external_identity_username,omitempty"` + HashedToken *string `json:"hashed_token,omitempty"` + Org *string `json:"org,omitempty"` + OrgID *int64 `json:"org_id,omitempty"` + Timestamp *Timestamp `json:"@timestamp,omitempty"` // The time the audit log event occurred, given as a [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time). + TokenID *int64 `json:"token_id,omitempty"` + TokenScopes *string `json:"token_scopes,omitempty"` + User *string `json:"user,omitempty"` // The user that was affected by the action performed (if available). + UserID *int64 `json:"user_id,omitempty"` + + // Some events types have a data field that contains additional information about the event. + Data map[string]any `json:"data,omitempty"` + + // All fields that are not explicitly defined in the struct are captured here. + AdditionalFields map[string]any `json:"-"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// +// GitHub's audit-log API occasionally returns "org" as a JSON array of strings +// and "org_id" as a JSON array of integers instead of the documented scalar types. +// This implementation normalises both fields to their scalar forms +// (joining multiple org names with a comma, and using the first org_id) so +// callers always receive a consistent type regardless of the API response shape. +func (a *AuditEntry) UnmarshalJSON(data []byte) error { + type entryAlias AuditEntry + var raw struct { + entryAlias + Org json.RawMessage `json:"org,omitempty"` + OrgID json.RawMessage `json:"org_id,omitempty"` + } + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + v := raw.entryAlias + + if len(raw.Org) > 0 && string(raw.Org) != "null" { + var err error + v.Org, err = unmarshalStringOrArray(raw.Org) + if err != nil { + return fmt.Errorf("unmarshal org: %w", err) + } + } + + if len(raw.OrgID) > 0 && string(raw.OrgID) != "null" { + var err error + v.OrgID, err = unmarshalIntOrArray(raw.OrgID) + if err != nil { + return fmt.Errorf("unmarshal org_id: %w", err) + } + } + + rawDefinedFields, err := json.Marshal(v) + if err != nil { + return err + } + definedFields := map[string]any{} + if err := json.Unmarshal(rawDefinedFields, &definedFields); err != nil { + return err + } + + if err := json.Unmarshal(data, &v.AdditionalFields); err != nil { + return err + } + + for key, val := range v.AdditionalFields { + if _, ok := definedFields[key]; ok || val == nil { + delete(v.AdditionalFields, key) + } + } + + *a = AuditEntry(v) + if len(v.AdditionalFields) == 0 { + a.AdditionalFields = nil + } + return nil +} + +// unmarshalStringOrArray decodes a JSON value that is either a plain string or an array of strings. +// Arrays are joined with ", ". +func unmarshalStringOrArray(raw json.RawMessage) (*string, error) { + var s string + if err := json.Unmarshal(raw, &s); err == nil { + return &s, nil + } + var ss []string + if err := json.Unmarshal(raw, &ss); err != nil { + return nil, err + } + if len(ss) == 0 { + return nil, nil + } + return Ptr(strings.Join(ss, ", ")), nil +} + +// unmarshalIntOrArray decodes a JSON value that is either a plain integer or an array of integers. +// Arrays use the first element. +func unmarshalIntOrArray(raw json.RawMessage) (*int64, error) { + var n int64 + if err := json.Unmarshal(raw, &n); err == nil { + return &n, nil + } + var ns []int64 + if err := json.Unmarshal(raw, &ns); err != nil { + return nil, err + } + if len(ns) == 0 { + return nil, nil + } + return &ns[0], nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (a AuditEntry) MarshalJSON() ([]byte, error) { + type entryAlias AuditEntry + v := entryAlias(a) + defBytes, err := json.Marshal(v) + if err != nil { + return nil, err + } + if len(a.AdditionalFields) == 0 { + return defBytes, err + } + resMap := map[string]any{} + if err := json.Unmarshal(defBytes, &resMap); err != nil { + return nil, err + } + for key, val := range a.AdditionalFields { + if val == nil { + continue + } + if _, ok := resMap[key]; ok { + return nil, fmt.Errorf("unexpected field in AdditionalFields: %v", key) + } + resMap[key] = val + } + return json.Marshal(resMap) +} + +// GetAuditLog gets the audit-log entries for an organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#get-the-audit-log-for-an-organization +// +//meta:operation GET /orgs/{org}/audit-log +func (s *OrganizationsService) GetAuditLog(ctx context.Context, org string, opts *GetAuditLogOptions) ([]*AuditEntry, *Response, error) { + u := fmt.Sprintf("orgs/%v/audit-log", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var auditEntries []*AuditEntry + resp, err := s.client.Do(req, &auditEntries) + if err != nil { + return nil, resp, err + } + + return auditEntries, resp, nil +} diff --git a/github/orgs_audit_log_test.go b/github/orgs_audit_log_test.go new file mode 100644 index 00000000000..67833e90ccd --- /dev/null +++ b/github/orgs_audit_log_test.go @@ -0,0 +1,464 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + "time" +) + +func TestOrganizationService_GetAuditLog(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/audit-log", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"include": "all", "phrase": "action:workflows", "order": "asc"}) + + fmt.Fprint(w, `[ + { + "@timestamp": 1615077308538, + "_document_id": "beeZYapIUe-wKg5-beadb33", + "action": "workflows.completed_workflow_run", + "active": true, + "actor": "testactor", + "actor_ip": "10.0.0.1", + "actor_location": { + "country_code": "US" + }, + "cancelled_at": "2021-03-07T00:35:08.000Z", + "completed_at": "2021-03-07T00:35:08.000Z", + "conclusion": "success", + "config": { + "content_type": "json", + "insecure_ssl": "0", + "url": "https://example.com/deadbeef-new-hook" + }, + "created_at": 1615077308538, + "event": "schedule", + "events": ["code_scanning_alert"], + "hashed_token": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "head_branch": "master", + "head_sha": "5acdeadbeef64d1a62388e901e5cdc9358644b37", + "job_workflow_ref": "testorg/testrepo/.github/workflows/testjob.yml@refs/pull/1/merge", + "name": "Code scanning - action", + "oauth_application_id": 1, + "old_permission": "read", + "org": "o", + "org_id": 1, + "overridden_codes": [ + "review_policy_not_satisfied" + ], + "permission": "admin", + "pull_request_id": 1, + "pull_request_title": "a pr title", + "pull_request_url": "https://github.com/testorg/testrepo/pull/1", + "reasons": [ + { + "code": "a code", + "message": "a message" + } + ], + "programmatic_access_type": "GitHub App server-to-server token", + "referrer": "a referrer", + "repo": "o/blue-crayon-1", + "run_attempt": 1, + "run_number": 1, + "started_at": "2021-03-07T00:33:04.000Z", + "token_id": 1, + "token_scopes": "gist,repo:read", + "topic": "cp1-iad.ingest.github.actions.v0.WorkflowUpdate", + "trigger_id": null, + "user_agent": "a user agent", + "workflow_id": 123456, + "workflow_run_id": 628312345 + }]`) + }) + ctx := t.Context() + getOpts := GetAuditLogOptions{ + Include: Ptr("all"), + Phrase: Ptr("action:workflows"), + Order: Ptr("asc"), + } + + auditEntries, _, err := client.Organizations.GetAuditLog(ctx, "o", &getOpts) + if err != nil { + t.Errorf("Organizations.GetAuditLog returned error: %v", err) + } + timestamp := time.Unix(0, 1615077308538*1e6) + + want := []*AuditEntry{ + { + Timestamp: &Timestamp{timestamp}, + DocumentID: Ptr("beeZYapIUe-wKg5-beadb33"), + Action: Ptr("workflows.completed_workflow_run"), + Actor: Ptr("testactor"), + ActorLocation: &ActorLocation{ + CountryCode: Ptr("US"), + }, + CreatedAt: &Timestamp{timestamp}, + HashedToken: Ptr("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="), + Org: Ptr("o"), + OrgID: Ptr(int64(1)), + TokenID: Ptr(int64(1)), + TokenScopes: Ptr("gist,repo:read"), + AdditionalFields: map[string]any{ + "actor_ip": "10.0.0.1", + "active": true, + "cancelled_at": "2021-03-07T00:35:08.000Z", + "completed_at": "2021-03-07T00:35:08.000Z", + "conclusion": "success", + "event": "schedule", + "head_branch": "master", + "head_sha": "5acdeadbeef64d1a62388e901e5cdc9358644b37", + "job_workflow_ref": "testorg/testrepo/.github/workflows/testjob.yml@refs/pull/1/merge", + "name": "Code scanning - action", + "oauth_application_id": float64(1), + "old_permission": "read", + "overridden_codes": []any{"review_policy_not_satisfied"}, + "permission": "admin", + "programmatic_access_type": "GitHub App server-to-server token", + "pull_request_id": float64(1), + "pull_request_title": "a pr title", + "pull_request_url": "https://github.com/testorg/testrepo/pull/1", + "reasons": []any{map[string]any{ + "code": "a code", + "message": "a message", + }}, + "referrer": "a referrer", + "repo": "o/blue-crayon-1", + "run_attempt": float64(1), + "run_number": float64(1), + "started_at": "2021-03-07T00:33:04.000Z", + "topic": "cp1-iad.ingest.github.actions.v0.WorkflowUpdate", + "user_agent": "a user agent", + "workflow_id": float64(123456), + "workflow_run_id": float64(628312345), + "events": []any{"code_scanning_alert"}, + "config": map[string]any{ + "content_type": "json", + "insecure_ssl": "0", + "url": "https://example.com/deadbeef-new-hook", + }, + }, + }, + } + + assertNoDiff(t, want, auditEntries) + + const methodName = "GetAuditLog" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetAuditLog(ctx, "\n", &getOpts) + return err + }) + + testNewRequestAndDoFailureCategory(t, methodName, client, AuditLogCategory, func() (*Response, error) { + got, resp, err := client.Organizations.GetAuditLog(ctx, "o", &GetAuditLogOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestAuditEntry_MarshalJSON(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &AuditEntry{}, "{}") + + u := AuditEntry{ + Action: Ptr("a"), + Actor: Ptr("ac"), + ActorLocation: &ActorLocation{CountryCode: Ptr("alcc")}, + Business: Ptr("b"), + CreatedAt: &Timestamp{referenceTime}, + DocumentID: Ptr("did"), + ExternalIdentityNameID: Ptr("ein"), + ExternalIdentityUsername: Ptr("eiu"), + HashedToken: Ptr("ht"), + Org: Ptr("o"), + OrgID: Ptr(int64(1)), + Timestamp: &Timestamp{referenceTime}, + TokenID: Ptr(int64(1)), + TokenScopes: Ptr("ts"), + User: Ptr("u"), + Data: map[string]any{ + "old_name": "on", + "old_login": "ol", + }, + AdditionalFields: map[string]any{ + "active": false, + "active_was": false, + "actor_ip": "aip", + "blocked_user": "bu", + "cancelled_at": "2021-03-07T00:35:08.000Z", + "completed_at": "2021-03-07T00:35:08.000Z", + "conclusion": "c", + "config": map[string]any{ + "url": "s", + }, + "config_was": map[string]any{ + "url": "s", + }, + "content_type": "ct", + "deploy_key_fingerprint": "dkf", + "emoji": "e", + "environment_name": "en", + "event": "e", + "events": []any{"s"}, + "events_were": []any{"s"}, + "explanation": "e", + "fingerprint": "f", + "head_branch": "hb", + "head_sha": "hsha", + "hook_id": float64(1), + "is_hosted_runner": false, + "job_name": "jn", + "limited_availability": false, + "message": "m", + "name": "n", + "old_permission": "op", + "old_user": "ou", + "openssh_public_key": "osshpk", + "permission": "p", + "previous_visibility": "pv", + "programmatic_access_type": "pat", + "pull_request_id": float64(1), + "pull_request_title": "prt", + "pull_request_url": "pru", + "read_only": "ro", + "reasons": []any{ + map[string]any{ + "code": "c", + "message": "m", + }, + }, + "referrer": "a referrer", + "repo": "r", + "repository": "repo", + "repository_public": false, + "run_attempt": 1, + "runner_group_id": 1, + "runner_group_name": "rgn", + "runner_id": 1, + "runner_labels": []any{"s"}, + "runner_name": "rn", + "secrets_passed": []any{"s"}, + "source_version": "sv", + "started_at": "2006-01-02T15:04:05Z", + "target_login": "tl", + "target_version": "tv", + "team": "t", + "topic": "cp1-iad.ingest.github.actions.v0.WorkflowUpdate", + "transport_protocol": 1, + "transport_protocol_name": "tpn", + "trigger_id": 1, + "user_agent": "ua", + "visibility": "v", + "workflow_id": 1, + "workflow_run_id": 1, + }, + } + + want := `{ + "action": "a", + "active": false, + "active_was": false, + "actor": "ac", + "actor_ip": "aip", + "actor_location": { + "country_code": "alcc" + }, + "blocked_user": "bu", + "business": "b", + "cancelled_at": "2021-03-07T00:35:08.000Z", + "completed_at": "2021-03-07T00:35:08.000Z", + "conclusion": "c", + "config": { + "url": "s" + }, + "config_was": { + "url": "s" + }, + "content_type": "ct", + "created_at": ` + referenceTimeStr + `, + "deploy_key_fingerprint": "dkf", + "_document_id": "did", + "emoji": "e", + "environment_name": "en", + "event": "e", + "events": [ + "s" + ], + "events_were": [ + "s" + ], + "explanation": "e", + "external_identity_nameid": "ein", + "external_identity_username": "eiu", + "fingerprint": "f", + "hashed_token": "ht", + "head_branch": "hb", + "head_sha": "hsha", + "hook_id": 1, + "is_hosted_runner": false, + "job_name": "jn", + "limited_availability": false, + "message": "m", + "name": "n", + "old_permission": "op", + "old_user": "ou", + "openssh_public_key": "osshpk", + "org": "o", + "org_id": 1, + "permission": "p", + "previous_visibility": "pv", + "programmatic_access_type": "pat", + "pull_request_id": 1, + "pull_request_title": "prt", + "pull_request_url": "pru", + "reasons": [{ + "code": "c", + "message": "m" + }], + "referrer": "a referrer", + "read_only": "ro", + "repo": "r", + "repository": "repo", + "repository_public": false, + "run_attempt": 1, + "runner_group_id": 1, + "runner_group_name": "rgn", + "runner_id": 1, + "runner_labels": [ + "s" + ], + "runner_name": "rn", + "secrets_passed": [ + "s" + ], + "source_version": "sv", + "started_at": ` + referenceTimeStr + `, + "target_login": "tl", + "target_version": "tv", + "team": "t", + "@timestamp": ` + referenceTimeStr + `, + "token_id": 1, + "token_scopes": "ts", + "topic": "cp1-iad.ingest.github.actions.v0.WorkflowUpdate", + "transport_protocol_name": "tpn", + "transport_protocol": 1, + "trigger_id": 1, + "user": "u", + "user_agent": "ua", + "visibility": "v", + "workflow_id": 1, + "workflow_run_id": 1, + "data": { + "old_name": "on", + "old_login": "ol" + } + }` + + testJSONMarshalOnly(t, u, want) + testJSONMarshalOnly(t, &u, want) +} + +func TestAuditEntry_UnmarshalJSON(t *testing.T) { + t.Parallel() + testJSONUnmarshalOnly(t, &AuditEntry{}, "{}") + + tests := []struct { + name string + payload string + want *AuditEntry + }{ + { + name: "org is scalar", + payload: `{"action":"test","org":"myorg","org_id":42}`, + want: &AuditEntry{Action: Ptr("test"), Org: Ptr("myorg"), OrgID: Ptr(int64(42))}, + }, + { + name: "org is single element array", + payload: `{"action":"test","org":["myorg"],"org_id":[42]}`, + want: &AuditEntry{Action: Ptr("test"), Org: Ptr("myorg"), OrgID: Ptr(int64(42))}, + }, + { + name: "org is multi-element array", + payload: `{"action":"test","org":["org1","org2","org3"],"org_id":[1,2,3]}`, + want: &AuditEntry{Action: Ptr("test"), Org: Ptr("org1, org2, org3"), OrgID: Ptr(int64(1))}, + }, + { + name: "org_id is scalar", + payload: `{"action":"test","org_id":42}`, + want: &AuditEntry{Action: Ptr("test"), OrgID: Ptr(int64(42))}, + }, + { + name: "org_id is array", + payload: `{"action":"test","org_id":[42,43,44]}`, + want: &AuditEntry{Action: Ptr("test"), OrgID: Ptr(int64(42))}, // first element + }, + { + name: "org is empty array", + payload: `{"action":"test","org":[]}`, + want: &AuditEntry{Action: Ptr("test"), AdditionalFields: map[string]any{"org": []any{}}}, + }, + { + name: "org_id is empty array", + payload: `{"action":"test","org_id":[]}`, + want: &AuditEntry{Action: Ptr("test"), AdditionalFields: map[string]any{"org_id": []any{}}}, + }, + { + name: "org and org_id are null", + payload: `{"action":"test","org":null,"org_id":null}`, + want: &AuditEntry{Action: Ptr("test")}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + testJSONUnmarshalOnly(t, tc.want, tc.payload) + }) + } +} + +func TestAuditEntry_UnmarshalJSON_Errors(t *testing.T) { + t.Parallel() + tests := []struct { + name string + payload string + }{ + { + name: "malformed json", + payload: `{`, + }, + { + name: "invalid defined field type", + payload: `{"actor_id":"not-an-int"}`, + }, + { + name: "invalid org type", + payload: `{"action":"test","org":{"key":"value"}}`, + }, + { + name: "invalid org_id type", + payload: `{"action":"test","org_id":{"key":"value"}}`, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + var entry AuditEntry + if err := json.Unmarshal([]byte(tc.payload), &entry); err == nil { + t.Errorf("AuditEntry.UnmarshalJSON(%q) = nil, but want error", tc.payload) + } + }) + } +} diff --git a/github/orgs_codesecurity_configurations.go b/github/orgs_codesecurity_configurations.go new file mode 100644 index 00000000000..dafdf1ace01 --- /dev/null +++ b/github/orgs_codesecurity_configurations.go @@ -0,0 +1,382 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" +) + +// DependencyGraphAutosubmitActionOptions represents the options for the DependencyGraphAutosubmitAction. +type DependencyGraphAutosubmitActionOptions struct { + LabeledRunners *bool `json:"labeled_runners,omitempty"` +} + +// CodeScanningOptions represents the options for the Security Configuration code scanning feature. +type CodeScanningOptions struct { + AllowAdvanced *bool `json:"allow_advanced,omitempty"` +} + +// CodeScanningDefaultSetupOptions represents the feature options for the code scanning default options. +type CodeScanningDefaultSetupOptions struct { + RunnerType string `json:"runner_type"` + RunnerLabel *string `json:"runner_label,omitempty"` +} + +// RepositoryAttachment represents a repository attachment to a code security configuration. +type RepositoryAttachment struct { + Status *string `json:"status"` + Repository *Repository `json:"repository"` +} + +// SecretScanningDelegatedBypassOptions represents the feature options for the secret scanning delegated bypass. +type SecretScanningDelegatedBypassOptions struct { + Reviewers []*BypassReviewer `json:"reviewers,omitzero"` +} + +// BypassReviewer represents the bypass reviewers for the delegated bypass of a code security configuration. +// SecurityConfigurationID is added by GitHub in responses. +type BypassReviewer struct { + ReviewerID int64 `json:"reviewer_id"` + ReviewerType string `json:"reviewer_type"` + SecurityConfigurationID *int64 `json:"security_configuration_id,omitempty"` +} + +// CodeSecurityConfiguration represents a code security configuration. +type CodeSecurityConfiguration struct { + ID *int64 `json:"id,omitempty"` + TargetType *string `json:"target_type,omitempty"` + Name string `json:"name"` + Description string `json:"description"` + AdvancedSecurity *string `json:"advanced_security,omitempty"` + DependencyGraph *string `json:"dependency_graph,omitempty"` + DependencyGraphAutosubmitAction *string `json:"dependency_graph_autosubmit_action,omitempty"` + DependencyGraphAutosubmitActionOptions *DependencyGraphAutosubmitActionOptions `json:"dependency_graph_autosubmit_action_options,omitempty"` + DependabotAlerts *string `json:"dependabot_alerts,omitempty"` + DependabotDelegatedAlertDismissal *string `json:"dependabot_delegated_alert_dismissal,omitempty"` + DependabotSecurityUpdates *string `json:"dependabot_security_updates,omitempty"` + CodeScanningDefaultSetup *string `json:"code_scanning_default_setup,omitempty"` + CodeScanningDefaultSetupOptions *CodeScanningDefaultSetupOptions `json:"code_scanning_default_setup_options,omitempty"` + CodeScanningDelegatedAlertDismissal *string `json:"code_scanning_delegated_alert_dismissal,omitempty"` + CodeScanningOptions *CodeScanningOptions `json:"code_scanning_options,omitempty"` + CodeSecurity *string `json:"code_security,omitempty"` + SecretScanning *string `json:"secret_scanning,omitempty"` + SecretScanningPushProtection *string `json:"secret_scanning_push_protection,omitempty"` + SecretScanningDelegatedBypass *string `json:"secret_scanning_delegated_bypass,omitempty"` + SecretScanningDelegatedBypassOptions *SecretScanningDelegatedBypassOptions `json:"secret_scanning_delegated_bypass_options,omitempty"` + SecretScanningValidityChecks *string `json:"secret_scanning_validity_checks,omitempty"` + SecretScanningNonProviderPatterns *string `json:"secret_scanning_non_provider_patterns,omitempty"` + SecretScanningGenericSecrets *string `json:"secret_scanning_generic_secrets,omitempty"` + SecretScanningDelegatedAlertDismissal *string `json:"secret_scanning_delegated_alert_dismissal,omitempty"` + SecretScanningExtendedMetadata *string `json:"secret_scanning_extended_metadata,omitempty"` + SecretProtection *string `json:"secret_protection,omitempty"` + PrivateVulnerabilityReporting *string `json:"private_vulnerability_reporting,omitempty"` + Enforcement *string `json:"enforcement,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// CodeSecurityConfigurationWithDefaultForNewRepos represents a code security configuration with default for new repos param. +type CodeSecurityConfigurationWithDefaultForNewRepos struct { + Configuration *CodeSecurityConfiguration `json:"configuration"` + DefaultForNewRepos *string `json:"default_for_new_repos,omitempty"` +} + +// RepositoryCodeSecurityConfiguration represents a code security configuration for a repository. +type RepositoryCodeSecurityConfiguration struct { + State *string `json:"state,omitempty"` + Configuration *CodeSecurityConfiguration `json:"configuration,omitempty"` +} + +// ListOrgCodeSecurityConfigurationOptions specifies optional parameters to get security configurations for orgs. +// +// Note: Pagination is powered by before/after cursor-style pagination. After the initial call, +// inspect the returned *Response. Use resp.After as the opts.After value to request +// the next page, and resp.Before as the opts.Before value to request the previous +// page. Set either Before or After for a request; if both are +// supplied GitHub API will return an error. PerPage controls the number of items +// per page (max 100 per GitHub API docs). +type ListOrgCodeSecurityConfigurationOptions struct { + // A cursor, as given in the Link header. If specified, the query only searches for security configurations before this cursor. + Before string `url:"before,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for security configurations after this cursor. + After string `url:"after,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty"` + + // The target type of the code security configurations to get. + // + // `target_type` defaults to all, can be one of `global`, `all` + TargetType string `url:"target_type,omitempty"` +} + +// ListCodeSecurityConfigurationRepositoriesOptions specifies optional parameters to list repositories for security configurations for orgs and enterprises. +// +// Note: Pagination is powered by before/after cursor-style pagination. After the initial call, +// inspect the returned *Response. Use resp.After as the opts.After value to request +// the next page, and resp.Before as the opts.Before value to request the previous +// page. Set either Before or After for a request; if both are +// supplied GitHub API will return an error. PerPage controls the number of items +// per page (max 100 per GitHub API docs). +type ListCodeSecurityConfigurationRepositoriesOptions struct { + // A cursor, as given in the Link header. If specified, the query only searches for repositories before this cursor. + Before string `url:"before,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for repositories after this cursor. + After string `url:"after,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty"` + + // A comma-separated list of statuses. If specified, only repositories with these attachment statuses will be returned. + // + // `status` defaults to all, can be one of `all`, `attached`, `attaching`, `removed`, `enforced`, `failed`, `updating`, `removed_by_enterprise` and also `detached` but only for the org endpoint. + Status string `url:"status,omitempty"` +} + +// ListCodeSecurityConfigurations gets code security configurations for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-code-security-configurations-for-an-organization +// +//meta:operation GET /orgs/{org}/code-security/configurations +func (s *OrganizationsService) ListCodeSecurityConfigurations(ctx context.Context, org string, opts *ListOrgCodeSecurityConfigurationOptions) ([]*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configurations []*CodeSecurityConfiguration + resp, err := s.client.Do(req, &configurations) + if err != nil { + return nil, resp, err + } + return configurations, resp, nil +} + +// CreateCodeSecurityConfiguration creates a code security configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#create-a-code-security-configuration +// +//meta:operation POST /orgs/{org}/code-security/configurations +func (s *OrganizationsService) CreateCodeSecurityConfiguration(ctx context.Context, org string, body CodeSecurityConfiguration) (*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var configuration *CodeSecurityConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// ListDefaultCodeSecurityConfigurations gets default code security configurations for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-default-code-security-configurations +// +//meta:operation GET /orgs/{org}/code-security/configurations/defaults +func (s *OrganizationsService) ListDefaultCodeSecurityConfigurations(ctx context.Context, org string) ([]*CodeSecurityConfigurationWithDefaultForNewRepos, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/defaults", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configurations []*CodeSecurityConfigurationWithDefaultForNewRepos + resp, err := s.client.Do(req, &configurations) + if err != nil { + return nil, resp, err + } + return configurations, resp, nil +} + +// DetachCodeSecurityConfigurationsFromRepositories detaches code security configuration from an organization's repositories. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#detach-configurations-from-repositories +// +//meta:operation DELETE /orgs/{org}/code-security/configurations/detach +func (s *OrganizationsService) DetachCodeSecurityConfigurationsFromRepositories(ctx context.Context, org string, repoIDs []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/detach", org) + type selectedRepoIDs struct { + SelectedIDs []int64 `json:"selected_repository_ids"` + } + req, err := s.client.NewRequest(ctx, "DELETE", u, selectedRepoIDs{SelectedIDs: repoIDs}) + if err != nil { + return nil, err + } + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// GetCodeSecurityConfiguration gets a code security configuration available in an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-a-code-security-configuration +// +//meta:operation GET /orgs/{org}/code-security/configurations/{configuration_id} +func (s *OrganizationsService) GetCodeSecurityConfiguration(ctx context.Context, org string, configurationID int64) (*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/%v", org, configurationID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configuration *CodeSecurityConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// UpdateCodeSecurityConfiguration updates a code security configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#update-a-code-security-configuration +// +//meta:operation PATCH /orgs/{org}/code-security/configurations/{configuration_id} +func (s *OrganizationsService) UpdateCodeSecurityConfiguration(ctx context.Context, org string, configurationID int64, body CodeSecurityConfiguration) (*CodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/%v", org, configurationID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var configuration *CodeSecurityConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// DeleteCodeSecurityConfiguration deletes a code security configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#delete-a-code-security-configuration +// +//meta:operation DELETE /orgs/{org}/code-security/configurations/{configuration_id} +func (s *OrganizationsService) DeleteCodeSecurityConfiguration(ctx context.Context, org string, configurationID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/%v", org, configurationID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// AttachCodeSecurityConfigurationToRepositories attaches code security configurations to repositories for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#attach-a-configuration-to-repositories +// +//meta:operation POST /orgs/{org}/code-security/configurations/{configuration_id}/attach +func (s *OrganizationsService) AttachCodeSecurityConfigurationToRepositories(ctx context.Context, org string, configurationID int64, scope string, repoIDs []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/%v/attach", org, configurationID) + type selectedRepoIDs struct { + Scope string `json:"scope"` + SelectedIDs []int64 `json:"selected_repository_ids,omitempty"` + } + req, err := s.client.NewRequest(ctx, "POST", u, selectedRepoIDs{Scope: scope, SelectedIDs: repoIDs}) + if err != nil { + return nil, err + } + resp, err := s.client.Do(req, nil) + if err != nil && resp.StatusCode != http.StatusAccepted { // StatusAccepted(202) is the expected status code as job is queued for processing + return resp, err + } + return resp, nil +} + +// SetDefaultCodeSecurityConfiguration sets a code security configuration as the default for an organization. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#set-a-code-security-configuration-as-a-default-for-an-organization +// +//meta:operation PUT /orgs/{org}/code-security/configurations/{configuration_id}/defaults +func (s *OrganizationsService) SetDefaultCodeSecurityConfiguration(ctx context.Context, org string, configurationID int64, newReposParam string) (*CodeSecurityConfigurationWithDefaultForNewRepos, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/%v/defaults", org, configurationID) + type configParam struct { + DefaultForNewRepos string `json:"default_for_new_repos"` + } + req, err := s.client.NewRequest(ctx, "PUT", u, configParam{DefaultForNewRepos: newReposParam}) + if err != nil { + return nil, nil, err + } + var config *CodeSecurityConfigurationWithDefaultForNewRepos + resp, err := s.client.Do(req, &config) + if err != nil { + return nil, resp, err + } + return config, resp, nil +} + +// ListCodeSecurityConfigurationRepositories gets repositories associated with a code security configuration. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-repositories-associated-with-a-code-security-configuration +// +//meta:operation GET /orgs/{org}/code-security/configurations/{configuration_id}/repositories +func (s *OrganizationsService) ListCodeSecurityConfigurationRepositories(ctx context.Context, org string, configurationID int64, opts *ListCodeSecurityConfigurationRepositoriesOptions) ([]*RepositoryAttachment, *Response, error) { + u := fmt.Sprintf("orgs/%v/code-security/configurations/%v/repositories", org, configurationID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var attachments []*RepositoryAttachment + resp, err := s.client.Do(req, &attachments) + if err != nil { + return nil, resp, err + } + + return attachments, resp, nil +} + +// GetCodeSecurityConfigurationForRepository gets code security configuration that manages a repository's code security settings. +// +// GitHub API docs: https://docs.github.com/rest/code-security/configurations?apiVersion=2022-11-28#get-the-code-security-configuration-associated-with-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/code-security-configuration +func (s *OrganizationsService) GetCodeSecurityConfigurationForRepository(ctx context.Context, org, repo string) (*RepositoryCodeSecurityConfiguration, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/code-security-configuration", org, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + var repoConfig *RepositoryCodeSecurityConfiguration + resp, err := s.client.Do(req, &repoConfig) + if err != nil { + return nil, resp, err + } + return repoConfig, resp, nil +} diff --git a/github/orgs_codesecurity_configurations_test.go b/github/orgs_codesecurity_configurations_test.go new file mode 100644 index 00000000000..15f845aac41 --- /dev/null +++ b/github/orgs_codesecurity_configurations_test.go @@ -0,0 +1,610 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListCodeSecurityConfigurations(t *testing.T) { + t.Parallel() + opts := &ListOrgCodeSecurityConfigurationOptions{Before: "1", After: "2", PerPage: 30, TargetType: "all"} + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/code-security/configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"before": "1", "after": "2", "per_page": "30", "target_type": "all"}) + fmt.Fprint(w, `[ + { + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }, + { + "id":2, + "name":"config2", + "description":"desc2", + "private_vulnerability_reporting": "enabled" + }]`) + }) + + configurations, _, err := client.Organizations.ListCodeSecurityConfigurations(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListCodeSecurityConfigurations returned error: %v", err) + } + + want := []*CodeSecurityConfiguration{ + {ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")}, + {ID: Ptr(int64(2)), Name: "config2", Description: "desc2", PrivateVulnerabilityReporting: Ptr("enabled")}, + } + if !cmp.Equal(configurations, want) { + t.Errorf("Organizations.ListCodeSecurityConfigurations returned %+v, want %+v", configurations, want) + } + const methodName = "ListCodeSecurityConfigurations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListCodeSecurityConfigurations(ctx, "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListCodeSecurityConfigurations(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + mux.HandleFunc("/orgs/o/code-security/configurations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }`) + }) + + configuration, _, err := client.Organizations.GetCodeSecurityConfiguration(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.GetCodeSecurityConfiguration returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + if !cmp.Equal(configuration, want) { + t.Errorf("Organizations.GetCodeSecurityConfiguration returned %+v, want %+v", configuration, want) + } + + const methodName = "GetCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetCodeSecurityConfiguration(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetCodeSecurityConfiguration(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + input := CodeSecurityConfiguration{ + Name: "config1", + Description: "desc1", + CodeScanningDefaultSetup: Ptr("enabled"), + } + + mux.HandleFunc("/orgs/o/code-security/configurations", func(w http.ResponseWriter, r *http.Request) { + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }`) + }) + + configuration, _, err := client.Organizations.CreateCodeSecurityConfiguration(ctx, "o", input) + if err != nil { + t.Errorf("Organizations.CreateCodeSecurityConfiguration returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + if !cmp.Equal(configuration, want) { + t.Errorf("Organizations.CreateCodeSecurityConfiguration returned %+v, want %+v", configuration, want) + } + + const methodName = "CreateCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateCodeSecurityConfiguration(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateCodeSecurityConfiguration(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateCodeSecurityConfigurationWithDelegatedBypass(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + input := CodeSecurityConfiguration{ + Name: "config1", + Description: "desc1", + SecretProtection: Ptr("enabled"), // required to configure bypass + SecretScanning: Ptr("enabled"), // required to configure bypass + SecretScanningPushProtection: Ptr("enabled"), // required to configure bypass + SecretScanningDelegatedBypass: Ptr("enabled"), + SecretScanningDelegatedBypassOptions: &SecretScanningDelegatedBypassOptions{ + Reviewers: []*BypassReviewer{ + { + ReviewerType: "TEAM", + ReviewerID: 456, + }, + { + ReviewerType: "ROLE", + ReviewerID: 789, + }, + }, + }, + } + + mux.HandleFunc("/orgs/o/code-security/configurations", func(w http.ResponseWriter, r *http.Request) { + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id":123, + "name":"config1", + "description":"desc1", + "secret_protection": "enabled", + "secret_scanning": "enabled", + "secret_scanning_push_protection": "enabled", + "secret_scanning_delegated_bypass": "enabled", + "secret_scanning_delegated_bypass_options": { + "reviewers": [ + { + "security_configuration_id": 123, + "reviewer_type": "TEAM", + "reviewer_id": 456 + }, + { + "security_configuration_id": 123, + "reviewer_type": "ROLE", + "reviewer_id": 789 + } + ] + } + }`) + }) + + configuration, _, err := client.Organizations.CreateCodeSecurityConfiguration(ctx, "o", input) + if err != nil { + t.Errorf("Organizations.CreateCodeSecurityConfiguration with Bypass returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ + ID: Ptr(int64(123)), + Name: "config1", + Description: "desc1", + SecretProtection: Ptr("enabled"), + SecretScanning: Ptr("enabled"), + SecretScanningPushProtection: Ptr("enabled"), + SecretScanningDelegatedBypass: Ptr("enabled"), + SecretScanningDelegatedBypassOptions: &SecretScanningDelegatedBypassOptions{ + Reviewers: []*BypassReviewer{ + { + SecurityConfigurationID: Ptr(int64(123)), + ReviewerType: "TEAM", + ReviewerID: 456, + }, + { + SecurityConfigurationID: Ptr(int64(123)), + ReviewerType: "ROLE", + ReviewerID: 789, + }, + }, + }, + } + if !cmp.Equal(configuration, want) { + t.Errorf("Organizations.CreateCodeSecurityConfiguration with Bypass returned %+v, want %+v", configuration, want) + } + + const methodName = "CreateCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateCodeSecurityConfiguration(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateCodeSecurityConfiguration(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListDefaultCodeSecurityConfigurations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + mux.HandleFunc("/orgs/o/code-security/configurations/defaults", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "default_for_new_repos": "public", + "configuration": { + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + } + }, + { + "default_for_new_repos": "private_and_internal", + "configuration": { + "id":2, + "name":"config2", + "description":"desc2", + "private_vulnerability_reporting": "enabled" + } + } + ]`) + }) + + configurations, _, err := client.Organizations.ListDefaultCodeSecurityConfigurations(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListDefaultCodeSecurityConfigurations returned error: %v", err) + } + + want := []*CodeSecurityConfigurationWithDefaultForNewRepos{ + {DefaultForNewRepos: Ptr("public"), Configuration: &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")}}, + {DefaultForNewRepos: Ptr("private_and_internal"), Configuration: &CodeSecurityConfiguration{ID: Ptr(int64(2)), Name: "config2", Description: "desc2", PrivateVulnerabilityReporting: Ptr("enabled")}}, + } + if !cmp.Equal(configurations, want) { + t.Errorf("Organizations.ListDefaultCodeSecurityConfigurations returned %+v, want %+v", configurations, want) + } + + const methodName = "ListDefaultCodeSecurityConfigurations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListDefaultCodeSecurityConfigurations(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListDefaultCodeSecurityConfigurations(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DetachCodeSecurityConfigurationsFromRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + ctx := t.Context() + + mux.HandleFunc("/orgs/o/code-security/configurations/detach", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + resp, err := client.Organizations.DetachCodeSecurityConfigurationsFromRepositories(ctx, "o", []int64{1}) + if err != nil { + t.Errorf("Organizations.DetachCodeSecurityConfigurationsFromRepositories returned error: %v", err) + } + + want := http.StatusNoContent + if resp.StatusCode != want { + t.Errorf("Organizations.DetachCodeSecurityConfigurationsFromRepositories returned status %v, want %v", resp.StatusCode, want) + } + + const methodName = "DetachCodeSecurityConfigurationsFromRepositories" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DetachCodeSecurityConfigurationsFromRepositories(ctx, "\n", []int64{1}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Organizations.DetachCodeSecurityConfigurationsFromRepositories(ctx, "o", []int64{1}) + return resp, err + }) +} + +func TestOrganizationsService_UpdateCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + input := CodeSecurityConfiguration{ + Name: "config1", + Description: "desc1", + CodeScanningDefaultSetup: Ptr("enabled"), + } + + mux.HandleFunc("/orgs/o/code-security/configurations/1", func(w http.ResponseWriter, r *http.Request) { + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id":1, + "name":"config1", + "description":"desc1", + "code_scanning_default_setup": "enabled" + }`) + }) + + configuration, _, err := client.Organizations.UpdateCodeSecurityConfiguration(ctx, "o", 1, input) + if err != nil { + t.Errorf("Organizations.UpdateCodeSecurityConfiguration returned error: %v", err) + } + + want := &CodeSecurityConfiguration{ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + if !cmp.Equal(configuration, want) { + t.Errorf("Organizations.UpdateCodeSecurityConfiguration returned %+v, want %+v", configuration, want) + } + + const methodName = "UpdateCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateCodeSecurityConfiguration(ctx, "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateCodeSecurityConfiguration(ctx, "o", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/code-security/configurations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + resp, err := client.Organizations.DeleteCodeSecurityConfiguration(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.DeleteCodeSecurityConfiguration returned error: %v", err) + } + + want := http.StatusNoContent + if resp.StatusCode != want { + t.Errorf("Organizations.DeleteCodeSecurityConfiguration returned status %v, want %v", resp.StatusCode, want) + } + + const methodName = "DeleteCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DeleteCodeSecurityConfiguration(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Organizations.DeleteCodeSecurityConfiguration(ctx, "o", 1) + return resp, err + }) +} + +func TestOrganizationsService_AttachCodeSecurityConfigurationToRepositories(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + request := struct { + Scope string `json:"scope"` + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` + }{ + Scope: "selected", + SelectedRepositoryIDs: []int64{5, 20}, + } + + mux.HandleFunc("/orgs/o/code-security/configurations/1/attach", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, request) + w.WriteHeader(http.StatusAccepted) + }) + + resp, err := client.Organizations.AttachCodeSecurityConfigurationToRepositories(ctx, "o", int64(1), "selected", []int64{5, 20}) + if err != nil { + t.Errorf("Organizations.AttachCodeSecurityConfigurationToRepositories returned error: %v", err) + } + + want := http.StatusAccepted + if resp.StatusCode != want { + t.Errorf("Organizations.AttachCodeSecurityConfigurationToRepositories returned status %v, want %v", resp.StatusCode, want) + } + + const methodName = "AttachCodeSecurityConfigurationToRepositories" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.AttachCodeSecurityConfigurationToRepositories(ctx, "\n", -1, "", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Organizations.AttachCodeSecurityConfigurationToRepositories(ctx, "o", 1, "selected", []int64{5, 20}) + return resp, err + }) +} + +func TestOrganizationsService_SetDefaultCodeSecurityConfiguration(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/code-security/configurations/1/defaults", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, ` + { + "default_for_new_repos": "all", + "configuration": + { + "id": 1, + "name": "config1", + "description": "desc1", + "code_scanning_default_setup": "enabled" + } + }`) + }) + got, resp, err := client.Organizations.SetDefaultCodeSecurityConfiguration(ctx, "o", 1, "all") + if err != nil { + t.Errorf("Organizations.SetDefaultCodeSecurityConfiguration returned error: %v", err) + } + wantStatus := http.StatusOK + if resp.StatusCode != wantStatus { + t.Errorf("Organizations.SetDefaultCodeSecurityConfiguration returned status %v, want %v", resp.StatusCode, wantStatus) + } + want := &CodeSecurityConfigurationWithDefaultForNewRepos{ + DefaultForNewRepos: Ptr("all"), + Configuration: &CodeSecurityConfiguration{ + ID: Ptr(int64(1)), Name: "config1", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled"), + }, + } + if !cmp.Equal(got, want) { + t.Errorf("Organizations.SetDefaultCodeSecurityConfiguration returned %+v, want %+v", got, want) + } + + const methodName = "SetDefaultCodeSecurityConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.SetDefaultCodeSecurityConfiguration(ctx, "\n", -1, "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.SetDefaultCodeSecurityConfiguration(ctx, "o", 1, "all") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListCodeSecurityConfigurationRepositories(t *testing.T) { + t.Parallel() + opts := &ListCodeSecurityConfigurationRepositoriesOptions{Before: "1", After: "2", PerPage: 30, Status: "attached"} + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/code-security/configurations/1/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"before": "1", "after": "2", "per_page": "30", "status": "attached"}) + fmt.Fprint(w, `[ + { + "status": "attached", + "repository": { + "id":8, + "name":"repo8" + } + }, + { + "status": "attached", + "repository": { + "id":42, + "name":"repo42" + } + } + ]`) + }) + + attachments, _, err := client.Organizations.ListCodeSecurityConfigurationRepositories(ctx, "o", 1, opts) + if err != nil { + t.Errorf("Organizations.ListCodeSecurityConfigurationRepositories returned error: %v", err) + } + want := []*RepositoryAttachment{ + {Status: Ptr("attached"), Repository: &Repository{ID: Ptr(int64(8)), Name: Ptr("repo8")}}, + {Status: Ptr("attached"), Repository: &Repository{ID: Ptr(int64(42)), Name: Ptr("repo42")}}, + } + if !cmp.Equal(attachments, want) { + t.Errorf("Organizations.ListCodeSecurityConfigurationRepositories returned %+v, want %+v", attachments, want) + } + + const methodName = "ListCodeSecurityConfigurationRepositories" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListCodeSecurityConfigurationRepositories(ctx, "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListCodeSecurityConfigurationRepositories(ctx, "o", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetCodeSecurityConfigurationForRepository(t *testing.T) { + t.Parallel() + ctx := t.Context() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo8/code-security-configuration", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "state": "attached", + "configuration": { + "id":42, + "name":"config42", + "description":"desc1", + "code_scanning_default_setup": "enabled" + } + }`) + }) + + rc, _, err := client.Organizations.GetCodeSecurityConfigurationForRepository(ctx, "o", "repo8") + if err != nil { + t.Errorf("Organizations.GetCodeSecurityConfigurationForRepository returned error: %v", err) + } + c := &CodeSecurityConfiguration{ID: Ptr(int64(42)), Name: "config42", Description: "desc1", CodeScanningDefaultSetup: Ptr("enabled")} + want := &RepositoryCodeSecurityConfiguration{ + State: Ptr("attached"), + Configuration: c, + } + if !cmp.Equal(rc, want) { + t.Errorf("Organizations.GetCodeSecurityConfigurationForRepository returned %+v, want %+v", rc, want) + } + + const methodName = "GetCodeSecurityConfigurationForRepository" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetCodeSecurityConfigurationForRepository(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetCodeSecurityConfigurationForRepository(ctx, "o", "repo8") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_credential_authorizations.go b/github/orgs_credential_authorizations.go new file mode 100644 index 00000000000..c21bfaf5a1a --- /dev/null +++ b/github/orgs_credential_authorizations.go @@ -0,0 +1,108 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CredentialAuthorization represents a credential authorized through SAML SSO. +type CredentialAuthorization struct { + // User login that owns the underlying credential. + Login *string `json:"login,omitempty"` + + // Unique identifier for the credential. + CredentialID *int64 `json:"credential_id,omitempty"` + + // Human-readable description of the credential type. + CredentialType *string `json:"credential_type,omitempty"` + + // Last eight characters of the credential. + // Only included in responses with credential_type of personal access token. + TokenLastEight *string `json:"token_last_eight,omitempty"` + + // Date when the credential was authorized for use. + CredentialAuthorizedAt *Timestamp `json:"credential_authorized_at,omitempty"` + + // Date when the credential was last accessed. + // May be null if it was never accessed. + CredentialAccessedAt *Timestamp `json:"credential_accessed_at,omitempty"` + + // List of oauth scopes the token has been granted. + Scopes []string `json:"scopes,omitempty"` + + // Unique string to distinguish the credential. + // Only included in responses with credential_type of SSH Key. + Fingerprint *string `json:"fingerprint,omitempty"` + + AuthorizedCredentialID *int64 `json:"authorized_credential_id,omitempty"` + + // The title given to the ssh key. + // This will only be present when the credential is an ssh key. + AuthorizedCredentialTitle *string `json:"authorized_credential_title,omitempty"` + + // The note given to the token. + // This will only be present when the credential is a token. + AuthorizedCredentialNote *string `json:"authorized_credential_note,omitempty"` + + // The expiry for the token. + // This will only be present when the credential is a token. + AuthorizedCredentialExpiresAt *Timestamp `json:"authorized_credential_expires_at,omitempty"` +} + +// CredentialAuthorizationsListOptions adds the Login option as supported by the +// list SAML SSO authorizations for organizations endpoint alongside paging options +// such as Page and PerPage. +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#list-saml-sso-authorizations-for-an-organization +type CredentialAuthorizationsListOptions struct { + ListOptions + // For credentials authorizations for an organization, limit the list of authorizations to a specific login (aka github username) + Login string `url:"login,omitempty"` +} + +// ListCredentialAuthorizations lists credentials authorized through SAML SSO +// for a given organization. Only available with GitHub Enterprise Cloud. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#list-saml-sso-authorizations-for-an-organization +// +//meta:operation GET /orgs/{org}/credential-authorizations +func (s *OrganizationsService) ListCredentialAuthorizations(ctx context.Context, org string, opts *CredentialAuthorizationsListOptions) ([]*CredentialAuthorization, *Response, error) { + u := fmt.Sprintf("orgs/%v/credential-authorizations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var creds []*CredentialAuthorization + resp, err := s.client.Do(req, &creds) + if err != nil { + return nil, resp, err + } + + return creds, resp, nil +} + +// RemoveCredentialAuthorization revokes the SAML SSO authorization for a given +// credential within an organization. Only available with GitHub Enterprise Cloud. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs?apiVersion=2022-11-28#remove-a-saml-sso-authorization-for-an-organization +// +//meta:operation DELETE /orgs/{org}/credential-authorizations/{credential_id} +func (s *OrganizationsService) RemoveCredentialAuthorization(ctx context.Context, org string, credentialID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/credential-authorizations/%v", org, credentialID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/orgs_credential_authorizations_test.go b/github/orgs_credential_authorizations_test.go new file mode 100644 index 00000000000..8770c77c8ec --- /dev/null +++ b/github/orgs_credential_authorizations_test.go @@ -0,0 +1,100 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListCredentialAuthorizations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/credential-authorizations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2", "login": "l"}) + fmt.Fprint(w, `[ + { + "login": "l", + "credential_id": 1, + "credential_type": "t", + "credential_authorized_at": `+refTimeStr(1136178000)+`, + "credential_accessed_at": `+refTimeStr(1136178001)+`, + "authorized_credential_id": 1 + } + ]`) + }) + + opts := &CredentialAuthorizationsListOptions{ + ListOptions: ListOptions{Page: 2, PerPage: 2}, + Login: "l", + } + + ctx := t.Context() + creds, _, err := client.Organizations.ListCredentialAuthorizations(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListCredentialAuthorizations returned error: %v", err) + } + + want := []*CredentialAuthorization{ + { + Login: Ptr("l"), + CredentialID: Ptr(int64(1)), + CredentialType: Ptr("t"), + CredentialAuthorizedAt: refTimestamp(1136178000), + CredentialAccessedAt: refTimestamp(1136178001), + AuthorizedCredentialID: Ptr(int64(1)), + }, + } + if !cmp.Equal(creds, want) { + t.Errorf("Organizations.ListCredentialAuthorizations returned %+v, want %+v", creds, want) + } + + const methodName = "ListCredentialAuthorizations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListCredentialAuthorizations(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.Organizations.ListCredentialAuthorizations(ctx, "o", opts) + return resp, err + }) +} + +func TestOrganizationsService_RemoveCredentialAuthorization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/credential-authorizations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.RemoveCredentialAuthorization(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.RemoveCredentialAuthorization returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Organizations.RemoveCredentialAuthorization returned %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "RemoveCredentialAuthorization" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveCredentialAuthorization(ctx, "\n", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveCredentialAuthorization(ctx, "o", 1) + }) +} diff --git a/github/orgs_custom_repository_roles.go b/github/orgs_custom_repository_roles.go new file mode 100644 index 00000000000..c98785398cc --- /dev/null +++ b/github/orgs_custom_repository_roles.go @@ -0,0 +1,191 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// OrganizationCustomRepoRoles represents custom repository roles available in specified organization. +type OrganizationCustomRepoRoles struct { + TotalCount *int `json:"total_count,omitempty"` + CustomRepoRoles []*CustomRepoRoles `json:"custom_roles,omitempty"` +} + +// CustomRepoRoles represents custom repository roles for an organization. +// See https://docs.github.com/enterprise-cloud@latest/organizations/managing-peoples-access-to-your-organization-with-roles/managing-custom-repository-roles-for-an-organization +// for more information. +type CustomRepoRoles struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + BaseRole *string `json:"base_role,omitempty"` + Permissions []string `json:"permissions,omitempty"` + Org *Organization `json:"organization,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// CreateCustomRepoRoleRequest represents the parameters to create a custom repository role. +type CreateCustomRepoRoleRequest struct { + Name string `json:"name"` + Description *string `json:"description,omitempty"` + BaseRole string `json:"base_role"` + Permissions []string `json:"permissions"` +} + +// UpdateCustomRepoRoleRequest represents the parameters to update a custom repository role. +type UpdateCustomRepoRoleRequest struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + BaseRole *string `json:"base_role,omitempty"` + Permissions []string `json:"permissions,omitzero"` +} + +// RepoFineGrainedPermission represents a fine-grained permission that can be used in a custom repository role. +type RepoFineGrainedPermission struct { + Name string `json:"name"` + Description string `json:"description"` +} + +// ListCustomRepoRoles lists the custom repository roles available in this organization. +// In order to see custom repository roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#list-custom-repository-roles-in-an-organization +// +//meta:operation GET /orgs/{org}/custom-repository-roles +func (s *OrganizationsService) ListCustomRepoRoles(ctx context.Context, org string) (*OrganizationCustomRepoRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/custom-repository-roles", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customRepoRoles *OrganizationCustomRepoRoles + resp, err := s.client.Do(req, &customRepoRoles) + if err != nil { + return nil, resp, err + } + + return customRepoRoles, resp, nil +} + +// GetCustomRepoRole gets a custom repository roles available in this organization. +// In order to see custom repository roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#get-a-custom-repository-role +// +//meta:operation GET /orgs/{org}/custom-repository-roles/{role_id} +func (s *OrganizationsService) GetCustomRepoRole(ctx context.Context, org string, roleID int64) (*CustomRepoRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/custom-repository-roles/%v", org, roleID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var resultingRole *CustomRepoRoles + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, nil +} + +// CreateCustomRepoRole creates a custom repository role in this organization. +// In order to create custom repository roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#create-a-custom-repository-role +// +//meta:operation POST /orgs/{org}/custom-repository-roles +func (s *OrganizationsService) CreateCustomRepoRole(ctx context.Context, org string, body CreateCustomRepoRoleRequest) (*CustomRepoRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/custom-repository-roles", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var resultingRole *CustomRepoRoles + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// UpdateCustomRepoRole updates a custom repository role in this organization. +// In order to update custom repository roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#update-a-custom-repository-role +// +//meta:operation PATCH /orgs/{org}/custom-repository-roles/{role_id} +func (s *OrganizationsService) UpdateCustomRepoRole(ctx context.Context, org string, roleID int64, body UpdateCustomRepoRoleRequest) (*CustomRepoRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/custom-repository-roles/%v", org, roleID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var resultingRole *CustomRepoRoles + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// DeleteCustomRepoRole deletes an existing custom repository role in this organization. +// In order to delete custom repository roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#delete-a-custom-repository-role +// +//meta:operation DELETE /orgs/{org}/custom-repository-roles/{role_id} +func (s *OrganizationsService) DeleteCustomRepoRole(ctx context.Context, org string, roleID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/custom-repository-roles/%v", org, roleID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + var resultingRole *CustomRepoRoles + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListRepositoryFineGrainedPermissions lists the fine-grained permissions that can be used in custom repository roles for an organization. +// The authenticated user must be an administrator of the organization or of a repository of the organization to use this endpoint. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles?apiVersion=2022-11-28#list-repository-fine-grained-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/repository-fine-grained-permissions +func (s *OrganizationsService) ListRepositoryFineGrainedPermissions(ctx context.Context, org string) ([]*RepoFineGrainedPermission, *Response, error) { + u := fmt.Sprintf("orgs/%v/repository-fine-grained-permissions", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var perms []*RepoFineGrainedPermission + resp, err := s.client.Do(req, &perms) + if err != nil { + return nil, resp, err + } + + return perms, resp, nil +} diff --git a/github/orgs_custom_repository_roles_test.go b/github/orgs_custom_repository_roles_test.go new file mode 100644 index 00000000000..4a980a4809e --- /dev/null +++ b/github/orgs_custom_repository_roles_test.go @@ -0,0 +1,337 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListCustomRepoRoles(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/custom-repository-roles", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count": 1, "custom_roles": [ + { + "id": 1, + "name": "Developer", + "base_role": "write", + "permissions": ["delete_alerts_code_scanning"], + "organization": { + "login": "l", + "id": 1, + "node_id": "n", + "avatar_url": "a", + "html_url": "h", + "name": "n", + "company": "c", + "blog": "b", + "location": "l", + "email": "e" + }, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` + } + ] + }`) + }) + + ctx := t.Context() + apps, _, err := client.Organizations.ListCustomRepoRoles(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListCustomRepoRoles returned error: %v", err) + } + + want := &OrganizationCustomRepoRoles{ + TotalCount: Ptr(1), + CustomRepoRoles: []*CustomRepoRoles{ + { + ID: Ptr(int64(1)), + Name: Ptr("Developer"), + BaseRole: Ptr("write"), + Permissions: []string{"delete_alerts_code_scanning"}, + Org: &Organization{ + Login: Ptr("l"), + ID: Ptr(int64(1)), + NodeID: Ptr("n"), + AvatarURL: Ptr("a"), + HTMLURL: Ptr("h"), + Name: Ptr("n"), + Company: Ptr("c"), + Blog: Ptr("b"), + Location: Ptr("l"), + Email: Ptr("e"), + }, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + }, + }, + } + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListCustomRepoRoles returned %+v, want %+v", apps, want) + } + + const methodName = "ListCustomRepoRoles" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListCustomRepoRoles(ctx, "\no") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListCustomRepoRoles(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetCustomRepoRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/custom-repository-roles/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 1, + "name": "Developer", + "base_role": "write", + "permissions": ["delete_alerts_code_scanning"], + "organization": { + "login": "l", + "id": 1, + "node_id": "n", + "avatar_url": "a", + "html_url": "h", + "name": "n", + "company": "c", + "blog": "b", + "location": "l", + "email": "e" + }, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` + }`) + }) + + ctx := t.Context() + role, _, err := client.Organizations.GetCustomRepoRole(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.GetCustomRepoRole returned error: %v", err) + } + + want := &CustomRepoRoles{ + ID: Ptr(int64(1)), + Name: Ptr("Developer"), + BaseRole: Ptr("write"), + Permissions: []string{"delete_alerts_code_scanning"}, + Org: &Organization{ + Login: Ptr("l"), + ID: Ptr(int64(1)), + NodeID: Ptr("n"), + AvatarURL: Ptr("a"), + HTMLURL: Ptr("h"), + Name: Ptr("n"), + Company: Ptr("c"), + Blog: Ptr("b"), + Location: Ptr("l"), + Email: Ptr("e"), + }, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + if !cmp.Equal(role, want) { + t.Errorf("Organizations.GetCustomRepoRole returned %+v, want %+v", role, want) + } + + const methodName = "GetCustomRepoRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetCustomRepoRole(ctx, "\no", 1) + return err + }) + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetCustomRepoRole(ctx, "o", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetCustomRepoRole(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateCustomRepoRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/custom-repository-roles", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":8030,"name":"Labeler","description":"A role for issue and PR labelers","base_role":"read","permissions":["add_label"]}`) + }) + + ctx := t.Context() + + body := CreateCustomRepoRoleRequest{ + Name: "Labeler", + Description: Ptr("A role for issue and PR labelers"), + BaseRole: "read", + Permissions: []string{"add_label"}, + } + apps, _, err := client.Organizations.CreateCustomRepoRole(ctx, "o", body) + if err != nil { + t.Errorf("Organizations.CreateCustomRepoRole returned error: %v", err) + } + + want := &CustomRepoRoles{ID: Ptr(int64(8030)), Name: Ptr("Labeler"), BaseRole: Ptr("read"), Permissions: []string{"add_label"}, Description: Ptr("A role for issue and PR labelers")} + + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.CreateCustomRepoRole returned %+v, want %+v", apps, want) + } + + const methodName = "CreateCustomRepoRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateCustomRepoRole(ctx, "\no", CreateCustomRepoRoleRequest{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateCustomRepoRole(ctx, "o", CreateCustomRepoRoleRequest{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateCustomRepoRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/custom-repository-roles/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"id":8030,"name":"Updated Name","description":"Updated Description","base_role":"read","permissions":["add_label"]}`) + }) + + ctx := t.Context() + + body := UpdateCustomRepoRoleRequest{ + Name: Ptr("Updated Name"), + Description: Ptr("Updated Description"), + } + apps, _, err := client.Organizations.UpdateCustomRepoRole(ctx, "o", 8030, body) + if err != nil { + t.Errorf("Organizations.UpdateCustomRepoRole returned error: %v", err) + } + + want := &CustomRepoRoles{ID: Ptr(int64(8030)), Name: Ptr("Updated Name"), BaseRole: Ptr("read"), Permissions: []string{"add_label"}, Description: Ptr("Updated Description")} + + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.UpdateCustomRepoRole returned %+v, want %+v", apps, want) + } + + const methodName = "UpdateCustomRepoRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateCustomRepoRole(ctx, "\no", 8030, UpdateCustomRepoRoleRequest{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateCustomRepoRole(ctx, "o", 8030, UpdateCustomRepoRoleRequest{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteCustomRepoRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/custom-repository-roles/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + + resp, err := client.Organizations.DeleteCustomRepoRole(ctx, "o", 8030) + if err != nil { + t.Errorf("Organizations.DeleteCustomRepoRole returned error: %v", err) + } + + if !cmp.Equal(resp.StatusCode, 204) { + t.Errorf("Organizations.DeleteCustomRepoRole returned status code %+v, want %+v", resp.StatusCode, "204") + } + + const methodName = "DeleteCustomRepoRole" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DeleteCustomRepoRole(ctx, "\no", 8030) + return err + }) +} + +func TestOrganizationsService_ListRepositoryFineGrainedPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/repository-fine-grained-permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "name": "add_assignee", + "description": "Assign or remove a user" + }, + { + "name": "add_label", + "description": "Add or remove a label" + } + ]`) + }) + + ctx := t.Context() + perms, _, err := client.Organizations.ListRepositoryFineGrainedPermissions(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListRepositoryFineGrainedPermissions returned error: %v", err) + } + + want := []*RepoFineGrainedPermission{ + { + Name: "add_assignee", + Description: "Assign or remove a user", + }, + { + Name: "add_label", + Description: "Add or remove a label", + }, + } + if !cmp.Equal(perms, want) { + t.Errorf("Organizations.ListRepositoryFineGrainedPermissions returned %+v, want %+v", perms, want) + } + + const methodName = "ListRepositoryFineGrainedPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListRepositoryFineGrainedPermissions(ctx, "\no") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListRepositoryFineGrainedPermissions(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_hooks.go b/github/orgs_hooks.go index 5357eb87840..12f03bb7455 100644 --- a/github/orgs_hooks.go +++ b/github/orgs_hooks.go @@ -7,26 +7,29 @@ package github import ( "context" + "errors" "fmt" ) // ListHooks lists all Hooks for the specified organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks -func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *ListOptions) ([]*Hook, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#list-organization-webhooks +// +//meta:operation GET /orgs/{org}/hooks +func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opts *ListOptions) ([]*Hook, *Response, error) { u := fmt.Sprintf("orgs/%v/hooks", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var hooks []*Hook - resp, err := s.client.Do(ctx, req, &hooks) + resp, err := s.client.Do(req, &hooks) if err != nil { return nil, resp, err } @@ -36,16 +39,23 @@ func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *L // GetHook returns a single specified Hook. // -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#get-an-organization-webhook +// +//meta:operation GET /orgs/{org}/hooks/{hook_id} func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int64) (*Hook, *Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("orgs/%v/hooks/%v", org, id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - hook := new(Hook) - resp, err := s.client.Do(ctx, req, hook) - return hook, resp, err + + var hook *Hook + resp, err := s.client.Do(req, &hook) + if err != nil { + return nil, resp, err + } + + return hook, resp, nil } // CreateHook creates a Hook for the specified org. @@ -54,8 +64,14 @@ func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int64 // Note that only a subset of the hook fields are used and hook must // not be nil. // -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#create-an-organization-webhook +// +//meta:operation POST /orgs/{org}/hooks func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) { + if hook == nil { + return nil, nil, errors.New("hook must be provided") + } + u := fmt.Sprintf("orgs/%v/hooks", org) hookReq := &createHookRequest{ @@ -65,13 +81,13 @@ func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook Config: hook.Config, } - req, err := s.client.NewRequest("POST", u, hookReq) + req, err := s.client.NewRequest(ctx, "POST", u, hookReq) if err != nil { return nil, nil, err } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) + var h *Hook + resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } @@ -81,38 +97,51 @@ func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook // EditHook updates a specified Hook. // -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook -func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int64, hook *Hook) (*Hook, *Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) - req, err := s.client.NewRequest("PATCH", u, hook) +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#update-an-organization-webhook +// +//meta:operation PATCH /orgs/{org}/hooks/{hook_id} +func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int64, body *Hook) (*Hook, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%v", org, id) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) - return h, resp, err + + var h *Hook + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil } // PingHook triggers a 'ping' event to be sent to the Hook. // -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#ping-an-organization-webhook +// +//meta:operation POST /orgs/{org}/hooks/{hook_id}/pings func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int64) (*Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id) - req, err := s.client.NewRequest("POST", u, nil) + u := fmt.Sprintf("orgs/%v/hooks/%v/pings", org, id) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } // DeleteHook deletes a specified Hook. // -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#delete-an-organization-webhook +// +//meta:operation DELETE /orgs/{org}/hooks/{hook_id} func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int64) (*Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("orgs/%v/hooks/%v", org, id) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } diff --git a/github/orgs_hooks_configuration.go b/github/orgs_hooks_configuration.go new file mode 100644 index 00000000000..1bba535aff5 --- /dev/null +++ b/github/orgs_hooks_configuration.go @@ -0,0 +1,53 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetHookConfiguration returns the configuration for the specified organization webhook. +// +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#get-a-webhook-configuration-for-an-organization +// +//meta:operation GET /orgs/{org}/hooks/{hook_id}/config +func (s *OrganizationsService) GetHookConfiguration(ctx context.Context, org string, id int64) (*HookConfig, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%v/config", org, id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var config *HookConfig + resp, err := s.client.Do(req, &config) + if err != nil { + return nil, resp, err + } + + return config, resp, nil +} + +// UpdateHookConfiguration updates the configuration for the specified organization webhook. +// +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#update-a-webhook-configuration-for-an-organization +// +//meta:operation PATCH /orgs/{org}/hooks/{hook_id}/config +func (s *OrganizationsService) UpdateHookConfiguration(ctx context.Context, org string, id int64, body HookConfig) (*HookConfig, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%v/config", org, id) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var c *HookConfig + resp, err := s.client.Do(req, &c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} diff --git a/github/orgs_hooks_configuration_test.go b/github/orgs_hooks_configuration_test.go new file mode 100644 index 00000000000..db31cf9df25 --- /dev/null +++ b/github/orgs_hooks_configuration_test.go @@ -0,0 +1,115 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_GetHookConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/hooks/1/config", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"content_type": "json", "insecure_ssl": "0", "secret": "********", "url": "https://example.com/webhook"}`) + }) + + ctx := t.Context() + config, _, err := client.Organizations.GetHookConfiguration(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.GetHookConfiguration returned error: %v", err) + } + + want := &HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("0"), + Secret: Ptr("********"), + URL: Ptr("https://example.com/webhook"), + } + if !cmp.Equal(config, want) { + t.Errorf("Organizations.GetHookConfiguration returned %+v, want %+v", config, want) + } + + const methodName = "GetHookConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetHookConfiguration(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetHookConfiguration(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetHookConfiguration_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.GetHookConfiguration(ctx, "%", 1) + testURLParseError(t, err) +} + +func TestOrganizationsService_UpdateHookConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := HookConfig{} + + mux.HandleFunc("/orgs/o/hooks/1/config", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"content_type": "json", "insecure_ssl": "0", "secret": "********", "url": "https://example.com/webhook"}`) + }) + + ctx := t.Context() + config, _, err := client.Organizations.UpdateHookConfiguration(ctx, "o", 1, input) + if err != nil { + t.Errorf("Organizations.UpdateHookConfiguration returned error: %v", err) + } + + want := &HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("0"), + Secret: Ptr("********"), + URL: Ptr("https://example.com/webhook"), + } + if !cmp.Equal(config, want) { + t.Errorf("Organizations.UpdateHookConfiguration returned %+v, want %+v", config, want) + } + + const methodName = "UpdateHookConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateHookConfiguration(ctx, "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateHookConfiguration(ctx, "o", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateHookConfiguration_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.UpdateHookConfiguration(ctx, "%", 1, HookConfig{}) + testURLParseError(t, err) +} diff --git a/github/orgs_hooks_deliveries.go b/github/orgs_hooks_deliveries.go new file mode 100644 index 00000000000..0150bc6951a --- /dev/null +++ b/github/orgs_hooks_deliveries.go @@ -0,0 +1,79 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListHookDeliveries lists webhook deliveries for a webhook configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#list-deliveries-for-an-organization-webhook +// +//meta:operation GET /orgs/{org}/hooks/{hook_id}/deliveries +func (s *OrganizationsService) ListHookDeliveries(ctx context.Context, org string, id int64, opts *ListCursorOptions) ([]*HookDelivery, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%v/deliveries", org, id) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + deliveries := []*HookDelivery{} + resp, err := s.client.Do(req, &deliveries) + if err != nil { + return nil, resp, err + } + + return deliveries, resp, nil +} + +// GetHookDelivery returns a delivery for a webhook configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#get-a-webhook-delivery-for-an-organization-webhook +// +//meta:operation GET /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id} +func (s *OrganizationsService) GetHookDelivery(ctx context.Context, owner string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%v/deliveries/%v", owner, hookID, deliveryID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var h *HookDelivery + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} + +// RedeliverHookDelivery redelivers a delivery for a webhook configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/webhooks?apiVersion=2022-11-28#redeliver-a-delivery-for-an-organization-webhook +// +//meta:operation POST /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts +func (s *OrganizationsService) RedeliverHookDelivery(ctx context.Context, owner string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { + u := fmt.Sprintf("orgs/%v/hooks/%v/deliveries/%v/attempts", owner, hookID, deliveryID) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var h *HookDelivery + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} diff --git a/github/orgs_hooks_deliveries_test.go b/github/orgs_hooks_deliveries_test.go new file mode 100644 index 00000000000..66c85ff905c --- /dev/null +++ b/github/orgs_hooks_deliveries_test.go @@ -0,0 +1,149 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListHookDeliveries(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/hooks/1/deliveries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"cursor": "v1_12077215967"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListCursorOptions{Cursor: "v1_12077215967"} + + ctx := t.Context() + hooks, _, err := client.Organizations.ListHookDeliveries(ctx, "o", 1, opt) + if err != nil { + t.Errorf("Organizations.ListHookDeliveries returned error: %v", err) + } + + want := []*HookDelivery{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if d := cmp.Diff(hooks, want); d != "" { + t.Errorf("Organizations.ListHooks want (-), got (+):\n%v", d) + } + + const methodName = "ListHookDeliveries" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListHookDeliveries(ctx, "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListHookDeliveries(ctx, "o", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListHookDeliveries_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.ListHookDeliveries(ctx, "%", 1, nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_GetHookDelivery(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/hooks/1/deliveries/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + hook, _, err := client.Organizations.GetHookDelivery(ctx, "o", 1, 1) + if err != nil { + t.Errorf("Organizations.GetHookDelivery returned error: %v", err) + } + + want := &HookDelivery{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { + t.Errorf("Organizations.GetHookDelivery returned %+v, want %+v", hook, want) + } + + const methodName = "GetHookDelivery" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetHookDelivery(ctx, "\n", -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetHookDelivery(ctx, "o", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetHookDelivery_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.GetHookDelivery(ctx, "%", 1, 1) + testURLParseError(t, err) +} + +func TestOrganizationsService_RedeliverHookDelivery(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/hooks/1/deliveries/1/attempts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + hook, _, err := client.Organizations.RedeliverHookDelivery(ctx, "o", 1, 1) + if err != nil { + t.Errorf("Organizations.RedeliverHookDelivery returned error: %v", err) + } + + want := &HookDelivery{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { + t.Errorf("Organizations.RedeliverHookDelivery returned %+v, want %+v", hook, want) + } + + const methodName = "Rede;overHookDelivery" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.RedeliverHookDelivery(ctx, "\n", -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.RedeliverHookDelivery(ctx, "o", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_RedeliverHookDelivery_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.RedeliverHookDelivery(ctx, "%", 1, 1) + testURLParseError(t, err) +} diff --git a/github/orgs_hooks_test.go b/github/orgs_hooks_test.go index 5ed2e165519..40cdae6096b 100644 --- a/github/orgs_hooks_test.go +++ b/github/orgs_hooks_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestOrganizationsService_ListHooks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -26,152 +25,230 @@ func TestOrganizationsService_ListHooks(t *testing.T) { opt := &ListOptions{Page: 2} - hooks, _, err := client.Organizations.ListHooks(context.Background(), "o", opt) + ctx := t.Context() + hooks, _, err := client.Organizations.ListHooks(ctx, "o", opt) if err != nil { t.Errorf("Organizations.ListHooks returned error: %v", err) } - want := []*Hook{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(hooks, want) { + want := []*Hook{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(hooks, want) { t.Errorf("Organizations.ListHooks returned %+v, want %+v", hooks, want) } + + const methodName = "ListHooks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListHooks(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListHooks(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_ListHooks_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.ListHooks(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Organizations.ListHooks(ctx, "%", nil) testURLParseError(t, err) } func TestOrganizationsService_CreateHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Hook{CreatedAt: &referenceTime} + input := &Hook{CreatedAt: &Timestamp{referenceTime}} mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) { - v := new(createHookRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") want := &createHookRequest{Name: "web"} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } - + testJSONBody(t, r, want) fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Organizations.CreateHook(context.Background(), "o", input) + ctx := t.Context() + hook, _, err := client.Organizations.CreateHook(ctx, "o", input) if err != nil { t.Errorf("Organizations.CreateHook returned error: %v", err) } - want := &Hook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &Hook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Organizations.CreateHook returned %+v, want %+v", hook, want) } + + const methodName = "CreateHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateHook(ctx, "o", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateHook(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateHook(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_GetHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Organizations.GetHook(context.Background(), "o", 1) + ctx := t.Context() + hook, _, err := client.Organizations.GetHook(ctx, "o", 1) if err != nil { t.Errorf("Organizations.GetHook returned error: %v", err) } - want := &Hook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &Hook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Organizations.GetHook returned %+v, want %+v", hook, want) } + + const methodName = "GetHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetHook(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetHook(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_GetHook_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.GetHook(context.Background(), "%", 1) + ctx := t.Context() + _, _, err := client.Organizations.GetHook(ctx, "%", 1) testURLParseError(t, err) } func TestOrganizationsService_EditHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := &Hook{} mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { - v := new(Hook) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Organizations.EditHook(context.Background(), "o", 1, input) + ctx := t.Context() + hook, _, err := client.Organizations.EditHook(ctx, "o", 1, input) if err != nil { t.Errorf("Organizations.EditHook returned error: %v", err) } - want := &Hook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &Hook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Organizations.EditHook returned %+v, want %+v", hook, want) } + + const methodName = "EditHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.EditHook(ctx, "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.EditHook(ctx, "o", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_EditHook_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.EditHook(context.Background(), "%", 1, nil) + ctx := t.Context() + _, _, err := client.Organizations.EditHook(ctx, "%", 1, nil) testURLParseError(t, err) } func TestOrganizationsService_PingHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/orgs/o/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/hooks/1/pings", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") }) - _, err := client.Organizations.PingHook(context.Background(), "o", 1) + ctx := t.Context() + _, err := client.Organizations.PingHook(ctx, "o", 1) if err != nil { t.Errorf("Organizations.PingHook returned error: %v", err) } + + const methodName = "PingHook" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.PingHook(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.PingHook(ctx, "o", 1) + }) } func TestOrganizationsService_DeleteHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/hooks/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Organizations.DeleteHook(context.Background(), "o", 1) + ctx := t.Context() + _, err := client.Organizations.DeleteHook(ctx, "o", 1) if err != nil { t.Errorf("Organizations.DeleteHook returned error: %v", err) } + + const methodName = "DeleteHook" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DeleteHook(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DeleteHook(ctx, "o", 1) + }) } func TestOrganizationsService_DeleteHook_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Organizations.DeleteHook(context.Background(), "%", 1) + ctx := t.Context() + _, err := client.Organizations.DeleteHook(ctx, "%", 1) testURLParseError(t, err) } diff --git a/github/orgs_immutable_releases.go b/github/orgs_immutable_releases.go new file mode 100644 index 00000000000..3f969f516b6 --- /dev/null +++ b/github/orgs_immutable_releases.go @@ -0,0 +1,174 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ImmutableReleaseSettings represents the response from the immutable releases settings endpoint. +type ImmutableReleaseSettings struct { + // EnforcedRepositories specifies how immutable releases are enforced in the organization. Possible values include "all", "none", or "selected". + EnforcedRepositories *string `json:"enforced_repositories,omitempty"` + // SelectedRepositoriesURL provides the API URL for managing the repositories + // selected for immutable releases enforcement when EnforcedRepositories is set to "selected". + SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` +} + +// ImmutableReleasePolicy is for setting the immutable releases policy for repositories in an organization. +type ImmutableReleasePolicy struct { + // EnforcedRepositories specifies how immutable releases are enforced in the organization. Possible values include "all", "none", or "selected". + EnforcedRepositories *string `json:"enforced_repositories,omitempty"` + // An array of repository ids for which immutable releases enforcement should be applied. + // You can only provide a list of repository ids when the enforced_repositories is set to "selected" + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` +} + +// setImmutableReleasesRepositoriesOptions represents the request body for setting repositories. +type setImmutableReleasesRepositoriesOptions struct { + SelectedRepositoryIDs []int64 `json:"selected_repository_ids"` +} + +// GetImmutableReleasesSettings returns the immutable releases configuration that applies to repositories within the given organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#get-immutable-releases-settings-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/immutable-releases +func (s *OrganizationsService) GetImmutableReleasesSettings(ctx context.Context, org string) (*ImmutableReleaseSettings, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/immutable-releases", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var settings *ImmutableReleaseSettings + resp, err := s.client.Do(req, &settings) + if err != nil { + return nil, resp, err + } + + return settings, resp, nil +} + +// UpdateImmutableReleasesSettings sets immutable releases settings for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#set-immutable-releases-settings-for-an-organization +// +//meta:operation PUT /orgs/{org}/settings/immutable-releases +func (s *OrganizationsService) UpdateImmutableReleasesSettings(ctx context.Context, org string, body ImmutableReleasePolicy) (*Response, error) { + u := fmt.Sprintf("orgs/%v/settings/immutable-releases", org) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListImmutableReleaseRepositories lists selected repositories for immutable releases enforcement in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#list-selected-repositories-for-immutable-releases-enforcement +// +//meta:operation GET /orgs/{org}/settings/immutable-releases/repositories +func (s *OrganizationsService) ListImmutableReleaseRepositories(ctx context.Context, org string, opts *ListOptions) (*ListRepositories, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/immutable-releases/repositories", org) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repositories *ListRepositories + resp, err := s.client.Do(req, &repositories) + if err != nil { + return nil, resp, err + } + + return repositories, resp, nil +} + +// SetImmutableReleaseRepositories sets selected repositories for immutable releases enforcement. +// It requires the organization's immutable releases policy for enforced_repositories to be set to "selected". +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#set-selected-repositories-for-immutable-releases-enforcement +// +//meta:operation PUT /orgs/{org}/settings/immutable-releases/repositories +func (s *OrganizationsService) SetImmutableReleaseRepositories(ctx context.Context, org string, repositoryIDs []int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/settings/immutable-releases/repositories", org) + + body := &setImmutableReleasesRepositoriesOptions{ + SelectedRepositoryIDs: repositoryIDs, + } + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// EnableRepositoryForImmutableRelease enables a selected repository for immutable releases in an organization. +// It requires enforced_repositories to be set to "selected". +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#enable-a-selected-repository-for-immutable-releases-in-an-organization +// +//meta:operation PUT /orgs/{org}/settings/immutable-releases/repositories/{repository_id} +func (s *OrganizationsService) EnableRepositoryForImmutableRelease(ctx context.Context, org string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/settings/immutable-releases/repositories/%v", org, repoID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DisableRepositoryForImmutableRelease removes a repository from the organization's selected list for immutable releases enforcement. +// It requires enforced_repositories to be set to "selected". +// +// GitHub API docs: https://docs.github.com/rest/orgs/orgs?apiVersion=2022-11-28#disable-a-selected-repository-for-immutable-releases-in-an-organization +// +//meta:operation DELETE /orgs/{org}/settings/immutable-releases/repositories/{repository_id} +func (s *OrganizationsService) DisableRepositoryForImmutableRelease(ctx context.Context, org string, repoID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/settings/immutable-releases/repositories/%v", org, repoID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/orgs_immutable_releases_test.go b/github/orgs_immutable_releases_test.go new file mode 100644 index 00000000000..e6729ed4f08 --- /dev/null +++ b/github/orgs_immutable_releases_test.go @@ -0,0 +1,245 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_GetImmutableReleasesSettings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "enforced_repositories": "selected", + "selected_repositories_url": "https://api.github.com/orgs/o/r" + }`) + }) + + ctx := t.Context() + settings, _, err := client.Organizations.GetImmutableReleasesSettings(ctx, "o") + if err != nil { + t.Errorf("Organizations.GetImmutableReleasesSettings returned error: %v", err) + } + + want := &ImmutableReleaseSettings{ + EnforcedRepositories: Ptr("selected"), + SelectedRepositoriesURL: Ptr("https://api.github.com/orgs/o/r"), + } + + if !cmp.Equal(settings, want) { + t.Errorf("Organizations.GetImmutableReleasesSettings returned %+v, want %+v", settings, want) + } + + const methodName = "GetImmutableReleasesSettings" + + testBadOptions(t, methodName, func() error { + _, _, err := client.Organizations.GetImmutableReleasesSettings(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetImmutableReleasesSettings(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateImmutableReleasesSettings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ImmutableReleasePolicy{ + EnforcedRepositories: Ptr("selected"), + } + + mux.HandleFunc("/orgs/o/settings/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + fmt.Fprint(w, `{"enforced_repositories":"selected"}`) + }) + + ctx := t.Context() + resp, err := client.Organizations.UpdateImmutableReleasesSettings(ctx, "o", input) + if err != nil { + t.Errorf("Organizations.UpdateImmutableReleasesSettings returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Expected status 204 No Content, got %v", resp.StatusCode) + } + + const methodName = "UpdateImmutableReleasesSettings" + + testBadOptions(t, methodName, func() error { + _, err := client.Organizations.UpdateImmutableReleasesSettings(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Organizations.UpdateImmutableReleasesSettings(ctx, "o", input) + return resp, err + }) +} + +func TestOrganizationsService_ListImmutableReleaseRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + responseBody := `{ + "total_count": 2, + "repositories": [ + {"id": 1, "name": "repo1"}, + {"id": 2, "name": "repo2"} + ] + }` + + mux.HandleFunc("/orgs/o/settings/immutable-releases/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, responseBody) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 10} + repos, _, err := client.Organizations.ListImmutableReleaseRepositories(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListImmutableReleaseRepositories returned error: %v", err) + } + + want := &ListRepositories{ + TotalCount: Ptr(2), + Repositories: []*Repository{ + {ID: Ptr(int64(1)), Name: Ptr("repo1")}, + {ID: Ptr(int64(2)), Name: Ptr("repo2")}, + }, + } + + if !cmp.Equal(repos, want) { + t.Errorf("Organizations.ListImmutableReleaseRepositories returned %+v, want %+v", repos, want) + } + + const methodName = "ListImmutableReleaseRepositories" + + testBadOptions(t, methodName, func() error { + _, _, err := client.Organizations.ListImmutableReleaseRepositories(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListImmutableReleaseRepositories(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_SetImmutableReleaseRepositories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := []int64{1, 2, 3} + mux.HandleFunc("/orgs/o/settings/immutable-releases/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + want := setImmutableReleasesRepositoriesOptions{SelectedRepositoryIDs: input} + testJSONBody(t, r, want) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.SetImmutableReleaseRepositories(ctx, "o", input) + if err != nil { + t.Fatalf("SetImmutableReleaseRepositories returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Expected status 204 No Content, got %v", resp.StatusCode) + } + + const methodName = "SetImmutableReleaseRepositories" + + testBadOptions(t, methodName, func() error { + _, err := client.Organizations.SetImmutableReleaseRepositories(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.SetImmutableReleaseRepositories(ctx, "o", input) + }) +} + +func TestOrganizationsService_EnableRepositoryForImmutableRelease(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + repoID := int64(42) + + mux.HandleFunc(fmt.Sprintf("/orgs/o/settings/immutable-releases/repositories/%v", repoID), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.EnableRepositoryForImmutableRelease(ctx, "o", repoID) + if err != nil { + t.Errorf("EnableRepositoryForImmutableRelease returned error: %v", err) + } + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Expected status 204 No Content, got %v", resp.StatusCode) + } + + const methodName = "EnableRepositoryForImmutableRelease" + + testBadOptions(t, methodName, func() error { + _, err := client.Organizations.EnableRepositoryForImmutableRelease(ctx, "o", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.EnableRepositoryForImmutableRelease(ctx, "o", repoID) + }) +} + +func TestOrganizationsService_DisableRepositoryForImmutableRelease(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + repoID := int64(42) + + mux.HandleFunc(fmt.Sprintf("/orgs/o/settings/immutable-releases/repositories/%v", repoID), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.DisableRepositoryForImmutableRelease(ctx, "o", repoID) + if err != nil { + t.Errorf("DisableRepositoryForImmutableRelease returned error: %v", err) + } + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Expected status 204 No Content, got %v", resp.StatusCode) + } + + const methodName = "DisableRepositoryForImmutableRelease" + + testBadOptions(t, methodName, func() error { + _, err := client.Organizations.DisableRepositoryForImmutableRelease(ctx, "o", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DisableRepositoryForImmutableRelease(ctx, "o", repoID) + }) +} diff --git a/github/orgs_issue_types.go b/github/orgs_issue_types.go new file mode 100644 index 00000000000..0edc7ad5036 --- /dev/null +++ b/github/orgs_issue_types.go @@ -0,0 +1,99 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CreateOrUpdateIssueTypesOptions represents the parameters for creating or updating an issue type. +type CreateOrUpdateIssueTypesOptions struct { + Name string `json:"name"` // Name of the issue type. (Required.) + IsEnabled bool `json:"is_enabled"` // Whether or not the issue type is enabled at the organization level. (Required.) + IsPrivate *bool `json:"is_private,omitempty"` // Whether or not the issue type is restricted to issues in private repositories. (Optional.) + Description *string `json:"description,omitempty"` // Description of the issue type. (Optional.) + Color *string `json:"color,omitempty"` // Color for the issue type. Can be one of "gray", "blue", green "orange", "red", "pink", "purple", "null". (Optional.) +} + +// ListIssueTypes lists all issue types for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/issue-types?apiVersion=2022-11-28#list-issue-types-for-an-organization +// +//meta:operation GET /orgs/{org}/issue-types +func (s *OrganizationsService) ListIssueTypes(ctx context.Context, org string) ([]*IssueType, *Response, error) { + u := fmt.Sprintf("orgs/%v/issue-types", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var issueTypes []*IssueType + resp, err := s.client.Do(req, &issueTypes) + if err != nil { + return nil, resp, err + } + + return issueTypes, resp, nil +} + +// CreateIssueType creates a new issue type for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/issue-types?apiVersion=2022-11-28#create-issue-type-for-an-organization +// +//meta:operation POST /orgs/{org}/issue-types +func (s *OrganizationsService) CreateIssueType(ctx context.Context, org string, body *CreateOrUpdateIssueTypesOptions) (*IssueType, *Response, error) { + u := fmt.Sprintf("orgs/%v/issue-types", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var issueType *IssueType + resp, err := s.client.Do(req, &issueType) + if err != nil { + return nil, resp, err + } + + return issueType, resp, nil +} + +// UpdateIssueType updates GitHub Pages for the named repo. +// +// GitHub API docs: https://docs.github.com/rest/orgs/issue-types?apiVersion=2022-11-28#update-issue-type-for-an-organization +// +//meta:operation PUT /orgs/{org}/issue-types/{issue_type_id} +func (s *OrganizationsService) UpdateIssueType(ctx context.Context, org string, issueTypeID int64, body *CreateOrUpdateIssueTypesOptions) (*IssueType, *Response, error) { + u := fmt.Sprintf("orgs/%v/issue-types/%v", org, issueTypeID) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var issueType *IssueType + resp, err := s.client.Do(req, &issueType) + if err != nil { + return nil, resp, err + } + + return issueType, resp, nil +} + +// DeleteIssueType deletes an issue type for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/issue-types?apiVersion=2022-11-28#delete-issue-type-for-an-organization +// +//meta:operation DELETE /orgs/{org}/issue-types/{issue_type_id} +func (s *OrganizationsService) DeleteIssueType(ctx context.Context, org string, issueTypeID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/issue-types/%v", org, issueTypeID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/orgs_issue_types_test.go b/github/orgs_issue_types_test.go new file mode 100644 index 00000000000..dad06d3cbf0 --- /dev/null +++ b/github/orgs_issue_types_test.go @@ -0,0 +1,224 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListIssueTypes(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/issue-types", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "id": 410, + "node_id": "IT_kwDNAd3NAZo", + "name": "Task", + "description": "A specific piece of work", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` + }, + { + "id": 411, + "node_id": "IT_kwDNAd3NAZs", + "name": "Bug", + "description": "An unexpected problem or behavior", + "created_at": `+refTimeStr(1136178002)+`, + "updated_at": `+refTimeStr(1136178003)+` + } + ]`) + }) + + ctx := t.Context() + issueTypes, _, err := client.Organizations.ListIssueTypes(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListIssueTypes returned error: %v", err) + } + + want := []*IssueType{ + { + ID: Ptr(int64(410)), + NodeID: Ptr("IT_kwDNAd3NAZo"), + Name: Ptr("Task"), + Description: Ptr("A specific piece of work"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + }, + { + ID: Ptr(int64(411)), + NodeID: Ptr("IT_kwDNAd3NAZs"), + Name: Ptr("Bug"), + Description: Ptr("An unexpected problem or behavior"), + CreatedAt: refTimestamp(1136178002), + UpdatedAt: refTimestamp(1136178003), + }, + } + if !cmp.Equal(issueTypes, want) { + t.Errorf("Organizations.ListIssueTypes returned %+v, want %+v", issueTypes, want) + } + + const methodName = "ListIssueTypes" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListIssueTypes(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListIssueTypes(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateIssueType(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateOrUpdateIssueTypesOptions{ + Name: "Epic", + Description: Ptr("An issue type for a multi-week tracking of work"), + IsEnabled: true, + Color: Ptr("green"), + IsPrivate: Ptr(true), + } + + mux.HandleFunc("/orgs/o/issue-types", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 410, + "node_id": "IT_kwDNAd3NAZo", + "name": "Epic", + "description": "An issue type for a multi-week tracking of work", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` + }`) + }) + + ctx := t.Context() + issueType, _, err := client.Organizations.CreateIssueType(ctx, "o", input) + if err != nil { + t.Errorf("Organizations.CreateIssueType returned error: %v", err) + } + want := &IssueType{ + ID: Ptr(int64(410)), + NodeID: Ptr("IT_kwDNAd3NAZo"), + Name: Ptr("Epic"), + Description: Ptr("An issue type for a multi-week tracking of work"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + + if !cmp.Equal(issueType, want) { + t.Errorf("Organizations.CreateIssueType returned %+v, want %+v", issueType, want) + } + + const methodName = "CreateIssueType" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateIssueType(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateIssueType(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateIssueType(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateOrUpdateIssueTypesOptions{ + Name: "Epic", + Description: Ptr("An issue type for a multi-week tracking of work"), + IsEnabled: true, + Color: Ptr("green"), + IsPrivate: Ptr(true), + } + + mux.HandleFunc("/orgs/o/issue-types/410", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 410, + "node_id": "IT_kwDNAd3NAZo", + "name": "Epic", + "description": "An issue type for a multi-week tracking of work", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` + }`) + }) + + ctx := t.Context() + issueType, _, err := client.Organizations.UpdateIssueType(ctx, "o", 410, input) + if err != nil { + t.Errorf("Organizations.UpdateIssueType returned error: %v", err) + } + want := &IssueType{ + ID: Ptr(int64(410)), + NodeID: Ptr("IT_kwDNAd3NAZo"), + Name: Ptr("Epic"), + Description: Ptr("An issue type for a multi-week tracking of work"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + } + + if !cmp.Equal(issueType, want) { + t.Errorf("Organizations.UpdateIssueType returned %+v, want %+v", issueType, want) + } + + const methodName = "UpdateIssueType" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateIssueType(ctx, "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateIssueType(ctx, "o", 410, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteIssueType(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/issue-types/410", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.DeleteIssueType(ctx, "o", 410) + if err != nil { + t.Errorf("Organizations.DeleteIssueType returned error: %v", err) + } + + const methodName = "DeleteIssueType" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DeleteIssueType(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DeleteIssueType(ctx, "o", 410) + }) +} diff --git a/github/orgs_members.go b/github/orgs_members.go index d18435999c9..5d3272e89f0 100644 --- a/github/orgs_members.go +++ b/github/orgs_members.go @@ -69,28 +69,33 @@ type ListMembersOptions struct { // ListMembers lists the members for an organization. If the authenticated // user is an owner of the organization, this will return both concealed and -// public members, otherwise it will only return public members. +// public members; otherwise, it will only return public members. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#members-list -func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opt *ListMembersOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#list-organization-members +// +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#list-public-organization-members +// +//meta:operation GET /orgs/{org}/members +//meta:operation GET /orgs/{org}/public_members +func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opts *ListMembersOptions) ([]*User, *Response, error) { var u string - if opt != nil && opt.PublicOnly { + if opts != nil && opts.PublicOnly { u = fmt.Sprintf("orgs/%v/public_members", org) } else { u = fmt.Sprintf("orgs/%v/members", org) } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var members []*User - resp, err := s.client.Do(ctx, req, &members) + resp, err := s.client.Do(req, &members) if err != nil { return nil, resp, err } @@ -100,72 +105,96 @@ func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opt // IsMember checks if a user is a member of an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#check-organization-membership-for-a-user +// +//meta:operation GET /orgs/{org}/members/{username} func (s *OrganizationsService) IsMember(ctx context.Context, org, user string) (bool, *Response, error) { u := fmt.Sprintf("orgs/%v/members/%v", org, user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) member, err := parseBoolResponse(err) return member, resp, err } // IsPublicMember checks if a user is a public member of an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-public-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#check-public-organization-membership-for-a-user +// +//meta:operation GET /orgs/{org}/public_members/{username} func (s *OrganizationsService) IsPublicMember(ctx context.Context, org, user string) (bool, *Response, error) { u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) member, err := parseBoolResponse(err) return member, resp, err } // RemoveMember removes a user from all teams of an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-a-member +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#remove-an-organization-member +// +//meta:operation DELETE /orgs/{org}/members/{username} func (s *OrganizationsService) RemoveMember(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/members/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// CancelInvite cancels an organization invitation. +// +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#cancel-an-organization-invitation +// +//meta:operation DELETE /orgs/{org}/invitations/{invitation_id} +func (s *OrganizationsService) CancelInvite(ctx context.Context, org string, invitationID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/invitations/%v", org, invitationID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(req, nil) } // PublicizeMembership publicizes a user's membership in an organization. (A // user cannot publicize the membership for another user.) // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#publicize-a-users-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#set-public-organization-membership-for-the-authenticated-user +// +//meta:operation PUT /orgs/{org}/public_members/{username} func (s *OrganizationsService) PublicizeMembership(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ConcealMembership conceals a user's membership in an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#conceal-a-users-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#remove-public-organization-membership-for-the-authenticated-user +// +//meta:operation DELETE /orgs/{org}/public_members/{username} func (s *OrganizationsService) ConcealMembership(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListOrgMembershipsOptions specifies optional parameters to the @@ -180,21 +209,23 @@ type ListOrgMembershipsOptions struct { // ListOrgMemberships lists the organization memberships for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships -func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opt *ListOrgMembershipsOptions) ([]*Membership, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#list-organization-memberships-for-the-authenticated-user +// +//meta:operation GET /user/memberships/orgs +func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opts *ListOrgMembershipsOptions) ([]*Membership, *Response, error) { u := "user/memberships/orgs" - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var memberships []*Membership - resp, err := s.client.Do(ctx, req, &memberships) + resp, err := s.client.Do(req, &memberships) if err != nil { return nil, resp, err } @@ -206,9 +237,12 @@ func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opt *List // Passing an empty string for user will get the membership for the // authenticated user. // -// GitHub API docs: -// https://developer.github.com/v3/orgs/members/#get-organization-membership -// https://developer.github.com/v3/orgs/members/#get-your-organization-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#get-an-organization-membership-for-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#get-organization-membership-for-a-user +// +//meta:operation GET /orgs/{org}/memberships/{username} +//meta:operation GET /user/memberships/orgs/{org} func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org string) (*Membership, *Response, error) { var u string if user != "" { @@ -217,13 +251,13 @@ func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org s u = fmt.Sprintf("user/memberships/orgs/%v", org) } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - membership := new(Membership) - resp, err := s.client.Do(ctx, req, membership) + var membership *Membership + resp, err := s.client.Do(req, &membership) if err != nil { return nil, resp, err } @@ -235,8 +269,12 @@ func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org s // Passing an empty string for user will edit the membership for the // authenticated user. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership -// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#set-organization-membership-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#update-an-organization-membership-for-the-authenticated-user +// +//meta:operation PUT /orgs/{org}/memberships/{username} +//meta:operation PATCH /user/memberships/orgs/{org} func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org string, membership *Membership) (*Membership, *Response, error) { var u, method string if user != "" { @@ -247,13 +285,13 @@ func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org method = "PATCH" } - req, err := s.client.NewRequest(method, u, membership) + req, err := s.client.NewRequest(ctx, method, u, membership) if err != nil { return nil, nil, err } - m := new(Membership) - resp, err := s.client.Do(ctx, req, m) + var m *Membership + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -264,37 +302,42 @@ func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org // RemoveOrgMembership removes user from the specified organization. If the // user has been invited to the organization, this will cancel their invitation. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-organization-membership +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#remove-organization-membership-for-a-user +// +//meta:operation DELETE /orgs/{org}/memberships/{username} func (s *OrganizationsService) RemoveOrgMembership(ctx context.Context, user, org string) (*Response, error) { u := fmt.Sprintf("orgs/%v/memberships/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListPendingOrgInvitations returns a list of pending invitations. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations -func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, org string, opt *ListOptions) ([]*Invitation, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#list-pending-organization-invitations +// +//meta:operation GET /orgs/{org}/invitations +func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, org string, opts *ListOptions) ([]*Invitation, *Response, error) { u := fmt.Sprintf("orgs/%v/invitations", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var pendingInvitations []*Invitation - resp, err := s.client.Do(ctx, req, &pendingInvitations) + resp, err := s.client.Do(req, &pendingInvitations) if err != nil { return nil, resp, err } + return pendingInvitations, resp, nil } @@ -314,57 +357,83 @@ type CreateOrgInvitationOptions struct { // * billing_manager - Non-owner organization members with ability to // manage the billing settings of your organization. // Default is "direct_member". - Role *string `json:"role"` - TeamID []int64 `json:"team_ids"` + Role *string `json:"role,omitempty"` + TeamID []int64 `json:"team_ids,omitempty"` } // CreateOrgInvitation invites people to an organization by using their GitHub user ID or their email address. // In order to create invitations in an organization, // the authenticated user must be an organization owner. // -// https://developer.github.com/v3/orgs/members/#create-organization-invitation -func (s *OrganizationsService) CreateOrgInvitation(ctx context.Context, org string, opt *CreateOrgInvitationOptions) (*Invitation, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#create-an-organization-invitation +// +//meta:operation POST /orgs/{org}/invitations +func (s *OrganizationsService) CreateOrgInvitation(ctx context.Context, org string, body *CreateOrgInvitationOptions) (*Invitation, *Response, error) { u := fmt.Sprintf("orgs/%v/invitations", org) - req, err := s.client.NewRequest("POST", u, opt) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeOrganizationInvitationPreview) - var invitation *Invitation - resp, err := s.client.Do(ctx, req, &invitation) + resp, err := s.client.Do(req, &invitation) if err != nil { return nil, resp, err } + return invitation, resp, nil } // ListOrgInvitationTeams lists all teams associated with an invitation. In order to see invitations in an organization, // the authenticated user must be an organization owner. // -// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-organization-invitation-teams -func (s *OrganizationsService) ListOrgInvitationTeams(ctx context.Context, org, invitationID string, opt *ListOptions) ([]*Team, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#list-organization-invitation-teams +// +//meta:operation GET /orgs/{org}/invitations/{invitation_id}/teams +func (s *OrganizationsService) ListOrgInvitationTeams(ctx context.Context, org, invitationID string, opts *ListOptions) ([]*Team, *Response, error) { u := fmt.Sprintf("orgs/%v/invitations/%v/teams", org, invitationID) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeOrganizationInvitationPreview) - var orgInvitationTeams []*Team - resp, err := s.client.Do(ctx, req, &orgInvitationTeams) + resp, err := s.client.Do(req, &orgInvitationTeams) if err != nil { return nil, resp, err } + return orgInvitationTeams, resp, nil } + +// ListFailedOrgInvitations returns a list of failed invitations. +// +// GitHub API docs: https://docs.github.com/rest/orgs/members?apiVersion=2022-11-28#list-failed-organization-invitations +// +//meta:operation GET /orgs/{org}/failed_invitations +func (s *OrganizationsService) ListFailedOrgInvitations(ctx context.Context, org string, opts *ListOptions) ([]*Invitation, *Response, error) { + u := fmt.Sprintf("orgs/%v/failed_invitations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var failedInvitations []*Invitation + resp, err := s.client.Do(req, &failedInvitations) + if err != nil { + return nil, resp, err + } + + return failedInvitations, resp, nil +} diff --git a/github/orgs_members_test.go b/github/orgs_members_test.go index e524d03f169..576620a77ec 100644 --- a/github/orgs_members_test.go +++ b/github/orgs_members_test.go @@ -6,18 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestOrganizationsService_ListMembers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -35,28 +33,44 @@ func TestOrganizationsService_ListMembers(t *testing.T) { Role: "admin", ListOptions: ListOptions{Page: 2}, } - members, _, err := client.Organizations.ListMembers(context.Background(), "o", opt) + ctx := t.Context() + members, _, err := client.Organizations.ListMembers(ctx, "o", opt) if err != nil { t.Errorf("Organizations.ListMembers returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(members, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) } + + const methodName = "ListMembers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListMembers(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListMembers(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_ListMembers_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.ListMembers(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Organizations.ListMembers(ctx, "%", nil) testURLParseError(t, err) } func TestOrganizationsService_ListMembers_public(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/public_members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -64,46 +78,63 @@ func TestOrganizationsService_ListMembers_public(t *testing.T) { }) opt := &ListMembersOptions{PublicOnly: true} - members, _, err := client.Organizations.ListMembers(context.Background(), "o", opt) + ctx := t.Context() + members, _, err := client.Organizations.ListMembers(ctx, "o", opt) if err != nil { t.Errorf("Organizations.ListMembers returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(members, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) } } func TestOrganizationsService_IsMember(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - member, _, err := client.Organizations.IsMember(context.Background(), "o", "u") + ctx := t.Context() + member, _, err := client.Organizations.IsMember(ctx, "o", "u") if err != nil { t.Errorf("Organizations.IsMember returned error: %v", err) } if want := true; member != want { t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) } + + const methodName = "IsMember" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.IsMember(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.IsMember(ctx, "o", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -// ensure that a 404 response is interpreted as "false" and not an error +// Ensure that a 404 response is interpreted as "false" and not an error. func TestOrganizationsService_IsMember_notMember(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - member, _, err := client.Organizations.IsMember(context.Background(), "o", "u") + ctx := t.Context() + member, _, err := client.Organizations.IsMember(ctx, "o", "u") if err != nil { t.Errorf("Organizations.IsMember returned error: %+v", err) } @@ -112,20 +143,21 @@ func TestOrganizationsService_IsMember_notMember(t *testing.T) { } } -// ensure that a 400 response is interpreted as an actual error, and not simply -// as "false" like the above case of a 404 +// Ensure that a 400 response is interpreted as an actual error, and not simply +// as "false" like the above case of a 404. func TestOrganizationsService_IsMember_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") http.Error(w, "BadRequest", http.StatusBadRequest) }) - member, _, err := client.Organizations.IsMember(context.Background(), "o", "u") + ctx := t.Context() + member, _, err := client.Organizations.IsMember(ctx, "o", "u") if err == nil { - t.Errorf("Expected HTTP 400 response") + t.Error("Expected HTTP 400 response") } if want := false; member != want { t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) @@ -133,42 +165,59 @@ func TestOrganizationsService_IsMember_error(t *testing.T) { } func TestOrganizationsService_IsMember_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.IsMember(context.Background(), "%", "u") + ctx := t.Context() + _, _, err := client.Organizations.IsMember(ctx, "%", "u") testURLParseError(t, err) } func TestOrganizationsService_IsPublicMember(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - member, _, err := client.Organizations.IsPublicMember(context.Background(), "o", "u") + ctx := t.Context() + member, _, err := client.Organizations.IsPublicMember(ctx, "o", "u") if err != nil { t.Errorf("Organizations.IsPublicMember returned error: %v", err) } if want := true; member != want { t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) } + + const methodName = "IsPublicMember" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.IsPublicMember(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.IsPublicMember(ctx, "o", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -// ensure that a 404 response is interpreted as "false" and not an error +// Ensure that a 404 response is interpreted as "false" and not an error. func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - member, _, err := client.Organizations.IsPublicMember(context.Background(), "o", "u") + ctx := t.Context() + member, _, err := client.Organizations.IsPublicMember(ctx, "o", "u") if err != nil { t.Errorf("Organizations.IsPublicMember returned error: %v", err) } @@ -177,20 +226,21 @@ func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) { } } -// ensure that a 400 response is interpreted as an actual error, and not simply -// as "false" like the above case of a 404 +// Ensure that a 400 response is interpreted as an actual error, and not simply +// as "false" like the above case of a 404. func TestOrganizationsService_IsPublicMember_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") http.Error(w, "BadRequest", http.StatusBadRequest) }) - member, _, err := client.Organizations.IsPublicMember(context.Background(), "o", "u") + ctx := t.Context() + member, _, err := client.Organizations.IsPublicMember(ctx, "o", "u") if err == nil { - t.Errorf("Expected HTTP 400 response") + t.Error("Expected HTTP 400 response") } if want := false; member != want { t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) @@ -198,38 +248,127 @@ func TestOrganizationsService_IsPublicMember_error(t *testing.T) { } func TestOrganizationsService_IsPublicMember_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.IsPublicMember(context.Background(), "%", "u") + ctx := t.Context() + _, _, err := client.Organizations.IsPublicMember(ctx, "%", "u") testURLParseError(t, err) } func TestOrganizationsService_RemoveMember(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/members/u", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Organizations.RemoveMember(context.Background(), "o", "u") + ctx := t.Context() + _, err := client.Organizations.RemoveMember(ctx, "o", "u") if err != nil { t.Errorf("Organizations.RemoveMember returned error: %v", err) } + + const methodName = "RemoveMember" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveMember(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveMember(ctx, "o", "u") + }) +} + +func TestOrganizationsService_CancelInvite(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/invitations/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Organizations.CancelInvite(ctx, "o", 1) + if err != nil { + t.Errorf("Organizations.CancelInvite returned error: %v", err) + } + + const methodName = "CancelInvite" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.CancelInvite(ctx, "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.CancelInvite(ctx, "o", 1) + }) } func TestOrganizationsService_RemoveMember_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Organizations.RemoveMember(context.Background(), "%", "u") + ctx := t.Context() + _, err := client.Organizations.RemoveMember(ctx, "%", "u") testURLParseError(t, err) } +func TestOrganizationsService_PublicizeMembership(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/public_members/u", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + ctx := t.Context() + _, err := client.Organizations.PublicizeMembership(ctx, "o", "u") + if err != nil { + t.Errorf("Organizations.PublicizeMembership returned error: %v", err) + } + + const methodName = "PublicizeMembership" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.PublicizeMembership(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.PublicizeMembership(ctx, "o", "u") + }) +} + +func TestOrganizationsService_ConcealMembership(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/public_members/u", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.ConcealMembership(ctx, "o", "u") + if err != nil { + t.Errorf("Organizations.ConcealMembership returned error: %v", err) + } + + const methodName = "ConcealMembership" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.ConcealMembership(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.ConcealMembership(ctx, "o", "u") + }) +} + func TestOrganizationsService_ListOrgMemberships(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/memberships/orgs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -244,133 +383,174 @@ func TestOrganizationsService_ListOrgMemberships(t *testing.T) { State: "active", ListOptions: ListOptions{Page: 2}, } - memberships, _, err := client.Organizations.ListOrgMemberships(context.Background(), opt) + ctx := t.Context() + memberships, _, err := client.Organizations.ListOrgMemberships(ctx, opt) if err != nil { t.Errorf("Organizations.ListOrgMemberships returned error: %v", err) } - want := []*Membership{{URL: String("u")}} - if !reflect.DeepEqual(memberships, want) { + want := []*Membership{{URL: Ptr("u")}} + if !cmp.Equal(memberships, want) { t.Errorf("Organizations.ListOrgMemberships returned %+v, want %+v", memberships, want) } + + const methodName = "ListOrgMemberships" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListOrgMemberships(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_GetOrgMembership_AuthenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"url":"u"}`) }) - membership, _, err := client.Organizations.GetOrgMembership(context.Background(), "", "o") + ctx := t.Context() + membership, _, err := client.Organizations.GetOrgMembership(ctx, "", "o") if err != nil { t.Errorf("Organizations.GetOrgMembership returned error: %v", err) } - want := &Membership{URL: String("u")} - if !reflect.DeepEqual(membership, want) { + want := &Membership{URL: Ptr("u")} + if !cmp.Equal(membership, want) { t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) } + + const methodName = "GetOrgMembership" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetOrgMembership(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetOrgMembership(ctx, "", "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_GetOrgMembership_SpecifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"url":"u"}`) }) - membership, _, err := client.Organizations.GetOrgMembership(context.Background(), "u", "o") + ctx := t.Context() + membership, _, err := client.Organizations.GetOrgMembership(ctx, "u", "o") if err != nil { t.Errorf("Organizations.GetOrgMembership returned error: %v", err) } - want := &Membership{URL: String("u")} - if !reflect.DeepEqual(membership, want) { + want := &Membership{URL: Ptr("u")} + if !cmp.Equal(membership, want) { t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) } } func TestOrganizationsService_EditOrgMembership_AuthenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Membership{State: String("active")} + input := &Membership{State: Ptr("active")} mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { - v := new(Membership) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"url":"u"}`) }) - membership, _, err := client.Organizations.EditOrgMembership(context.Background(), "", "o", input) + ctx := t.Context() + membership, _, err := client.Organizations.EditOrgMembership(ctx, "", "o", input) if err != nil { t.Errorf("Organizations.EditOrgMembership returned error: %v", err) } - want := &Membership{URL: String("u")} - if !reflect.DeepEqual(membership, want) { + want := &Membership{URL: Ptr("u")} + if !cmp.Equal(membership, want) { t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) } + + const methodName = "EditOrgMembership" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.EditOrgMembership(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.EditOrgMembership(ctx, "", "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_EditOrgMembership_SpecifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Membership{State: String("active")} + input := &Membership{State: Ptr("active")} mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { - v := new(Membership) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"url":"u"}`) }) - membership, _, err := client.Organizations.EditOrgMembership(context.Background(), "u", "o", input) + ctx := t.Context() + membership, _, err := client.Organizations.EditOrgMembership(ctx, "u", "o", input) if err != nil { t.Errorf("Organizations.EditOrgMembership returned error: %v", err) } - want := &Membership{URL: String("u")} - if !reflect.DeepEqual(membership, want) { + want := &Membership{URL: Ptr("u")} + if !cmp.Equal(membership, want) { t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) } } func TestOrganizationsService_RemoveOrgMembership(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Organizations.RemoveOrgMembership(context.Background(), "u", "o") + ctx := t.Context() + _, err := client.Organizations.RemoveOrgMembership(ctx, "u", "o") if err != nil { t.Errorf("Organizations.RemoveOrgMembership returned error: %v", err) } + + const methodName = "RemoveOrgMembership" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveOrgMembership(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveOrgMembership(ctx, "u", "o") + }) } func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/invitations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -381,7 +561,7 @@ func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) { "login": "monalisa", "email": "octocat@github.com", "role": "direct_member", - "created_at": "2017-01-21T00:00:00Z", + "created_at": `+referenceTimeStr+`, "inviter": { "login": "other_user", "id": 1, @@ -402,59 +582,75 @@ func TestOrganizationsService_ListPendingOrgInvitations(t *testing.T) { "site_admin": false }, "team_count": 2, - "invitation_team_url": "https://api.github.com/organizations/2/invitations/1/teams" + "invitation_team_url": "https://api.github.com/organizations/2/invitations/1/teams" } ]`) }) opt := &ListOptions{Page: 1} - invitations, _, err := client.Organizations.ListPendingOrgInvitations(context.Background(), "o", opt) + ctx := t.Context() + invitations, _, err := client.Organizations.ListPendingOrgInvitations(ctx, "o", opt) if err != nil { t.Errorf("Organizations.ListPendingOrgInvitations returned error: %v", err) } - createdAt := time.Date(2017, time.January, 21, 0, 0, 0, 0, time.UTC) want := []*Invitation{ { - ID: Int64(1), - Login: String("monalisa"), - Email: String("octocat@github.com"), - Role: String("direct_member"), - CreatedAt: &createdAt, + ID: Ptr(int64(1)), + Login: Ptr("monalisa"), + Email: Ptr("octocat@github.com"), + Role: Ptr("direct_member"), + CreatedAt: &referenceTimestamp, Inviter: &User{ - Login: String("other_user"), - ID: Int64(1), - AvatarURL: String("https://github.com/images/error/other_user_happy.gif"), - GravatarID: String(""), - URL: String("https://api.github.com/users/other_user"), - HTMLURL: String("https://github.com/other_user"), - FollowersURL: String("https://api.github.com/users/other_user/followers"), - FollowingURL: String("https://api.github.com/users/other_user/following/other_user"), - GistsURL: String("https://api.github.com/users/other_user/gists/gist_id"), - StarredURL: String("https://api.github.com/users/other_user/starred/owner/repo"), - SubscriptionsURL: String("https://api.github.com/users/other_user/subscriptions"), - OrganizationsURL: String("https://api.github.com/users/other_user/orgs"), - ReposURL: String("https://api.github.com/users/other_user/repos"), - EventsURL: String("https://api.github.com/users/other_user/events/privacy"), - ReceivedEventsURL: String("https://api.github.com/users/other_user/received_events/privacy"), - Type: String("User"), - SiteAdmin: Bool(false), + Login: Ptr("other_user"), + ID: Ptr(int64(1)), + AvatarURL: Ptr("https://github.com/images/error/other_user_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/other_user"), + HTMLURL: Ptr("https://github.com/other_user"), + FollowersURL: Ptr("https://api.github.com/users/other_user/followers"), + FollowingURL: Ptr("https://api.github.com/users/other_user/following/other_user"), + GistsURL: Ptr("https://api.github.com/users/other_user/gists/gist_id"), + StarredURL: Ptr("https://api.github.com/users/other_user/starred/owner/repo"), + SubscriptionsURL: Ptr("https://api.github.com/users/other_user/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/other_user/orgs"), + ReposURL: Ptr("https://api.github.com/users/other_user/repos"), + EventsURL: Ptr("https://api.github.com/users/other_user/events/privacy"), + ReceivedEventsURL: Ptr("https://api.github.com/users/other_user/received_events/privacy"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), }, - TeamCount: Int(2), - InvitationTeamURL: String("https://api.github.com/organizations/2/invitations/1/teams"), - }} + TeamCount: Ptr(2), + InvitationTeamURL: Ptr("https://api.github.com/organizations/2/invitations/1/teams"), + }, + } - if !reflect.DeepEqual(invitations, want) { + if !cmp.Equal(invitations, want) { t.Errorf("Organizations.ListPendingOrgInvitations returned %+v, want %+v", invitations, want) } + + const methodName = "ListPendingOrgInvitations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListPendingOrgInvitations(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListPendingOrgInvitations(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_CreateOrgInvitation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + input := &CreateOrgInvitationOptions{ - Email: String("octocat@github.com"), - Role: String("direct_member"), + Email: Ptr("octocat@github.com"), + Role: Ptr("direct_member"), TeamID: []int64{ 12, 26, @@ -462,38 +658,44 @@ func TestOrganizationsService_CreateOrgInvitation(t *testing.T) { } mux.HandleFunc("/orgs/o/invitations", func(w http.ResponseWriter, r *http.Request) { - v := new(CreateOrgInvitationOptions) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeOrganizationInvitationPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprintln(w, `{"email": "octocat@github.com"}`) - }) - invitations, _, err := client.Organizations.CreateOrgInvitation(context.Background(), "o", input) + ctx := t.Context() + invitations, _, err := client.Organizations.CreateOrgInvitation(ctx, "o", input) if err != nil { t.Errorf("Organizations.CreateOrgInvitation returned error: %v", err) } - want := &Invitation{Email: String("octocat@github.com")} - if !reflect.DeepEqual(invitations, want) { - t.Errorf("Organizations.ListPendingOrgInvitations returned %+v, want %+v", invitations, want) + want := &Invitation{Email: Ptr("octocat@github.com")} + if !cmp.Equal(invitations, want) { + t.Errorf("Organizations.CreateOrgInvitation returned %+v, want %+v", invitations, want) } + + const methodName = "CreateOrgInvitation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateOrgInvitation(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateOrgInvitation(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_ListOrgInvitationTeams(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/invitations/22/teams", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"page": "1"}) - testHeader(t, r, "Accept", mediaTypeOrganizationInvitationPreview) fmt.Fprint(w, `[ { "id": 1, @@ -510,26 +712,145 @@ func TestOrganizationsService_ListOrgInvitationTeams(t *testing.T) { }) opt := &ListOptions{Page: 1} - invitations, _, err := client.Organizations.ListOrgInvitationTeams(context.Background(), "o", "22", opt) + ctx := t.Context() + invitations, _, err := client.Organizations.ListOrgInvitationTeams(ctx, "o", "22", opt) if err != nil { t.Errorf("Organizations.ListOrgInvitationTeams returned error: %v", err) } want := []*Team{ { - ID: Int64(1), - URL: String("https://api.github.com/teams/1"), - Name: String("Justice League"), - Slug: String("justice-league"), - Description: String("A great team."), - Privacy: String("closed"), - Permission: String("admin"), - MembersURL: String("https://api.github.com/teams/1/members{/member}"), - RepositoriesURL: String("https://api.github.com/teams/1/repos"), + ID: Ptr(int64(1)), + URL: Ptr("https://api.github.com/teams/1"), + Name: Ptr("Justice League"), + Slug: Ptr("justice-league"), + Description: Ptr("A great team."), + Privacy: Ptr("closed"), + Permission: Ptr("admin"), + MembersURL: Ptr("https://api.github.com/teams/1/members{/member}"), + RepositoriesURL: Ptr("https://api.github.com/teams/1/repos"), }, } - if !reflect.DeepEqual(invitations, want) { + if !cmp.Equal(invitations, want) { t.Errorf("Organizations.ListOrgInvitationTeams returned %+v, want %+v", invitations, want) } + + const methodName = "ListOrgInvitationTeams" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListOrgInvitationTeams(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListOrgInvitationTeams(ctx, "o", "22", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListFailedOrgInvitations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/failed_invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2", "per_page": "1"}) + fmt.Fprint(w, `[ + { + "id":1, + "login":"monalisa", + "node_id":"MDQ6VXNlcjE=", + "email":"octocat@github.com", + "role":"direct_member", + "created_at":`+refTimeStr(1136178000)+`, + "failed_at":`+refTimeStr(1136178001)+`, + "failed_reason":"the reason", + "inviter":{ + "login":"other_user", + "id":1, + "node_id":"MDQ6VXNlcjE=", + "avatar_url":"https://github.com/images/error/other_user_happy.gif", + "gravatar_id":"", + "url":"https://api.github.com/users/other_user", + "html_url":"https://github.com/other_user", + "followers_url":"https://api.github.com/users/other_user/followers", + "following_url":"https://api.github.com/users/other_user/following{/other_user}", + "gists_url":"https://api.github.com/users/other_user/gists{/gist_id}", + "starred_url":"https://api.github.com/users/other_user/starred{/owner}{/repo}", + "subscriptions_url":"https://api.github.com/users/other_user/subscriptions", + "organizations_url":"https://api.github.com/users/other_user/orgs", + "repos_url":"https://api.github.com/users/other_user/repos", + "events_url":"https://api.github.com/users/other_user/events{/privacy}", + "received_events_url":"https://api.github.com/users/other_user/received_events", + "type":"User", + "site_admin":false + }, + "team_count":2, + "invitation_team_url":"https://api.github.com/organizations/2/invitations/1/teams" + } + ]`) + }) + + opts := &ListOptions{Page: 2, PerPage: 1} + ctx := t.Context() + failedInvitations, _, err := client.Organizations.ListFailedOrgInvitations(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListFailedOrgInvitations returned error: %v", err) + } + + want := []*Invitation{ + { + ID: Ptr(int64(1)), + Login: Ptr("monalisa"), + NodeID: Ptr("MDQ6VXNlcjE="), + Email: Ptr("octocat@github.com"), + Role: Ptr("direct_member"), + CreatedAt: refTimestamp(1136178000), + FailedAt: refTimestamp(1136178001), + FailedReason: Ptr("the reason"), + Inviter: &User{ + Login: Ptr("other_user"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VXNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/other_user_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/other_user"), + HTMLURL: Ptr("https://github.com/other_user"), + FollowersURL: Ptr("https://api.github.com/users/other_user/followers"), + FollowingURL: Ptr("https://api.github.com/users/other_user/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/other_user/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/other_user/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/other_user/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/other_user/orgs"), + ReposURL: Ptr("https://api.github.com/users/other_user/repos"), + EventsURL: Ptr("https://api.github.com/users/other_user/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/other_user/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + TeamCount: Ptr(2), + InvitationTeamURL: Ptr("https://api.github.com/organizations/2/invitations/1/teams"), + }, + } + + if !cmp.Equal(failedInvitations, want) { + t.Errorf("Organizations.ListFailedOrgInvitations returned %+v, want %+v", failedInvitations, want) + } + + const methodName = "ListFailedOrgInvitations" + testBadOptions(t, methodName, func() error { + _, _, err := client.Organizations.ListFailedOrgInvitations(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListFailedOrgInvitations(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/orgs_network_configurations.go b/github/orgs_network_configurations.go new file mode 100644 index 00000000000..0de09e97bbf --- /dev/null +++ b/github/orgs_network_configurations.go @@ -0,0 +1,239 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" + "regexp" +) + +// ComputeService represents a hosted compute service the network configuration supports. +type ComputeService string + +const ( + ComputeServiceNone ComputeService = "none" + ComputeServiceActions ComputeService = "actions" + ComputeServiceCodespaces ComputeService = "codespaces" +) + +// NetworkConfigurations represents a hosted compute network configuration. This type is identical +// for enterprise and organization endpoints. +type NetworkConfigurations struct { + TotalCount *int64 `json:"total_count,omitempty"` + NetworkConfigurations []*NetworkConfiguration `json:"network_configurations,omitempty"` +} + +// NetworkConfiguration represents a hosted compute network configurations. This type is identical +// for enterprise and organization endpoints. +type NetworkConfiguration struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ComputeService *ComputeService `json:"compute_service,omitempty"` + NetworkSettingsIDs []string `json:"network_settings_ids,omitempty"` + CreatedOn *Timestamp `json:"created_on"` +} + +// NetworkSettingsResource represents a hosted compute network settings resource. This type is identical +// for enterprise and organization endpoints. +type NetworkSettingsResource struct { + ID *string `json:"id,omitempty"` + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` + Name *string `json:"name,omitempty"` + SubnetID *string `json:"subnet_id,omitempty"` + Region *string `json:"region,omitempty"` +} + +func validateComputeService(compute *ComputeService) error { + if compute == nil { + return nil + } + if *compute != ComputeServiceNone && *compute != ComputeServiceActions { + return errors.New("compute service can only be one of: none, actions") + } + return nil +} + +var validNetworkNameRE = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`) + +func validateNetworkName(name string) error { + if len(name) < 1 || len(name) > 100 { + return errors.New("must be between 1 and 100 characters") + } + if !validNetworkNameRE.MatchString(name) { + return errors.New("may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'") + } + return nil +} + +func validateNetworkSettingsID(settingsID []string) error { + if len(settingsID) != 1 { + return errors.New("exactly one network settings id must be specified") + } + return nil +} + +func validateNetworkConfigurationRequest(req NetworkConfigurationRequest) error { + networkName := req.GetName() + if err := validateNetworkName(networkName); err != nil { + return err + } + + computeService := req.GetComputeService() + if err := validateComputeService(computeService); err != nil { + return err + } + + networkIDs := req.NetworkSettingsIDs + return validateNetworkSettingsID(networkIDs) +} + +// NetworkConfigurationRequest represents a request to create or update a network configuration for an organization. +type NetworkConfigurationRequest struct { + Name *string `json:"name,omitempty"` + ComputeService *ComputeService `json:"compute_service,omitempty"` + NetworkSettingsIDs []string `json:"network_settings_ids,omitempty"` +} + +// ListNetworkConfigurations lists all hosted compute network configurations configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations?apiVersion=2022-11-28#list-hosted-compute-network-configurations-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/network-configurations +func (s *OrganizationsService) ListNetworkConfigurations(ctx context.Context, org string, opts *ListOptions) (*NetworkConfigurations, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-configurations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configurations *NetworkConfigurations + resp, err := s.client.Do(req, &configurations) + if err != nil { + return nil, resp, err + } + + return configurations, resp, nil +} + +// CreateNetworkConfiguration creates a hosted compute network configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations?apiVersion=2022-11-28#create-a-hosted-compute-network-configuration-for-an-organization +// +//meta:operation POST /orgs/{org}/settings/network-configurations +func (s *OrganizationsService) CreateNetworkConfiguration(ctx context.Context, org string, body NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(body); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/settings/network-configurations", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var configuration *NetworkConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + + return configuration, resp, nil +} + +// GetNetworkConfiguration gets a hosted compute network configuration configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations?apiVersion=2022-11-28#get-a-hosted-compute-network-configuration-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/network-configurations/{network_configuration_id} +func (s *OrganizationsService) GetNetworkConfiguration(ctx context.Context, org, networkID string) (*NetworkConfiguration, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-configurations/%v", org, networkID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var configuration *NetworkConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + + return configuration, resp, nil +} + +// UpdateNetworkConfiguration updates a hosted compute network configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations?apiVersion=2022-11-28#update-a-hosted-compute-network-configuration-for-an-organization +// +//meta:operation PATCH /orgs/{org}/settings/network-configurations/{network_configuration_id} +func (s *OrganizationsService) UpdateNetworkConfiguration(ctx context.Context, org, networkID string, body NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(body); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/settings/network-configurations/%v", org, networkID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var configuration *NetworkConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return nil, resp, err + } + + return configuration, resp, nil +} + +// DeleteNetworkConfigurations deletes a hosted compute network configuration from an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations?apiVersion=2022-11-28#delete-a-hosted-compute-network-configuration-from-an-organization +// +//meta:operation DELETE /orgs/{org}/settings/network-configurations/{network_configuration_id} +func (s *OrganizationsService) DeleteNetworkConfigurations(ctx context.Context, org, networkID string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-configurations/%v", org, networkID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + var configuration *NetworkConfiguration + resp, err := s.client.Do(req, &configuration) + if err != nil { + return resp, err + } + + return resp, nil +} + +// GetNetworkConfigurationResource gets a hosted compute network settings resource configured for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations?apiVersion=2022-11-28#get-a-hosted-compute-network-settings-resource-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/network-settings/{network_settings_id} +func (s *OrganizationsService) GetNetworkConfigurationResource(ctx context.Context, org, networkID string) (*NetworkSettingsResource, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-settings/%v", org, networkID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var resource *NetworkSettingsResource + resp, err := s.client.Do(req, &resource) + if err != nil { + return nil, resp, err + } + + return resource, resp, nil +} diff --git a/github/orgs_network_configurations_test.go b/github/orgs_network_configurations_test.go new file mode 100644 index 00000000000..cb2767cfe51 --- /dev/null +++ b/github/orgs_network_configurations_test.go @@ -0,0 +1,487 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListOrgsNetworkConfigurations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/network-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1", "per_page": "3"}) + fmt.Fprint(w, `{ + "total_count": 3, + "network_configurations": [ + { + "id": "123456789ABCDEF", + "name": "Network Configuration One", + "compute_service": "actions", + "network_settings_ids": [ + "23456789ABDCEF1", + "3456789ABDCEF12" + ], + "created_on": `+referenceTimeStr+` + }, + { + "id": "456789ABDCEF123", + "name": "Network Configuration Two", + "compute_service": "none", + "network_settings_ids": [ + "56789ABDCEF1234", + "6789ABDCEF12345" + ], + "created_on": `+referenceTimeStr+` + }, + { + "id": "789ABDCEF123456", + "name": "Network Configuration Three", + "compute_service": "codespaces", + "network_settings_ids": [ + "56789ABDCEF1234", + "6789ABDCEF12345" + ], + "created_on": `+referenceTimeStr+` + } + ] + }`) + }) + + ctx := t.Context() + + opts := &ListOptions{Page: 1, PerPage: 3} + configurations, _, err := client.Organizations.ListNetworkConfigurations(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListNetworkConfigurations returned error %v", err) + } + want := &NetworkConfigurations{ + TotalCount: Ptr(int64(3)), + NetworkConfigurations: []*NetworkConfiguration{ + { + ID: Ptr("123456789ABCDEF"), + Name: Ptr("Network Configuration One"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{ + "23456789ABDCEF1", + "3456789ABDCEF12", + }, + CreatedOn: &referenceTimestamp, + }, + { + ID: Ptr("456789ABDCEF123"), + Name: Ptr("Network Configuration Two"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + "6789ABDCEF12345", + }, + CreatedOn: &referenceTimestamp, + }, + { + ID: Ptr("789ABDCEF123456"), + Name: Ptr("Network Configuration Three"), + ComputeService: Ptr(ComputeService("codespaces")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + "6789ABDCEF12345", + }, + CreatedOn: &referenceTimestamp, + }, + }, + } + if !cmp.Equal(want, configurations) { + t.Errorf("Organizations.ListNetworkConfigurations mismatch (-want +got):\n%v", cmp.Diff(want, configurations)) + } + + const methodName = "ListNetworkConfigurations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListNetworkConfigurations(ctx, "\no", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListNetworkConfigurations(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateOrgsNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/network-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": "456789ABDCEF123", + "name": "network-configuration-two", + "compute_service": "none", + "network_settings_ids": [ + "56789ABDCEF1234" + ], + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + + req := NetworkConfigurationRequest{ + Name: Ptr("network-configuration-two"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + } + + configuration, _, err := client.Organizations.CreateNetworkConfiguration(ctx, "o", req) + if err != nil { + t.Errorf("Organizations.CreateNetworkConfiguration returned error %v", err) + } + + want := &NetworkConfiguration{ + ID: Ptr("456789ABDCEF123"), + Name: Ptr("network-configuration-two"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + CreatedOn: &referenceTimestamp, + } + + if !cmp.Equal(want, configuration) { + t.Errorf("Organizations.CreateNetworkConfiguration mismatch (-want +got):\n%v", cmp.Diff(want, configuration)) + } + + validationTests := []struct { + name string + request NetworkConfigurationRequest + want string + }{ + { + name: "invalid network configuration name length", + request: NetworkConfigurationRequest{ + Name: Ptr(""), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + }, + want: "validation failed: must be between 1 and 100 characters", + }, + { + name: "invalid network configuration name", + // may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'. + request: NetworkConfigurationRequest{ + Name: Ptr("network configuration two"), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + }, + want: "validation failed: may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'", + }, + { + name: "invalid network settings ids", + request: NetworkConfigurationRequest{ + Name: Ptr("network-configuration-two"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + "3456789ABDCEF12", + }, + }, + want: "validation failed: exactly one network settings id must be specified", + }, + { + name: "invalid compute service", + request: NetworkConfigurationRequest{ + Name: Ptr("network-configuration-two"), + ComputeService: Ptr(ComputeService("codespaces")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + }, + want: "validation failed: compute service can only be one of: none, actions", + }, + } + + for _, tc := range validationTests { + _, _, err := client.Organizations.CreateNetworkConfiguration(ctx, "o", tc.request) + if err == nil || err.Error() != tc.want { + t.Errorf("expected error to be %v, got %v", tc.want, err) + } + } + + const methodName = "CreateNetworkConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateNetworkConfiguration(ctx, "\no", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateNetworkConfiguration(ctx, "o", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetOrgsNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/network-configurations/789ABDCEF123456", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "789ABDCEF123456", + "name": "Network Configuration Three", + "compute_service": "codespaces", + "network_settings_ids": [ + "56789ABDCEF1234", + "6789ABDCEF12345" + ], + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + + configuration, _, err := client.Organizations.GetNetworkConfiguration(ctx, "o", "789ABDCEF123456") + if err != nil { + t.Errorf("Organizations.GetNetworkConfiguration returned error: %v", err) + } + want := &NetworkConfiguration{ + ID: Ptr("789ABDCEF123456"), + Name: Ptr("Network Configuration Three"), + ComputeService: Ptr(ComputeService("codespaces")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + "6789ABDCEF12345", + }, + CreatedOn: &referenceTimestamp, + } + if !cmp.Equal(want, configuration) { + t.Errorf("Organizations.GetNetworkConfiguration mismatch (-want +got):\n%v", cmp.Diff(want, configuration)) + } + + const methodName = "GetNetworkConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetNetworkConfiguration(ctx, "\no", "789ABDCEF123456") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetNetworkConfiguration(ctx, "o", "789ABDCEF123456") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateOrgsNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/network-configurations/789ABDCEF123456", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{ + "id": "789ABDCEF123456", + "name": "Network Configuration Three Update", + "compute_service": "actions", + "network_settings_ids": [ + "56789ABDCEF1234", + "6789ABDCEF12345" + ], + "created_on": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + + req := NetworkConfigurationRequest{ + Name: Ptr("network-configuration-three-update"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + } + configuration, _, err := client.Organizations.UpdateNetworkConfiguration(ctx, "o", "789ABDCEF123456", req) + if err != nil { + t.Errorf("Organizations.UpdateNetworkConfiguration returned error: %v", err) + } + + want := &NetworkConfiguration{ + ID: Ptr("789ABDCEF123456"), + Name: Ptr("Network Configuration Three Update"), + ComputeService: Ptr(ComputeService("actions")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + "6789ABDCEF12345", + }, + CreatedOn: &referenceTimestamp, + } + if !cmp.Equal(want, configuration) { + t.Errorf("Organizations.UpdateNetworkConfiguration mismatch (-want +got):\n%v", cmp.Diff(want, configuration)) + } + + validationTests := []struct { + name string + request NetworkConfigurationRequest + want string + }{ + { + name: "invalid network configuration name length", + request: NetworkConfigurationRequest{ + Name: Ptr(""), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + }, + want: "validation failed: must be between 1 and 100 characters", + }, + { + name: "invalid network configuration name", + // may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'. + request: NetworkConfigurationRequest{ + Name: Ptr("network configuration three update"), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + }, + want: "validation failed: may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'", + }, + { + name: "invalid network settings ids", + request: NetworkConfigurationRequest{ + Name: Ptr("network-configuration-three-update"), + ComputeService: Ptr(ComputeService("none")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + "3456789ABDCEF12", + }, + }, + want: "validation failed: exactly one network settings id must be specified", + }, + { + name: "invalid compute service", + request: NetworkConfigurationRequest{ + Name: Ptr("network-configuration-three-update"), + ComputeService: Ptr(ComputeService("codespaces")), + NetworkSettingsIDs: []string{ + "56789ABDCEF1234", + }, + }, + want: "validation failed: compute service can only be one of: none, actions", + }, + } + + for _, tc := range validationTests { + _, _, err := client.Organizations.UpdateNetworkConfiguration(ctx, "o", "789ABDCEF123456", tc.request) + if err == nil || err.Error() != tc.want { + t.Errorf("expected error to be %v, got %v", tc.want, err) + } + } + + const methodName = "UpdateNetworkConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateNetworkConfiguration(ctx, "\no", "789ABDCEF123456", req) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateNetworkConfiguration(ctx, "o", "789ABDCEF123456", req) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteOrgsNetworkConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/network-configurations/789ABDCEF123456", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.DeleteNetworkConfigurations(ctx, "o", "789ABDCEF123456") + if err != nil { + t.Errorf("Organizations.DeleteNetworkConfigurations returned error %v", err) + } + + const methodName = "DeleteNetworkConfigurations" + testBadOptions(t, methodName, func() error { + _, err = client.Organizations.DeleteNetworkConfigurations(ctx, "\ne", "123456789ABCDEF") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DeleteNetworkConfigurations(ctx, "e", "123456789ABCDEF") + }) +} + +func TestOrganizationsService_GetOrgsNetworkConfigurationResource(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/settings/network-settings/789ABDCEF123456", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": "220F78DACB92BBFBC5E6F22DE1CCF52309D", + "network_configuration_id": "934E208B3EE0BD60CF5F752C426BFB53562", + "name": "my_network_settings", + "subnet_id": "/subscriptions/14839728-3ad9-43ab-bd2b-fa6ad0f75e2a/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet", + "region": "germanywestcentral" + } + `) + }) + + ctx := t.Context() + + resource, _, err := client.Organizations.GetNetworkConfigurationResource(ctx, "o", "789ABDCEF123456") + if err != nil { + t.Errorf("Organizations.GetNetworkConfigurationResource returned error %v", err) + } + + want := &NetworkSettingsResource{ + ID: Ptr("220F78DACB92BBFBC5E6F22DE1CCF52309D"), + Name: Ptr("my_network_settings"), + NetworkConfigurationID: Ptr("934E208B3EE0BD60CF5F752C426BFB53562"), + SubnetID: Ptr("/subscriptions/14839728-3ad9-43ab-bd2b-fa6ad0f75e2a/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet"), + Region: Ptr("germanywestcentral"), + } + + if !cmp.Equal(want, resource) { + t.Errorf("Organizations.GetNetworkConfigurationResource mismatch (-want +got):\n%v", cmp.Diff(want, resource)) + } + + const methodName = "GetNetworkConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetNetworkConfigurationResource(ctx, "\no", "789ABDCEF123456") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetNetworkConfigurationResource(ctx, "o", "789ABDCEF123456") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_organization_properties.go b/github/orgs_organization_properties.go new file mode 100644 index 00000000000..c5954e2d0a3 --- /dev/null +++ b/github/orgs_organization_properties.go @@ -0,0 +1,60 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// OrganizationCustomPropertyValues represents the custom property values for an organization. +type OrganizationCustomPropertyValues struct { + // List of custom property names and associated values to apply to the organization. + Properties []*CustomPropertyValue `json:"properties,omitempty"` +} + +// GetOrganizationCustomPropertyValues returns all custom property names and their values for an organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-properties-for-orgs?apiVersion=2022-11-28#get-all-custom-property-values-for-an-organization +// +//meta:operation GET /organizations/{org}/org-properties/values +func (s *OrganizationsService) GetOrganizationCustomPropertyValues(ctx context.Context, org string) ([]*CustomPropertyValue, *Response, error) { + u := fmt.Sprintf("organizations/%v/org-properties/values", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var values []*CustomPropertyValue + resp, err := s.client.Do(req, &values) + if err != nil { + return nil, resp, err + } + + return values, resp, nil +} + +// CreateOrUpdateOrganizationCustomPropertyValues creates or updates custom property values for an organization. +// To remove a custom property value from an organization, set the property value to null. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-properties-for-orgs?apiVersion=2022-11-28#create-or-update-custom-property-values-for-an-organization +// +//meta:operation PATCH /organizations/{org}/org-properties/values +func (s *OrganizationsService) CreateOrUpdateOrganizationCustomPropertyValues(ctx context.Context, org string, body OrganizationCustomPropertyValues) (*Response, error) { + u := fmt.Sprintf("organizations/%v/org-properties/values", org) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/orgs_organization_properties_test.go b/github/orgs_organization_properties_test.go new file mode 100644 index 00000000000..e7bc715c164 --- /dev/null +++ b/github/orgs_organization_properties_test.go @@ -0,0 +1,94 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_GetOrganizationCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/o/org-properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "property_name": "team", + "value": "core" + }, + { + "property_name": "level", + "value": "gold" + }]`) + }) + + ctx := t.Context() + got, _, err := client.Organizations.GetOrganizationCustomPropertyValues(ctx, "o") + if err != nil { + t.Fatalf("Organizations.GetOrganizationCustomPropertyValues returned error: %v", err) + } + + want := []*CustomPropertyValue{ + {PropertyName: "team", Value: "core"}, + {PropertyName: "level", Value: "gold"}, + } + + if !cmp.Equal(got, want) { + t.Errorf("Organizations.GetOrganizationCustomPropertyValues = %+v, want %+v", got, want) + } + + const methodName = "GetOrganizationCustomPropertyValues" + + testBadOptions(t, methodName, func() error { + _, _, err := client.Organizations.GetOrganizationCustomPropertyValues(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetOrganizationCustomPropertyValues(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateOrUpdateOrganizationCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/o/org-properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{}`) + }) + + ctx := t.Context() + values := []*CustomPropertyValue{ + {PropertyName: "team", Value: Ptr("core")}, + {PropertyName: "level", Value: Ptr("gold")}, + } + + props := OrganizationCustomPropertyValues{ + Properties: values, + } + _, err := client.Organizations.CreateOrUpdateOrganizationCustomPropertyValues(ctx, "o", props) + if err != nil { + t.Errorf("Organizations.CreateOrUpdateOrganizationCustomPropertyValues returned error: %v", err) + } + + const methodName = "CreateOrUpdateOrganizationCustomPropertyValues" + testBadOptions(t, methodName, func() error { + _, err := client.Organizations.CreateOrUpdateOrganizationCustomPropertyValues(ctx, "\n", props) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.CreateOrUpdateOrganizationCustomPropertyValues(ctx, "o", props) + }) +} diff --git a/github/orgs_organization_roles.go b/github/orgs_organization_roles.go new file mode 100644 index 00000000000..36cf57b66f3 --- /dev/null +++ b/github/orgs_organization_roles.go @@ -0,0 +1,337 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// OrganizationCustomRoles represents custom organization roles available in specified organization. +type OrganizationCustomRoles struct { + TotalCount *int `json:"total_count,omitempty"` + CustomRepoRoles []*CustomOrgRole `json:"roles,omitempty"` +} + +// CustomOrgRole represents custom organization role available in specified organization. +type CustomOrgRole struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Permissions []string `json:"permissions,omitempty"` + Org *Organization `json:"organization,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Source *string `json:"source,omitempty"` + BaseRole *string `json:"base_role,omitempty"` +} + +// CreateCustomOrgRoleRequest represents body parameters required to create a custom organization role. +type CreateCustomOrgRoleRequest struct { + Name string `json:"name"` + Description *string `json:"description,omitempty"` + Permissions []string `json:"permissions"` + BaseRole *string `json:"base_role,omitempty"` // Can be one of: read, triage, write, maintain, admin +} + +// UpdateCustomOrgRoleRequest represents body parameters to update a custom organization role. +type UpdateCustomOrgRoleRequest struct { + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Permissions []string `json:"permissions,omitempty"` + BaseRole *string `json:"base_role,omitempty"` // Can be one of: none, read, triage, write, maintain, admin +} + +// OrganizationFineGrainedPermission represents a fine-grained permission that protects organization resources. +type OrganizationFineGrainedPermission struct { + Name string `json:"name"` + Description string `json:"description"` +} + +// ListRoles lists the custom roles available in this organization. +// In order to see custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#get-all-organization-roles-for-an-organization +// +//meta:operation GET /orgs/{org}/organization-roles +func (s *OrganizationsService) ListRoles(ctx context.Context, org string) (*OrganizationCustomRoles, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customRepoRoles *OrganizationCustomRoles + resp, err := s.client.Do(req, &customRepoRoles) + if err != nil { + return nil, resp, err + } + + return customRepoRoles, resp, nil +} + +// GetOrgRole gets an organization role in this organization. +// In order to get organization roles in an organization, the authenticated user must be an organization owner, or have access via an organization role. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#get-an-organization-role +// +//meta:operation GET /orgs/{org}/organization-roles/{role_id} +func (s *OrganizationsService) GetOrgRole(ctx context.Context, org string, roleID int64) (*CustomOrgRole, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v", org, roleID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var resultingRole *CustomOrgRole + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// CreateCustomOrgRole creates a custom role in this organization. +// In order to create custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles?apiVersion=2022-11-28#create-a-custom-organization-role +// +//meta:operation POST /orgs/{org}/organization-roles +func (s *OrganizationsService) CreateCustomOrgRole(ctx context.Context, org string, body CreateCustomOrgRoleRequest) (*CustomOrgRole, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var resultingRole *CustomOrgRole + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// UpdateCustomOrgRole updates a custom role in this organization. +// In order to update custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles?apiVersion=2022-11-28#update-a-custom-organization-role +// +//meta:operation PATCH /orgs/{org}/organization-roles/{role_id} +func (s *OrganizationsService) UpdateCustomOrgRole(ctx context.Context, org string, roleID int64, body UpdateCustomOrgRoleRequest) (*CustomOrgRole, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v", org, roleID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var resultingRole *CustomOrgRole + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return nil, resp, err + } + + return resultingRole, resp, err +} + +// DeleteCustomOrgRole deletes an existing custom role in this organization. +// In order to delete custom roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles?apiVersion=2022-11-28#delete-a-custom-organization-role +// +//meta:operation DELETE /orgs/{org}/organization-roles/{role_id} +func (s *OrganizationsService) DeleteCustomOrgRole(ctx context.Context, org string, roleID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v", org, roleID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + var resultingRole *CustomOrgRole + resp, err := s.client.Do(req, &resultingRole) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AssignOrgRoleToTeam assigns an existing organization role to a team in this organization. +// In order to assign organization roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#assign-an-organization-role-to-a-team +// +//meta:operation PUT /orgs/{org}/organization-roles/teams/{team_slug}/{role_id} +func (s *OrganizationsService) AssignOrgRoleToTeam(ctx context.Context, org, teamSlug string, roleID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/teams/%v/%v", org, teamSlug, roleID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// RemoveOrgRoleFromTeam removes an existing organization role assignment from a team in this organization. +// In order to remove organization role assignments in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#remove-an-organization-role-from-a-team +// +//meta:operation DELETE /orgs/{org}/organization-roles/teams/{team_slug}/{role_id} +func (s *OrganizationsService) RemoveOrgRoleFromTeam(ctx context.Context, org, teamSlug string, roleID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/teams/%v/%v", org, teamSlug, roleID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AssignOrgRoleToUser assigns an existing organization role to a user in this organization. +// In order to assign organization roles in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#assign-an-organization-role-to-a-user +// +//meta:operation PUT /orgs/{org}/organization-roles/users/{username}/{role_id} +func (s *OrganizationsService) AssignOrgRoleToUser(ctx context.Context, org, username string, roleID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/users/%v/%v", org, username, roleID) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// RemoveOrgRoleFromUser removes an existing organization role assignment from a user in this organization. +// In order to remove organization role assignments in an organization, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#remove-an-organization-role-from-a-user +// +//meta:operation DELETE /orgs/{org}/organization-roles/users/{username}/{role_id} +func (s *OrganizationsService) RemoveOrgRoleFromUser(ctx context.Context, org, username string, roleID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/users/%v/%v", org, username, roleID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// ListTeamsAssignedToOrgRole returns all teams assigned to a specific organization role. +// In order to list teams assigned to an organization role, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#list-teams-that-are-assigned-to-an-organization-role +// +//meta:operation GET /orgs/{org}/organization-roles/{role_id}/teams +func (s *OrganizationsService) ListTeamsAssignedToOrgRole(ctx context.Context, org string, roleID int64, opts *ListOptions) ([]*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v/teams", org, roleID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var teams []*Team + resp, err := s.client.Do(req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// ListUsersAssignedToOrgRole returns all users assigned to a specific organization role. +// In order to list users assigned to an organization role, the authenticated user must be an organization owner. +// +// GitHub API docs: https://docs.github.com/rest/orgs/organization-roles?apiVersion=2022-11-28#list-users-that-are-assigned-to-an-organization-role +// +//meta:operation GET /orgs/{org}/organization-roles/{role_id}/users +func (s *OrganizationsService) ListUsersAssignedToOrgRole(ctx context.Context, org string, roleID int64, opts *ListOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-roles/%v/users", org, roleID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var users []*User + resp, err := s.client.Do(req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// ListFineGrainedPermissions lists the fine-grained permissions that can be used in custom organization roles for an organization. +// +// To use this endpoint, the authenticated user must be one of: +// - An administrator for the organization. +// - A user, or a user on a team, with the fine-grained permissions of `read_organization_custom_org_role` in the organization. +// +// OAuth app tokens and personal access tokens (classic) need the `admin:org` scope to use this endpoint. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles?apiVersion=2022-11-28#list-organization-fine-grained-permissions-for-an-organization +// +//meta:operation GET /orgs/{org}/organization-fine-grained-permissions +func (s *OrganizationsService) ListFineGrainedPermissions(ctx context.Context, org string) ([]*OrganizationFineGrainedPermission, *Response, error) { + u := fmt.Sprintf("orgs/%v/organization-fine-grained-permissions", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions []*OrganizationFineGrainedPermission + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} diff --git a/github/orgs_organization_roles_test.go b/github/orgs_organization_roles_test.go new file mode 100644 index 00000000000..e1300ad5356 --- /dev/null +++ b/github/orgs_organization_roles_test.go @@ -0,0 +1,531 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListRoles(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count": 1, "roles": [ + { + "id": 1, + "name": "Auditor", + "permissions": ["read_audit_logs"], + "organization": { + "login": "l", + "id": 1, + "node_id": "n", + "avatar_url": "a", + "html_url": "h", + "name": "n", + "company": "c", + "blog": "b", + "location": "l", + "email": "e" + }, + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "source": "Organization", + "base_role": "admin" + } + ] + }`) + }) + + ctx := t.Context() + apps, _, err := client.Organizations.ListRoles(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListRoles returned error: %v", err) + } + + want := &OrganizationCustomRoles{ + TotalCount: Ptr(1), + CustomRepoRoles: []*CustomOrgRole{ + { + ID: Ptr(int64(1)), + Name: Ptr("Auditor"), + Permissions: []string{"read_audit_logs"}, + Org: &Organization{ + Login: Ptr("l"), + ID: Ptr(int64(1)), + NodeID: Ptr("n"), + AvatarURL: Ptr("a"), + HTMLURL: Ptr("h"), + Name: Ptr("n"), + Company: Ptr("c"), + Blog: Ptr("b"), + Location: Ptr("l"), + Email: Ptr("e"), + }, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Source: Ptr("Organization"), + BaseRole: Ptr("admin"), + }, + }, + } + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListRoles returned %+v, want %+v", apps, want) + } + + const methodName = "ListRoles" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListRoles(ctx, "\no") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListRoles(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetOrgRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // Test built-in org role + mux.HandleFunc("/orgs/o/organization-roles/8132", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 8132, + "name": "all_repo_read", + "description": "Grants read access to all repositories in the organization.", + "permissions": [], + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "source": "Predefined", + "base_role": "read" + }`) + }) + + ctx := t.Context() + + gotBuiltInRole, _, err := client.Organizations.GetOrgRole(ctx, "o", 8132) + if err != nil { + t.Errorf("Organizations.GetOrgRole returned error: %v", err) + } + + wantBuiltInRole := &CustomOrgRole{ + ID: Ptr(int64(8132)), + Name: Ptr("all_repo_read"), + Description: Ptr("Grants read access to all repositories in the organization."), + Permissions: []string{}, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Source: Ptr("Predefined"), + BaseRole: Ptr("read"), + } + + if !cmp.Equal(gotBuiltInRole, wantBuiltInRole) { + t.Errorf("Organizations.GetOrgRole returned %+v, want %+v", gotBuiltInRole, wantBuiltInRole) + } + + // Test custom org role + mux.HandleFunc("/orgs/o/organization-roles/123456", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 123456, + "name": "test-role", + "description": "test-role", + "permissions": [ + "read_organization_custom_org_role", + "read_organization_custom_repo_role", + "write_organization_custom_org_role" + ], + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "source": "Organization", + "base_role": null + }`) + }) + + gotCustomRole, _, err := client.Organizations.GetOrgRole(ctx, "o", 123456) + if err != nil { + t.Errorf("Organizations.GetOrgRole returned error: %v", err) + } + + wantCustomRole := &CustomOrgRole{ + ID: Ptr(int64(123456)), + Name: Ptr("test-role"), + Description: Ptr("test-role"), + Permissions: []string{ + "read_organization_custom_org_role", + "read_organization_custom_repo_role", + "write_organization_custom_org_role", + }, + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Source: Ptr("Organization"), + BaseRole: nil, + } + + if !cmp.Equal(gotCustomRole, wantCustomRole) { + t.Errorf("Organizations.GetOrgRole returned %+v, want %+v", gotCustomRole, wantCustomRole) + } + + const methodName = "GetOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetOrgRole(ctx, "\no", -8132) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetOrgRole(ctx, "o", 8132) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateCustomOrgRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":8030,"name":"Reader","description":"A role for reading custom org roles","permissions":["read_organization_custom_org_role"]}`) + }) + + ctx := t.Context() + + opts := CreateCustomOrgRoleRequest{ + Name: "Reader", + Permissions: []string{"read_organization_custom_org_role"}, + Description: Ptr("A role for reading custom org roles"), + } + gotRoles, _, err := client.Organizations.CreateCustomOrgRole(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.CreateCustomOrgRole returned error: %v", err) + } + + want := &CustomOrgRole{ID: Ptr(int64(8030)), Name: Ptr("Reader"), Permissions: []string{"read_organization_custom_org_role"}, Description: Ptr("A role for reading custom org roles")} + + if !cmp.Equal(gotRoles, want) { + t.Errorf("Organizations.CreateCustomOrgRole returned %+v, want %+v", gotRoles, want) + } + + const methodName = "CreateCustomOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.CreateCustomOrgRole(ctx, "\no", CreateCustomOrgRoleRequest{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateCustomOrgRole(ctx, "o", CreateCustomOrgRoleRequest{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateCustomOrgRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"id":8030,"name":"Updated Name","description":"Updated Description","permissions":["read_organization_custom_org_role"]}`) + }) + + ctx := t.Context() + + opts := UpdateCustomOrgRoleRequest{ + Name: Ptr("Updated Name"), + Description: Ptr("Updated Description"), + } + gotRoles, _, err := client.Organizations.UpdateCustomOrgRole(ctx, "o", 8030, opts) + if err != nil { + t.Errorf("Organizations.UpdateCustomOrgRole returned error: %v", err) + } + + want := &CustomOrgRole{ID: Ptr(int64(8030)), Name: Ptr("Updated Name"), Permissions: []string{"read_organization_custom_org_role"}, Description: Ptr("Updated Description")} + + if !cmp.Equal(gotRoles, want) { + t.Errorf("Organizations.UpdateCustomOrgRole returned %+v, want %+v", gotRoles, want) + } + + const methodName = "UpdateCustomOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.UpdateCustomOrgRole(ctx, "\no", 8030, UpdateCustomOrgRoleRequest{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateCustomOrgRole(ctx, "o", 8030, UpdateCustomOrgRoleRequest{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteCustomOrgRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + + resp, err := client.Organizations.DeleteCustomOrgRole(ctx, "o", 8030) + if err != nil { + t.Errorf("Organizations.DeleteCustomOrgRole returned error: %v", err) + } + + if !cmp.Equal(resp.StatusCode, 204) { + t.Errorf("Organizations.DeleteCustomOrgRole returned status code %+v, want %+v", resp.StatusCode, "204") + } + + const methodName = "DeleteCustomOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.DeleteCustomOrgRole(ctx, "\no", 8030) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DeleteCustomOrgRole(ctx, "o", 8030) + }) +} + +func TestOrganizationsService_AssignOrgRoleToTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/teams/t/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.AssignOrgRoleToTeam(ctx, "o", "t", 8030) + if err != nil { + t.Errorf("Organization.AssignOrgRoleToTeam return error: %v", err) + } + if !cmp.Equal(resp.StatusCode, http.StatusNoContent) { + t.Errorf("Organizations.AssignOrgRoleToTeam returned status code %+v, want %+v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "AssignOrgRoleToTeam" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.AssignOrgRoleToTeam(ctx, "\no", "\nt", -8030) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.AssignOrgRoleToTeam(ctx, "o", "t", 8030) + }) +} + +func TestOrganizationsService_RemoveOrgRoleFromTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/teams/t/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.RemoveOrgRoleFromTeam(ctx, "o", "t", 8030) + if err != nil { + t.Errorf("Organization.RemoveOrgRoleFromTeam return error: %v", err) + } + if !cmp.Equal(resp.StatusCode, http.StatusNoContent) { + t.Errorf("Organizations.RemoveOrgRoleFromTeam returned status code %+v, want %+v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "RemoveOrgRoleFromTeam" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveOrgRoleFromTeam(ctx, "\no", "\nt", -8030) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveOrgRoleFromTeam(ctx, "o", "t", 8030) + }) +} + +func TestOrganizationsService_AssignOrgRoleToUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/users/t/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.AssignOrgRoleToUser(ctx, "o", "t", 8030) + if err != nil { + t.Errorf("Organization.AssignOrgRoleToUser return error: %v", err) + } + if !cmp.Equal(resp.StatusCode, http.StatusNoContent) { + t.Errorf("Organizations.AssignOrgRoleToUser returned status code %+v, want %+v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "AssignOrgRoleToUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.AssignOrgRoleToUser(ctx, "\no", "\nt", -8030) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.AssignOrgRoleToUser(ctx, "o", "t", 8030) + }) +} + +func TestOrganizationsService_RemoveOrgRoleFromUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/users/t/8030", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Organizations.RemoveOrgRoleFromUser(ctx, "o", "t", 8030) + if err != nil { + t.Errorf("Organization.RemoveOrgRoleFromUser return error: %v", err) + } + if !cmp.Equal(resp.StatusCode, http.StatusNoContent) { + t.Errorf("Organizations.RemoveOrgRoleFromUser returned status code %+v, want %+v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "RemoveOrgRoleFromUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveOrgRoleFromUser(ctx, "\no", "\nt", -8030) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveOrgRoleFromUser(ctx, "o", "t", 8030) + }) +} + +func TestOrganizationsService_ListTeamsAssignedToOrgRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/1729/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + opt := &ListOptions{Page: 2} + ctx := t.Context() + apps, _, err := client.Organizations.ListTeamsAssignedToOrgRole(ctx, "o", 1729, opt) + if err != nil { + t.Errorf("Organizations.ListTeamsAssignedToOrgRole returned error: %v", err) + } + + want := []*Team{{ID: Ptr(int64(1))}} + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListTeamsAssignedToOrgRole returned %+v, want %+v", apps, want) + } + + const methodName = "ListTeamsAssignedToOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListTeamsAssignedToOrgRole(ctx, "\no", 1729, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListTeamsAssignedToOrgRole(ctx, "o", 1729, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListUsersAssignedToOrgRole(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-roles/1729/users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + opt := &ListOptions{Page: 2} + ctx := t.Context() + apps, _, err := client.Organizations.ListUsersAssignedToOrgRole(ctx, "o", 1729, opt) + if err != nil { + t.Errorf("Organizations.ListUsersAssignedToOrgRole returned error: %v", err) + } + + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListUsersAssignedToOrgRole returned %+v, want %+v", apps, want) + } + + const methodName = "ListUsersAssignedToOrgRole" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListUsersAssignedToOrgRole(ctx, "\no", 1729, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListUsersAssignedToOrgRole(ctx, "o", 1729, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListFineGrainedPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/organization-fine-grained-permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"name":"p1", "description":"d1"}]`) + }) + + ctx := t.Context() + permissions, _, err := client.Organizations.ListFineGrainedPermissions(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListFineGrainedPermissions returned error: %v", err) + } + + want := []*OrganizationFineGrainedPermission{{Name: "p1", Description: "d1"}} + if !cmp.Equal(permissions, want) { + t.Errorf("Organizations.ListFineGrainedPermissions returned %+v, want %+v", permissions, want) + } + + const methodName = "ListFineGrainedPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListFineGrainedPermissions(ctx, "\no") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListFineGrainedPermissions(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_outside_collaborators.go b/github/orgs_outside_collaborators.go index 85ffd05f61a..61a4861d427 100644 --- a/github/orgs_outside_collaborators.go +++ b/github/orgs_outside_collaborators.go @@ -27,21 +27,23 @@ type ListOutsideCollaboratorsOptions struct { // Warning: The API may change without advance notice during the preview period. // Preview features are not supported for production use. // -// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators -func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org string, opt *ListOutsideCollaboratorsOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/outside-collaborators?apiVersion=2022-11-28#list-outside-collaborators-for-an-organization +// +//meta:operation GET /orgs/{org}/outside_collaborators +func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org string, opts *ListOutsideCollaboratorsOptions) ([]*User, *Response, error) { u := fmt.Sprintf("orgs/%v/outside_collaborators", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var members []*User - resp, err := s.client.Do(ctx, req, &members) + resp, err := s.client.Do(req, &members) if err != nil { return nil, resp, err } @@ -52,15 +54,17 @@ func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org // RemoveOutsideCollaborator removes a user from the list of outside collaborators; // consequently, removing them from all the organization's repositories. // -// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#remove-outside-collaborator -func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/outside-collaborators?apiVersion=2022-11-28#remove-outside-collaborator-from-an-organization +// +//meta:operation DELETE /orgs/{org}/outside_collaborators/{username} +func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ConvertMemberToOutsideCollaborator reduces the permission level of a member of the @@ -69,13 +73,15 @@ func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, or // Responses for converting a non-member or the last owner to an outside collaborator // are listed in GitHub API docs. // -// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#convert-member-to-outside-collaborator -func (s *OrganizationsService) ConvertMemberToOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/outside-collaborators?apiVersion=2022-11-28#convert-an-organization-member-to-outside-collaborator +// +//meta:operation PUT /orgs/{org}/outside_collaborators/{username} +func (s *OrganizationsService) ConvertMemberToOutsideCollaborator(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/orgs_outside_collaborators_test.go b/github/orgs_outside_collaborators_test.go index 8b72feb2155..8d27a13b8b7 100644 --- a/github/orgs_outside_collaborators_test.go +++ b/github/orgs_outside_collaborators_test.go @@ -6,16 +6,17 @@ package github import ( - "context" + "errors" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestOrganizationsService_ListOutsideCollaborators(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/outside_collaborators", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -30,43 +31,70 @@ func TestOrganizationsService_ListOutsideCollaborators(t *testing.T) { Filter: "2fa_disabled", ListOptions: ListOptions{Page: 2}, } - members, _, err := client.Organizations.ListOutsideCollaborators(context.Background(), "o", opt) + ctx := t.Context() + members, _, err := client.Organizations.ListOutsideCollaborators(ctx, "o", opt) if err != nil { t.Errorf("Organizations.ListOutsideCollaborators returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(members, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { t.Errorf("Organizations.ListOutsideCollaborators returned %+v, want %+v", members, want) } + + const methodName = "ListOutsideCollaborators" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListOutsideCollaborators(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListOutsideCollaborators(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_ListOutsideCollaborators_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.ListOutsideCollaborators(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Organizations.ListOutsideCollaborators(ctx, "%", nil) testURLParseError(t, err) } func TestOrganizationsService_RemoveOutsideCollaborator(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - handler := func(w http.ResponseWriter, r *http.Request) { + handler := func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") } mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) - _, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u") + ctx := t.Context() + _, err := client.Organizations.RemoveOutsideCollaborator(ctx, "o", "u") if err != nil { t.Errorf("Organizations.RemoveOutsideCollaborator returned error: %v", err) } + + const methodName = "RemoveOutsideCollaborator" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveOutsideCollaborator(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveOutsideCollaborator(ctx, "o", "u") + }) } func TestOrganizationsService_RemoveOutsideCollaborator_NonMember(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) handler := func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -74,17 +102,19 @@ func TestOrganizationsService_RemoveOutsideCollaborator_NonMember(t *testing.T) } mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) - _, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u") - if err, ok := err.(*ErrorResponse); !ok { - t.Errorf("Organizations.RemoveOutsideCollaborator did not return an error") - } else if err.Response.StatusCode != http.StatusNotFound { - t.Errorf("Organizations.RemoveOutsideCollaborator did not return 404 status code") + ctx := t.Context() + _, err := client.Organizations.RemoveOutsideCollaborator(ctx, "o", "u") + var rerr *ErrorResponse + if !errors.As(err, &rerr) { + t.Error("Organizations.RemoveOutsideCollaborator did not return an error") + } else if rerr.Response.StatusCode != http.StatusNotFound { + t.Error("Organizations.RemoveOutsideCollaborator did not return 404 status code") } } func TestOrganizationsService_RemoveOutsideCollaborator_Member(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) handler := func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -92,32 +122,45 @@ func TestOrganizationsService_RemoveOutsideCollaborator_Member(t *testing.T) { } mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) - _, err := client.Organizations.RemoveOutsideCollaborator(context.Background(), "o", "u") - if err, ok := err.(*ErrorResponse); !ok { - t.Errorf("Organizations.RemoveOutsideCollaborator did not return an error") - } else if err.Response.StatusCode != http.StatusUnprocessableEntity { - t.Errorf("Organizations.RemoveOutsideCollaborator did not return 422 status code") + ctx := t.Context() + _, err := client.Organizations.RemoveOutsideCollaborator(ctx, "o", "u") + var rerr *ErrorResponse + if !errors.As(err, &rerr) { + t.Error("Organizations.RemoveOutsideCollaborator did not return an error") + } else if rerr.Response.StatusCode != http.StatusUnprocessableEntity { + t.Error("Organizations.RemoveOutsideCollaborator did not return 422 status code") } } func TestOrganizationsService_ConvertMemberToOutsideCollaborator(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - handler := func(w http.ResponseWriter, r *http.Request) { + handler := func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") } mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) - _, err := client.Organizations.ConvertMemberToOutsideCollaborator(context.Background(), "o", "u") + ctx := t.Context() + _, err := client.Organizations.ConvertMemberToOutsideCollaborator(ctx, "o", "u") if err != nil { t.Errorf("Organizations.ConvertMemberToOutsideCollaborator returned error: %v", err) } + + const methodName = "ConvertMemberToOutsideCollaborator" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.ConvertMemberToOutsideCollaborator(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.ConvertMemberToOutsideCollaborator(ctx, "o", "u") + }) } func TestOrganizationsService_ConvertMemberToOutsideCollaborator_NonMemberOrLastOwner(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) handler := func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -125,10 +168,12 @@ func TestOrganizationsService_ConvertMemberToOutsideCollaborator_NonMemberOrLast } mux.HandleFunc("/orgs/o/outside_collaborators/u", handler) - _, err := client.Organizations.ConvertMemberToOutsideCollaborator(context.Background(), "o", "u") - if err, ok := err.(*ErrorResponse); !ok { - t.Errorf("Organizations.ConvertMemberToOutsideCollaborator did not return an error") - } else if err.Response.StatusCode != http.StatusForbidden { - t.Errorf("Organizations.ConvertMemberToOutsideCollaborator did not return 403 status code") + ctx := t.Context() + _, err := client.Organizations.ConvertMemberToOutsideCollaborator(ctx, "o", "u") + var rerr *ErrorResponse + if !errors.As(err, &rerr) { + t.Error("Organizations.ConvertMemberToOutsideCollaborator did not return an error") + } else if rerr.Response.StatusCode != http.StatusForbidden { + t.Error("Organizations.ConvertMemberToOutsideCollaborator did not return 403 status code") } } diff --git a/github/orgs_packages.go b/github/orgs_packages.go new file mode 100644 index 00000000000..54762e5e795 --- /dev/null +++ b/github/orgs_packages.go @@ -0,0 +1,180 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/url" +) + +// ListPackages lists the packages for an organization. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#list-packages-for-an-organization +// +//meta:operation GET /orgs/{org}/packages +func (s *OrganizationsService) ListPackages(ctx context.Context, org string, opts *PackageListOptions) ([]*Package, *Response, error) { + u := fmt.Sprintf("orgs/%v/packages", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var packages []*Package + resp, err := s.client.Do(req, &packages) + if err != nil { + return nil, resp, err + } + + return packages, resp, nil +} + +// GetPackage gets a package by name from an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#get-a-package-for-an-organization +// +//meta:operation GET /orgs/{org}/packages/{package_type}/{package_name} +func (s *OrganizationsService) GetPackage(ctx context.Context, org, packageType, packageName string) (*Package, *Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v", org, packageType, url.PathEscape(packageName)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pack *Package + resp, err := s.client.Do(req, &pack) + if err != nil { + return nil, resp, err + } + + return pack, resp, nil +} + +// DeletePackage deletes a package from an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#delete-a-package-for-an-organization +// +//meta:operation DELETE /orgs/{org}/packages/{package_type}/{package_name} +func (s *OrganizationsService) DeletePackage(ctx context.Context, org, packageType, packageName string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v", org, packageType, url.PathEscape(packageName)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RestorePackage restores a package to an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#restore-a-package-for-an-organization +// +//meta:operation POST /orgs/{org}/packages/{package_type}/{package_name}/restore +func (s *OrganizationsService) RestorePackage(ctx context.Context, org, packageType, packageName string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v/restore", org, packageType, url.PathEscape(packageName)) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// PackageGetAllVersions gets all versions of a package in an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#list-package-versions-for-a-package-owned-by-an-organization +// +//meta:operation GET /orgs/{org}/packages/{package_type}/{package_name}/versions +func (s *OrganizationsService) PackageGetAllVersions(ctx context.Context, org, packageType, packageName string, opts *PackageListOptions) ([]*PackageVersion, *Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions", org, packageType, url.PathEscape(packageName)) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var versions []*PackageVersion + resp, err := s.client.Do(req, &versions) + if err != nil { + return nil, resp, err + } + + return versions, resp, nil +} + +// PackageGetVersion gets a specific version of a package in an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#get-a-package-version-for-an-organization +// +//meta:operation GET /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id} +func (s *OrganizationsService) PackageGetVersion(ctx context.Context, org, packageType, packageName string, packageVersionID int64) (*PackageVersion, *Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions/%v", org, packageType, url.PathEscape(packageName), packageVersionID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var version *PackageVersion + resp, err := s.client.Do(req, &version) + if err != nil { + return nil, resp, err + } + + return version, resp, nil +} + +// PackageDeleteVersion deletes a package version from an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#delete-package-version-for-an-organization +// +//meta:operation DELETE /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id} +func (s *OrganizationsService) PackageDeleteVersion(ctx context.Context, org, packageType, packageName string, packageVersionID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions/%v", org, packageType, url.PathEscape(packageName), packageVersionID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// PackageRestoreVersion restores a package version to an organization. +// +// Note that packageName is escaped for the URL path so that you don't need to. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#restore-package-version-for-an-organization +// +//meta:operation POST /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore +func (s *OrganizationsService) PackageRestoreVersion(ctx context.Context, org, packageType, packageName string, packageVersionID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions/%v/restore", org, packageType, url.PathEscape(packageName), packageVersionID) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/orgs_packages_test.go b/github/orgs_packages_test.go new file mode 100644 index 00000000000..18e506a006e --- /dev/null +++ b/github/orgs_packages_test.go @@ -0,0 +1,419 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "io" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListPackages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/packages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + _, err := io.WriteString(w, `[{ + "id": 197, + "name": "hello_docker", + "package_type": "container", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "site_admin": false + }, + "version_count": 1, + "visibility": "private", + "url": "https://api.github.com/orgs/github/packages/container/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/orgs/github/packages/container/package/hello_docker" + } + ]`) + if err != nil { + t.Fatal("Failed to write test response: ", err) + } + }) + + ctx := t.Context() + packages, _, err := client.Organizations.ListPackages(ctx, "o", &PackageListOptions{}) + if err != nil { + t.Errorf("Organizations.ListPackages returned error: %v", err) + } + + want := []*Package{{ + ID: Ptr(int64(197)), + Name: Ptr("hello_docker"), + PackageType: Ptr("container"), + VersionCount: Ptr(int64(1)), + Visibility: Ptr("private"), + URL: Ptr("https://api.github.com/orgs/github/packages/container/hello_docker"), + HTMLURL: Ptr("https://github.com/orgs/github/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + Owner: &User{ + Login: Ptr("github"), + ID: Ptr(int64(9919)), + NodeID: Ptr("MDEyOk9yZ2FuaXphdGlvbjk5MTk="), + AvatarURL: Ptr("https://avatars.githubusercontent.com/u/9919?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/github"), + HTMLURL: Ptr("https://github.com/github"), + FollowersURL: Ptr("https://api.github.com/users/github/followers"), + FollowingURL: Ptr("https://api.github.com/users/github/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/github/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/github/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/github/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/github/orgs"), + ReposURL: Ptr("https://api.github.com/users/github/repos"), + EventsURL: Ptr("https://api.github.com/users/github/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/github/received_events"), + Type: Ptr("Organization"), + SiteAdmin: Ptr(false), + }, + }} + if !cmp.Equal(packages, want) { + t.Errorf("Organizations.ListPackages returned %+v, want %+v", packages, want) + } + + const methodName = "ListPackages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListPackages(ctx, "\n", &PackageListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListPackages(ctx, "o", &PackageListOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetPackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2fhello_docker", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + _, err := io.WriteString(w, `{ + "id": 197, + "name": "hello/hello_docker", + "package_type": "container", + "version_count": 1, + "visibility": "private", + "url": "https://api.github.com/orgs/github/packages/container/hello%2Fhello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/orgs/github/packages/container/package/hello%2Fhello_docker" + }`) + if err != nil { + t.Fatal("Failed to write test response: ", err) + } + }) + + ctx := t.Context() + packages, _, err := client.Organizations.GetPackage(ctx, "o", "container", "hello/hello_docker") + if err != nil { + t.Errorf("Organizations.GetPackage returned error: %v", err) + } + + want := &Package{ + ID: Ptr(int64(197)), + Name: Ptr("hello/hello_docker"), + PackageType: Ptr("container"), + VersionCount: Ptr(int64(1)), + Visibility: Ptr("private"), + URL: Ptr("https://api.github.com/orgs/github/packages/container/hello%2Fhello_docker"), + HTMLURL: Ptr("https://github.com/orgs/github/packages/container/package/hello%2Fhello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + } + if !cmp.Equal(packages, want) { + t.Errorf("Organizations.GetPackage returned %+v, want %+v", packages, want) + } + + const methodName = "GetPackage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetPackage(ctx, "\n", "", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetPackage(ctx, "", "", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeletePackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2fhello_docker", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.DeletePackage(ctx, "o", "container", "hello/hello_docker") + if err != nil { + t.Errorf("Organizations.DeletePackage returned error: %v", err) + } + + const methodName = "DeletePackage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetPackage(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetPackage(ctx, "", "", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_RestorePackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/restore", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + _, err := client.Organizations.RestorePackage(ctx, "o", "container", "hello/hello_docker") + if err != nil { + t.Errorf("Organizations.RestorePackage returned error: %v", err) + } + + const methodName = "RestorePackage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RestorePackage(ctx, "\n", "", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RestorePackage(ctx, "", "container", "hello/hello_docker") + }) +} + +func TestOrganizationsService_ListPackagesVersions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + m := `{ + "package_type": "container", + "container": { + "tags": [ + "latest" + ] + } + }` + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/versions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "1", "state": "deleted", "visibility": "internal", "package_type": "container"}) + _, err := io.WriteString(w, `[ + { + "id": 45763, + "name": "sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9", + "url": "https://api.github.com/users/octocat/packages/container/hello%2Fhello_docker/versions/45763", + "package_html_url": "https://github.com/users/octocat/packages/container/package/hello%2Fhello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763", + "metadata": `+m+` + }]`) + if err != nil { + t.Fatal("Failed to write test response: ", err) + } + }) + + ctx := t.Context() + opts := &PackageListOptions{ + Ptr("internal"), Ptr("container"), Ptr("deleted"), ListOptions{Page: 1, PerPage: 2}, + } + packages, _, err := client.Organizations.PackageGetAllVersions(ctx, "o", "container", "hello/hello_docker", opts) + if err != nil { + t.Errorf("Organizations.PackageGetAllVersions returned error: %v", err) + } + + want := []*PackageVersion{{ + ID: Ptr(int64(45763)), + Name: Ptr("sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9"), + URL: Ptr("https://api.github.com/users/octocat/packages/container/hello%2Fhello_docker/versions/45763"), + PackageHTMLURL: Ptr("https://github.com/users/octocat/packages/container/package/hello%2Fhello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + HTMLURL: Ptr("https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763"), + Metadata: json.RawMessage(m), + }} + if !cmp.Equal(packages, want) { + t.Errorf("Organizations.PackageGetAllVersions returned %+v, want %+v", packages, want) + } + + const methodName = "PackageGetAllVersions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.PackageGetAllVersions(ctx, "\n", "", "", &PackageListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.PackageGetAllVersions(ctx, "", "", "", &PackageListOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_PackageGetVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + m := `{ + "package_type": "container", + "container": { + "tags": [ + "latest" + ] + } + }` + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/versions/45763", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + _, err := io.WriteString(w, ` + { + "id": 45763, + "name": "sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9", + "url": "https://api.github.com/users/octocat/packages/container/hello%2Fhello_docker/versions/45763", + "package_html_url": "https://github.com/users/octocat/packages/container/package/hello%2Fhello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763", + "metadata": `+m+` + }`) + if err != nil { + t.Fatal("Failed to write test response: ", err) + } + }) + + ctx := t.Context() + packages, _, err := client.Organizations.PackageGetVersion(ctx, "o", "container", "hello/hello_docker", 45763) + if err != nil { + t.Errorf("Organizations.PackageGetVersion returned error: %v", err) + } + + want := &PackageVersion{ + ID: Ptr(int64(45763)), + Name: Ptr("sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9"), + URL: Ptr("https://api.github.com/users/octocat/packages/container/hello%2Fhello_docker/versions/45763"), + PackageHTMLURL: Ptr("https://github.com/users/octocat/packages/container/package/hello%2Fhello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + HTMLURL: Ptr("https://github.com/users/octocat/packages/container/hello%2Fhello_docker/45763"), + Metadata: json.RawMessage(m), + } + if !cmp.Equal(packages, want) { + t.Errorf("Organizations.PackageGetVersion returned %+v, want %+v", packages, want) + } + + const methodName = "PackageGetVersion" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.PackageGetVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.PackageGetVersion(ctx, "", "", "", 45763) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_PackageDeleteVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/versions/45763", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.PackageDeleteVersion(ctx, "o", "container", "hello/hello_docker", 45763) + if err != nil { + t.Errorf("Organizations.PackageDeleteVersion returned error: %v", err) + } + + const methodName = "PackageDeleteVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.PackageDeleteVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.PackageDeleteVersion(ctx, "", "", "", 45763) + }) +} + +func TestOrganizationsService_PackageRestoreVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // don't url escape the package name here since mux will convert it to a slash automatically + mux.HandleFunc("/orgs/o/packages/container/hello%2Fhello_docker/versions/45763/restore", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + _, err := client.Organizations.PackageRestoreVersion(ctx, "o", "container", "hello/hello_docker", 45763) + if err != nil { + t.Errorf("Organizations.PackageRestoreVersion returned error: %v", err) + } + + const methodName = "PackageRestoreVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.PackageRestoreVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.PackageRestoreVersion(ctx, "", "", "", 45763) + }) +} diff --git a/github/orgs_personal_access_tokens.go b/github/orgs_personal_access_tokens.go new file mode 100644 index 00000000000..98b1ea81a4d --- /dev/null +++ b/github/orgs_personal_access_tokens.go @@ -0,0 +1,264 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "errors" + "fmt" + "net/url" + "strconv" + "strings" +) + +// PersonalAccessToken represents the minimal representation of an organization programmatic access grant. +// +// GitHub API docs: https://docs.github.com/rest/orgs/personal-access-tokens?apiVersion=2022-11-28 +type PersonalAccessToken struct { + // "Unique identifier of the fine-grained personal access token. + // The `pat_id` used to get details about an approved fine-grained personal access token. + ID *int64 `json:"id"` + + // Owner is the GitHub user associated with the token. + Owner *User `json:"owner"` + + // RepositorySelection is the type of repository selection requested. + // Possible values are: "none", "all", "subset". + RepositorySelection *string `json:"repository_selection"` + + // URL to the list of repositories the fine-grained personal access token can access. + // Only follow when `repository_selection` is `subset`. + RepositoriesURL *string `json:"repositories_url"` + + // Permissions are the permissions requested, categorized by type. + Permissions *PersonalAccessTokenPermissions `json:"permissions"` + + // Date and time when the fine-grained personal access token was approved to access the organization. + AccessGrantedAt *Timestamp `json:"access_granted_at"` + + // Whether the associated fine-grained personal access token has expired. + TokenExpired *bool `json:"token_expired"` + + // Date and time when the associated fine-grained personal access token expires. + TokenExpiresAt *Timestamp `json:"token_expires_at"` + + // TokenID + TokenID *int64 `json:"token_id"` + + // TokenName + TokenName *string `json:"token_name"` + + // Date and time when the associated fine-grained personal access token was last used for authentication. + TokenLastUsedAt *Timestamp `json:"token_last_used_at"` +} + +// ListFineGrainedPATOptions specifies optional parameters to ListFineGrainedPersonalAccessTokens. +type ListFineGrainedPATOptions struct { + // The property by which to sort the results. + // Default: created_at + // Value: created_at + Sort string `url:"sort,omitempty"` + + // The direction to sort the results by. + // Default: desc + // Value: asc, desc + Direction string `url:"direction,omitempty"` + + // A list of owner usernames to use to filter the results. + Owner []string `url:"-"` + + // The name of the repository to use to filter the results. + Repository string `url:"repository,omitempty"` + + // The permission to use to filter the results. + Permission string `url:"permission,omitempty"` + + // Only show fine-grained personal access tokens used before the given time. + // This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. + LastUsedBefore string `url:"last_used_before,omitempty"` + + // Only show fine-grained personal access tokens used after the given time. + // This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ. + LastUsedAfter string `url:"last_used_after,omitempty"` + + // TokenID filters results by the given fine-grained personal access token IDs. + TokenID []int64 `url:"-"` + + ListOptions +} + +// ListFineGrainedPersonalAccessTokens lists approved fine-grained personal access tokens owned by organization members that can access organization resources. +// Only GitHub Apps can call this API, using the `Personal access tokens` organization permissions (read). +// +// GitHub API docs: https://docs.github.com/rest/orgs/personal-access-tokens?apiVersion=2022-11-28#list-fine-grained-personal-access-tokens-with-access-to-organization-resources +// +//meta:operation GET /orgs/{org}/personal-access-tokens +func (s *OrganizationsService) ListFineGrainedPersonalAccessTokens(ctx context.Context, org string, opts *ListFineGrainedPATOptions) ([]*PersonalAccessToken, *Response, error) { + u := fmt.Sprintf("orgs/%v/personal-access-tokens", org) + // The `owner` parameter is a special case that uses the `owner[]=...` format and needs a custom function to format it correctly. + u, err := addListFineGrainedPATOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, opts) + if err != nil { + return nil, nil, err + } + + var pats []*PersonalAccessToken + + resp, err := s.client.Do(req, &pats) + if err != nil { + return nil, resp, err + } + + return pats, resp, nil +} + +// FineGrainedPersonalAccessTokenRequest represents the details of a request to access organization resources via a fine-grained personal access token. +type FineGrainedPersonalAccessTokenRequest struct { + // Unique identifier of the request for access via fine-grained personal access token. + ID int64 `json:"id"` + + // Reason is the reason for the request. + Reason string `json:"reason"` + + // Owner is the GitHub user associated with the token. + Owner User `json:"owner"` + + // RepositorySelection is the type of repository selection requested. + // Possible values are: "none", "all", "subset". + RepositorySelection string `json:"repository_selection"` + + // URL to the list of repositories the fine-grained personal access token can access. + // Only follow when `repository_selection` is `subset`. + RepositoriesURL string `json:"repositories_url"` + + // Permissions are the permissions requested, categorized by type. + Permissions PersonalAccessTokenPermissions `json:"permissions"` + + // Date and time when the request was created. + CreatedAt *Timestamp `json:"created_at"` + + // Whether the associated fine-grained personal access token has expired. + TokenExpired bool `json:"token_expired"` + + // Date and time when the associated fine-grained personal access token expires. + TokenExpiresAt *Timestamp `json:"token_expires_at"` + + // TokenID + TokenID int64 `json:"token_id"` + + // TokenName + TokenName string `json:"token_name"` + + // Date and time when the associated fine-grained personal access token was last used for authentication. + TokenLastUsedAt *Timestamp `json:"token_last_used_at"` +} + +// ListFineGrainedPersonalAccessTokenRequests lists requests to access organization resources via fine-grained personal access tokens. +// Only GitHub Apps can call this API, using the `Personal access tokens` organization permissions (read). +// +// GitHub API docs: https://docs.github.com/rest/orgs/personal-access-tokens?apiVersion=2022-11-28#list-requests-to-access-organization-resources-with-fine-grained-personal-access-tokens +// +//meta:operation GET /orgs/{org}/personal-access-token-requests +func (s *OrganizationsService) ListFineGrainedPersonalAccessTokenRequests(ctx context.Context, org string, opts *ListFineGrainedPATOptions) ([]*FineGrainedPersonalAccessTokenRequest, *Response, error) { + u := fmt.Sprintf("orgs/%v/personal-access-token-requests", org) + // The `owner` parameter is a special case that uses the `owner[]=...` format and needs a custom function to format it correctly. + u, err := addListFineGrainedPATOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, opts) + if err != nil { + return nil, nil, err + } + + var pats []*FineGrainedPersonalAccessTokenRequest + resp, err := s.client.Do(req, &pats) + if err != nil { + return nil, resp, err + } + + return pats, resp, nil +} + +// ReviewPersonalAccessTokenRequestOptions specifies the parameters to the ReviewPersonalAccessTokenRequest method. +type ReviewPersonalAccessTokenRequestOptions struct { + Action string `json:"action"` + Reason *string `json:"reason,omitempty"` +} + +// ReviewPersonalAccessTokenRequest approves or denies a pending request to access organization resources via a fine-grained personal access token. +// Only GitHub Apps can call this API, using the `organization_personal_access_token_requests: write` permission. +// `action` can be one of `approve` or `deny`. +// +// GitHub API docs: https://docs.github.com/rest/orgs/personal-access-tokens?apiVersion=2022-11-28#review-a-request-to-access-organization-resources-with-a-fine-grained-personal-access-token +// +//meta:operation POST /orgs/{org}/personal-access-token-requests/{pat_request_id} +func (s *OrganizationsService) ReviewPersonalAccessTokenRequest(ctx context.Context, org string, requestID int64, opts ReviewPersonalAccessTokenRequestOptions) (*Response, error) { + u := fmt.Sprintf("orgs/%v/personal-access-token-requests/%v", org, requestID) + + req, err := s.client.NewRequest(ctx, "POST", u, &opts) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// addListFineGrainedPATOptions adds the owner and token_id parameters to the URL query string with the correct format if they are set. +// +// GitHub API expects the owner parameter to be a list of strings in the `owner[]=...` format. +// For multiple owner and token_id values, the owner and token_id parameters are repeated in the query string. +// +// Example: +// owner[]=user1&owner[]=user2&token_id[]=123&token_id[]=456 +// This will filter the results to only include fine-grained personal access tokens owned by `user1` and `user2` and with token IDs `123` and `456`. +// +// This function ensures the owner and token_id parameters are formatted correctly in the URL query string. +func addListFineGrainedPATOptions(s string, opts *ListFineGrainedPATOptions) (string, error) { + u, err := addOptions(s, opts) + if err != nil { + return s, err + } + + if opts == nil { + return "", errors.New("opts must be provided") + } + + if len(opts.Owner) > 0 { + ownerVals := make([]string, len(opts.Owner)) + for i, owner := range opts.Owner { + ownerVals[i] = fmt.Sprintf("owner[]=%v", url.QueryEscape(owner)) + } + ownerQuery := strings.Join(ownerVals, "&") + + if strings.Contains(u, "?") { + u += "&" + ownerQuery + } else { + u += "?" + ownerQuery + } + } + if len(opts.TokenID) > 0 { + tokenIDVals := make([]string, len(opts.TokenID)) + for i, tokenID := range opts.TokenID { + tokenIDVals[i] = fmt.Sprintf("token_id[]=%v", url.QueryEscape(strconv.FormatInt(tokenID, 10))) + } + tokenIDQuery := strings.Join(tokenIDVals, "&") + + if strings.Contains(u, "?") { + u += "&" + tokenIDQuery + } else { + u += "?" + tokenIDQuery + } + return u, nil + } + + return u, nil +} diff --git a/github/orgs_personal_access_tokens_test.go b/github/orgs_personal_access_tokens_test.go new file mode 100644 index 00000000000..b2d82399cbb --- /dev/null +++ b/github/orgs_personal_access_tokens_test.go @@ -0,0 +1,369 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokens(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/personal-access-tokens", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValuesList(t, r, url.Values{ + "per_page": {"2"}, + "page": {"2"}, + "sort": {"created_at"}, + "direction": {"desc"}, + "owner[]": {"octocat", "octodog", "otherbot"}, + }) + + fmt.Fprint(w, ` + [ + { + "id": 25381, + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "all", + "repositories_url": "https://api.github.com/organizations/652551/personal-access-tokens/25381/repositories", + "permissions": { + "organization": { + "members": "read" + }, + "repository": { + "metadata": "read" + } + }, + "access_granted_at": `+refTimeStr(1136178000)+`, + "token_expired": false, + "token_expires_at": `+refTimeStr(1136178001)+`, + "token_last_used_at": null + } + ]`) + }) + + opts := &ListFineGrainedPATOptions{ + ListOptions: ListOptions{Page: 2, PerPage: 2}, + Sort: "created_at", + Direction: "desc", + Owner: []string{"octocat", "octodog", "otherbot"}, + } + ctx := t.Context() + tokens, resp, err := client.Organizations.ListFineGrainedPersonalAccessTokens(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokens returned error: %v", err) + } + + want := []*PersonalAccessToken{ + { + ID: Ptr(int64(25381)), + Owner: &User{ + Login: Ptr("octocat"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VXNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octocat"), + HTMLURL: Ptr("https://github.com/octocat"), + FollowersURL: Ptr("https://api.github.com/users/octocat/followers"), + FollowingURL: Ptr("https://api.github.com/users/octocat/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octocat/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octocat/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octocat/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octocat/orgs"), + ReposURL: Ptr("https://api.github.com/users/octocat/repos"), + EventsURL: Ptr("https://api.github.com/users/octocat/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octocat/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + RepositorySelection: Ptr("all"), + RepositoriesURL: Ptr("https://api.github.com/organizations/652551/personal-access-tokens/25381/repositories"), + Permissions: &PersonalAccessTokenPermissions{ + Org: map[string]string{"members": "read"}, + Repo: map[string]string{"metadata": "read"}, + }, + AccessGrantedAt: refTimestamp(1136178000), + TokenExpired: Ptr(false), + TokenExpiresAt: refTimestamp(1136178001), + TokenLastUsedAt: nil, + }, + } + if !cmp.Equal(tokens, want) { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokens returned %+v, want %+v", tokens, want) + } + + if resp == nil { + t.Error("Organizations.ListFineGrainedPersonalAccessTokens returned nil response") + } + + const methodName = "ListFineGrainedPersonalAccessTokens" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListFineGrainedPersonalAccessTokens(ctx, "o", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListFineGrainedPersonalAccessTokens(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListFineGrainedPersonalAccessTokens(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokens_ownerOnly(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/personal-access-tokens", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"owner[]": "octocat"}) + + fmt.Fprint(w, "[]") + }) + + opts := &ListFineGrainedPATOptions{Owner: []string{"octocat"}} + ctx := t.Context() + _, _, err := client.Organizations.ListFineGrainedPersonalAccessTokens(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokens returned error: %v", err) + } +} + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokenRequests(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/personal-access-token-requests", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValuesList(t, r, url.Values{ + "per_page": {"2"}, + "page": {"2"}, + "sort": {"created_at"}, + "direction": {"desc"}, + "owner[]": {"octocat", "octodog"}, + "token_id[]": {"11579703", "11579704"}, + }) + + fmt.Fprint(w, `[ + { + "id": 1848980, + "reason": null, + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "repository_selection": "all", + "repositories_url": "https://api.github.com/organizations/135028681/personal-access-token-requests/1848980/repositories", + "permissions": { + "repository": { + "metadata": "read" + } + }, + "created_at": `+refTimeStr(1136178000)+`, + "token_id": 11579703, + "token_name": "testFineGrained", + "token_expired": false, + "token_expires_at": `+refTimeStr(1136178001)+`, + "token_last_used_at": null + } + ]`) + }) + + opts := &ListFineGrainedPATOptions{ + ListOptions: ListOptions{Page: 2, PerPage: 2}, + Sort: "created_at", + Direction: "desc", + Owner: []string{"octocat", "octodog"}, + TokenID: []int64{11579703, 11579704}, + } + ctx := t.Context() + requests, resp, err := client.Organizations.ListFineGrainedPersonalAccessTokenRequests(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokenRequests returned error: %v", err) + } + + want := []*FineGrainedPersonalAccessTokenRequest{ + { + ID: 1848980, + Reason: "", + Owner: User{ + Login: Ptr("octocat"), + ID: Ptr(int64(1)), + NodeID: Ptr("MDQ6VXNlcjE="), + AvatarURL: Ptr("https://github.com/images/error/octocat_happy.gif"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/octocat"), + HTMLURL: Ptr("https://github.com/octocat"), + FollowersURL: Ptr("https://api.github.com/users/octocat/followers"), + FollowingURL: Ptr("https://api.github.com/users/octocat/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/octocat/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/octocat/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/octocat/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/octocat/orgs"), + ReposURL: Ptr("https://api.github.com/users/octocat/repos"), + EventsURL: Ptr("https://api.github.com/users/octocat/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/octocat/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + RepositorySelection: "all", + RepositoriesURL: "https://api.github.com/organizations/135028681/personal-access-token-requests/1848980/repositories", + Permissions: PersonalAccessTokenPermissions{ + Repo: map[string]string{"metadata": "read"}, + }, + CreatedAt: refTimestamp(1136178000), + TokenID: 11579703, + TokenName: "testFineGrained", + TokenExpired: false, + TokenExpiresAt: refTimestamp(1136178001), + TokenLastUsedAt: nil, + }, + } + if !cmp.Equal(requests, want) { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokenRequests returned %+v, want %+v", requests, want) + } + + if resp == nil { + t.Error("Organizations.ListFineGrainedPersonalAccessTokenRequests returned nil response") + } + + const methodName = "ListFineGrainedPersonalAccessTokenRequests" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListFineGrainedPersonalAccessTokenRequests(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListFineGrainedPersonalAccessTokenRequests(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokenRequests_ownerOnly(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/personal-access-token-requests", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"owner[]": "octocat"}) + fmt.Fprint(w, "[]") + }) + + opts := &ListFineGrainedPATOptions{ + Owner: []string{"octocat"}, + } + ctx := t.Context() + _, _, err := client.Organizations.ListFineGrainedPersonalAccessTokenRequests(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokenRequests returned error: %v", err) + } +} + +func TestOrganizationsService_ListFineGrainedPersonalAccessTokenRequests_tokenIDOnly(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/personal-access-token-requests", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"token_id[]": "11579703"}) + fmt.Fprint(w, "[]") + }) + + opts := &ListFineGrainedPATOptions{ + TokenID: []int64{11579703}, + } + ctx := t.Context() + _, _, err := client.Organizations.ListFineGrainedPersonalAccessTokenRequests(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListFineGrainedPersonalAccessTokenRequests returned error: %v", err) + } +} + +func TestOrganizationsService_ReviewPersonalAccessTokenRequest(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ReviewPersonalAccessTokenRequestOptions{ + Action: "a", + Reason: Ptr("r"), + } + + mux.HandleFunc("/orgs/o/personal-access-token-requests/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + res, err := client.Organizations.ReviewPersonalAccessTokenRequest(ctx, "o", 1, input) + if err != nil { + t.Errorf("Organizations.ReviewPersonalAccessTokenRequest returned error: %v", err) + } + + if res.StatusCode != http.StatusNoContent { + t.Errorf("Organizations.ReviewPersonalAccessTokenRequest returned %v, want %v", res.StatusCode, http.StatusNoContent) + } + + const methodName = "ReviewPersonalAccessTokenRequest" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.ReviewPersonalAccessTokenRequest(ctx, "\n", 0, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.ReviewPersonalAccessTokenRequest(ctx, "o", 1, input) + }) +} diff --git a/github/orgs_projects.go b/github/orgs_projects.go deleted file mode 100644 index e57cba97829..00000000000 --- a/github/orgs_projects.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "fmt" -) - -// ListProjects lists the projects for an organization. -// -// GitHub API docs: https://developer.github.com/v3/projects/#list-organization-projects -func (s *OrganizationsService) ListProjects(ctx context.Context, org string, opt *ProjectListOptions) ([]*Project, *Response, error) { - u := fmt.Sprintf("orgs/%v/projects", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - var projects []*Project - resp, err := s.client.Do(ctx, req, &projects) - if err != nil { - return nil, resp, err - } - - return projects, resp, nil -} - -// CreateProject creates a GitHub Project for the specified organization. -// -// GitHub API docs: https://developer.github.com/v3/projects/#create-an-organization-project -func (s *OrganizationsService) CreateProject(ctx context.Context, org string, opt *ProjectOptions) (*Project, *Response, error) { - u := fmt.Sprintf("orgs/%v/projects", org) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) - if err != nil { - return nil, resp, err - } - - return project, resp, nil -} diff --git a/github/orgs_projects_test.go b/github/orgs_projects_test.go deleted file mode 100644 index efea5cb4195..00000000000 --- a/github/orgs_projects_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "reflect" - "testing" -) - -func TestOrganizationsService_ListProjects(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/orgs/o/projects", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - testFormValues(t, r, values{"state": "open", "page": "2"}) - fmt.Fprint(w, `[{"id":1}]`) - }) - - opt := &ProjectListOptions{State: "open", ListOptions: ListOptions{Page: 2}} - projects, _, err := client.Organizations.ListProjects(context.Background(), "o", opt) - if err != nil { - t.Errorf("Organizations.ListProjects returned error: %v", err) - } - - want := []*Project{{ID: Int64(1)}} - if !reflect.DeepEqual(projects, want) { - t.Errorf("Organizations.ListProjects returned %+v, want %+v", projects, want) - } -} - -func TestOrganizationsService_CreateProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &ProjectOptions{Name: String("Project Name"), Body: String("Project body.")} - - mux.HandleFunc("/orgs/o/projects", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - - v := &ProjectOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - - fmt.Fprint(w, `{"id":1}`) - }) - - project, _, err := client.Organizations.CreateProject(context.Background(), "o", input) - if err != nil { - t.Errorf("Organizations.CreateProject returned error: %v", err) - } - - want := &Project{ID: Int64(1)} - if !reflect.DeepEqual(project, want) { - t.Errorf("Organizations.CreateProject returned %+v, want %+v", project, want) - } -} diff --git a/github/orgs_properties.go b/github/orgs_properties.go new file mode 100644 index 00000000000..f3492c47e87 --- /dev/null +++ b/github/orgs_properties.go @@ -0,0 +1,315 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strconv" +) + +// PropertyValueType represents the type of a custom property value. +type PropertyValueType string + +// Valid values for CustomProperty.ValueType. +const ( + PropertyValueTypeString PropertyValueType = "string" + PropertyValueTypeSingleSelect PropertyValueType = "single_select" + PropertyValueTypeMultiSelect PropertyValueType = "multi_select" + PropertyValueTypeTrueFalse PropertyValueType = "true_false" + PropertyValueTypeURL PropertyValueType = "url" +) + +// CustomProperty represents an organization custom property object. +type CustomProperty struct { + // PropertyName is required for most endpoints except when calling CreateOrUpdateCustomProperty; + // where this is sent in the path and thus can be omitted. + PropertyName *string `json:"property_name,omitempty"` + // The URL that can be used to fetch, update, or delete info about this property via the API. + URL *string `json:"url,omitempty"` + // SourceType is the source type of the property where it has been created. Can be one of: organization, enterprise. + SourceType *string `json:"source_type,omitempty"` + // The type of the value for the property. Can be one of: string, single_select, multi_select, true_false, url. + ValueType PropertyValueType `json:"value_type"` + // Whether the property is required. + Required *bool `json:"required,omitempty"` + // Default value of the property. + DefaultValue any `json:"default_value,omitempty"` + // Short description of the property. + Description *string `json:"description,omitempty"` + // An ordered list of the allowed values of the property. The property can have up to 200 + // allowed values. + AllowedValues []string `json:"allowed_values,omitempty"` + // Who can edit the values of the property. Can be one of: org_actors, org_and_repo_actors, nil (null). + ValuesEditableBy *string `json:"values_editable_by,omitempty"` +} + +// DefaultValueString returns the DefaultValue as a string if the ValueType is string or single_select or url. +func (cp CustomProperty) DefaultValueString() (string, bool) { + switch cp.ValueType { + case PropertyValueTypeString, PropertyValueTypeSingleSelect, PropertyValueTypeURL: + s, ok := cp.DefaultValue.(string) + return s, ok + default: + return "", false + } +} + +// DefaultValueStrings returns the DefaultValue as a slice of string if the ValueType is multi_select. +func (cp CustomProperty) DefaultValueStrings() ([]string, bool) { + switch cp.ValueType { + case PropertyValueTypeMultiSelect: + switch v := cp.DefaultValue.(type) { + case []string: + return v, true + case []any: + vals := make([]string, len(v)) + for i, item := range v { + s, ok := item.(string) + if !ok { + return nil, false + } + vals[i] = s + } + return vals, true + default: + return nil, false + } + default: + return nil, false + } +} + +// DefaultValueBool returns the DefaultValue as a string if the ValueType is true_false. +func (cp CustomProperty) DefaultValueBool() (bool, bool) { + switch cp.ValueType { + case PropertyValueTypeTrueFalse: + if s, ok := cp.DefaultValue.(string); ok { + b, err := strconv.ParseBool(s) + return b, err == nil + } + return false, false + default: + return false, false + } +} + +// RepoCustomPropertyValue represents a repository custom property value. +type RepoCustomPropertyValue struct { + RepositoryID int64 `json:"repository_id"` + RepositoryName string `json:"repository_name"` + RepositoryFullName string `json:"repository_full_name"` + Properties []*CustomPropertyValue `json:"properties"` +} + +// CustomPropertyValue represents a custom property value. +type CustomPropertyValue struct { + PropertyName string `json:"property_name"` + Value any `json:"value"` +} + +// ListCustomPropertyValuesOptions specifies the optional parameters to the +// OrganizationsService.ListCustomPropertyValues method. +type ListCustomPropertyValuesOptions struct { + RepositoryQuery string `url:"repository_query,omitempty"` + ListOptions +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// This helps us handle the fact that Value can be either a string, []string, or nil. +func (cpv *CustomPropertyValue) UnmarshalJSON(data []byte) error { + type aliasCustomPropertyValue CustomPropertyValue + aux := &struct { + *aliasCustomPropertyValue + }{ + aliasCustomPropertyValue: (*aliasCustomPropertyValue)(cpv), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + switch v := aux.Value.(type) { + case nil: + cpv.Value = nil + case string: + cpv.Value = v + case []any: + strSlice := make([]string, len(v)) + for i, item := range v { + str, ok := item.(string) + if !ok { + return errors.New("non-string value in string array") + } + strSlice[i] = str + } + cpv.Value = strSlice + default: + return fmt.Errorf("unexpected value type: %T", v) + } + return nil +} + +// GetAllCustomProperties gets all custom properties that are defined for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#get-all-custom-properties-for-an-organization +// +//meta:operation GET /orgs/{org}/properties/schema +func (s *OrganizationsService) GetAllCustomProperties(ctx context.Context, org string) ([]*CustomProperty, *Response, error) { + u := fmt.Sprintf("orgs/%v/properties/schema", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customProperties []*CustomProperty + resp, err := s.client.Do(req, &customProperties) + if err != nil { + return nil, resp, err + } + + return customProperties, resp, nil +} + +// CreateOrUpdateCustomProperties creates new or updates existing custom properties that are defined for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#create-or-update-custom-properties-for-an-organization +// +//meta:operation PATCH /orgs/{org}/properties/schema +func (s *OrganizationsService) CreateOrUpdateCustomProperties(ctx context.Context, org string, properties []*CustomProperty) ([]*CustomProperty, *Response, error) { + u := fmt.Sprintf("orgs/%v/properties/schema", org) + + params := struct { + Properties []*CustomProperty `json:"properties"` + }{ + Properties: properties, + } + + req, err := s.client.NewRequest(ctx, "PATCH", u, params) + if err != nil { + return nil, nil, err + } + + var customProperties []*CustomProperty + resp, err := s.client.Do(req, &customProperties) + if err != nil { + return nil, resp, err + } + + return customProperties, resp, nil +} + +// GetCustomProperty gets a custom property that is defined for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#get-a-custom-property-for-an-organization +// +//meta:operation GET /orgs/{org}/properties/schema/{custom_property_name} +func (s *OrganizationsService) GetCustomProperty(ctx context.Context, org, name string) (*CustomProperty, *Response, error) { + u := fmt.Sprintf("orgs/%v/properties/schema/%v", org, name) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customProperty *CustomProperty + resp, err := s.client.Do(req, &customProperty) + if err != nil { + return nil, resp, err + } + + return customProperty, resp, nil +} + +// CreateOrUpdateCustomProperty creates a new or updates an existing custom property that is defined for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#create-or-update-a-custom-property-for-an-organization +// +//meta:operation PUT /orgs/{org}/properties/schema/{custom_property_name} +func (s *OrganizationsService) CreateOrUpdateCustomProperty(ctx context.Context, org, customPropertyName string, body *CustomProperty) (*CustomProperty, *Response, error) { + u := fmt.Sprintf("orgs/%v/properties/schema/%v", org, customPropertyName) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var customProperty *CustomProperty + resp, err := s.client.Do(req, &customProperty) + if err != nil { + return nil, resp, err + } + + return customProperty, resp, nil +} + +// RemoveCustomProperty removes a custom property that is defined for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#remove-a-custom-property-for-an-organization +// +//meta:operation DELETE /orgs/{org}/properties/schema/{custom_property_name} +func (s *OrganizationsService) RemoveCustomProperty(ctx context.Context, org, customPropertyName string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/properties/schema/%v", org, customPropertyName) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListCustomPropertyValues lists all custom property values for repositories in the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#list-custom-property-values-for-organization-repositories +// +//meta:operation GET /orgs/{org}/properties/values +func (s *OrganizationsService) ListCustomPropertyValues(ctx context.Context, org string, opts *ListCustomPropertyValuesOptions) ([]*RepoCustomPropertyValue, *Response, error) { + u := fmt.Sprintf("orgs/%v/properties/values", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repoCustomPropertyValues []*RepoCustomPropertyValue + resp, err := s.client.Do(req, &repoCustomPropertyValues) + if err != nil { + return nil, resp, err + } + + return repoCustomPropertyValues, resp, nil +} + +// CreateOrUpdateRepoCustomPropertyValues creates new or updates existing custom property values across multiple repositories for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/custom-properties?apiVersion=2022-11-28#create-or-update-custom-property-values-for-organization-repositories +// +//meta:operation PATCH /orgs/{org}/properties/values +func (s *OrganizationsService) CreateOrUpdateRepoCustomPropertyValues(ctx context.Context, org string, repoNames []string, properties []*CustomPropertyValue) (*Response, error) { + u := fmt.Sprintf("orgs/%v/properties/values", org) + + params := struct { + RepositoryNames []string `json:"repository_names"` + Properties []*CustomPropertyValue `json:"properties"` + }{ + RepositoryNames: repoNames, + Properties: properties, + } + + req, err := s.client.NewRequest(ctx, "PATCH", u, params) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/orgs_properties_test.go b/github/orgs_properties_test.go new file mode 100644 index 00000000000..c2e5f8a5201 --- /dev/null +++ b/github/orgs_properties_test.go @@ -0,0 +1,806 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +func TestOrganizationsService_GetAllCustomProperties(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/properties/schema", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "property_name": "name", + "value_type": "single_select", + "required": true, + "default_value": "production", + "description": "Prod or dev environment", + "allowed_values":[ + "production", + "development" + ], + "values_editable_by": "org_actors" + }, + { + "property_name": "test", + "value_type": "multi_select", + "required": true, + "default_value": [ + "foo", + "baz" + ], + "description": "Prod or dev environment", + "allowed_values":[ + "foo", + "bar", + "baz" + ], + "values_editable_by": "org_actors" + }, + { + "property_name": "service", + "value_type": "string" + }, + { + "property_name": "team", + "value_type": "string", + "description": "Team owning the repository" + }, + { + "property_name": "documentation", + "value_type": "url", + "required": true, + "description": "Link to the documentation", + "default_value": "https://example.com/docs" + } +]`) + }) + + ctx := t.Context() + properties, _, err := client.Organizations.GetAllCustomProperties(ctx, "o") + if err != nil { + t.Errorf("Organizations.GetAllCustomProperties returned error: %v", err) + } + + want := []*CustomProperty{ + { + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + }, + { + PropertyName: Ptr("test"), + ValueType: PropertyValueTypeMultiSelect, + Required: Ptr(true), + DefaultValue: []any{"foo", "baz"}, + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"foo", "bar", "baz"}, + ValuesEditableBy: Ptr("org_actors"), + }, + { + PropertyName: Ptr("service"), + ValueType: PropertyValueTypeString, + }, + { + PropertyName: Ptr("team"), + ValueType: PropertyValueTypeString, + Description: Ptr("Team owning the repository"), + }, + { + PropertyName: Ptr("documentation"), + ValueType: PropertyValueTypeURL, + Required: Ptr(true), + Description: Ptr("Link to the documentation"), + DefaultValue: "https://example.com/docs", + }, + } + + const methodName = "GetAllCustomProperties" + + if diff := cmp.Diff(want, properties); diff != "" { + t.Errorf("Organizations.%v diff mismatch (-want +got):\n%v", methodName, diff) + } + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetAllCustomProperties(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateOrUpdateCustomProperties(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := struct { + Properties []*CustomProperty `json:"properties"` + }{ + Properties: []*CustomProperty{ + { + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + }, + { + PropertyName: Ptr("service"), + ValueType: PropertyValueTypeString, + }, + }, + } + + mux.HandleFunc("/orgs/o/properties/schema", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `[ + { + "property_name": "name", + "value_type": "single_select", + "required": true + }, + { + "property_name": "service", + "value_type": "string" + } + ]`) + }) + + ctx := t.Context() + properties, _, err := client.Organizations.CreateOrUpdateCustomProperties(ctx, "o", input.Properties) + if err != nil { + t.Errorf("Organizations.CreateOrUpdateCustomProperties returned error: %v", err) + } + + want := []*CustomProperty{ + { + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + }, + { + PropertyName: Ptr("service"), + ValueType: PropertyValueTypeString, + }, + } + + if !cmp.Equal(properties, want) { + t.Errorf("Organizations.CreateOrUpdateCustomProperties returned %+v, want %+v", properties, want) + } + + const methodName = "CreateOrUpdateCustomProperties" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateOrUpdateCustomProperties(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/properties/schema/name", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "property_name": "name", + "value_type": "single_select", + "required": true, + "default_value": "production", + "description": "Prod or dev environment", + "allowed_values":[ + "production", + "development" + ], + "values_editable_by": "org_actors" + }`) + }) + + ctx := t.Context() + property, _, err := client.Organizations.GetCustomProperty(ctx, "o", "name") + if err != nil { + t.Errorf("Organizations.GetCustomProperty returned error: %v", err) + } + + want := &CustomProperty{ + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + } + if !cmp.Equal(property, want) { + t.Errorf("Organizations.GetCustomProperty returned %+v, want %+v", property, want) + } + + const methodName = "GetCustomProperty" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetCustomProperty(ctx, "o", "name") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateOrUpdateCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/properties/schema/name", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "property_name": "name", + "value_type": "single_select", + "required": true, + "default_value": "production", + "description": "Prod or dev environment", + "allowed_values":[ + "production", + "development" + ], + "values_editable_by": "org_actors" + }`) + }) + + ctx := t.Context() + property, _, err := client.Organizations.CreateOrUpdateCustomProperty(ctx, "o", "name", &CustomProperty{ + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + }) + if err != nil { + t.Errorf("Organizations.CreateOrUpdateCustomProperty returned error: %v", err) + } + + want := &CustomProperty{ + PropertyName: Ptr("name"), + ValueType: PropertyValueTypeSingleSelect, + Required: Ptr(true), + DefaultValue: "production", + Description: Ptr("Prod or dev environment"), + AllowedValues: []string{"production", "development"}, + ValuesEditableBy: Ptr("org_actors"), + } + if !cmp.Equal(property, want) { + t.Errorf("Organizations.CreateOrUpdateCustomProperty returned %+v, want %+v", property, want) + } + + const methodName = "CreateOrUpdateCustomProperty" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateOrUpdateCustomProperty(ctx, "o", "name", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_RemoveCustomProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/properties/schema/name", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.RemoveCustomProperty(ctx, "o", "name") + if err != nil { + t.Errorf("Organizations.RemoveCustomProperty returned error: %v", err) + } + + const methodName = "RemoveCustomProperty" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveCustomProperty(ctx, "0", "name") + }) +} + +func TestOrganizationsService_ListCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "100", + "repository_query": "repo:octocat/Hello-World", + }) + fmt.Fprint(w, `[{ + "repository_id": 1296269, + "repository_name": "Hello-World", + "repository_full_name": "octocat/Hello-World", + "properties": [ + { + "property_name": "environment", + "value": "production" + }, + { + "property_name": "service", + "value": "web" + }, + { + "property_name": "languages", + "value": ["Go", "JavaScript"] + }, + { + "property_name": "null_property", + "value": null + } + ] + }]`) + }) + + ctx := t.Context() + repoPropertyValues, _, err := client.Organizations.ListCustomPropertyValues(ctx, "o", &ListCustomPropertyValuesOptions{ + ListOptions: ListOptions{ + Page: 1, + PerPage: 100, + }, + RepositoryQuery: "repo:octocat/Hello-World", + }) + if err != nil { + t.Errorf("Organizations.ListCustomPropertyValues returned error: %v", err) + } + + want := []*RepoCustomPropertyValue{ + { + RepositoryID: 1296269, + RepositoryName: "Hello-World", + RepositoryFullName: "octocat/Hello-World", + Properties: []*CustomPropertyValue{ + { + PropertyName: "environment", + Value: "production", + }, + { + PropertyName: "service", + Value: "web", + }, + { + PropertyName: "languages", + Value: []string{"Go", "JavaScript"}, + }, + { + PropertyName: "null_property", + Value: nil, + }, + }, + }, + } + + if !cmp.Equal(repoPropertyValues, want) { + t.Errorf("Organizations.ListCustomPropertyValues returned %+v, want %+v", repoPropertyValues, want) + } + + const methodName = "ListCustomPropertyValues" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListCustomPropertyValues(ctx, "\n", &ListCustomPropertyValuesOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListCustomPropertyValues(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestCustomPropertyValue_UnmarshalJSON(t *testing.T) { + t.Parallel() + tests := map[string]struct { + data string + want *CustomPropertyValue + wantErr bool + }{ + "Invalid JSON": { + data: `{`, + want: &CustomPropertyValue{}, + wantErr: true, + }, + "String value": { + data: `{ + "property_name": "environment", + "value": "production" + }`, + want: &CustomPropertyValue{ + PropertyName: "environment", + Value: "production", + }, + wantErr: false, + }, + "Array of strings value": { + data: `{ + "property_name": "languages", + "value": ["Go", "JavaScript"] + }`, + want: &CustomPropertyValue{ + PropertyName: "languages", + Value: []string{"Go", "JavaScript"}, + }, + wantErr: false, + }, + "Non-string value in array": { + data: `{ + "property_name": "languages", + "value": ["Go", 42] + }`, + want: &CustomPropertyValue{ + PropertyName: "languages", + Value: nil, + }, + wantErr: true, + }, + "Unexpected value type": { + data: `{ + "property_name": "environment", + "value": {"invalid": "type"} + }`, + want: &CustomPropertyValue{ + PropertyName: "environment", + Value: nil, + }, + wantErr: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + cpv := &CustomPropertyValue{} + err := cpv.UnmarshalJSON([]byte(tc.data)) + if (err != nil) != tc.wantErr { + t.Errorf("CustomPropertyValue.UnmarshalJSON error = %v, wantErr %v", err, tc.wantErr) + return + } + if !tc.wantErr && !cmp.Equal(tc.want, cpv) { + t.Errorf("CustomPropertyValue.UnmarshalJSON expected %+v, got %+v", tc.want, cpv) + } + }) + } +} + +func TestOrganizationsService_CreateOrUpdateRepoCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := struct { + RepositoryNames []string `json:"repository_names"` + Properties []*CustomPropertyValue `json:"properties"` + }{ + RepositoryNames: []string{"repo"}, + Properties: []*CustomPropertyValue{ + { + PropertyName: "service", + Value: "string", + }, + }, + } + + mux.HandleFunc("/orgs/o/properties/values", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + _, err := client.Organizations.CreateOrUpdateRepoCustomPropertyValues(ctx, "o", input.RepositoryNames, input.Properties) + if err != nil { + t.Errorf("Organizations.CreateOrUpdateRepoCustomPropertyValues returned error: %v", err) + } + + const methodName = "CreateOrUpdateRepoCustomPropertyValues" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.CreateOrUpdateRepoCustomPropertyValues(ctx, "o", nil, nil) + }) +} + +func TestCustomPropertyDefaultValueString(t *testing.T) { + t.Parallel() + for _, d := range []struct { + testName string + property *CustomProperty + ok bool + want string + }{ + { + testName: "invalid_type", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: []string{"a", "b"}, + }, + ok: false, + want: "", + }, + { + testName: "string_invalid_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeString, + DefaultValue: []string{"a", "b"}, + }, + ok: false, + want: "", + }, + { + testName: "string_nil_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeString, + DefaultValue: nil, + }, + ok: false, + want: "", + }, + { + testName: "string_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeString, + DefaultValue: "test-string", + }, + ok: true, + want: "test-string", + }, + { + testName: "single_select_invalid_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeSingleSelect, + DefaultValue: []string{"a", "b"}, + }, + ok: false, + want: "", + }, + { + testName: "single_select_nil_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeSingleSelect, + DefaultValue: nil, + }, + ok: false, + want: "", + }, + { + testName: "single_select_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeSingleSelect, + DefaultValue: "test-string", + }, + ok: true, + want: "test-string", + }, + { + testName: "url_invalid_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeURL, + DefaultValue: []string{"a", "b"}, + }, + ok: false, + want: "", + }, + { + testName: "url_nil_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeURL, + DefaultValue: nil, + }, + ok: false, + want: "", + }, + { + testName: "url_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeURL, + DefaultValue: "http://example.com", + }, + ok: true, + want: "http://example.com", + }, + } { + t.Run(d.testName, func(t *testing.T) { + t.Parallel() + got, ok := d.property.DefaultValueString() + + if ok != d.ok { + t.Fatalf("CustomProperty.DefaultValueString set ok to %+v, want %+v", ok, d.ok) + } + + if got != d.want { + t.Fatalf("CustomProperty.DefaultValueString returned %+v, want %+v", got, d.want) + } + }) + } +} + +func TestCustomPropertyDefaultValueStrings(t *testing.T) { + t.Parallel() + for _, d := range []struct { + testName string + property *CustomProperty + ok bool + want []string + }{ + { + testName: "invalid_type", + property: &CustomProperty{ + ValueType: PropertyValueTypeString, + DefaultValue: "test", + }, + ok: false, + want: []string{}, + }, + { + testName: "invalid_slice", + property: &CustomProperty{ + ValueType: PropertyValueTypeString, + DefaultValue: []any{1, 2, 3}, + }, + ok: false, + want: []string{}, + }, + { + testName: "multi_select_invalid_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: "test", + }, + ok: false, + want: []string{}, + }, + { + testName: "multi_select_nil_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: nil, + }, + ok: false, + want: []string{}, + }, + { + testName: "multi_select_any_slice_single_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: []any{"a"}, + }, + ok: true, + want: []string{"a"}, + }, + { + testName: "multi_select_string_slice_single_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: []string{"a"}, + }, + ok: true, + want: []string{"a"}, + }, + { + testName: "multi_select_any_slice_multi_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: []any{"a", "b"}, + }, + ok: true, + want: []string{"a", "b"}, + }, + { + testName: "multi_select_string_slice_multi_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeMultiSelect, + DefaultValue: []string{"a", "b"}, + }, + ok: true, + want: []string{"a", "b"}, + }, + } { + t.Run(d.testName, func(t *testing.T) { + t.Parallel() + got, ok := d.property.DefaultValueStrings() + + if ok != d.ok { + t.Fatalf("CustomProperty.DefaultValueStrings set ok to %+v, want %+v", ok, d.ok) + } + + if !cmp.Equal(got, d.want, cmpopts.EquateEmpty()) { + t.Fatalf("CustomProperty.DefaultValueStrings returned %+v, want %+v", got, d.want) + } + }) + } +} + +func TestCustomPropertyDefaultValueBool(t *testing.T) { + t.Parallel() + for _, d := range []struct { + testName string + property *CustomProperty + ok bool + want bool + }{ + { + testName: "invalid_type", + property: &CustomProperty{ + ValueType: PropertyValueTypeString, + DefaultValue: "test", + }, + ok: false, + want: false, + }, + { + testName: "true_false_invalid_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeTrueFalse, + DefaultValue: "test", + }, + ok: false, + want: false, + }, + { + testName: "true_false_nil_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeTrueFalse, + DefaultValue: nil, + }, + ok: false, + want: false, + }, + { + testName: "true_false_true_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeTrueFalse, + DefaultValue: "true", + }, + ok: true, + want: true, + }, + { + testName: "true_false_false_value", + property: &CustomProperty{ + ValueType: PropertyValueTypeTrueFalse, + DefaultValue: "false", + }, + ok: true, + want: false, + }, + } { + t.Run(d.testName, func(t *testing.T) { + t.Parallel() + got, ok := d.property.DefaultValueBool() + + if ok != d.ok { + t.Fatalf("CustomProperty.DefaultValueBool set ok to %+v, want %+v", ok, d.ok) + } + + if ok != d.ok { + t.Fatalf("CustomProperty.DefaultValueBool returned %+v, want %+v", got, d.want) + } + }) + } +} diff --git a/github/orgs_rules.go b/github/orgs_rules.go new file mode 100644 index 00000000000..fde484ab21c --- /dev/null +++ b/github/orgs_rules.go @@ -0,0 +1,120 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListAllRepositoryRulesets gets all the repository rulesets for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules?apiVersion=2022-11-28#get-all-organization-repository-rulesets +// +//meta:operation GET /orgs/{org}/rulesets +func (s *OrganizationsService) ListAllRepositoryRulesets(ctx context.Context, org string, opts *ListOptions) ([]*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets", org) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rulesets []*RepositoryRuleset + resp, err := s.client.Do(req, &rulesets) + if err != nil { + return nil, resp, err + } + + return rulesets, resp, nil +} + +// CreateRepositoryRuleset creates a repository ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules?apiVersion=2022-11-28#create-an-organization-repository-ruleset +// +//meta:operation POST /orgs/{org}/rulesets +func (s *OrganizationsService) CreateRepositoryRuleset(ctx context.Context, org string, body RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// GetRepositoryRuleset gets a repository ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules?apiVersion=2022-11-28#get-an-organization-repository-ruleset +// +//meta:operation GET /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) GetRepositoryRuleset(ctx context.Context, org string, rulesetID int64) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *RepositoryRuleset + resp, err := s.client.Do(req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRepositoryRuleset updates a repository ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules?apiVersion=2022-11-28#update-an-organization-repository-ruleset +// +//meta:operation PUT /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) UpdateRepositoryRuleset(ctx context.Context, org string, rulesetID int64, body RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// DeleteRepositoryRuleset deletes a repository ruleset from the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules?apiVersion=2022-11-28#delete-an-organization-repository-ruleset +// +//meta:operation DELETE /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) DeleteRepositoryRuleset(ctx context.Context, org string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/orgs_rules_test.go b/github/orgs_rules_test.go new file mode 100644 index 00000000000..8d46b456c33 --- /dev/null +++ b/github/orgs_rules_test.go @@ -0,0 +1,1671 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListAllRepositoryRulesets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "id": 21, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/21" + } + } + }]`) + }) + + ctx := t.Context() + rulesets, _, err := client.Organizations.ListAllRepositoryRulesets(ctx, "o", nil) + if err != nil { + t.Errorf("Organizations.ListAllRepositoryRulesets returned error: %v", err) + } + + want := []*RepositoryRuleset{{ + ID: Ptr(int64(21)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/orgs/o/rulesets/21")}, + }, + }} + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.ListAllRepositoryRulesets returned %+v, want %+v", rulesets, want) + } + + const methodName = "ListAllRepositoryRulesets" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListAllRepositoryRulesets(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListAllRepositoryRulesets_ListOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + "per_page": "35", + }) + fmt.Fprint(w, `[{ + "id": 21 + }]`) + }) + + opts := &ListOptions{Page: 2, PerPage: 35} + ctx := t.Context() + rulesets, _, err := client.Organizations.ListAllRepositoryRulesets(ctx, "o", opts) + if err != nil { + t.Errorf("Organizations.ListAllRepositoryRulesets returned error: %v", err) + } + + want := []*RepositoryRuleset{{ + ID: Ptr(int64(21)), + }} + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.ListAllRepositoryRulesets returned %+v, want %+v", rulesets, want) + } + + const methodName = "ListAllRepositoryRulesets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListAllRepositoryRulesets(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListAllRepositoryRulesets(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateRepositoryRuleset_RepoNames(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 21, + "name": "ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + }, + { + "actor_id": 345, + "actor_type": "Team", + "bypass_mode": "exempt" + } + ], + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Organizations.CreateRepositoryRuleset(ctx, "o", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + { + ActorID: Ptr(int64(345)), + ActorType: Ptr(BypassActorTypeTeam), + BypassMode: Ptr(BypassModeExempt), + }, + }, + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Organizations.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + { + ActorID: Ptr(int64(345)), + ActorType: Ptr(BypassActorTypeTeam), + BypassMode: Ptr(BypassModeExempt), + }, + }, + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Organizations.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateRepositoryRuleset(ctx, "o", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateRepositoryRuleset_RepoProperty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 21, + "name": "ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "repository_property": { + "include": [ + { + "name": "testIncludeProp", + "source": "custom", + "property_values": [ + "true" + ] + } + ], + "exclude": [ + { + "name": "testExcludeProp", + "property_values": [ + "false" + ] + } + ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Organizations.CreateRepositoryRuleset(ctx, "o", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testExcludeProp", + PropertyValues: []string{"false"}, + }, + }, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Organizations.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testExcludeProp", + PropertyValues: []string{"false"}, + }, + }, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Organizations.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateRepositoryRuleset(ctx, "o", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateRepositoryRuleset_RepoIDs(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 21, + "name": "ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_id": { + "repository_ids": [ 123, 456 ] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["rebase","squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": true, + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "code_scanning", + "parameters": { + "code_scanning_tools": [ + { + "tool": "CodeQL", + "security_alerts_threshold": "high_or_higher", + "alerts_threshold": "errors" + } + ] + } + } + ] + }`) + }) + + ctx := t.Context() + ruleset, _, err := client.Organizations.CreateRepositoryRuleset(ctx, "o", RepositoryRuleset{ + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryID: &RepositoryRulesetRepositoryIDsConditionParameters{ + RepositoryIDs: []int64{123, 456}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("Organizations.CreateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + BypassActors: []*BypassActor{ + { + ActorID: Ptr(int64(234)), + ActorType: Ptr(BypassActorTypeTeam), + }, + }, + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryID: &RepositoryRulesetRepositoryIDsConditionParameters{ + RepositoryIDs: []int64{123, 456}, + }, + }, + Rules: &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{PullRequestMergeMethodRebase, PullRequestMergeMethodSquash}, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + { + Context: "test", + IntegrationID: Ptr(int64(1)), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("avoid test commits"), + Negate: Ptr(true), + Operator: "starts_with", + Pattern: "[test]", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: "contains", + Pattern: "github", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("avoid commit emails"), + Negate: Ptr(true), + Operator: "ends_with", + Pattern: "abc", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid branch names"), + Negate: Ptr(true), + Operator: "regex", + Pattern: "github$", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("avoid tag names"), + Negate: Ptr(true), + Operator: "contains", + Pattern: "github", + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdErrors, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdHighOrHigher, + Tool: "CodeQL", + }, + }, + }, + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Organizations.CreateRepositoryRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateRepositoryRuleset(ctx, "o", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetRepositoryRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 21, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/21" + } + }, + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := t.Context() + rulesets, _, err := client.Organizations.GetRepositoryRuleset(ctx, "o", 21) + if err != nil { + t.Errorf("Organizations.GetRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/orgs/o/rulesets/21")}, + }, + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.GetRepositoryRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "GetRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetRepositoryRuleset(ctx, "o", 21) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetRepositoryRulesetWithRepoPropCondition(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 21, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/21" + } + }, + "conditions": { + "repository_property": { + "exclude": [], + "include": [ + { + "name": "testIncludeProp", + "source": "custom", + "property_values": [ + "true" + ] + } + ] + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := t.Context() + rulesets, _, err := client.Organizations.GetRepositoryRuleset(ctx, "o", 21) + if err != nil { + t.Errorf("Organizations.GetRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/orgs/o/rulesets/21")}, + }, + Conditions: &RepositoryRulesetConditions{ + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{}, + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.GetRepositoryRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "GetRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetRepositoryRuleset(ctx, "o", 21) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateRepositoryRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "id": 21, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/21" + } + }, + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := t.Context() + rulesets, _, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, RepositoryRuleset{ + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + }) + if err != nil { + t.Errorf("Organizations.UpdateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/orgs/o/rulesets/21")}, + }, + Conditions: &RepositoryRulesetConditions{ + RefName: &RepositoryRulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RepositoryRulesetRepositoryNamesConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Ptr(true), + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.UpdateRepositoryRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "UpdateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateRepositoryRulesetWithRepoProp(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "id": 21, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/21" + } + }, + "conditions": { + "repository_property": { + "exclude": [], + "include": [ + { + "name": "testIncludeProp", + "source": "custom", + "property_values": [ + "true" + ] + } + ] + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := t.Context() + rulesets, _, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, RepositoryRuleset{ + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + Enforcement: "active", + Conditions: &RepositoryRulesetConditions{ + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{}, + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + }) + if err != nil { + t.Errorf("Organizations.UpdateRepositoryRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(21)), + Name: "test ruleset", + Target: Ptr(RulesetTargetBranch), + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: "active", + NodeID: Ptr("nid"), + Links: &RepositoryRulesetLinks{ + Self: &RepositoryRulesetLink{HRef: Ptr("https://api.github.com/orgs/o/rulesets/21")}, + }, + Conditions: &RepositoryRulesetConditions{ + RepositoryProperty: &RepositoryRulesetRepositoryPropertyConditionParameters{ + Include: []*RepositoryRulesetRepositoryPropertyTargetParameters{ + { + Name: "testIncludeProp", + Source: Ptr("custom"), + PropertyValues: []string{"true"}, + }, + }, + Exclude: []*RepositoryRulesetRepositoryPropertyTargetParameters{}, + }, + }, + Rules: &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.UpdateRepositoryRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "UpdateRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateRepositoryRuleset_OmitZero_Nil(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepositoryRuleset{ + Name: "test ruleset", + Enforcement: RulesetEnforcementActive, + BypassActors: nil, + } + + mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + fmt.Fprint(w, `{ + "id": 21, + "name": "test ruleset", + "source_type": "Organization", + "source": "o", + "enforcement": "active" + }`) + }) + + ctx := t.Context() + + _, _, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, input) + if err != nil { + t.Errorf("Organizations.UpdateRepositoryRuleset returned error: %v", err) + } +} + +func TestOrganizationsService_UpdateRepositoryRuleset_OmitZero_EmptySlice(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepositoryRuleset{ + Name: "test ruleset", + Enforcement: RulesetEnforcementActive, + BypassActors: []*BypassActor{}, + } + + mux.HandleFunc("/orgs/o/rulesets/21", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + fmt.Fprint(w, `{ + "id": 21, + "name": "test ruleset", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_actors": [] + }`) + }) + + ctx := t.Context() + _, _, err := client.Organizations.UpdateRepositoryRuleset(ctx, "o", 21, input) + if err != nil { + t.Errorf("Organizations.UpdateRepositoryRuleset returned error: %v", err) + } +} + +func TestOrganizationsService_DeleteRepositoryRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/rulesets/21", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.DeleteRepositoryRuleset(ctx, "o", 21) + if err != nil { + t.Errorf("Organizations.DeleteRepositoryRuleset returned error: %v", err) + } + + const methodName = "DeleteRepositoryRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DeleteRepositoryRuleset(ctx, "0", 21) + }) +} diff --git a/github/orgs_security_managers.go b/github/orgs_security_managers.go new file mode 100644 index 00000000000..088a5700a38 --- /dev/null +++ b/github/orgs_security_managers.go @@ -0,0 +1,69 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListSecurityManagerTeams lists all security manager teams for an organization. +// +// Deprecated: Please use `client.Organizations.ListTeamsAssignedToOrgRole` instead. +// +// GitHub API docs: https://docs.github.com/rest/orgs/security-managers?apiVersion=2022-11-28#list-security-manager-teams +// +//meta:operation GET /orgs/{org}/security-managers +func (s *OrganizationsService) ListSecurityManagerTeams(ctx context.Context, org string) ([]*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/security-managers", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var teams []*Team + resp, err := s.client.Do(req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// AddSecurityManagerTeam adds a team to the list of security managers for an organization. +// +// Deprecated: Please use `client.Organizations.AssignOrgRoleToTeam` instead. +// +// GitHub API docs: https://docs.github.com/rest/orgs/security-managers?apiVersion=2022-11-28#add-a-security-manager-team +// +//meta:operation PUT /orgs/{org}/security-managers/teams/{team_slug} +func (s *OrganizationsService) AddSecurityManagerTeam(ctx context.Context, org, team string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/security-managers/teams/%v", org, team) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveSecurityManagerTeam removes a team from the list of security managers for an organization. +// +// Deprecated: Please use `client.Organizations.RemoveOrgRoleFromTeam` instead. +// +// GitHub API docs: https://docs.github.com/rest/orgs/security-managers?apiVersion=2022-11-28#remove-a-security-manager-team +// +//meta:operation DELETE /orgs/{org}/security-managers/teams/{team_slug} +func (s *OrganizationsService) RemoveSecurityManagerTeam(ctx context.Context, org, team string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/security-managers/teams/%v", org, team) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/orgs_security_managers_test.go b/github/orgs_security_managers_test.go new file mode 100644 index 00000000000..7ddce4b2951 --- /dev/null +++ b/github/orgs_security_managers_test.go @@ -0,0 +1,144 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_ListSecurityManagerTeams(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/security-managers", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + ctx := t.Context() + teams, _, err := client.Organizations.ListSecurityManagerTeams(ctx, "o") + if err != nil { + t.Errorf("Organizations.ListSecurityManagerTeams returned error: %v", err) + } + + want := []*Team{{ID: Ptr(int64(1))}} + if !cmp.Equal(teams, want) { + t.Errorf("Organizations.ListSecurityManagerTeams returned %+v, want %+v", teams, want) + } + + const methodName = "ListSecurityManagerTeams" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListSecurityManagerTeams(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListSecurityManagerTeams(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListSecurityManagerTeams_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.ListSecurityManagerTeams(ctx, "%") + testURLParseError(t, err) +} + +func TestOrganizationsService_AddSecurityManagerTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/security-managers/teams/t", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + ctx := t.Context() + _, err := client.Organizations.AddSecurityManagerTeam(ctx, "o", "t") + if err != nil { + t.Errorf("Organizations.AddSecurityManagerTeam returned error: %v", err) + } + + const methodName = "AddSecurityManagerTeam" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.AddSecurityManagerTeam(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.AddSecurityManagerTeam(ctx, "o", "t") + }) +} + +func TestOrganizationsService_AddSecurityManagerTeam_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Organizations.AddSecurityManagerTeam(ctx, "%", "t") + testURLParseError(t, err) +} + +func TestOrganizationsService_AddSecurityManagerTeam_invalidTeam(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Organizations.AddSecurityManagerTeam(ctx, "%", "t") + testURLParseError(t, err) +} + +func TestOrganizationsService_RemoveSecurityManagerTeam(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/security-managers/teams/t", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.RemoveSecurityManagerTeam(ctx, "o", "t") + if err != nil { + t.Errorf("Organizations.RemoveSecurityManagerTeam returned error: %v", err) + } + + const methodName = "RemoveSecurityManagerTeam" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.RemoveSecurityManagerTeam(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.RemoveSecurityManagerTeam(ctx, "o", "t") + }) +} + +func TestOrganizationsService_RemoveSecurityManagerTeam_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Organizations.RemoveSecurityManagerTeam(ctx, "%", "t") + testURLParseError(t, err) +} + +func TestOrganizationsService_RemoveSecurityManagerTeam_invalidTeam(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Organizations.RemoveSecurityManagerTeam(ctx, "%", "t") + testURLParseError(t, err) +} diff --git a/github/orgs_test.go b/github/orgs_test.go index 3b938fc993b..2ea5ecf05ef 100644 --- a/github/orgs_test.go +++ b/github/orgs_test.go @@ -6,60 +6,83 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestOrganizationsService_ListAll(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - since := int64(1342004) mux.HandleFunc("/organizations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testFormValues(t, r, values{"since": "1342004"}) + testFormValues(t, r, values{"since": "1342004", "per_page": "30"}) fmt.Fprint(w, `[{"id":4314092}]`) }) - opt := &OrganizationsListOptions{Since: since} - orgs, _, err := client.Organizations.ListAll(context.Background(), opt) + opt := &OrganizationsListOptions{Since: int64(1342004), PerPage: 30} + ctx := t.Context() + orgs, _, err := client.Organizations.ListAll(ctx, opt) if err != nil { t.Errorf("Organizations.ListAll returned error: %v", err) } - want := []*Organization{{ID: Int64(4314092)}} - if !reflect.DeepEqual(orgs, want) { + want := []*Organization{{ID: Ptr(int64(4314092))}} + if !cmp.Equal(orgs, want) { t.Errorf("Organizations.ListAll returned %+v, want %+v", orgs, want) } + + const methodName = "ListAll" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListAll(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_List_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/orgs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) - orgs, _, err := client.Organizations.List(context.Background(), "", nil) + ctx := t.Context() + orgs, _, err := client.Organizations.List(ctx, "", nil) if err != nil { t.Errorf("Organizations.List returned error: %v", err) } - want := []*Organization{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(orgs, want) { + want := []*Organization{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(orgs, want) { t.Errorf("Organizations.List returned %+v, want %+v", orgs, want) } + + const methodName = "List" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.List(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.List(ctx, "", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_List_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/orgs", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -68,28 +91,44 @@ func TestOrganizationsService_List_specifiedUser(t *testing.T) { }) opt := &ListOptions{Page: 2} - orgs, _, err := client.Organizations.List(context.Background(), "u", opt) + ctx := t.Context() + orgs, _, err := client.Organizations.List(ctx, "u", opt) if err != nil { t.Errorf("Organizations.List returned error: %v", err) } - want := []*Organization{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(orgs, want) { + want := []*Organization{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(orgs, want) { t.Errorf("Organizations.List returned %+v, want %+v", orgs, want) } + + const methodName = "List" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.List(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.List(ctx, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_List_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.List(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Organizations.List(ctx, "%", nil) testURLParseError(t, err) } func TestOrganizationsService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -97,78 +136,232 @@ func TestOrganizationsService_Get(t *testing.T) { fmt.Fprint(w, `{"id":1, "login":"l", "url":"u", "avatar_url": "a", "location":"l"}`) }) - org, _, err := client.Organizations.Get(context.Background(), "o") + ctx := t.Context() + org, _, err := client.Organizations.Get(ctx, "o") if err != nil { t.Errorf("Organizations.Get returned error: %v", err) } - want := &Organization{ID: Int64(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")} - if !reflect.DeepEqual(org, want) { + want := &Organization{ID: Ptr(int64(1)), Login: Ptr("l"), URL: Ptr("u"), AvatarURL: Ptr("a"), Location: Ptr("l")} + if !cmp.Equal(org, want) { t.Errorf("Organizations.Get returned %+v, want %+v", org, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.Get(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_Get_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.Get(context.Background(), "%") + ctx := t.Context() + _, _, err := client.Organizations.Get(ctx, "%") testURLParseError(t, err) } func TestOrganizationsService_GetByID(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/organizations/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1, "login":"l", "url":"u", "avatar_url": "a", "location":"l"}`) }) - org, _, err := client.Organizations.GetByID(context.Background(), 1) + ctx := t.Context() + org, _, err := client.Organizations.GetByID(ctx, 1) if err != nil { t.Fatalf("Organizations.GetByID returned error: %v", err) } - want := &Organization{ID: Int64(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")} - if !reflect.DeepEqual(org, want) { + want := &Organization{ID: Ptr(int64(1)), Login: Ptr("l"), URL: Ptr("u"), AvatarURL: Ptr("a"), Location: Ptr("l")} + if !cmp.Equal(org, want) { t.Errorf("Organizations.GetByID returned %+v, want %+v", org, want) } + + const methodName = "GetByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.GetByID(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetByID(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Organization{Login: String("l")} + input := &Organization{Login: Ptr("l")} mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) { - v := new(Organization) - json.NewDecoder(r.Body).Decode(v) - + testHeader(t, r, "Accept", mediaTypeMemberAllowedRepoCreationTypePreview) testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - org, _, err := client.Organizations.Edit(context.Background(), "o", input) + ctx := t.Context() + org, _, err := client.Organizations.Edit(ctx, "o", input) if err != nil { t.Errorf("Organizations.Edit returned error: %v", err) } - want := &Organization{ID: Int64(1)} - if !reflect.DeepEqual(org, want) { + want := &Organization{ID: Ptr(int64(1))} + if !cmp.Equal(org, want) { t.Errorf("Organizations.Edit returned %+v, want %+v", org, want) } + + const methodName = "Edit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.Edit(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.Edit(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_Edit_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Organizations.Edit(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestOrganizationsService_Delete(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Organizations.Delete(ctx, "o") + if err != nil { + t.Errorf("Organizations.Delete returned error: %v", err) + } + + const methodName = "Delete" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.Delete(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.Delete(ctx, "o") + }) +} + +func TestOrganizationsService_ListInstallations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/installations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count": 1, "installations": [{ "id": 1, "app_id": 5}]}`) + }) + + ctx := t.Context() + apps, _, err := client.Organizations.ListInstallations(ctx, "o", nil) + if err != nil { + t.Errorf("Organizations.ListInstallations returned error: %v", err) + } + + want := &OrganizationInstallations{TotalCount: Ptr(1), Installations: []*Installation{{ID: Ptr(int64(1)), AppID: Ptr(int64(5))}}} + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListInstallations returned %+v, want %+v", apps, want) + } + + const methodName = "ListInstallations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListInstallations(ctx, "\no", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListInstallations(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_ListInstallations_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Organizations.Edit(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Organizations.ListInstallations(ctx, "%", nil) testURLParseError(t, err) } + +func TestOrganizationsService_ListInstallations_withListOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/installations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `{"total_count": 2, "installations": [{ "id": 2, "app_id": 10}]}`) + }) + + ctx := t.Context() + apps, _, err := client.Organizations.ListInstallations(ctx, "o", &ListOptions{Page: 2}) + if err != nil { + t.Errorf("Organizations.ListInstallations returned error: %v", err) + } + + want := &OrganizationInstallations{TotalCount: Ptr(2), Installations: []*Installation{{ID: Ptr(int64(2)), AppID: Ptr(int64(10))}}} + if !cmp.Equal(apps, want) { + t.Errorf("Organizations.ListInstallations returned %+v, want %+v", apps, want) + } + + // Test ListOptions failure + _, _, err = client.Organizations.ListInstallations(ctx, "%", &ListOptions{}) + if err == nil { + t.Error("Organizations.ListInstallations returned error: nil") + } + + const methodName = "ListInstallations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListInstallations(ctx, "\n", &ListOptions{Page: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListInstallations(ctx, "o", &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/orgs_users_blocking.go b/github/orgs_users_blocking.go index b1aecf44532..62d578c9329 100644 --- a/github/orgs_users_blocking.go +++ b/github/orgs_users_blocking.go @@ -12,24 +12,25 @@ import ( // ListBlockedUsers lists all the users blocked by an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#list-blocked-users -func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, opt *ListOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/blocking?apiVersion=2022-11-28#list-users-blocked-by-an-organization +// +//meta:operation GET /orgs/{org}/blocks +func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, opts *ListOptions) ([]*User, *Response, error) { u := fmt.Sprintf("orgs/%v/blocks", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) var blockedUsers []*User - resp, err := s.client.Do(ctx, req, &blockedUsers) + resp, err := s.client.Do(req, &blockedUsers) if err != nil { return nil, resp, err } @@ -39,53 +40,56 @@ func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, // IsBlocked reports whether specified user is blocked from an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#check-whether-a-user-is-blocked-from-an-organization -func (s *OrganizationsService) IsBlocked(ctx context.Context, org string, user string) (bool, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/blocking?apiVersion=2022-11-28#check-if-a-user-is-blocked-by-an-organization +// +//meta:operation GET /orgs/{org}/blocks/{username} +func (s *OrganizationsService) IsBlocked(ctx context.Context, org, user string) (bool, *Response, error) { u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) isBlocked, err := parseBoolResponse(err) return isBlocked, resp, err } // BlockUser blocks specified user from an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#block-a-user -func (s *OrganizationsService) BlockUser(ctx context.Context, org string, user string) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/blocking?apiVersion=2022-11-28#block-a-user-from-an-organization +// +//meta:operation PUT /orgs/{org}/blocks/{username} +func (s *OrganizationsService) BlockUser(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UnblockUser unblocks specified user from an organization. // -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#unblock-a-user -func (s *OrganizationsService) UnblockUser(ctx context.Context, org string, user string) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/orgs/blocking?apiVersion=2022-11-28#unblock-a-user-from-an-organization +// +//meta:operation DELETE /orgs/{org}/blocks/{username} +func (s *OrganizationsService) UnblockUser(ctx context.Context, org, user string) (*Response, error) { u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/orgs_users_blocking_test.go b/github/orgs_users_blocking_test.go index 5d38cc44ea8..06165219152 100644 --- a/github/orgs_users_blocking_test.go +++ b/github/orgs_users_blocking_test.go @@ -6,16 +6,16 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestOrganizationsService_ListBlockedUsers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/blocks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,20 +27,35 @@ func TestOrganizationsService_ListBlockedUsers(t *testing.T) { }) opt := &ListOptions{Page: 2} - blockedUsers, _, err := client.Organizations.ListBlockedUsers(context.Background(), "o", opt) + ctx := t.Context() + blockedUsers, _, err := client.Organizations.ListBlockedUsers(ctx, "o", opt) if err != nil { t.Errorf("Organizations.ListBlockedUsers returned error: %v", err) } - want := []*User{{Login: String("octocat")}} - if !reflect.DeepEqual(blockedUsers, want) { + want := []*User{{Login: Ptr("octocat")}} + if !cmp.Equal(blockedUsers, want) { t.Errorf("Organizations.ListBlockedUsers returned %+v, want %+v", blockedUsers, want) } + + const methodName = "ListBlockedUsers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.ListBlockedUsers(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.ListBlockedUsers(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_IsBlocked(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -48,18 +63,33 @@ func TestOrganizationsService_IsBlocked(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - isBlocked, _, err := client.Organizations.IsBlocked(context.Background(), "o", "u") + ctx := t.Context() + isBlocked, _, err := client.Organizations.IsBlocked(ctx, "o", "u") if err != nil { t.Errorf("Organizations.IsBlocked returned error: %v", err) } if want := true; isBlocked != want { t.Errorf("Organizations.IsBlocked returned %+v, want %+v", isBlocked, want) } + + const methodName = "IsBlocked" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Organizations.IsBlocked(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.IsBlocked(ctx, "o", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestOrganizationsService_BlockUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -67,15 +97,26 @@ func TestOrganizationsService_BlockUser(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - _, err := client.Organizations.BlockUser(context.Background(), "o", "u") + ctx := t.Context() + _, err := client.Organizations.BlockUser(ctx, "o", "u") if err != nil { t.Errorf("Organizations.BlockUser returned error: %v", err) } + + const methodName = "BlockUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.BlockUser(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.BlockUser(ctx, "o", "u") + }) } func TestOrganizationsService_UnblockUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/blocks/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -83,8 +124,19 @@ func TestOrganizationsService_UnblockUser(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - _, err := client.Organizations.UnblockUser(context.Background(), "o", "u") + ctx := t.Context() + _, err := client.Organizations.UnblockUser(ctx, "o", "u") if err != nil { t.Errorf("Organizations.UnblockUser returned error: %v", err) } + + const methodName = "UnblockUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Organizations.UnblockUser(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.UnblockUser(ctx, "o", "u") + }) } diff --git a/github/packages.go b/github/packages.go new file mode 100644 index 00000000000..0ad0ed69ef1 --- /dev/null +++ b/github/packages.go @@ -0,0 +1,319 @@ +// Copyright 2020 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" +) + +// Package represents a GitHub package. +type Package struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + PackageType *string `json:"package_type,omitempty"` // One of "npm", "maven", "rubygems", "docker", "nuget", "container". For webhook events "container" is "CONTAINER" + HTMLURL *string `json:"html_url,omitempty"` + Visibility *string `json:"visibility,omitempty"` + Owner *User `json:"owner,omitempty"` + Repository *Repository `json:"repository,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + + // The following are only populated for webhook events + Namespace *string `json:"namespace,omitempty"` + Description *string `json:"description,omitempty"` + Ecosystem *string `json:"ecosystem,omitempty"` + PackageVersion *PackageVersion `json:"package_version,omitempty"` + Registry *PackageRegistry `json:"registry,omitempty"` + + // The following are NOT populated for webhook events + URL *string `json:"url,omitempty"` + VersionCount *int64 `json:"version_count,omitempty"` +} + +func (p Package) String() string { + return Stringify(p) +} + +// PackageVersion represents a GitHub package version. +type PackageVersion struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + PackageHTMLURL *string `json:"package_html_url,omitempty"` + License *string `json:"license,omitempty"` + Description *string `json:"description,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Metadata json.RawMessage `json:"metadata,omitempty"` // For webhook events this will be []interface, else it will be of type PackageMetadata + + // The following are only populated for webhook events + Version *string `json:"version,omitempty"` + Summary *string `json:"summary,omitempty"` + Body json.RawMessage `json:"body,omitempty"` // Can either be a string or of type PackageVersionBody + BodyHTML *string `json:"body_html,omitempty"` + Release *PackageRelease `json:"release,omitempty"` + Manifest *string `json:"manifest,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + TagName *string `json:"tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + TargetOID *string `json:"target_oid,omitempty"` + Draft *bool `json:"draft,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + ContainerMetadata *PackageEventContainerMetadata `json:"container_metadata,omitempty"` + DockerMetadata []any `json:"docker_metadata,omitempty"` + NPMMetadata *PackageNPMMetadata `json:"npm_metadata,omitempty"` + NugetMetadata []*PackageNugetMetadata `json:"nuget_metadata,omitempty"` + RubyMetadata map[string]any `json:"ruby_metadata,omitempty"` + PackageFiles []*PackageFile `json:"package_files,omitempty"` + PackageURL *string `json:"package_url,omitempty"` + Author *User `json:"author,omitempty"` + SourceURL *string `json:"source_url,omitempty"` + InstallationCommand *string `json:"installation_command,omitempty"` + + // The following are NOT populated for webhook events + DeletedAt *Timestamp `json:"deleted_at,omitempty"` +} + +// GetBody returns the body field as a string if it's valid. +func (pv *PackageVersion) GetBody() (body string, ok bool) { + if pv == nil || pv.Body == nil { + return "", false + } + + if err := json.Unmarshal(pv.Body, &body); err != nil { + return "", false + } + + return body, true +} + +// GetBodyAsPackageVersionBody returns the body field as a PackageVersionBody if it's valid. +func (pv *PackageVersion) GetBodyAsPackageVersionBody() (body *PackageVersionBody, ok bool) { + if pv == nil || pv.Body == nil { + return nil, false + } + + if err := json.Unmarshal(pv.Body, &body); err != nil { + return nil, false + } + + return body, true +} + +// GetMetadata returns the metadata field as PackageMetadata if it's valid. +func (pv *PackageVersion) GetMetadata() (metadata *PackageMetadata, ok bool) { + if pv == nil || pv.Metadata == nil { + return nil, false + } + + if err := json.Unmarshal(pv.Metadata, &metadata); err != nil { + return nil, false + } + + return metadata, true +} + +// GetRawMetadata returns the metadata field as a json.RawMessage. +func (pv *PackageVersion) GetRawMetadata() json.RawMessage { + if pv == nil || pv.Metadata == nil { + return json.RawMessage{} + } + + return pv.Metadata +} + +func (pv PackageVersion) String() string { + return Stringify(pv) +} + +// PackageRelease represents a GitHub package version release. +type PackageRelease struct { + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + ID *int64 `json:"id,omitempty"` + TagName *string `json:"tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + Name *string `json:"name,omitempty"` + Draft *bool `json:"draft,omitempty"` + Author *User `json:"author,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PublishedAt *Timestamp `json:"published_at,omitempty"` +} + +func (r PackageRelease) String() string { + return Stringify(r) +} + +// PackageFile represents a GitHub package version release file. +type PackageFile struct { + DownloadURL *string `json:"download_url,omitempty"` + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + SHA256 *string `json:"sha256,omitempty"` + SHA1 *string `json:"sha1,omitempty"` + MD5 *string `json:"md5,omitempty"` + ContentType *string `json:"content_type,omitempty"` + State *string `json:"state,omitempty"` + Author *User `json:"author,omitempty"` + Size *int64 `json:"size,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +func (pf PackageFile) String() string { + return Stringify(pf) +} + +// PackageRegistry represents a GitHub package registry. +type PackageRegistry struct { + AboutURL *string `json:"about_url,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + URL *string `json:"url,omitempty"` + Vendor *string `json:"vendor,omitempty"` +} + +func (r PackageRegistry) String() string { + return Stringify(r) +} + +// PackageListOptions represents the optional list options for a package. +type PackageListOptions struct { + // Visibility of packages "public", "internal" or "private". + Visibility *string `url:"visibility,omitempty"` + + // PackageType represents the type of package. + // It can be one of "npm", "maven", "rubygems", "nuget", "docker", or "container". + PackageType *string `url:"package_type,omitempty"` + + // State of package either "active" or "deleted". + State *string `url:"state,omitempty"` + + ListOptions +} + +// PackageMetadata represents metadata from a package. +type PackageMetadata struct { + PackageType *string `json:"package_type,omitempty"` + Container *PackageContainerMetadata `json:"container,omitempty"` +} + +func (r PackageMetadata) String() string { + return Stringify(r) +} + +// PackageContainerMetadata represents container metadata for docker container packages. +type PackageContainerMetadata struct { + Tags []string `json:"tags,omitempty"` +} + +func (r PackageContainerMetadata) String() string { + return Stringify(r) +} + +// PackageVersionBody represents the body field of a package version. +type PackageVersionBody struct { + Repo *Repository `json:"repository,omitempty"` + Info *PackageVersionBodyInfo `json:"info,omitempty"` +} + +func (b PackageVersionBody) String() string { + return Stringify(b) +} + +// PackageVersionBodyInfo represents the info field of a PackageVersionBody. +type PackageVersionBodyInfo struct { + Type *string `json:"type,omitempty"` + OID *string `json:"oid,omitempty"` + Mode *int64 `json:"mode,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + Size *int64 `json:"size,omitempty"` + Collection *bool `json:"collection,omitempty"` +} + +func (bi PackageVersionBodyInfo) String() string { + return Stringify(bi) +} + +// PackageEventContainerMetadata represents metadata for container packages as part of a webhook event. +// See also PackageContainerMetadata. +type PackageEventContainerMetadata struct { + Labels map[string]any `json:"labels,omitempty"` + Manifest map[string]any `json:"manifest,omitempty"` + Tag *PackageEventContainerMetadataTag `json:"tag,omitempty"` +} + +func (m PackageEventContainerMetadata) String() string { + return Stringify(m) +} + +// PackageEventContainerMetadataTag represents a tag of a GitHub container package. +type PackageEventContainerMetadataTag struct { + Name *string `json:"name,omitempty"` + Digest *string `json:"digest,omitempty"` +} + +func (mt PackageEventContainerMetadataTag) String() string { + return Stringify(mt) +} + +// PackageNugetMetadata represents nuget metadata for a GitHub package. +type PackageNugetMetadata struct { + ID json.RawMessage `json:"id,omitempty"` // Can either be an int64 or string + Name *string `json:"name,omitempty"` + Value json.RawMessage `json:"value,omitempty"` // Can either be a bool, string, integer or object +} + +func (nm PackageNugetMetadata) String() string { + return Stringify(nm) +} + +// PackageNPMMetadata represents NPM metadata for a GitHub package. +type PackageNPMMetadata struct { + Name *string `json:"name,omitempty"` + Version *string `json:"version,omitempty"` + NPMUser *string `json:"npm_user,omitempty"` + Author map[string]string `json:"author,omitempty"` + Bugs map[string]string `json:"bugs,omitempty"` + Dependencies map[string]string `json:"dependencies,omitempty"` + DevDependencies map[string]string `json:"dev_dependencies,omitempty"` + PeerDependencies map[string]string `json:"peer_dependencies,omitempty"` + OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"` + Description *string `json:"description,omitempty"` + Dist map[string]string `json:"dist,omitempty"` + GitHead *string `json:"git_head,omitempty"` + Homepage *string `json:"homepage,omitempty"` + License *string `json:"license,omitempty"` + Main *string `json:"main,omitempty"` + Repository map[string]string `json:"repository,omitempty"` + Scripts map[string]any `json:"scripts,omitempty"` + ID *string `json:"id,omitempty"` + NodeVersion *string `json:"node_version,omitempty"` + NPMVersion *string `json:"npm_version,omitempty"` + HasShrinkwrap *bool `json:"has_shrinkwrap,omitempty"` + Maintainers []any `json:"maintainers,omitempty"` + Contributors []any `json:"contributors,omitempty"` + Engines map[string]string `json:"engines,omitempty"` + Keywords []string `json:"keywords,omitempty"` + Files []string `json:"files,omitempty"` + Bin map[string]any `json:"bin,omitempty"` + Man map[string]any `json:"man,omitempty"` + Directories map[string]string `json:"directories,omitempty"` + OS []string `json:"os,omitempty"` + CPU []string `json:"cpu,omitempty"` + Readme *string `json:"readme,omitempty"` + InstallationCommand *string `json:"installation_command,omitempty"` + ReleaseID *int64 `json:"release_id,omitempty"` + CommitOID *string `json:"commit_oid,omitempty"` + PublishedViaActions *bool `json:"published_via_actions,omitempty"` + DeletedByID *int64 `json:"deleted_by_id,omitempty"` +} + +func (nm PackageNPMMetadata) String() string { + return Stringify(nm) +} diff --git a/github/packages_test.go b/github/packages_test.go new file mode 100644 index 00000000000..3a42ba998a4 --- /dev/null +++ b/github/packages_test.go @@ -0,0 +1,229 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestPackageVersion_GetBody(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + pv *PackageVersion + wantValue string + wantOk bool + }{ + "pv nil": { + pv: nil, + wantValue: "", + wantOk: false, + }, + "body nil": { + pv: &PackageVersion{ + Body: nil, + }, + wantValue: "", + wantOk: false, + }, + "invalid body": { + pv: &PackageVersion{ + Body: json.RawMessage(`{ + "repository": { + "name": "n" + }, + "info": { + "type": "t" + } + }`), + }, + wantValue: "", + wantOk: false, + }, + "valid body": { + pv: &PackageVersion{ + Body: json.RawMessage(`"body"`), + }, + wantValue: "body", + wantOk: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + resValue, resOk := test.pv.GetBody() + + if resValue != test.wantValue || resOk != test.wantOk { + t.Errorf("PackageVersion.GetBody() - got: %v, %v; want: %v, %v", resValue, resOk, test.wantValue, test.wantOk) + } + }) + } +} + +func TestPackageVersion_GetBodyAsPackageVersionBody(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + pv *PackageVersion + wantValue *PackageVersionBody + wantOk bool + }{ + "pv nil": { + pv: nil, + wantValue: nil, + wantOk: false, + }, + "body nil": { + pv: &PackageVersion{ + Body: nil, + }, + wantValue: nil, + wantOk: false, + }, + "invalid body": { + pv: &PackageVersion{ + Body: json.RawMessage(`"body"`), + }, + wantValue: nil, + wantOk: false, + }, + "valid body": { + pv: &PackageVersion{ + Body: json.RawMessage(`{ + "repository": { + "name": "n" + }, + "info": { + "type": "t" + } + }`), + }, + wantValue: &PackageVersionBody{ + Repo: &Repository{ + Name: Ptr("n"), + }, + Info: &PackageVersionBodyInfo{ + Type: Ptr("t"), + }, + }, + wantOk: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + resValue, resOk := test.pv.GetBodyAsPackageVersionBody() + + if !cmp.Equal(resValue, test.wantValue) || resOk != test.wantOk { + t.Errorf("PackageVersion.GetBodyAsPackageVersionBody() - got: %v, %v; want: %v, %v", resValue, resOk, test.wantValue, test.wantOk) + } + }) + } +} + +func TestPackageVersion_GetMetadata(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + pv *PackageVersion + wantValue *PackageMetadata + wantOk bool + }{ + "pv nil": { + pv: nil, + wantValue: nil, + wantOk: false, + }, + "metadata nil": { + pv: &PackageVersion{ + Metadata: nil, + }, + wantValue: nil, + wantOk: false, + }, + "invalid metadata": { + pv: &PackageVersion{ + Metadata: json.RawMessage(`[]`), + }, + wantValue: nil, + wantOk: false, + }, + "valid metadata": { + pv: &PackageVersion{ + Metadata: json.RawMessage(`{ + "package_type": "container", + "container": { + "tags": ["a"] + } + }`), + }, + wantValue: &PackageMetadata{ + PackageType: Ptr("container"), + Container: &PackageContainerMetadata{ + Tags: []string{"a"}, + }, + }, + wantOk: true, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + resValue, resOk := test.pv.GetMetadata() + + if !cmp.Equal(resValue, test.wantValue) || resOk != test.wantOk { + t.Errorf("PackageVersion.GetMetadata() - got: %v, %v; want: %v, %v", resValue, resOk, test.wantValue, test.wantOk) + } + }) + } +} + +func TestPackageVersion_GetRawMetadata(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + pv *PackageVersion + want json.RawMessage + }{ + "pv nil": { + pv: nil, + want: nil, + }, + "metadata nil": { + pv: &PackageVersion{ + Metadata: nil, + }, + want: json.RawMessage{}, + }, + "valid metadata": { + pv: &PackageVersion{ + Metadata: json.RawMessage(`"a"`), + }, + want: json.RawMessage(`"a"`), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + res := test.pv.GetRawMetadata() + + if string(res) != string(test.want) { + t.Errorf("PackageVersion.GetRawMetadata() - got: %v; want: %v", res, test.want) + } + }) + } +} diff --git a/github/private_registries.go b/github/private_registries.go new file mode 100644 index 00000000000..192cb086186 --- /dev/null +++ b/github/private_registries.go @@ -0,0 +1,362 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// PrivateRegistriesService handles communication with the private registries +// methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/private-registries?apiVersion=2026-03-10 +type PrivateRegistriesService service + +// PrivateRegistryType represents the type of private registry. +type PrivateRegistryType string + +const ( + PrivateRegistryTypeMavenRepository PrivateRegistryType = "maven_repository" + PrivateRegistryTypeNugetFeed PrivateRegistryType = "nuget_feed" + PrivateRegistryTypeGoProxyServer PrivateRegistryType = "goproxy_server" + PrivateRegistryTypeNpmRegistry PrivateRegistryType = "npm_registry" + PrivateRegistryTypeRubygemsServer PrivateRegistryType = "rubygems_server" + PrivateRegistryTypeCargoRegistry PrivateRegistryType = "cargo_registry" + PrivateRegistryTypeComposerRepository PrivateRegistryType = "composer_repository" + PrivateRegistryTypeDockerRegistry PrivateRegistryType = "docker_registry" + PrivateRegistryTypeGitSource PrivateRegistryType = "git_source" + PrivateRegistryTypeHelmRegistry PrivateRegistryType = "helm_registry" + PrivateRegistryTypeHexOrganization PrivateRegistryType = "hex_organization" + PrivateRegistryTypeHexRepository PrivateRegistryType = "hex_repository" + PrivateRegistryTypePubRepository PrivateRegistryType = "pub_repository" + PrivateRegistryTypePythonIndex PrivateRegistryType = "python_index" + PrivateRegistryTypeTerraformRegistry PrivateRegistryType = "terraform_registry" +) + +// PrivateRegistryVisibility represents the visibility of a private registry. +type PrivateRegistryVisibility string + +const ( + PrivateRegistryVisibilityPrivate PrivateRegistryVisibility = "private" + PrivateRegistryVisibilityAll PrivateRegistryVisibility = "all" + PrivateRegistryVisibilitySelected PrivateRegistryVisibility = "selected" +) + +// PrivateRegistryAuthType represents the authentication type for a private registry. +type PrivateRegistryAuthType string + +const ( + PrivateRegistryAuthTypeToken PrivateRegistryAuthType = "token" + PrivateRegistryAuthTypeUsernamePassword PrivateRegistryAuthType = "username_password" + PrivateRegistryAuthTypeOIDCAzure PrivateRegistryAuthType = "oidc_azure" + PrivateRegistryAuthTypeOIDCAWS PrivateRegistryAuthType = "oidc_aws" + PrivateRegistryAuthTypeOIDCJFrog PrivateRegistryAuthType = "oidc_jfrog" +) + +// PrivateRegistry represents a private registry configuration. +type PrivateRegistry struct { + // Name of the private registry. + Name *string `json:"name,omitempty"` + // RegistryType is the type of private registry. You can find the list of supported types in PrivateRegistryType. + RegistryType *PrivateRegistryType `json:"registry_type,omitempty"` + // AuthType is the authentication type for the private registry. + AuthType *PrivateRegistryAuthType `json:"auth_type,omitempty"` + // URL is the URL of the private registry. + URL *string `json:"url,omitempty"` + // Username to use when authenticating with the private registry. + // This field is omitted if the private registry does not require a username for authentication. + Username *string `json:"username,omitempty"` + // ReplacesBase indicates whether this private registry should replace the base registry. + ReplacesBase *bool `json:"replaces_base,omitempty"` + // Visibility is the visibility of the private registry. Possible values are: "private", "all", and "selected". + Visibility *PrivateRegistryVisibility `json:"visibility,omitempty"` + // SelectedRepositoryIDs is an array of repository IDs that can access the organization private registry. + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` + // TenantID is the tenant ID of the Azure AD application. + TenantID *string `json:"tenant_id,omitempty"` + // ClientID is the client ID of the Azure AD application. + ClientID *string `json:"client_id,omitempty"` + // AWSRegion is the AWS region. + AWSRegion *string `json:"aws_region,omitempty"` + // AccountID is the AWS account ID. + AccountID *string `json:"account_id,omitempty"` + // RoleName is the AWS IAM role name. + RoleName *string `json:"role_name,omitempty"` + // Domain is the CodeArtifact domain. + Domain *string `json:"domain,omitempty"` + // DomainOwner is the CodeArtifact domain owner. + DomainOwner *string `json:"domain_owner,omitempty"` + // JFrogOIDCProviderName is the JFrog OIDC provider name. + JFrogOIDCProviderName *string `json:"jfrog_oidc_provider_name,omitempty"` + // Audience is the OIDC audience. + Audience *string `json:"audience,omitempty"` + // IdentityMappingName is the JFrog identity mapping name. + IdentityMappingName *string `json:"identity_mapping_name,omitempty"` + // CreatedAt is the timestamp when the private registry was created. + CreatedAt *Timestamp `json:"created_at,omitempty"` + // UpdatedAt is the timestamp when the private registry was last updated. + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// PrivateRegistries represents a list of private registries. +type PrivateRegistries struct { + // TotalCount is the total number of private registries. + TotalCount *int `json:"total_count,omitempty"` + // Configurations is the list of private registry configurations. + Configurations []*PrivateRegistry `json:"configurations,omitempty"` +} + +// CreateOrganizationPrivateRegistry represents the payload to create a private registry. +type CreateOrganizationPrivateRegistry struct { + // RegistryType is the type of private registry. + // You can find the list of supported types in PrivateRegistryType. + RegistryType PrivateRegistryType `json:"registry_type"` + + // URL is the URL of the private registry. + URL string `json:"url"` + + // The username to use when authenticating with the private registry. + // This field should be omitted if the private registry does not require a username for authentication. + Username *string `json:"username,omitempty"` + + // ReplacesBase indicates whether this private registry should replace the base registry + // (e.g., npmjs.org for npm, rubygems.org for rubygems). + ReplacesBase *bool `json:"replaces_base,omitempty"` + + // The value for your secret, encrypted with [LibSodium](https://libsodium.gitbook.io/doc/bindings_for_other_languages) + // using the public key retrieved from the PrivateRegistriesService.GetOrganizationPrivateRegistriesPublicKey. + // Required when AuthType is "token" or "username_password". Should be omitted for OIDC auth types. + EncryptedValue *string `json:"encrypted_value,omitempty"` + // KeyID is the ID of the public key used to encrypt the secret. + // Required when AuthType is "token" or "username_password". Should be omitted for OIDC auth types. + KeyID *string `json:"key_id,omitempty"` + // Visibility is the visibility of the private registry. + // Possible values are: "private", "all", and "selected". + Visibility PrivateRegistryVisibility `json:"visibility"` + + // An array of repository IDs that can access the organization private registry. + // You can only provide a list of repository IDs when CreateOrganizationPrivateRegistry.Visibility is set to PrivateRegistryVisibilitySelected. + // This field should be omitted if visibility is set to PrivateRegistryVisibilityAll or PrivateRegistryVisibilityPrivate. + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` + + // AuthType is the authentication type for the private registry. + // Defaults to "token" if not specified. Use "oidc_azure", "oidc_aws", or "oidc_jfrog" for OIDC authentication. + AuthType *string `json:"auth_type,omitempty"` + + // TenantID is the tenant ID of the Azure AD application. Required when AuthType is "oidc_azure". + TenantID *string `json:"tenant_id,omitempty"` + // ClientID is the client ID of the Azure AD application. Required when AuthType is "oidc_azure". + ClientID *string `json:"client_id,omitempty"` + + // AWSRegion is the AWS region. Required when AuthType is "oidc_aws". + AWSRegion *string `json:"aws_region,omitempty"` + // AccountID is the AWS account ID. Required when AuthType is "oidc_aws". + AccountID *string `json:"account_id,omitempty"` + // RoleName is the AWS IAM role name. Required when AuthType is "oidc_aws". + RoleName *string `json:"role_name,omitempty"` + // Domain is the CodeArtifact domain. Required when AuthType is "oidc_aws". + Domain *string `json:"domain,omitempty"` + // DomainOwner is the CodeArtifact domain owner (AWS account ID). Required when AuthType is "oidc_aws". + DomainOwner *string `json:"domain_owner,omitempty"` + + // JFrogOIDCProviderName is the JFrog OIDC provider name. Required when AuthType is "oidc_jfrog". + JFrogOIDCProviderName *string `json:"jfrog_oidc_provider_name,omitempty"` + + // Audience is the OIDC audience. Optional for "oidc_aws" and "oidc_jfrog" auth types. + Audience *string `json:"audience,omitempty"` + // IdentityMappingName is the JFrog identity mapping name. Optional for "oidc_jfrog" auth type. + IdentityMappingName *string `json:"identity_mapping_name,omitempty"` +} + +// UpdateOrganizationPrivateRegistry represents the payload to update a private registry. +type UpdateOrganizationPrivateRegistry struct { + // RegistryType is the type of private registry. + // You can find the list of supported types in PrivateRegistryType. + RegistryType *PrivateRegistryType `json:"registry_type,omitempty"` + + // URL is the URL of the private registry. + URL *string `json:"url,omitempty"` + + // The username to use when authenticating with the private registry. + // This field should be omitted if the private registry does not require a username for authentication. + Username *string `json:"username,omitempty"` + + // ReplacesBase indicates whether this private registry should replace the base registry + // (e.g., npmjs.org for npm, rubygems.org for rubygems). + ReplacesBase *bool `json:"replaces_base,omitempty"` + + // The value for your secret, encrypted with [LibSodium](https://libsodium.gitbook.io/doc/bindings_for_other_languages) + // using the public key retrieved from the PrivateRegistriesService.GetOrganizationPrivateRegistriesPublicKey. + EncryptedValue *string `json:"encrypted_value,omitempty"` + // KeyID is the ID of the public key used to encrypt the secret. + KeyID *string `json:"key_id,omitempty"` + // Visibility is the visibility of the private registry. + // Possible values are: "private", "all", and "selected". + Visibility *PrivateRegistryVisibility `json:"visibility,omitempty"` + + // An array of repository IDs that can access the organization private registry. + // You can only provide a list of repository IDs when UpdateOrganizationPrivateRegistry.Visibility is set to PrivateRegistryVisibilitySelected. + // This field should be omitted if visibility is set to PrivateRegistryVisibilityAll or PrivateRegistryVisibilityPrivate. + SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` + + // AuthType is the authentication type for the private registry. + // This field cannot be changed after creation. If provided, it must match the existing auth_type. + AuthType *string `json:"auth_type,omitempty"` + + // TenantID is the tenant ID of the Azure AD application. Required when AuthType is "oidc_azure". + TenantID *string `json:"tenant_id,omitempty"` + // ClientID is the client ID of the Azure AD application. Required when AuthType is "oidc_azure". + ClientID *string `json:"client_id,omitempty"` + + // AWSRegion is the AWS region. Required when AuthType is "oidc_aws". + AWSRegion *string `json:"aws_region,omitempty"` + // AccountID is the AWS account ID. Required when AuthType is "oidc_aws". + AccountID *string `json:"account_id,omitempty"` + // RoleName is the AWS IAM role name. Required when AuthType is "oidc_aws". + RoleName *string `json:"role_name,omitempty"` + // Domain is the CodeArtifact domain. Required when AuthType is "oidc_aws". + Domain *string `json:"domain,omitempty"` + // DomainOwner is the CodeArtifact domain owner (AWS account ID). Required when AuthType is "oidc_aws". + DomainOwner *string `json:"domain_owner,omitempty"` + + // JFrogOIDCProviderName is the JFrog OIDC provider name. Required when AuthType is "oidc_jfrog". + JFrogOIDCProviderName *string `json:"jfrog_oidc_provider_name,omitempty"` + + // Audience is the OIDC audience. Optional for "oidc_aws" and "oidc_jfrog" auth types. + Audience *string `json:"audience,omitempty"` + // IdentityMappingName is the JFrog identity mapping name. Optional for "oidc_jfrog" auth type. + IdentityMappingName *string `json:"identity_mapping_name,omitempty"` +} + +// ListOrganizationPrivateRegistries lists private registries for an organization. +// +// GitHub API docs: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#list-private-registries-for-an-organization +// +//meta:operation GET /orgs/{org}/private-registries +func (s *PrivateRegistriesService) ListOrganizationPrivateRegistries(ctx context.Context, org string, opts *ListOptions) (*PrivateRegistries, *Response, error) { + u := fmt.Sprintf("orgs/%v/private-registries", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var privateRegistries *PrivateRegistries + resp, err := s.client.Do(req, &privateRegistries) + if err != nil { + return nil, resp, err + } + return privateRegistries, resp, nil +} + +// CreateOrganizationPrivateRegistry creates a private registry configuration with an encrypted value for an organization. +// +// GitHub API docs: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#create-a-private-registry-for-an-organization +// +//meta:operation POST /orgs/{org}/private-registries +func (s *PrivateRegistriesService) CreateOrganizationPrivateRegistry(ctx context.Context, org string, body CreateOrganizationPrivateRegistry) (*PrivateRegistry, *Response, error) { + u := fmt.Sprintf("orgs/%v/private-registries", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var result *PrivateRegistry + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + return result, resp, nil +} + +// GetOrganizationPrivateRegistriesPublicKey retrieves the public key for encrypting secrets for an organization's private registries. +// +// GitHub API docs: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#get-private-registries-public-key-for-an-organization +// +//meta:operation GET /orgs/{org}/private-registries/public-key +func (s *PrivateRegistriesService) GetOrganizationPrivateRegistriesPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) { + u := fmt.Sprintf("orgs/%v/private-registries/public-key", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var publicKey *PublicKey + resp, err := s.client.Do(req, &publicKey) + if err != nil { + return nil, resp, err + } + return publicKey, resp, nil +} + +// GetOrganizationPrivateRegistry gets a specific private registry for an organization. +// The `name` parameter is the name of the private registry to retrieve. It is the same as PrivateRegistry.Name. +// +// GitHub API docs: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#get-a-private-registry-for-an-organization +// +//meta:operation GET /orgs/{org}/private-registries/{secret_name} +func (s *PrivateRegistriesService) GetOrganizationPrivateRegistry(ctx context.Context, org, secretName string) (*PrivateRegistry, *Response, error) { + u := fmt.Sprintf("orgs/%v/private-registries/%v", org, secretName) + + req, err := s.client.NewRequest(ctx, "GET", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, nil, err + } + + var privateRegistry *PrivateRegistry + resp, err := s.client.Do(req, &privateRegistry) + if err != nil { + return nil, resp, err + } + + return privateRegistry, resp, nil +} + +// UpdateOrganizationPrivateRegistry updates a specific private registry for an organization. +// The `name` parameter is the name of the private registry to update. It is the same as PrivateRegistry.Name. +// +// GitHub API docs: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#update-a-private-registry-for-an-organization +// +//meta:operation PATCH /orgs/{org}/private-registries/{secret_name} +func (s *PrivateRegistriesService) UpdateOrganizationPrivateRegistry(ctx context.Context, org, secretName string, body UpdateOrganizationPrivateRegistry) (*Response, error) { + u := fmt.Sprintf("orgs/%v/private-registries/%v", org, secretName) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// DeleteOrganizationPrivateRegistry deletes a specific private registry for an organization. +// The `name` parameter is the name of the private registry to delete. It is the same as PrivateRegistry.Name. +// +// GitHub API docs: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#delete-a-private-registry-for-an-organization +// +//meta:operation DELETE /orgs/{org}/private-registries/{secret_name} +func (s *PrivateRegistriesService) DeleteOrganizationPrivateRegistry(ctx context.Context, org, secretName string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/private-registries/%v", org, secretName) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil, WithVersion(api20260310)) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/private_registries_test.go b/github/private_registries_test.go new file mode 100644 index 00000000000..ec0500ef7dd --- /dev/null +++ b/github/private_registries_test.go @@ -0,0 +1,422 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestPrivateRegistriesService_ListOrganizationPrivateRegistries(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/private-registries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testFormValues(t, r, values{ + "page": "2", + }) + fmt.Fprint(w, `{ + "total_count": 1, + "configurations": [ + { + "name": "MAVEN_REPOSITORY_SECRET", + "registry_type": "maven_repository", + "username": "monalisa", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "visibility": "selected" + } + ] +}`) + }) + + opts := &ListOptions{Page: 2} + ctx := t.Context() + privateRegistries, _, err := client.PrivateRegistries.ListOrganizationPrivateRegistries(ctx, "o", opts) + if err != nil { + t.Fatalf("PrivateRegistries.ListOrganizationPrivateRegistries returned error: %v", err) + } + + want := &PrivateRegistries{ + TotalCount: Ptr(1), + Configurations: []*PrivateRegistry{ + { + Name: Ptr("MAVEN_REPOSITORY_SECRET"), + RegistryType: Ptr(PrivateRegistryTypeMavenRepository), + Username: Ptr("monalisa"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Visibility: Ptr(PrivateRegistryVisibilitySelected), + }, + }, + } + if diff := cmp.Diff(want, privateRegistries); diff != "" { + t.Errorf("PrivateRegistries.ListOrganizationPrivateRegistries mismatch (-want +got):\\n%v", diff) + } + + const methodName = "ListOrganizationPrivateRegistries" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PrivateRegistries.ListOrganizationPrivateRegistries(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PrivateRegistries.ListOrganizationPrivateRegistries(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + // still allow both set (no validation enforced) – ensure it does not error + ctxBypass := context.WithValue(t.Context(), BypassRateLimitCheck, true) + if _, _, err = client.PrivateRegistries.ListOrganizationPrivateRegistries(ctxBypass, "o", opts); err != nil { + t.Fatalf("unexpected error when both before/after set: %v", err) + } +} + +func TestPrivateRegistriesService_CreateOrganizationPrivateRegistry(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateOrganizationPrivateRegistry{ + RegistryType: PrivateRegistryTypeMavenRepository, + URL: "https://example.com/OWNER/REPOSITORY", + Username: Ptr("monalisa"), + EncryptedValue: Ptr("encrypted_value"), + KeyID: Ptr("key_id"), + Visibility: PrivateRegistryVisibilitySelected, + SelectedRepositoryIDs: []int64{1, 2, 3}, + } + + mux.HandleFunc("/orgs/o/private-registries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{ + "name": "MAVEN_REPOSITORY_SECRET", + "registry_type": "maven_repository", + "username": "monalisa", + "visibility": "selected", + "selected_repository_ids": [1, 2, 3], + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` +}`) + }) + + ctx := t.Context() + privateRegistry, _, err := client.PrivateRegistries.CreateOrganizationPrivateRegistry(ctx, "o", *input) + if err != nil { + t.Fatalf("PrivateRegistries.CreateOrganizationPrivateRegistry returned error: %v", err) + } + + want := &PrivateRegistry{ + Name: Ptr("MAVEN_REPOSITORY_SECRET"), + RegistryType: Ptr(PrivateRegistryTypeMavenRepository), + Username: Ptr("monalisa"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Visibility: Ptr(PrivateRegistryVisibilitySelected), + SelectedRepositoryIDs: []int64{1, 2, 3}, + } + if diff := cmp.Diff(want, privateRegistry); diff != "" { + t.Errorf("PrivateRegistries.CreateOrganizationPrivateRegistries mismatch (-want +got):\\n%v", diff) + } + + const methodName = "CreateOrganizationPrivateRegistry" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PrivateRegistries.CreateOrganizationPrivateRegistry(ctx, "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PrivateRegistries.CreateOrganizationPrivateRegistry(ctx, "o", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestPrivateRegistriesService_CreateOrganizationPrivateRegistry_OIDC(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateOrganizationPrivateRegistry{ + RegistryType: PrivateRegistryTypeMavenRepository, + URL: "https://example.com/maven", + AuthType: Ptr("oidc_azure"), + TenantID: Ptr("my-tenant-id"), + ClientID: Ptr("my-client-id"), + ReplacesBase: Ptr(true), + Visibility: PrivateRegistryVisibilitySelected, + SelectedRepositoryIDs: []int64{1, 2, 3}, + } + + mux.HandleFunc("/orgs/o/private-registries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{ + "name": "MAVEN_REPOSITORY_SECRET", + "registry_type": "maven_repository", + "visibility": "selected", + "selected_repository_ids": [1, 2, 3], + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` +}`) + }) + + ctx := t.Context() + privateRegistry, _, err := client.PrivateRegistries.CreateOrganizationPrivateRegistry(ctx, "o", *input) + if err != nil { + t.Fatalf("PrivateRegistries.CreateOrganizationPrivateRegistry (OIDC) returned error: %v", err) + } + + want := &PrivateRegistry{ + Name: Ptr("MAVEN_REPOSITORY_SECRET"), + RegistryType: Ptr(PrivateRegistryTypeMavenRepository), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Visibility: Ptr(PrivateRegistryVisibilitySelected), + SelectedRepositoryIDs: []int64{1, 2, 3}, + } + if diff := cmp.Diff(want, privateRegistry); diff != "" { + t.Errorf("PrivateRegistries.CreateOrganizationPrivateRegistry (OIDC) mismatch (-want +got):\\n%v", diff) + } +} + +func TestPrivateRegistries_UpdateOrganizationPrivateRegistry_OIDC(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &UpdateOrganizationPrivateRegistry{ + AuthType: Ptr("oidc_aws"), + AWSRegion: Ptr("us-east-1"), + AccountID: Ptr("123456789012"), + RoleName: Ptr("my-role"), + Domain: Ptr("my-domain"), + DomainOwner: Ptr("123456789012"), + Audience: Ptr("example.com"), + Visibility: Ptr(PrivateRegistryVisibilitySelected), + } + + mux.HandleFunc("/orgs/o/private-registries/AWS_REGISTRY_SECRET", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.PrivateRegistries.UpdateOrganizationPrivateRegistry(ctx, "o", "AWS_REGISTRY_SECRET", *input) + if err != nil { + t.Fatalf("PrivateRegistries.UpdateOrganizationPrivateRegistry (OIDC) returned error: %v", err) + } +} + +func TestPrivateRegistriesService_CreateOrganizationPrivateRegistry_OIDCJFrog(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateOrganizationPrivateRegistry{ + RegistryType: PrivateRegistryTypeNpmRegistry, + URL: "https://example.com/npm", + AuthType: Ptr("oidc_jfrog"), + JFrogOIDCProviderName: Ptr("my-jfrog-provider"), + Audience: Ptr("jfrog"), + IdentityMappingName: Ptr("my-identity-mapping"), + Visibility: PrivateRegistryVisibilityPrivate, + } + + mux.HandleFunc("/orgs/o/private-registries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{ + "name": "NPM_REGISTRY_SECRET", + "registry_type": "npm_registry", + "visibility": "private", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+` +}`) + }) + + ctx := t.Context() + privateRegistry, _, err := client.PrivateRegistries.CreateOrganizationPrivateRegistry(ctx, "o", *input) + if err != nil { + t.Fatalf("PrivateRegistries.CreateOrganizationPrivateRegistry (OIDC JFrog) returned error: %v", err) + } + + want := &PrivateRegistry{ + Name: Ptr("NPM_REGISTRY_SECRET"), + RegistryType: Ptr(PrivateRegistryTypeNpmRegistry), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Visibility: Ptr(PrivateRegistryVisibilityPrivate), + } + if diff := cmp.Diff(want, privateRegistry); diff != "" { + t.Errorf("PrivateRegistries.CreateOrganizationPrivateRegistry (OIDC JFrog) mismatch (-want +got):\\n%v", diff) + } +} + +func TestPrivateRegistriesService_GetOrganizationPrivateRegistriesPublicKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/private-registries/public-key", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{ + "key_id": "0123456789", + "key": "public_key" +}`) + }) + ctx := t.Context() + publicKey, _, err := client.PrivateRegistries.GetOrganizationPrivateRegistriesPublicKey(ctx, "o") + if err != nil { + t.Fatalf("PrivateRegistries.GetOrganizationPrivateRegistriesPublicKey returned error: %v", err) + } + + want := &PublicKey{ + KeyID: Ptr("0123456789"), + Key: Ptr("public_key"), + } + if diff := cmp.Diff(want, publicKey); diff != "" { + t.Errorf("PrivateRegistries.GetOrganizationPrivateRegistriesPublicKey mismatch (-want +got):\\n%v", diff) + } + + const methodName = "GetOrganizationPrivateRegistriesPublicKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PrivateRegistries.GetOrganizationPrivateRegistriesPublicKey(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PrivateRegistries.GetOrganizationPrivateRegistriesPublicKey(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestPrivateRegistriesService_GetOrganizationPrivateRegistry(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/private-registries/MAVEN_REPOSITORY_SECRET", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "X-Github-Api-Version", api20260310) + fmt.Fprint(w, `{ + "name": "MAVEN_REPOSITORY_SECRET", + "registry_type": "maven_repository", + "username": "monalisa", + "created_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "visibility": "selected" +}`) + }) + ctx := t.Context() + privateRegistry, _, err := client.PrivateRegistries.GetOrganizationPrivateRegistry(ctx, "o", "MAVEN_REPOSITORY_SECRET") + if err != nil { + t.Fatalf("PrivateRegistries.GetOrganizationPrivateRegistry returned error: %v", err) + } + + want := &PrivateRegistry{ + Name: Ptr("MAVEN_REPOSITORY_SECRET"), + RegistryType: Ptr(PrivateRegistryTypeMavenRepository), + Username: Ptr("monalisa"), + CreatedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + Visibility: Ptr(PrivateRegistryVisibilitySelected), + } + if diff := cmp.Diff(want, privateRegistry); diff != "" { + t.Errorf("PrivateRegistries.GetOrganizationPrivateRegistry mismatch (-want +got):\\n%v", diff) + } + + const methodName = "GetOrganizationPrivateRegistry" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PrivateRegistries.GetOrganizationPrivateRegistry(ctx, "\n", "MAVEN_REPOSITORY_SECRET") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PrivateRegistries.GetOrganizationPrivateRegistry(ctx, "o", "MAVEN_REPOSITORY_SECRET") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestPrivateRegistries_UpdateOrganizationPrivateRegistry(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &UpdateOrganizationPrivateRegistry{ + Username: Ptr("monalisa"), + EncryptedValue: Ptr("encrypted_value"), + KeyID: Ptr("key_id"), + Visibility: Ptr(PrivateRegistryVisibilitySelected), + } + + mux.HandleFunc("/orgs/o/private-registries/MAVEN_REPOSITORY_SECRET", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "X-Github-Api-Version", api20260310) + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.PrivateRegistries.UpdateOrganizationPrivateRegistry(ctx, "o", "MAVEN_REPOSITORY_SECRET", *input) + if err != nil { + t.Fatalf("PrivateRegistries.UpdateOrganizationPrivateRegistry returned error: %v", err) + } + + const methodName = "UpdateOrganizationPrivateRegistry" + testBadOptions(t, methodName, func() (err error) { + _, err = client.PrivateRegistries.UpdateOrganizationPrivateRegistry(ctx, "\n", "MAVEN_REPOSITORY_SECRET", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.PrivateRegistries.UpdateOrganizationPrivateRegistry(ctx, "o", "MAVEN_REPOSITORY_SECRET", *input) + }) +} + +func TestPrivateRegistriesService_DeleteOrganizationPrivateRegistry(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/private-registries/MAVEN_REPOSITORY_SECRET", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "X-Github-Api-Version", api20260310) + w.WriteHeader(http.StatusNoContent) + }) + ctx := t.Context() + _, err := client.PrivateRegistries.DeleteOrganizationPrivateRegistry(ctx, "o", "MAVEN_REPOSITORY_SECRET") + if err != nil { + t.Fatalf("PrivateRegistries.DeleteOrganizationPrivateRegistry returned error: %v", err) + } + + const methodName = "DeleteOrganizationPrivateRegistry" + testBadOptions(t, methodName, func() (err error) { + _, err = client.PrivateRegistries.DeleteOrganizationPrivateRegistry(ctx, "\n", "MAVEN_REPOSITORY_SECRET") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.PrivateRegistries.DeleteOrganizationPrivateRegistry(ctx, "o", "MAVEN_REPOSITORY_SECRET") + }) +} diff --git a/github/projects.go b/github/projects.go index 1e9620dfd9f..53c999121ee 100644 --- a/github/projects.go +++ b/github/projects.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-github AUTHORS. All rights reserved. +// Copyright 2025 The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -7,588 +7,1049 @@ package github import ( "context" + "encoding/json" "fmt" + "strconv" ) -// ProjectsService provides access to the projects functions in the -// GitHub API. +// ProjectsService handles communication with the project V2 +// methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/projects/ +// GitHub API docs: https://docs.github.com/rest/projects?apiVersion=2022-11-28 type ProjectsService service -// Project represents a GitHub Project. -type Project struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - ColumnsURL *string `json:"columns_url,omitempty"` - OwnerURL *string `json:"owner_url,omitempty"` - Name *string `json:"name,omitempty"` - Body *string `json:"body,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - NodeID *string `json:"node_id,omitempty"` +// ProjectV2ItemContentType represents the type of content in a ProjectV2Item. +type ProjectV2ItemContentType string - // The User object that generated the project. - Creator *User `json:"creator,omitempty"` +// This is the set of possible content types for a ProjectV2Item. +const ( + ProjectV2ItemContentTypeDraftIssue ProjectV2ItemContentType = "DraftIssue" + ProjectV2ItemContentTypeIssue ProjectV2ItemContentType = "Issue" + ProjectV2ItemContentTypePullRequest ProjectV2ItemContentType = "PullRequest" +) + +// ProjectV2StatusUpdate represents a status update for a project. +type ProjectV2StatusUpdate struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + ProjectNodeID *string `json:"project_node_id,omitempty"` + Creator *User `json:"creator,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + // Status can be one of: "INACTIVE", "ON_TRACK", "AT_RISK", "OFF_TRACK", "COMPLETE". + Status *string `json:"status,omitempty"` + StartDate *string `json:"start_date,omitempty"` + TargetDate *string `json:"target_date,omitempty"` + Body *string `json:"body,omitempty"` +} + +// ProjectV2DraftIssue represents a draft issue in a project. +type ProjectV2DraftIssue struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + User *User `json:"user,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// ProjectV2 represents a v2 project. +type ProjectV2 struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Owner *User `json:"owner,omitempty"` + Creator *User `json:"creator,omitempty"` + Title *string `json:"title,omitempty"` + Description *string `json:"description,omitempty"` + Public *bool `json:"public,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + DeletedAt *Timestamp `json:"deleted_at,omitempty"` + Number *int `json:"number,omitempty"` + ShortDescription *string `json:"short_description,omitempty"` + DeletedBy *User `json:"deleted_by,omitempty"` + State *string `json:"state,omitempty"` + LatestStatusUpdate *ProjectV2StatusUpdate `json:"latest_status_update,omitempty"` + IsTemplate *bool `json:"is_template,omitempty"` + + // Fields migrated from the Project (classic) struct: + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + ColumnsURL *string `json:"columns_url,omitempty"` + OwnerURL *string `json:"owner_url,omitempty"` + Name *string `json:"name,omitempty"` + Body *string `json:"body,omitempty"` + OrganizationPermission *string `json:"organization_permission,omitempty"` + Private *bool `json:"private,omitempty"` +} + +func (p ProjectV2) String() string { return Stringify(p) } + +// ListProjectsPaginationOptions specifies optional parameters to list projects for user / organization. +// +// Note: Pagination is powered by before/after cursor-style pagination. After the initial call, +// inspect the returned *Response. Use resp.After as the opts.After value to request +// the next page, and resp.Before as the opts.Before value to request the previous +// page. Set either Before or After for a request; if both are +// supplied GitHub API will return an error. PerPage controls the number of items +// per page (max 100 per GitHub API docs). +type ListProjectsPaginationOptions struct { + // A cursor, as given in the Link header. If specified, the query only searches for events before this cursor. + Before string `url:"before,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for events after this cursor. + After string `url:"after,omitempty"` + + // For paginated result sets, the number of results to include per page. + PerPage int `url:"per_page,omitempty"` +} + +// ListProjectsOptions specifies optional parameters to list projects for user / organization. +type ListProjectsOptions struct { + ListProjectsPaginationOptions + + // Q is an optional query string to limit results to projects of the specified type. + Query string `url:"q,omitempty"` } -func (p Project) String() string { - return Stringify(p) +// ProjectV2TextContent represents text content in a project field option or iteration. +// It includes both HTML and raw text representations. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28 +type ProjectV2TextContent struct { + HTML *string `json:"html,omitempty"` + Raw *string `json:"raw,omitempty"` } -// GetProject gets a GitHub Project for a repo. +// ProjectV2FieldOption represents an option for a project field of type single_select or multi_select. +// It defines the available choices that can be selected for dropdown-style fields. // -// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project -func (s *ProjectsService) GetProject(ctx context.Context, id int64) (*Project, *Response, error) { - u := fmt.Sprintf("projects/%v", id) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28 +type ProjectV2FieldOption struct { + ID *string `json:"id,omitempty"` // The unique identifier for this option. + Color *string `json:"color,omitempty"` // The color associated with this option (e.g., "blue", "red"). + Description *ProjectV2TextContent `json:"description,omitempty"` // An optional description for this option. + Name *ProjectV2TextContent `json:"name,omitempty"` // The display name of the option. +} + +// ProjectV2FieldIteration represents an iteration within a project field of type iteration. +// It defines a specific time-bound period that can be associated with project items. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28 +type ProjectV2FieldIteration struct { + ID *string `json:"id,omitempty"` // The unique identifier for the iteration. + Title *ProjectV2TextContent `json:"title,omitempty"` // The title of the iteration. + StartDate *string `json:"start_date,omitempty"` // The start date of the iteration in ISO 8601 format. + Duration *int `json:"duration,omitempty"` // The duration of the iteration in seconds. +} + +// ProjectV2FieldConfiguration represents the configuration for a project field of type iteration. +// It defines settings such as duration and start day for iterations within the project. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28 +type ProjectV2FieldConfiguration struct { + Duration *int `json:"duration,omitempty"` // The duration of the iteration field in seconds. + StartDay *int `json:"start_day,omitempty"` // The start day for the iteration. + Iterations []*ProjectV2FieldIteration `json:"iterations,omitempty"` // The list of iterations associated with the configuration. +} + +// ProjectV2ItemContent is a union type that holds the content of a ProjectV2Item. +// The actual type depends on the ContentType field of the parent ProjectV2Item. +// Only one of the fields will be populated after unmarshaling. +type ProjectV2ItemContent struct { + Issue *Issue `json:"-"` + PullRequest *PullRequest `json:"-"` + DraftIssue *ProjectV2DraftIssue `json:"-"` +} + +// MarshalJSON implements custom marshaling for ProjectV2ItemContent. +func (c ProjectV2ItemContent) MarshalJSON() ([]byte, error) { + if c.Issue != nil { + return json.Marshal(c.Issue) + } + if c.PullRequest != nil { + return json.Marshal(c.PullRequest) + } + if c.DraftIssue != nil { + return json.Marshal(c.DraftIssue) + } + return []byte("null"), nil +} + +// ProjectV2Item represents a full project item with field values. +// This type is used by Get, List, and Update operations which return field values. +// The Content field is automatically unmarshaled into the appropriate type based on ContentType. +type ProjectV2Item struct { + ArchivedAt *Timestamp `json:"archived_at,omitempty"` + Content *ProjectV2ItemContent `json:"content,omitempty"` + ContentType *ProjectV2ItemContentType `json:"content_type,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + Creator *User `json:"creator,omitempty"` + Fields []*ProjectV2ItemFieldValue `json:"fields,omitempty"` + ID *int64 `json:"id,omitempty"` + ItemURL *string `json:"item_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` + ProjectURL *string `json:"project_url,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + + // ProjectNodeID and ContentNodeID are used in ProjectsV2Item Webhook payloads. + // They may not be populated in all API responses, but are included here for completeness. + // See: https://docs.github.com/en/webhooks/webhook-events-and-payloads#projects_v2_item + ProjectNodeID *string `json:"project_node_id,omitempty"` + ContentNodeID *string `json:"content_node_id,omitempty"` +} + +// UnmarshalJSON implements custom unmarshaling for ProjectV2Item. +// It uses the ContentType field to determine how to unmarshal the Content field. +func (p *ProjectV2Item) UnmarshalJSON(data []byte) error { + type contentAlias ProjectV2Item + + aux := &struct { + Content json.RawMessage `json:"content,omitempty"` + *contentAlias + }{ + contentAlias: (*contentAlias)(p), + } + + if err := json.Unmarshal(data, aux); err != nil { + return err + } + + // Now unmarshal the content based on ContentType + if len(aux.Content) > 0 && string(aux.Content) != "null" && p.ContentType != nil { + p.Content = &ProjectV2ItemContent{} + switch *p.ContentType { + case ProjectV2ItemContentTypeIssue: + p.Content.Issue = &Issue{} + return json.Unmarshal(aux.Content, p.Content.Issue) + case ProjectV2ItemContentTypePullRequest: + p.Content.PullRequest = &PullRequest{} + return json.Unmarshal(aux.Content, p.Content.PullRequest) + case ProjectV2ItemContentTypeDraftIssue: + p.Content.DraftIssue = &ProjectV2DraftIssue{} + return json.Unmarshal(aux.Content, p.Content.DraftIssue) + } + } + + return nil +} + +// ProjectV2Field represents a field in a GitHub Projects V2 project. +// Fields define the structure and data types for project items. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28 +type ProjectV2Field struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + DataType *string `json:"data_type,omitempty"` + ProjectURL *string `json:"project_url,omitempty"` + Options []*ProjectV2FieldOption `json:"options,omitempty"` + Configuration *ProjectV2FieldConfiguration `json:"configuration,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// ProjectV2ItemFieldValue represents a field value of a project item. +type ProjectV2ItemFieldValue struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + DataType *string `json:"data_type,omitempty"` + // Value set for the field. The type depends on the field type: + // - text: string + // - number: float64 + // - date: string (ISO 8601 date format, e.g. "2023-06-23") or null + // - single_select: object with "id", "name", "color", "description" fields or null + // - iteration: object with "id", "title", "start_date", "duration" fields or null + // - title: object with "text" field (read-only, reflects the item's title) or null + // - assignees: array of user objects with "login", "id", etc. or null + // - labels: array of label objects with "id", "name", "color", etc. or null + // - linked_pull_requests: array of pull request objects or null + // - milestone: milestone object with "id", "title", "description", etc. or null + // - repository: repository object with "id", "name", "full_name", etc. or null + // - reviewers: array of user objects or null + // - status: object with "id", "name", "color", "description" fields (same structure as single_select) or null + Value any `json:"value,omitempty"` +} + +// ListOrganizationProjects lists Projects V2 for an organization. +// +// GitHub API docs: https://docs.github.com/rest/projects/projects?apiVersion=2022-11-28#list-projects-for-organization +// +//meta:operation GET /orgs/{org}/projectsV2 +func (s *ProjectsService) ListOrganizationProjects(ctx context.Context, org string, opts *ListProjectsOptions) ([]*ProjectV2, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2", org) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - project := &Project{} - resp, err := s.client.Do(ctx, req, project) + var projects []*ProjectV2 + resp, err := s.client.Do(req, &projects) if err != nil { return nil, resp, err } - return project, resp, nil + return projects, resp, nil } -// ProjectOptions specifies the parameters to the -// RepositoriesService.CreateProject and -// ProjectsService.UpdateProject methods. -type ProjectOptions struct { - // The name of the project. (Required for creation; optional for update.) - Name *string `json:"name,omitempty"` - // The body of the project. (Optional.) - Body *string `json:"body,omitempty"` +// GetOrganizationProject gets a Projects V2 project for an organization by ID. +// +// GitHub API docs: https://docs.github.com/rest/projects/projects?apiVersion=2022-11-28#get-project-for-organization +// +//meta:operation GET /orgs/{org}/projectsV2/{project_number} +func (s *ProjectsService) GetOrganizationProject(ctx context.Context, org string, projectNumber int) (*ProjectV2, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v", org, projectNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - // The following field(s) are only applicable for update. - // They should be left with zero values for creation. + var project *ProjectV2 + resp, err := s.client.Do(req, &project) + if err != nil { + return nil, resp, err + } - // State of the project. Either "open" or "closed". (Optional.) - State *string `json:"state,omitempty"` - // The permission level that all members of the project's organization - // will have on this project. - // Setting the organization permission is only available - // for organization projects. (Optional.) - OrganizationPermission *string `json:"organization_permission,omitempty"` - // Sets visibility of the project within the organization. - // Setting visibility is only available - // for organization projects.(Optional.) - Public *bool `json:"public,omitempty"` + return project, resp, nil } -// UpdateProject updates a repository project. +// ListUserProjects lists Projects V2 for a user. +// +// GitHub API docs: https://docs.github.com/rest/projects/projects?apiVersion=2022-11-28#list-projects-for-user // -// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project -func (s *ProjectsService) UpdateProject(ctx context.Context, id int64, opt *ProjectOptions) (*Project, *Response, error) { - u := fmt.Sprintf("projects/%v", id) - req, err := s.client.NewRequest("PATCH", u, opt) +//meta:operation GET /users/{username}/projectsV2 +func (s *ProjectsService) ListUserProjects(ctx context.Context, username string, opts *ListProjectsOptions) ([]*ProjectV2, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2", username) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) + var projects []*ProjectV2 + resp, err := s.client.Do(req, &projects) if err != nil { return nil, resp, err } - return project, resp, nil + return projects, resp, nil } -// DeleteProject deletes a GitHub Project from a repository. +// GetUserProject gets a Projects V2 project for a user by ID. +// +// GitHub API docs: https://docs.github.com/rest/projects/projects?apiVersion=2022-11-28#get-project-for-user // -// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project -func (s *ProjectsService) DeleteProject(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("projects/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) +//meta:operation GET /users/{username}/projectsV2/{project_number} +func (s *ProjectsService) GetUserProject(ctx context.Context, username string, projectNumber int) (*ProjectV2, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v", username, projectNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { - return nil, err + return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + var project *ProjectV2 + resp, err := s.client.Do(req, &project) + if err != nil { + return nil, resp, err + } - return s.client.Do(ctx, req, nil) + return project, resp, nil } -// ProjectColumn represents a column of a GitHub Project. +// ListOrganizationProjectFields lists Projects V2 for an organization. // -// GitHub API docs: https://developer.github.com/v3/repos/projects/ -type ProjectColumn struct { - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - URL *string `json:"url,omitempty"` - ProjectURL *string `json:"project_url,omitempty"` - CardsURL *string `json:"cards_url,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -// ListProjectColumns lists the columns of a GitHub Project for a repo. +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28#list-project-fields-for-organization // -// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns -func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int64, opt *ListOptions) ([]*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/%v/columns", projectID) - u, err := addOptions(u, opt) +//meta:operation GET /orgs/{org}/projectsV2/{project_number}/fields +func (s *ProjectsService) ListOrganizationProjectFields(ctx context.Context, org string, projectNumber int, opts *ListProjectsOptions) ([]*ProjectV2Field, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/fields", org, projectNumber) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - columns := []*ProjectColumn{} - resp, err := s.client.Do(ctx, req, &columns) + var fields []*ProjectV2Field + resp, err := s.client.Do(req, &fields) if err != nil { return nil, resp, err } - return columns, resp, nil + return fields, resp, nil } -// GetProjectColumn gets a column of a GitHub Project for a repo. +// ListUserProjectFields lists Projects V2 for a user. // -// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column -func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int64) (*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/columns/%v", id) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28#list-project-fields-for-user +// +//meta:operation GET /users/{username}/projectsV2/{project_number}/fields +func (s *ProjectsService) ListUserProjectFields(ctx context.Context, user string, projectNumber int, opts *ListProjectsOptions) ([]*ProjectV2Field, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/fields", user, projectNumber) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - column := &ProjectColumn{} - resp, err := s.client.Do(ctx, req, column) + var fields []*ProjectV2Field + resp, err := s.client.Do(req, &fields) if err != nil { return nil, resp, err } - return column, resp, nil + return fields, resp, nil } -// ProjectColumnOptions specifies the parameters to the -// ProjectsService.CreateProjectColumn and -// ProjectsService.UpdateProjectColumn methods. -type ProjectColumnOptions struct { - // The name of the project column. (Required for creation and update.) - Name string `json:"name"` +// GetOrganizationProjectField gets a single project field from an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28#get-project-field-for-organization +// +//meta:operation GET /orgs/{org}/projectsV2/{project_number}/fields/{field_id} +func (s *ProjectsService) GetOrganizationProjectField(ctx context.Context, org string, projectNumber int, fieldID int64) (*ProjectV2Field, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/fields/%v", org, projectNumber, fieldID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var field *ProjectV2Field + resp, err := s.client.Do(req, &field) + if err != nil { + return nil, resp, err + } + + return field, resp, nil } -// CreateProjectColumn creates a column for the specified (by number) project. +// GetUserProjectField gets a single project field from a user owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column -func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/%v/columns", projectID) - req, err := s.client.NewRequest("POST", u, opt) +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28#get-project-field-for-user +// +//meta:operation GET /users/{username}/projectsV2/{project_number}/fields/{field_id} +func (s *ProjectsService) GetUserProjectField(ctx context.Context, user string, projectNumber int, fieldID int64) (*ProjectV2Field, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/fields/%v", user, projectNumber, fieldID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - column := &ProjectColumn{} - resp, err := s.client.Do(ctx, req, column) + var field *ProjectV2Field + resp, err := s.client.Do(req, &field) if err != nil { return nil, resp, err } - return column, resp, nil + return field, resp, nil +} + +// ListProjectItemsOptions specifies optional parameters when listing project items. +// Note: Pagination uses before/after cursor-style pagination similar to ListProjectsOptions. +// "Fields" can be used to restrict which field values are returned (by their numeric IDs). +type ListProjectItemsOptions struct { + // Embed ListProjectsOptions to reuse pagination and query parameters. + ListProjectsOptions + // Fields restricts which field values are returned by numeric field IDs. + Fields []int64 `url:"fields,omitempty,comma"` +} + +// GetProjectItemOptions specifies optional parameters when getting a project item. +type GetProjectItemOptions struct { + // Fields restricts which field values are returned by numeric field IDs. + Fields []int64 `url:"fields,omitempty,comma"` +} + +// AddProjectItemOptions represents the payload to add an item (issue or pull request) +// to a project. The Type must be either "Issue" or "PullRequest" (as per API docs) and +// ID is the numerical ID of that issue or pull request. +type AddProjectItemOptions struct { + Type *ProjectV2ItemContentType `json:"type,omitempty"` + ID *int64 `json:"id,omitempty"` +} + +// UpdateProjectV2Field represents a field update for a project item. +// +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#update-project-item-for-organization +type UpdateProjectV2Field struct { + // ID is the field ID to update. + ID int64 `json:"id"` + // Value is the new value to set for the field. The type depends on the field type. + // For text fields: string + // For number fields: float64 or int + // For single_select fields: string (option ID) + // For date fields: string (ISO 8601 date) + // For iteration fields: string (iteration ID) + // Note: Some field types (title, assignees, labels, etc.) are read-only or managed through other API endpoints. + Value any `json:"value"` +} + +// UpdateProjectItemOptions represents fields that can be modified for a project item. +// The GitHub API expects either archived status updates or field value updates. +type UpdateProjectItemOptions struct { + // Archived indicates whether the item should be archived (true) or unarchived (false). + // This is used for archive/unarchive operations. + Archived *bool `json:"archived,omitempty"` + // Fields contains field updates to apply to the project item. + // Each entry specifies a field ID and its new value. + Fields []*UpdateProjectV2Field `json:"fields,omitempty"` } -// UpdateProjectColumn updates a column of a GitHub Project. +// ListOrganizationProjectItems lists items for an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#list-items-for-an-organization-owned-project // -// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column -func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/columns/%v", columnID) - req, err := s.client.NewRequest("PATCH", u, opt) +//meta:operation GET /orgs/{org}/projectsV2/{project_number}/items +func (s *ProjectsService) ListOrganizationProjectItems(ctx context.Context, org string, projectNumber int, opts *ListProjectItemsOptions) ([]*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/items", org, projectNumber) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - column := &ProjectColumn{} - resp, err := s.client.Do(ctx, req, column) + var items []*ProjectV2Item + resp, err := s.client.Do(req, &items) if err != nil { return nil, resp, err } - return column, resp, nil + return items, resp, nil } -// DeleteProjectColumn deletes a column from a GitHub Project. +// AddOrganizationProjectItem adds an issue or pull request item to an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#add-item-to-organization-owned-project // -// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column -func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int64) (*Response, error) { - u := fmt.Sprintf("projects/columns/%v", columnID) - req, err := s.client.NewRequest("DELETE", u, nil) +//meta:operation POST /orgs/{org}/projectsV2/{project_number}/items +func (s *ProjectsService) AddOrganizationProjectItem(ctx context.Context, org string, projectNumber int, body *AddProjectItemOptions) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/items", org, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { - return nil, err + return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - return s.client.Do(ctx, req, nil) -} + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) + if err != nil { + return nil, resp, err + } -// ProjectColumnMoveOptions specifies the parameters to the -// ProjectsService.MoveProjectColumn method. -type ProjectColumnMoveOptions struct { - // Position can be one of "first", "last", or "after:", where - // is the ID of a column in the same project. (Required.) - Position string `json:"position"` + return item, resp, nil } -// MoveProjectColumn moves a column within a GitHub Project. +// GetOrganizationProjectItem gets a single item from an organization owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column -func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnMoveOptions) (*Response, error) { - u := fmt.Sprintf("projects/columns/%v/moves", columnID) - req, err := s.client.NewRequest("POST", u, opt) +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#get-an-item-for-an-organization-owned-project +// +//meta:operation GET /orgs/{org}/projectsV2/{project_number}/items/{item_id} +func (s *ProjectsService) GetOrganizationProjectItem(ctx context.Context, org string, projectNumber int, itemID int64, opts *GetProjectItemOptions) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/items/%v", org, projectNumber, itemID) + u, err := addOptions(u, opts) if err != nil { - return nil, err + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) + if err != nil { + return nil, resp, err + } - return s.client.Do(ctx, req, nil) + return item, resp, nil } -// ProjectCard represents a card in a column of a GitHub Project. +// UpdateOrganizationProjectItem updates an item in an organization owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card -type ProjectCard struct { - URL *string `json:"url,omitempty"` - ColumnURL *string `json:"column_url,omitempty"` - ContentURL *string `json:"content_url,omitempty"` - ID *int64 `json:"id,omitempty"` - Note *string `json:"note,omitempty"` - Creator *User `json:"creator,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Archived *bool `json:"archived,omitempty"` +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#update-project-item-for-organization +// +//meta:operation PATCH /orgs/{org}/projectsV2/{project_number}/items/{item_id} +func (s *ProjectsService) UpdateOrganizationProjectItem(ctx context.Context, org string, projectNumber int, itemID int64, body *UpdateProjectItemOptions) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/items/%v", org, projectNumber, itemID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } - // The following fields are only populated by Webhook events. - ColumnID *int64 `json:"column_id,omitempty"` + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) + if err != nil { + return nil, resp, err + } - // The following fields are only populated by Events API. - ProjectID *int64 `json:"project_id,omitempty"` - ProjectURL *string `json:"project_url,omitempty"` - ColumnName *string `json:"column_name,omitempty"` - PreviousColumnName *string `json:"previous_column_name,omitempty"` // Populated in "moved_columns_in_project" event deliveries. + return item, resp, nil } -// ProjectCardListOptions specifies the optional parameters to the -// ProjectsService.ListProjectCards method. -type ProjectCardListOptions struct { - // ArchivedState is used to list all, archived, or not_archived project cards. - // Defaults to not_archived when you omit this parameter. - ArchivedState *string `url:"archived_state,omitempty"` +// DeleteOrganizationProjectItem deletes an item from an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#delete-project-item-for-organization +// +//meta:operation DELETE /orgs/{org}/projectsV2/{project_number}/items/{item_id} +func (s *ProjectsService) DeleteOrganizationProjectItem(ctx context.Context, org string, projectNumber int, itemID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/items/%v", org, projectNumber, itemID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } - ListOptions + return s.client.Do(req, nil) } -// ListProjectCards lists the cards in a column of a GitHub Project. +// ListUserProjectItems lists items for a user owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards -func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int64, opt *ProjectCardListOptions) ([]*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/%v/cards", columnID) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#list-items-for-a-user-owned-project +// +//meta:operation GET /users/{username}/projectsV2/{project_number}/items +func (s *ProjectsService) ListUserProjectItems(ctx context.Context, username string, projectNumber int, opts *ListProjectItemsOptions) ([]*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/items", username, projectNumber) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - cards := []*ProjectCard{} - resp, err := s.client.Do(ctx, req, &cards) + var items []*ProjectV2Item + resp, err := s.client.Do(req, &items) if err != nil { return nil, resp, err } - return cards, resp, nil + return items, resp, nil } -// GetProjectCard gets a card in a column of a GitHub Project. +// AddUserProjectItem adds an issue or pull request item to a user owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card -func (s *ProjectsService) GetProjectCard(ctx context.Context, cardID int64) (*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v", cardID) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#add-item-to-user-owned-project +// +//meta:operation POST /users/{username}/projectsV2/{project_number}/items +func (s *ProjectsService) AddUserProjectItem(ctx context.Context, username string, projectNumber int, body *AddProjectItemOptions) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/items", username, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - card := &ProjectCard{} - resp, err := s.client.Do(ctx, req, card) + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) if err != nil { return nil, resp, err } - return card, resp, nil + return item, resp, nil } -// ProjectCardOptions specifies the parameters to the -// ProjectsService.CreateProjectCard and -// ProjectsService.UpdateProjectCard methods. -type ProjectCardOptions struct { - // The note of the card. Note and ContentID are mutually exclusive. - Note string `json:"note,omitempty"` - // The ID (not Number) of the Issue to associate with this card. - // Note and ContentID are mutually exclusive. - ContentID int64 `json:"content_id,omitempty"` - // The type of content to associate with this card. Possible values are: "Issue" and "PullRequest". - ContentType string `json:"content_type,omitempty"` - // Use true to archive a project card. - // Specify false if you need to restore a previously archived project card. - Archived *bool `json:"archived,omitempty"` -} - -// CreateProjectCard creates a card in the specified column of a GitHub Project. +// GetUserProjectItem gets a single item from a user owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#get-an-item-for-a-user-owned-project // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card -func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/%v/cards", columnID) - req, err := s.client.NewRequest("POST", u, opt) +//meta:operation GET /users/{username}/projectsV2/{project_number}/items/{item_id} +func (s *ProjectsService) GetUserProjectItem(ctx context.Context, username string, projectNumber int, itemID int64, opts *GetProjectItemOptions) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/items/%v", username, projectNumber, itemID) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - card := &ProjectCard{} - resp, err := s.client.Do(ctx, req, card) + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) if err != nil { return nil, resp, err } - return card, resp, nil + return item, resp, nil } -// UpdateProjectCard updates a card of a GitHub Project. +// UpdateUserProjectItem updates an item in a user owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card -func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v", cardID) - req, err := s.client.NewRequest("PATCH", u, opt) +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#update-project-item-for-user +// +//meta:operation PATCH /users/{username}/projectsV2/{project_number}/items/{item_id} +func (s *ProjectsService) UpdateUserProjectItem(ctx context.Context, username string, projectNumber int, itemID int64, body *UpdateProjectItemOptions) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/items/%v", username, projectNumber, itemID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - card := &ProjectCard{} - resp, err := s.client.Do(ctx, req, card) + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) if err != nil { return nil, resp, err } - return card, resp, nil + return item, resp, nil } -// DeleteProjectCard deletes a card from a GitHub Project. +// DeleteUserProjectItem deletes an item from a user owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card -func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int64) (*Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v", cardID) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#delete-project-item-for-user +// +//meta:operation DELETE /users/{username}/projectsV2/{project_number}/items/{item_id} +func (s *ProjectsService) DeleteUserProjectItem(ctx context.Context, username string, projectNumber int, itemID int64) (*Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/items/%v", username, projectNumber, itemID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + return s.client.Do(req, nil) +} - return s.client.Do(ctx, req, nil) +// CreateProjectV2DraftItemRequest specifies the parameters to create a draft item in a project. +type CreateProjectV2DraftItemRequest struct { + // Title is the title of the draft issue item to create. + Title string `json:"title"` + // Body is the body content of the draft issue item to create. + Body *string `json:"body,omitempty"` } -// ProjectCardMoveOptions specifies the parameters to the -// ProjectsService.MoveProjectCard method. -type ProjectCardMoveOptions struct { - // Position can be one of "top", "bottom", or "after:", where - // is the ID of a card in the same project. - Position string `json:"position"` - // ColumnID is the ID of a column in the same project. Note that ColumnID - // is required when using Position "after:" when that card is in - // another column; otherwise it is optional. - ColumnID int64 `json:"column_id,omitempty"` +// CreateOrganizationProjectDraftItem creates a draft item in an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/drafts?apiVersion=2022-11-28#create-draft-item-for-organization-owned-project +// +//meta:operation POST /orgs/{org}/projectsV2/{project_number}/drafts +func (s *ProjectsService) CreateOrganizationProjectDraftItem(ctx context.Context, org string, projectNumber int, body CreateProjectV2DraftItemRequest) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/drafts", org, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) + if err != nil { + return nil, resp, err + } + + return item, resp, nil } -// MoveProjectCard moves a card within a GitHub Project. +// CreateUserProjectDraftItem creates a draft item in a user owned project. +// +// Note: this endpoint identifies the user by their unique numeric ID (user_id) +// in the path, not by username, mirroring the GitHub REST API. +// +// GitHub API docs: https://docs.github.com/rest/projects/drafts?apiVersion=2022-11-28#create-draft-item-for-user-owned-project // -// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card -func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int64, opt *ProjectCardMoveOptions) (*Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v/moves", cardID) - req, err := s.client.NewRequest("POST", u, opt) +//meta:operation POST /user/{user_id}/projectsV2/{project_number}/drafts +func (s *ProjectsService) CreateUserProjectDraftItem(ctx context.Context, userID int64, projectNumber int, body CreateProjectV2DraftItemRequest) (*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("user/%v/projectsV2/%v/drafts", userID, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { - return nil, err + return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + var item *ProjectV2Item + resp, err := s.client.Do(req, &item) + if err != nil { + return nil, resp, err + } + + return item, resp, nil +} - return s.client.Do(ctx, req, nil) +// ProjectV2FieldSingleSelectOption represents an option to create for a single_select project field. +type ProjectV2FieldSingleSelectOption struct { + // Name is the display name of the option. + Name string `json:"name"` + // Color is the color associated with the option. + // One of: BLUE, GRAY, GREEN, ORANGE, PINK, PURPLE, RED, YELLOW. + Color *string `json:"color,omitempty"` + // Description is an optional description of the option. + Description *string `json:"description,omitempty"` } -// ProjectCollaboratorOptions specifies the optional parameters to the -// ProjectsService.AddProjectCollaborator method. -type ProjectCollaboratorOptions struct { - // Permission specifies the permission to grant to the collaborator. - // Possible values are: - // "read" - can read, but not write to or administer this project. - // "write" - can read and write, but not administer this project. - // "admin" - can read, write and administer this project. - // - // Default value is "write" - Permission *string `json:"permission,omitempty"` +// ProjectV2FieldIterationConfiguration represents the configuration to create an iteration project field. +type ProjectV2FieldIterationConfiguration struct { + // StartDate is the start date of the first iteration in ISO 8601 (YYYY-MM-DD) format. + StartDate *string `json:"start_date,omitempty"` + // Duration is the default duration for iterations in days. + Duration *int `json:"duration,omitempty"` + // Iterations holds zero or more iterations for the field. + Iterations []*ProjectV2FieldIterationConfigurationIteration `json:"iterations,omitempty"` } -// AddProjectCollaborator adds a collaborator to an organization project and sets -// their permission level. You must be an organization owner or a project admin to add a collaborator. +// ProjectV2FieldIterationConfigurationIteration represents a single iteration within a ProjectV2FieldIterationConfiguration. +type ProjectV2FieldIterationConfigurationIteration struct { + // Title is the title of the iteration. + Title *string `json:"title,omitempty"` + // StartDate is the start date of the iteration in ISO 8601 (YYYY-MM-DD) format. + StartDate *string `json:"start_date,omitempty"` + // Duration is the duration of the iteration in days. + Duration *int `json:"duration,omitempty"` +} + +// AddProjectV2FieldRequest specifies the parameters to add a field to a project. // -// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#add-user-as-a-collaborator -func (s *ProjectsService) AddProjectCollaborator(ctx context.Context, id int64, username string, opt *ProjectCollaboratorOptions) (*Response, error) { - u := fmt.Sprintf("projects/%v/collaborators/%v", id, username) - req, err := s.client.NewRequest("PUT", u, opt) +// The GitHub API models the body as a one-of: set IssueFieldID to link a +// built-in issue field, or set Name and DataType to create a new field. When +// DataType is "single_select", SingleSelectOptions is required; when DataType +// is "iteration", IterationConfiguration is required. +type AddProjectV2FieldRequest struct { + // Name is the name of the field. + Name *string `json:"name,omitempty"` + // DataType is the field's data type. + // One of: text, number, date, single_select, iteration. + DataType *string `json:"data_type,omitempty"` + // SingleSelectOptions holds the options for a single_select field. (Required for single_select.) + SingleSelectOptions []*ProjectV2FieldSingleSelectOption `json:"single_select_options,omitempty"` + // IterationConfiguration holds the configuration for an iteration field. (Required for iteration.) + IterationConfiguration *ProjectV2FieldIterationConfiguration `json:"iteration_configuration,omitempty"` + // IssueFieldID links an existing built-in issue field to the project. (Organization owned projects only.) + IssueFieldID *int64 `json:"issue_field_id,omitempty"` +} + +// AddOrganizationProjectField adds a field to an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28#add-a-field-to-an-organization-owned-project +// +//meta:operation POST /orgs/{org}/projectsV2/{project_number}/fields +func (s *ProjectsService) AddOrganizationProjectField(ctx context.Context, org string, projectNumber int, body AddProjectV2FieldRequest) (*ProjectV2Field, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/fields", org, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { - return nil, err + return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + var field *ProjectV2Field + resp, err := s.client.Do(req, &field) + if err != nil { + return nil, resp, err + } - return s.client.Do(ctx, req, nil) + return field, resp, nil } -// RemoveProjectCollaborator removes a collaborator from an organization project. -// You must be an organization owner or a project admin to remove a collaborator. +// AddUserProjectField adds a field to a user owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/fields?apiVersion=2022-11-28#add-field-to-user-owned-project // -// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#remove-user-as-a-collaborator -func (s *ProjectsService) RemoveProjectCollaborator(ctx context.Context, id int64, username string) (*Response, error) { - u := fmt.Sprintf("projects/%v/collaborators/%v", id, username) - req, err := s.client.NewRequest("DELETE", u, nil) +//meta:operation POST /users/{username}/projectsV2/{project_number}/fields +func (s *ProjectsService) AddUserProjectField(ctx context.Context, username string, projectNumber int, body AddProjectV2FieldRequest) (*ProjectV2Field, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/fields", username, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { - return nil, err + return nil, nil, err + } + + var field *ProjectV2Field + resp, err := s.client.Do(req, &field) + if err != nil { + return nil, resp, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + return field, resp, nil +} + +// ProjectV2View represents a view in a project. +type ProjectV2View struct { + ID int64 `json:"id"` + Number int `json:"number"` + Name string `json:"name"` + Layout string `json:"layout"` + NodeID string `json:"node_id"` + ProjectURL string `json:"project_url"` + HTMLURL string `json:"html_url"` + Creator User `json:"creator"` + Filter *string `json:"filter,omitempty"` + VisibleFields []int64 `json:"visible_fields"` + SortBy []*ProjectV2ViewSortBy `json:"sort_by"` + GroupBy []int64 `json:"group_by"` + VerticalGroupBy []int64 `json:"vertical_group_by"` + CreatedAt Timestamp `json:"created_at"` + UpdatedAt Timestamp `json:"updated_at"` +} - return s.client.Do(ctx, req, nil) +// ProjectV2ViewSortBy represents a single sort criterion of a project view. +// +// On the wire it is encoded as a [field_id, direction] tuple rather than an +// object, so it has custom JSON (un)marshaling. +type ProjectV2ViewSortBy struct { + // FieldID is the ID of the field to sort by. + FieldID *int64 `json:"-"` + // Direction is the sort direction, one of "asc" or "desc". + Direction *string `json:"-"` } -// ListCollaboratorOptions specifies the optional parameters to the -// ProjectsService.ListProjectCollaborators method. -type ListCollaboratorOptions struct { - // Affiliation specifies how collaborators should be filtered by their affiliation. - // Possible values are: - // "outside" - All outside collaborators of an organization-owned repository - // "direct" - All collaborators with permissions to an organization-owned repository, - // regardless of organization membership status - // "all" - All collaborators the authenticated user can see - // - // Default value is "all". - Affiliation *string `url:"affiliation,omitempty"` +// UnmarshalJSON implements custom unmarshaling for the [field_id, direction] +// tuple. The field_id may be encoded as either a number or a string. +func (s *ProjectV2ViewSortBy) UnmarshalJSON(data []byte) error { + var tuple []json.RawMessage + if err := json.Unmarshal(data, &tuple); err != nil { + return err + } + if len(tuple) != 2 { + return fmt.Errorf("expected a [field_id, direction] tuple, got %v elements", len(tuple)) + } + + var fieldID int64 + if err := json.Unmarshal(tuple[0], &fieldID); err != nil { + // The OpenAPI schema allows the field_id to be a string as well. + var str string + if err2 := json.Unmarshal(tuple[0], &str); err2 != nil { + return fmt.Errorf("invalid field_id: %w", err2) + } + fieldID, err = strconv.ParseInt(str, 10, 64) + if err != nil { + return fmt.Errorf("invalid field_id %q: %w", str, err) + } + } + + var direction string + if err := json.Unmarshal(tuple[1], &direction); err != nil { + return fmt.Errorf("invalid direction: %w", err) + } + + s.FieldID = &fieldID + s.Direction = &direction + return nil +} + +// MarshalJSON implements custom marshaling to the [field_id, direction] tuple. +func (s ProjectV2ViewSortBy) MarshalJSON() ([]byte, error) { + return json.Marshal([]any{s.FieldID, s.Direction}) +} - ListOptions +// CreateProjectV2ViewRequest specifies the parameters to create a project view. +type CreateProjectV2ViewRequest struct { + // Name is the view's display name. + Name string `json:"name"` + // Layout is the view's layout. One of: table, board, roadmap. + Layout string `json:"layout"` + // Filter is an optional query string to filter the items shown in the view. + Filter *string `json:"filter,omitempty"` + // VisibleFields lists the field IDs to display. Not applicable to the roadmap layout. + VisibleFields []int64 `json:"visible_fields,omitempty"` } -// ListProjectCollaborators lists the collaborators for an organization project. For a project, -// the list of collaborators includes outside collaborators, organization members that are direct -// collaborators, organization members with access through team memberships, organization members -// with access through default organization permissions, and organization owners. You must be an -// organization owner or a project admin to list collaborators. +// CreateOrganizationProjectView creates a view for an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/views?apiVersion=2022-11-28#create-a-view-for-an-organization-owned-project // -// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#list-collaborators -func (s *ProjectsService) ListProjectCollaborators(ctx context.Context, id int64, opt *ListCollaboratorOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("projects/%v/collaborators", id) - u, err := addOptions(u, opt) +//meta:operation POST /orgs/{org}/projectsV2/{project_number}/views +func (s *ProjectsService) CreateOrganizationProjectView(ctx context.Context, org string, projectNumber int, body CreateProjectV2ViewRequest) (*ProjectV2View, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/views", org, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + var view *ProjectV2View + resp, err := s.client.Do(req, &view) if err != nil { - return nil, nil, err + return nil, resp, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + return view, resp, nil +} - var users []*User - resp, err := s.client.Do(ctx, req, &users) +// CreateUserProjectView creates a view for a user owned project. +// +// Note: this endpoint identifies the user by their unique numeric ID (user_id) +// in the path, not by username, mirroring the GitHub REST API. +// +// GitHub API docs: https://docs.github.com/rest/projects/views?apiVersion=2022-11-28#create-a-view-for-a-user-owned-project +// +//meta:operation POST /users/{user_id}/projectsV2/{project_number}/views +func (s *ProjectsService) CreateUserProjectView(ctx context.Context, userID int64, projectNumber int, body CreateProjectV2ViewRequest) (*ProjectV2View, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/views", userID, projectNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var view *ProjectV2View + resp, err := s.client.Do(req, &view) if err != nil { return nil, resp, err } - return users, resp, nil + return view, resp, nil } -// ProjectPermissionLevel represents the permission level an organization -// member has for a given project. -type ProjectPermissionLevel struct { - // Possible values: "admin", "write", "read", "none" - Permission *string `json:"permission,omitempty"` +// ListOrganizationProjectViewItems lists the items of a view in an organization owned project. +// +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#list-items-for-an-organization-project-view +// +//meta:operation GET /orgs/{org}/projectsV2/{project_number}/views/{view_number}/items +func (s *ProjectsService) ListOrganizationProjectViewItems(ctx context.Context, org string, projectNumber, viewNumber int, opts *ListProjectItemsOptions) ([]*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("orgs/%v/projectsV2/%v/views/%v/items", org, projectNumber, viewNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - User *User `json:"user,omitempty"` + var items []*ProjectV2Item + resp, err := s.client.Do(req, &items) + if err != nil { + return nil, resp, err + } + + return items, resp, nil } -// ReviewProjectCollaboratorPermission returns the collaborator's permission level for an organization -// project. Possible values for the permission key: "admin", "write", "read", "none". -// You must be an organization owner or a project admin to review a user's permission level. +// ListUserProjectViewItems lists the items of a view in a user owned project. // -// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#review-a-users-permission-level -func (s *ProjectsService) ReviewProjectCollaboratorPermission(ctx context.Context, id int64, username string) (*ProjectPermissionLevel, *Response, error) { - u := fmt.Sprintf("projects/%v/collaborators/%v/permission", id, username) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/projects/items?apiVersion=2022-11-28#list-items-for-a-user-project-view +// +//meta:operation GET /users/{username}/projectsV2/{project_number}/views/{view_number}/items +func (s *ProjectsService) ListUserProjectViewItems(ctx context.Context, username string, projectNumber, viewNumber int, opts *ListProjectItemsOptions) ([]*ProjectV2Item, *Response, error) { + u := fmt.Sprintf("users/%v/projectsV2/%v/views/%v/items", username, projectNumber, viewNumber) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - ppl := new(ProjectPermissionLevel) - resp, err := s.client.Do(ctx, req, ppl) + var items []*ProjectV2Item + resp, err := s.client.Do(req, &items) if err != nil { return nil, resp, err } - return ppl, resp, nil + + return items, resp, nil } diff --git a/github/projects_test.go b/github/projects_test.go index 666c0e66bfd..70221313a3d 100644 --- a/github/projects_test.go +++ b/github/projects_test.go @@ -1,4 +1,4 @@ -// Copyright 2016 The go-github AUTHORS. All rights reserved. +// Copyright 2025 The go-github AUTHORS. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -10,579 +10,1798 @@ import ( "encoding/json" "fmt" "net/http" - "reflect" - "strings" "testing" + + "github.com/google/go-cmp/cmp" ) -func TestProject_marshall(t *testing.T) { - testJSONMarshal(t, &Project{}, "{}") - - u := &Project{ - ID: Int64(1), - URL: String("u"), - HTMLURL: String("h"), - ColumnsURL: String("c"), - OwnerURL: String("o"), - Name: String("n"), - Body: String("b"), - Number: Int(1), - State: String("s"), - CreatedAt: &Timestamp{referenceTime}, - UpdatedAt: &Timestamp{referenceTime}, - NodeID: String("n"), - Creator: &User{ - Login: String("l"), - ID: Int64(1), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - URL: String("u"), - }, - } - want := `{ - "id": 1, - "url": "u", - "html_url": "h", - "columns_url": "c", - "owner_url": "o", - "name": "n", - "body": "b", - "number": 1, - "state": "s", - "created_at": ` + referenceTimeStr + `, - "updated_at": ` + referenceTimeStr + `, - "node_id": "n", - "creator": { - "login": "l", - "id": 1, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "url": "u" - } - }` - testJSONMarshal(t, u, want) -} -func TestProjectsService_UpdateProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListOrganizationProjects(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &ProjectOptions{ - Name: String("Project Name"), - Body: String("Project body."), - State: String("open"), - Public: Bool(true), + // Combined handler: supports initial test case and dual before/after validation scenario. + mux.HandleFunc("/orgs/o/projectsV2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + q := r.URL.Query() + if q.Get("before") == "b" && q.Get("after") == "a" { + fmt.Fprint(w, `[]`) + return + } + // default expectation for main part of test + testFormValues(t, r, values{"q": "alpha", "after": "2", "before": "1"}) + fmt.Fprint(w, `[{"id":1,"title":"T1","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + }) - OrganizationPermission: String("read"), + opts := &ListProjectsOptions{Query: "alpha", ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: "2", Before: "1"}} + ctx := t.Context() + projects, _, err := client.Projects.ListOrganizationProjects(ctx, "o", opts) + if err != nil { + t.Fatalf("Projects.ListOrganizationProjects returned error: %v", err) + } + if len(projects) != 1 || projects[0].GetID() != 1 || projects[0].GetTitle() != "T1" { + t.Fatalf("Projects.ListOrganizationProjects returned %+v", projects) } - mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + const methodName = "ListOrganizationProjects" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListOrganizationProjects(ctx, "\n", opts) + return err + }) - v := &ProjectOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListOrganizationProjects(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) - fmt.Fprint(w, `{"id":1}`) + // still allow both set (no validation enforced) – ensure it does not error + ctxBypass := context.WithValue(t.Context(), BypassRateLimitCheck, true) + if _, _, err = client.Projects.ListOrganizationProjects(ctxBypass, "o", &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{Before: "b", After: "a"}}); err != nil { + t.Fatalf("unexpected error when both before/after set: %v", err) + } +} + +func TestProjectsService_GetOrganizationProject(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/projectsV2/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1,"title":"OrgProj","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}`) }) - project, _, err := client.Projects.UpdateProject(context.Background(), 1, input) + ctx := t.Context() + project, _, err := client.Projects.GetOrganizationProject(ctx, "o", 1) if err != nil { - t.Errorf("Projects.UpdateProject returned error: %v", err) + t.Fatalf("Projects.GetOrganizationProject returned error: %v", err) } - - want := &Project{ID: Int64(1)} - if !reflect.DeepEqual(project, want) { - t.Errorf("Projects.UpdateProject returned %+v, want %+v", project, want) + if project.GetID() != 1 || project.GetTitle() != "OrgProj" { + t.Fatalf("Projects.GetOrganizationProject returned %+v", project) } + + const methodName = "GetOrganizationProject" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetOrganizationProject(ctx, "o", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_GetProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListUserProjects(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { + // Combined handler: supports initial test case and dual before/after scenario. + mux.HandleFunc("/users/u/projectsV2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - fmt.Fprint(w, `{"id":1}`) + q := r.URL.Query() + if q.Get("before") == "b" && q.Get("after") == "a" { + fmt.Fprint(w, `[]`) + return + } + testFormValues(t, r, values{"q": "beta", "before": "1", "after": "2", "per_page": "2"}) + fmt.Fprint(w, `[{"id":2,"title":"UProj","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) }) - project, _, err := client.Projects.GetProject(context.Background(), 1) + opts := &ListProjectsOptions{Query: "beta", ListProjectsPaginationOptions: ListProjectsPaginationOptions{Before: "1", After: "2", PerPage: 2}} + ctx := t.Context() + var ctxBypass context.Context + projects, _, err := client.Projects.ListUserProjects(ctx, "u", opts) if err != nil { - t.Errorf("Projects.GetProject returned error: %v", err) + t.Fatalf("Projects.ListUserProjects returned error: %v", err) } + if len(projects) != 1 || projects[0].GetID() != 2 || projects[0].GetTitle() != "UProj" { + t.Fatalf("Projects.ListUserProjects returned %+v", projects) + } + + const methodName = "ListUserProjects" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListUserProjects(ctx, "\n", opts) + return err + }) - want := &Project{ID: Int64(1)} - if !reflect.DeepEqual(project, want) { - t.Errorf("Projects.GetProject returned %+v, want %+v", project, want) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListUserProjects(ctx, "u", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + // still allow both set (no validation enforced) – ensure it does not error + ctxBypass = context.WithValue(t.Context(), BypassRateLimitCheck, true) + if _, _, err = client.Projects.ListUserProjects(ctxBypass, "u", &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{Before: "b", After: "a"}}); err != nil { + t.Fatalf("unexpected error when both before/after set: %v", err) } } -func TestProjectsService_DeleteProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_GetUserProject(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + mux.HandleFunc("/users/u/projectsV2/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":3,"title":"UserProj","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}`) }) - _, err := client.Projects.DeleteProject(context.Background(), 1) + ctx := t.Context() + project, _, err := client.Projects.GetUserProject(ctx, "u", 3) if err != nil { - t.Errorf("Projects.DeleteProject returned error: %v", err) + t.Fatalf("Projects.GetUserProject returned error: %v", err) + } + if project.GetID() != 3 || project.GetTitle() != "UserProj" { + t.Fatalf("Projects.GetUserProject returned %+v", project) } + + const methodName = "GetUserProject" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetUserProject(ctx, "u", 3) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_ListProjectColumns(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListOrganizationProjectFields(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeProjectsPreview} - mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/projectsV2/1/fields", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - testFormValues(t, r, values{"page": "2"}) - fmt.Fprint(w, `[{"id":1}]`) + q := r.URL.Query() + if q.Get("before") == "b" && q.Get("after") == "a" { // bypass scenario + fmt.Fprint(w, `[]`) + return + } + testFormValues(t, r, values{"after": "2", "before": "1", "q": "text"}) + fmt.Fprint(w, `[ + { + "id": 1, + "node_id": "node_1", + "name": "Status", + "data_type": "single_select", + "url": "https://api.github.com/projects/1/fields/field1", + "options": [ + {"id": "1", "name": {"raw": "Todo", "html": "Todo"}, "color": "blue", "description": {"raw": "Tasks to be done", "html": "Tasks to be done"}}, + {"id": "2", "name": {"raw": "In Progress", "html": "In Progress"}, "color": "yellow"} + ], + "created_at": "2011-01-02T15:04:05Z", + "updated_at": "2012-01-02T15:04:05Z" + }, + { + "id": 2, + "node_id": "node_2", + "name": "Priority", + "data_type": "text", + "url": "https://api.github.com/projects/1/fields/field2", + "created_at": "2011-01-02T15:04:05Z", + "updated_at": "2012-01-02T15:04:05Z" + } + ]`) }) - opt := &ListOptions{Page: 2} - columns, _, err := client.Projects.ListProjectColumns(context.Background(), 1, opt) + opts := &ListProjectsOptions{Query: "text", ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: "2", Before: "1"}} + ctx := t.Context() + fields, _, err := client.Projects.ListOrganizationProjectFields(ctx, "o", 1, opts) if err != nil { - t.Errorf("Projects.ListProjectColumns returned error: %v", err) + t.Fatalf("Projects.ListOrganizationProjectFields returned error: %v", err) + } + if len(fields) != 2 { + t.Fatalf("Projects.ListOrganizationProjectFields returned %d fields, want 2", len(fields)) + } + if fields[0].ID == nil || *fields[0].ID != 1 || fields[1].ID == nil || *fields[1].ID != 2 { + t.Fatalf("unexpected field IDs: %+v", fields) } - want := []*ProjectColumn{{ID: Int64(1)}} - if !reflect.DeepEqual(columns, want) { - t.Errorf("Projects.ListProjectColumns returned %+v, want %+v", columns, want) + const methodName = "ListOrganizationProjectFields" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListOrganizationProjectFields(ctx, "\n", 1, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListOrganizationProjectFields(ctx, "o", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + ctxBypass := context.WithValue(ctx, BypassRateLimitCheck, true) + if _, _, err = client.Projects.ListOrganizationProjectFields(ctxBypass, "o", 1, &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{Before: ("b"), After: ("a")}}); err != nil { + t.Fatalf("unexpected error when both before/after set: %v", err) } } -func TestProjectsService_GetProjectColumn(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListUserProjectFields(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/users/u/projectsV2/1/fields", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - fmt.Fprint(w, `{"id":1}`) + q := r.URL.Query() + if q.Get("before") == "b" && q.Get("after") == "a" { // bypass scenario + fmt.Fprint(w, `[]`) + return + } + testFormValues(t, r, values{"after": "2", "before": "1", "q": "text"}) + fmt.Fprint(w, `[ + { + "id": 1, + "node_id": "node_1", + "name": "Status", + "data_type": "single_select", + "url": "https://api.github.com/projects/1/fields/field1", + "options": [ + {"id": "1", "name": {"raw": "Todo", "html": "Todo"}, "color": "blue", "description": {"raw": "Tasks to be done", "html": "Tasks to be done"}}, + {"id": "2", "name": {"raw": "In Progress", "html": "In Progress"}, "color": "yellow"} + ], + "created_at": "2011-01-02T15:04:05Z", + "updated_at": "2012-01-02T15:04:05Z" + }, + { + "id": 2, + "node_id": "node_2", + "name": "Priority", + "data_type": "text", + "url": "https://api.github.com/projects/1/fields/field2", + "created_at": "2011-01-02T15:04:05Z", + "updated_at": "2012-01-02T15:04:05Z" + } + ]`) }) - column, _, err := client.Projects.GetProjectColumn(context.Background(), 1) + opts := &ListProjectsOptions{Query: ("text"), ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: ("2"), Before: ("1")}} + ctx := t.Context() + fields, _, err := client.Projects.ListUserProjectFields(ctx, "u", 1, opts) if err != nil { - t.Errorf("Projects.GetProjectColumn returned error: %v", err) + t.Fatalf("Projects.ListUserProjectFields returned error: %v", err) + } + if len(fields) != 2 { + t.Fatalf("Projects.ListUserProjectFields returned %d fields, want 2", len(fields)) + } + if fields[0].ID == nil || *fields[0].ID != 1 || fields[1].ID == nil || *fields[1].ID != 2 { + t.Fatalf("unexpected field IDs: %+v", fields) } - want := &ProjectColumn{ID: Int64(1)} - if !reflect.DeepEqual(column, want) { - t.Errorf("Projects.GetProjectColumn returned %+v, want %+v", column, want) + const methodName = "ListUserProjectFields" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListUserProjectFields(ctx, "\n", 1, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListUserProjectFields(ctx, "u", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + ctxBypass := context.WithValue(ctx, BypassRateLimitCheck, true) + if _, _, err = client.Projects.ListUserProjectFields(ctxBypass, "u", 1, &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{Before: ("b"), After: ("a")}}); err != nil { + t.Fatalf("unexpected error when both before/after set: %v", err) } } -func TestProjectsService_CreateProjectColumn(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_GetOrganizationProjectField(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &ProjectColumnOptions{Name: "Column Name"} + mux.HandleFunc("/orgs/o/projectsV2/1/fields/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, ` + { + "id": 1, + "node_id": "node_1", + "name": "Status", + "data_type": "single_select", + "url": "https://api.github.com/projects/1/fields/field1", + "options": [ + {"id": "1", "name": {"raw": "Todo", "html": "Todo"}, "color": "blue", "description": {"raw": "Tasks to be done", "html": "Tasks to be done"}}, + {"id": "2", "name": {"raw": "In Progress", "html": "In Progress"}, "color": "yellow"} + ], + "created_at": "2011-01-02T15:04:05Z", + "updated_at": "2012-01-02T15:04:05Z" + }`) + }) - mux.HandleFunc("/projects/1/columns", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + ctx := t.Context() + field, _, err := client.Projects.GetOrganizationProjectField(ctx, "o", 1, 1) + if err != nil { + t.Fatalf("Projects.GetOrganizationProjectField returned error: %v", err) + } + if field == nil || field.ID == nil || *field.ID != 1 { + t.Fatalf("unexpected field: %+v", field) + } - v := &ProjectColumnOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + const methodName = "GetOrganizationProjectField" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetOrganizationProjectField(ctx, "o", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} - fmt.Fprint(w, `{"id":1}`) +func TestProjectsService_GetUserProjectField(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/projectsV2/1/fields/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, ` + { + "id": 3, + "node_id": "node_3", + "name": "Status", + "data_type": "single_select", + "url": "https://api.github.com/projects/1/fields/field3", + "options": [ + {"id": "1", "name": {"raw": "Done", "html": "Done"}, "color": "red", "description": {"raw": "Done task", "html": "Done task"}}, + {"id": "2", "name": {"raw": "In Progress", "html": "In Progress"}, "color": "yellow"} + ], + "created_at": "2011-01-02T15:04:05Z", + "updated_at": "2012-01-02T15:04:05Z" + }`) }) - column, _, err := client.Projects.CreateProjectColumn(context.Background(), 1, input) + ctx := t.Context() + field, _, err := client.Projects.GetUserProjectField(ctx, "u", 1, 3) if err != nil { - t.Errorf("Projects.CreateProjectColumn returned error: %v", err) + t.Fatalf("Projects.GetUserProjectField returned error: %v", err) } - - want := &ProjectColumn{ID: Int64(1)} - if !reflect.DeepEqual(column, want) { - t.Errorf("Projects.CreateProjectColumn returned %+v, want %+v", column, want) + if field == nil || field.ID == nil || *field.ID != 3 { + t.Fatalf("unexpected field: %+v", field) } + + const methodName = "GetUserProjectField" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetUserProjectField(ctx, "u", 1, 3) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_UpdateProjectColumn(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListUserProjects_pagination(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + after := q.Get("after") + before := q.Get("before") + if after == "" && before == "" { + w.Header().Set("Link", "; rel=\"next\"") + fmt.Fprint(w, `[{"id":10,"title":"UP1","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + return + } + if after == "ucursor2" { + w.Header().Set("Link", "; rel=\"prev\"") + fmt.Fprint(w, `[{"id":11,"title":"UP2","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + return + } + http.Error(w, "unexpected query", http.StatusBadRequest) + }) + ctx := t.Context() + first, resp, err := client.Projects.ListUserProjects(ctx, "u", nil) + if err != nil { + t.Fatalf("first page error: %v", err) + } + if len(first) != 1 || first[0].GetID() != 10 { + t.Fatalf("unexpected first page %+v", first) + } + if resp.After != "ucursor2" { + t.Fatalf("expected resp.After=ucursor2 got %q", resp.After) + } - input := &ProjectColumnOptions{Name: "Column Name"} + opts := &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: (resp.After)}} + second, resp2, err := client.Projects.ListUserProjects(ctx, "u", opts) + if err != nil { + t.Fatalf("second page error: %v", err) + } + if len(second) != 1 || second[0].GetID() != 11 { + t.Fatalf("unexpected second page %+v", second) + } + if resp2.Before != "ucursor2" { + t.Fatalf("expected resp2.Before=ucursor2 got %q", resp2.Before) + } +} - mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) +func TestProjectsService_ListUserProjects_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[]`) + }) + ctx := t.Context() + const methodName = "ListUserProjects" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListUserProjects(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + // bad options (bad username) should error + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListUserProjects(ctx, "\n", nil) + return err + }) +} - v := &ProjectColumnOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) +func TestProjectsService_ListOrganizationProjectFields_pagination(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // First page returns a Link header with rel="next" containing an after cursor + mux.HandleFunc("/orgs/o/projectsV2/1/fields", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + after := q.Get("after") + before := q.Get("before") + if after == "" && before == "" { + // first request + w.Header().Set("Link", "; rel=\"next\"") + fmt.Fprint(w, `[{"id":1,"name":"Status","data_type":"single_select","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + return } + if after == "cursor2" { + // second request simulates a previous link + w.Header().Set("Link", "; rel=\"prev\"") + fmt.Fprint(w, `[{"id":2,"name":"Priority","data_type":"text","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + return + } + // unexpected state + http.Error(w, "unexpected query", http.StatusBadRequest) + }) - fmt.Fprint(w, `{"id":1}`) + ctx := t.Context() + first, resp, err := client.Projects.ListOrganizationProjectFields(ctx, "o", 1, nil) + if err != nil { + t.Fatalf("first page error: %v", err) + } + if len(first) != 1 || first[0].ID == nil || *first[0].ID != 1 { + t.Fatalf("unexpected first page %+v", first) + } + if resp.After != "cursor2" { + t.Fatalf("expected resp.After=cursor2 got %q", resp.After) + } + + opts := &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: (resp.After)}} + second, resp2, err := client.Projects.ListOrganizationProjectFields(ctx, "o", 1, opts) + if err != nil { + t.Fatalf("second page error: %v", err) + } + if len(second) != 1 || second[0].ID == nil || *second[0].ID != 2 { + t.Fatalf("unexpected second page %+v", second) + } + if resp2.Before != "cursor2" { + t.Fatalf("expected resp2.Before=cursor2 got %q", resp2.Before) + } +} + +func TestProjectsService_ListOrganizationProjects_pagination(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/projectsV2", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + after := q.Get("after") + before := q.Get("before") + if after == "" && before == "" { + w.Header().Set("Link", "; rel=\"next\"") + fmt.Fprint(w, `[{"id":20,"title":"OP1","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + return + } + if after == "ocursor2" { + w.Header().Set("Link", "; rel=\"prev\"") + fmt.Fprint(w, `[{"id":21,"title":"OP2","created_at":"2011-01-02T15:04:05Z","updated_at":"2012-01-02T15:04:05Z"}]`) + return + } + http.Error(w, "unexpected query", http.StatusBadRequest) }) - column, _, err := client.Projects.UpdateProjectColumn(context.Background(), 1, input) + ctx := t.Context() + first, resp, err := client.Projects.ListOrganizationProjects(ctx, "o", nil) if err != nil { - t.Errorf("Projects.UpdateProjectColumn returned error: %v", err) + t.Fatalf("first page error: %v", err) + } + if len(first) != 1 || first[0].GetID() != 20 { + t.Fatalf("unexpected first page %+v", first) + } + if resp.After != "ocursor2" { + t.Fatalf("expected resp.After=ocursor2 got %q", resp.After) } - want := &ProjectColumn{ID: Int64(1)} - if !reflect.DeepEqual(column, want) { - t.Errorf("Projects.UpdateProjectColumn returned %+v, want %+v", column, want) + opts := &ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: (resp.After)}} + second, resp2, err := client.Projects.ListOrganizationProjects(ctx, "o", opts) + if err != nil { + t.Fatalf("second page error: %v", err) + } + if len(second) != 1 || second[0].GetID() != 21 { + t.Fatalf("unexpected second page %+v", second) + } + if resp2.Before != "ocursor2" { + t.Fatalf("expected resp2.Before=ocursor2 got %q", resp2.Before) } } -func TestProjectsService_DeleteProjectColumn(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListOrganizationProjectItems(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/columns/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + mux.HandleFunc("/orgs/o/projectsV2/1/items", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + q := r.URL.Query() + if q.Get("before") == "b" && q.Get("after") == "a" { // bypass scenario + fmt.Fprint(w, `[]`) + return + } + testFormValues(t, r, values{"after": "2", "before": "1", "per_page": "50", "fields": "10,11", "q": "status:open"}) + fmt.Fprint(w, `[{"id":17,"node_id":"PVTI_node"}]`) }) - _, err := client.Projects.DeleteProjectColumn(context.Background(), 1) + opts := &ListProjectItemsOptions{ListProjectsOptions: ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: ("2"), Before: ("1"), PerPage: (50)}, Query: ("status:open")}, Fields: []int64{10, 11}} + ctx := t.Context() + items, _, err := client.Projects.ListOrganizationProjectItems(ctx, "o", 1, opts) if err != nil { - t.Errorf("Projects.DeleteProjectColumn returned error: %v", err) + t.Fatalf("Projects.ListOrganizationProjectItems returned error: %v", err) + } + if len(items) != 1 || items[0].GetID() != 17 { + t.Fatalf("Projects.ListOrganizationProjectItems returned %+v", items) + } + + const methodName = "ListOrganizationProjectItems" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListOrganizationProjectItems(ctx, "\n", 1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListOrganizationProjectItems(ctx, "o", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + ctxBypass := context.WithValue(ctx, BypassRateLimitCheck, true) + if _, _, err = client.Projects.ListOrganizationProjectItems(ctxBypass, "o", 1, &ListProjectItemsOptions{ListProjectsOptions: ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{Before: ("b"), After: ("a")}}}); err != nil { + t.Fatalf("unexpected error when both before/after set: %v", err) } } -func TestProjectsService_MoveProjectColumn(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_AddOrganizationProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &ProjectColumnMoveOptions{Position: "after:12345"} + input := &AddProjectItemOptions{Type: Ptr(ProjectV2ItemContentType("Issue")), ID: Ptr(int64(99))} - mux.HandleFunc("/projects/columns/1/moves", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/projectsV2/1/items", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":99,"node_id":"PVTI_new"}`) + }) + + ctx := t.Context() + item, _, err := client.Projects.AddOrganizationProjectItem(ctx, "o", 1, input) + if err != nil { + t.Fatalf("Projects.AddOrganizationProjectItem returned error: %v", err) + } + if item.GetID() != 99 { + t.Fatalf("unexpected item: %+v", item) + } +} - v := &ProjectColumnMoveOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) +func TestProjectsService_AddProjectItemForOrg_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/orgs/o/projectsV2/1/items", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"id":1}`) + }) + ctx := t.Context() + const methodName = "AddOrganizationProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.AddOrganizationProjectItem(ctx, "o", 1, &AddProjectItemOptions{Type: Ptr(ProjectV2ItemContentType("Issue")), ID: Ptr(int64(1))}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err }) +} - _, err := client.Projects.MoveProjectColumn(context.Background(), 1, input) +func TestProjectsService_GetOrganizationProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":17,"node_id":"PVTI_node"}`) + }) + ctx := t.Context() + opts := &GetProjectItemOptions{} + item, _, err := client.Projects.GetOrganizationProjectItem(ctx, "o", 1, 17, opts) if err != nil { - t.Errorf("Projects.MoveProjectColumn returned error: %v", err) + t.Fatalf("GetOrganizationProjectItem error: %v", err) + } + if item.GetID() != 17 { + t.Fatalf("unexpected item: %+v", item) } } -func TestProjectsService_ListProjectCards(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) { +func TestProjectsService_GetOrganizationProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - testFormValues(t, r, values{ - "archived_state": "all", - "page": "2"}) - fmt.Fprint(w, `[{"id":1}]`) + fmt.Fprint(w, `{"id":17}`) + }) + ctx := t.Context() + const methodName = "GetOrganizationProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetOrganizationProjectItem(ctx, "o", 1, 17, &GetProjectItemOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err }) +} - opt := &ProjectCardListOptions{ - ArchivedState: String("all"), - ListOptions: ListOptions{Page: 2}} - cards, _, err := client.Projects.ListProjectCards(context.Background(), 1, opt) +func TestProjectsService_GetOrganizationProjectItem_WithFieldsOption(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + // Verify that fields option is properly added as comma-separated URL parameter + testFormValues(t, r, values{"fields": "123,456,789"}) + fmt.Fprint(w, `{ + "id":17, + "node_id":"PVTI_node_fields", + "fields":[ + {"id":123,"name":"Status","data_type":"single_select"}, + {"id":456,"name":"Priority","data_type":"single_select"}, + {"id":789,"name":"Assignee","data_type":"text"} + ] + }`) + }) + ctx := t.Context() + opts := &GetProjectItemOptions{ + Fields: []int64{123, 456, 789}, // Request specific field IDs + } + item, _, err := client.Projects.GetOrganizationProjectItem(ctx, "o", 1, 17, opts) if err != nil { - t.Errorf("Projects.ListProjectCards returned error: %v", err) + t.Fatalf("GetOrganizationProjectItem error: %v", err) + } + if item.GetID() != 17 { + t.Fatalf("unexpected item: %+v", item) } + const methodName = "GetOrganizationProjectItemWithFields" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.GetOrganizationProjectItem(ctx, "\n", 1, 17, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetOrganizationProjectItem(ctx, "o", 1, 17, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - want := []*ProjectCard{{ID: Int64(1)}} - if !reflect.DeepEqual(cards, want) { - t.Errorf("Projects.ListProjectCards returned %+v, want %+v", cards, want) +func TestProjectsService_UpdateOrganizationProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + input := &UpdateProjectItemOptions{Archived: Ptr(true)} + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":17}`) + }) + ctx := t.Context() + item, _, err := client.Projects.UpdateOrganizationProjectItem(ctx, "o", 1, 17, input) + if err != nil { + t.Fatalf("UpdateOrganizationProjectItem error: %v", err) + } + if item.GetID() != 17 { + t.Fatalf("unexpected item: %+v", item) } } -func TestProjectsService_GetProjectCard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_UpdateOrganizationProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + input := &UpdateProjectItemOptions{Archived: Ptr(true)} + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":17}`) + }) + ctx := t.Context() + const methodName = "UpdateProjectItemForOrg" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.UpdateOrganizationProjectItem(ctx, "o", 1, 17, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - fmt.Fprint(w, `{"id":1}`) +func TestProjectsService_UpdateOrganizationProjectItem_WithFieldUpdates(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + input := &UpdateProjectItemOptions{ + Fields: []*UpdateProjectV2Field{ + {ID: 123, Value: "Updated text value"}, + {ID: 456, Value: "Done"}, + }, + } + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":17,"node_id":"PVTI_node_updated"}`) }) - card, _, err := client.Projects.GetProjectCard(context.Background(), 1) + ctx := t.Context() + item, _, err := client.Projects.UpdateOrganizationProjectItem(ctx, "o", 1, 17, input) if err != nil { - t.Errorf("Projects.GetProjectCard returned error: %v", err) + t.Fatalf("UpdateOrganizationProjectItem error: %v", err) + } + if item.GetID() != 17 { + t.Fatalf("unexpected item: %+v", item) } - want := &ProjectCard{ID: Int64(1)} - if !reflect.DeepEqual(card, want) { - t.Errorf("Projects.GetProjectCard returned %+v, want %+v", card, want) + const methodName = "UpdateOrganizationProjectItemWithFields" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.UpdateOrganizationProjectItem(ctx, "\n", 1, 17, input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.UpdateOrganizationProjectItem(ctx, "o", 1, 17, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestProjectsService_DeleteOrganizationProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + ctx := t.Context() + if _, err := client.Projects.DeleteOrganizationProjectItem(ctx, "o", 1, 17); err != nil { + t.Fatalf("DeleteOrganizationProjectItem error: %v", err) } } -func TestProjectsService_CreateProjectCard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_DeleteOrganizationProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/orgs/o/projectsV2/1/items/17", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + ctx := t.Context() + const methodName = "DeleteOrganizationProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Projects.DeleteOrganizationProjectItem(ctx, "o", 1, 17) + }) +} - input := &ProjectCardOptions{ - ContentID: 12345, - ContentType: "Issue", +func TestProjectsService_ListUserProjectItems(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "20", "q": "type:issue"}) + fmt.Fprint(w, `[{"id":7,"node_id":"PVTI_user"}]`) + }) + ctx := t.Context() + items, _, err := client.Projects.ListUserProjectItems(ctx, "u", 2, &ListProjectItemsOptions{ListProjectsOptions: ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{PerPage: (20)}, Query: ("type:issue")}}) + if err != nil { + t.Fatalf("ListUserProjectItems error: %v", err) + } + if len(items) != 1 || items[0].GetID() != 7 { + t.Fatalf("unexpected items: %+v", items) } +} - mux.HandleFunc("/projects/columns/1/cards", func(w http.ResponseWriter, r *http.Request) { +func TestProjectsService_ListUserProjectItems_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[]`) + }) + ctx := t.Context() + const methodName = "ListUserProjectItems" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListUserProjectItems(ctx, "u", 2, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListUserProjectItems(ctx, "\n", 2, nil) + return err + }) +} + +func TestProjectsService_AddUserProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + input := &AddProjectItemOptions{Type: Ptr(ProjectV2ItemContentType("PullRequest")), ID: Ptr(int64(123))} + mux.HandleFunc("/users/u/projectsV2/2/items", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":123,"node_id":"PVTI_new_user"}`) + }) + ctx := t.Context() + item, _, err := client.Projects.AddUserProjectItem(ctx, "u", 2, input) + if err != nil { + t.Fatalf("AddUserProjectItem error: %v", err) + } + if item.GetID() != 123 { + t.Fatalf("unexpected item: %+v", item) + } +} - v := &ProjectCardOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) +func TestProjectsService_AddUserProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":5}`) + }) + ctx := t.Context() + const methodName = "AddUserProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.AddUserProjectItem(ctx, "u", 2, &AddProjectItemOptions{Type: Ptr(ProjectV2ItemContentType("Issue")), ID: Ptr(int64(5))}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } - - fmt.Fprint(w, `{"id":1}`) + return resp, err }) +} - card, _, err := client.Projects.CreateProjectCard(context.Background(), 1, input) +func TestProjectsService_GetUserProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":55,"node_id":"PVTI_user_item"}`) + }) + ctx := t.Context() + opts := &GetProjectItemOptions{} + item, _, err := client.Projects.GetUserProjectItem(ctx, "u", 2, 55, opts) if err != nil { - t.Errorf("Projects.CreateProjectCard returned error: %v", err) + t.Fatalf("GetUserProjectItem error: %v", err) } - - want := &ProjectCard{ID: Int64(1)} - if !reflect.DeepEqual(card, want) { - t.Errorf("Projects.CreateProjectCard returned %+v, want %+v", card, want) + if item.GetID() != 55 { + t.Fatalf("unexpected item: %+v", item) } } -func TestProjectsService_UpdateProjectCard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_GetUserProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":55}`) + }) + ctx := t.Context() + const methodName = "GetUserProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetUserProjectItem(ctx, "u", 2, 55, &GetProjectItemOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - input := &ProjectCardOptions{ - ContentID: 12345, - ContentType: "Issue", +func TestProjectsService_GetUserProjectItem_WithFieldsOption(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + // Verify that fields option is properly added as comma-separated URL parameter + testFormValues(t, r, values{"fields": "100,200"}) + fmt.Fprint(w, `{ + "id":55, + "node_id":"PVTI_user_item_fields", + "fields":[ + {"id":100,"name":"Status","data_type":"single_select"}, + {"id":200,"name":"Milestone","data_type":"text"} + ] + }`) + }) + ctx := t.Context() + opts := &GetProjectItemOptions{ + Fields: []int64{100, 200}, // Request specific field IDs + } + item, _, err := client.Projects.GetUserProjectItem(ctx, "u", 2, 55, opts) + if err != nil { + t.Fatalf("GetUserProjectItem error: %v", err) } + if item.GetID() != 55 { + t.Fatalf("unexpected item: %+v", item) + } + + const methodName = "GetUserProjectItemWithFields" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.GetUserProjectItem(ctx, "\n", 2, 55, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.GetUserProjectItem(ctx, "u", 2, 55, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) { +func TestProjectsService_UpdateUserProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + input := &UpdateProjectItemOptions{Archived: Ptr(false)} + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":55}`) + }) + ctx := t.Context() + item, _, err := client.Projects.UpdateUserProjectItem(ctx, "u", 2, 55, input) + if err != nil { + t.Fatalf("UpdateUserProjectItem error: %v", err) + } + if item.GetID() != 55 { + t.Fatalf("unexpected item: %+v", item) + } +} - v := &ProjectCardOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) +func TestProjectsService_UpdateUserProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"id":55}`) + }) + ctx := t.Context() + const methodName = "UpdateUserProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.UpdateUserProjectItem(ctx, "u", 2, 55, &UpdateProjectItemOptions{Archived: Ptr(false)}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} - fmt.Fprint(w, `{"id":1, "archived":false}`) +func TestProjectsService_UpdateUserProjectItem_WithFieldUpdates(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + opts := &UpdateProjectItemOptions{ + Fields: []*UpdateProjectV2Field{ + {ID: 100, Value: "In Progress"}, + {ID: 200, Value: float64(5)}, // number field + }, + } + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, opts) + fmt.Fprint(w, `{"id":55,"node_id":"PVTI_user_updated"}`) }) - card, _, err := client.Projects.UpdateProjectCard(context.Background(), 1, input) + ctx := t.Context() + item, _, err := client.Projects.UpdateUserProjectItem(ctx, "u", 2, 55, opts) if err != nil { - t.Errorf("Projects.UpdateProjectCard returned error: %v", err) + t.Fatalf("UpdateUserProjectItem error: %v", err) } - - want := &ProjectCard{ID: Int64(1), Archived: Bool(false)} - if !reflect.DeepEqual(card, want) { - t.Errorf("Projects.UpdateProjectCard returned %+v, want %+v", card, want) + if item.GetID() != 55 { + t.Fatalf("unexpected item: %+v", item) } + + const methodName = "UpdateUserProjectItemWithFields" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.UpdateUserProjectItem(ctx, "\n", 2, 55, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.UpdateUserProjectItem(ctx, "u", 2, 55, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_DeleteProjectCard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_DeleteUserProjectItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + ctx := t.Context() + if _, err := client.Projects.DeleteUserProjectItem(ctx, "u", 2, 55); err != nil { + t.Fatalf("DeleteUserProjectItem error: %v", err) + } +} - mux.HandleFunc("/projects/columns/cards/1", func(w http.ResponseWriter, r *http.Request) { +func TestProjectsService_DeleteUserProjectItem_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/projectsV2/2/items/55", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + w.WriteHeader(http.StatusNoContent) }) + ctx := t.Context() + const methodName = "DeleteUserProjectItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Projects.DeleteUserProjectItem(ctx, "u", 2, 55) + }) +} - _, err := client.Projects.DeleteProjectCard(context.Background(), 1) - if err != nil { - t.Errorf("Projects.DeleteProjectCard returned error: %v", err) +func TestProjectV2Item_UnmarshalJSON_Issue(t *testing.T) { + t.Parallel() + + item := ProjectV2Item{ + ID: Ptr(int64(123)), + NodeID: Ptr("PVTI_test"), + ContentType: Ptr(ProjectV2ItemContentTypeIssue), + Content: &ProjectV2ItemContent{ + Issue: &Issue{ + ID: Ptr(int64(456)), + Number: Ptr(10), + Title: Ptr("Test Issue"), + State: Ptr("open"), + Body: Ptr("Issue body"), + Repository: &Repository{ + ID: Ptr(int64(789)), + Name: Ptr("test-repo"), + }, + }, + }, + CreatedAt: &referenceTimestamp, } + + want := `{ + "id": 123, + "node_id": "PVTI_test", + "content_type": "Issue", + "content": { + "id": 456, + "number": 10, + "title": "Test Issue", + "state": "open", + "body": "Issue body", + "repository": { + "id": 789, + "name": "test-repo" + } + }, + "created_at": ` + referenceTimeStr + ` + }` + + testJSONUnmarshalOnly(t, item, want) } -func TestProjectsService_MoveProjectCard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectV2Item_UnmarshalJSON_PullRequest(t *testing.T) { + t.Parallel() + + item := ProjectV2Item{ + ID: Ptr(int64(124)), + NodeID: Ptr("PVTI_pr"), + ContentType: Ptr(ProjectV2ItemContentTypePullRequest), + Content: &ProjectV2ItemContent{ + PullRequest: &PullRequest{ + ID: Ptr(int64(457)), + Number: Ptr(20), + Title: Ptr("Test PR"), + State: Ptr("closed"), + Merged: Ptr(true), + MergeCommitSHA: Ptr("abc123"), + Head: &PullRequestBranch{ + Ref: Ptr("feature-branch"), + SHA: Ptr("def456"), + }, + Base: &PullRequestBranch{ + Ref: Ptr("main"), + SHA: Ptr("ghi789"), + }, + }, + }, + CreatedAt: &referenceTimestamp, + } + + want := `{ + "id": 124, + "node_id": "PVTI_pr", + "content_type": "PullRequest", + "content": { + "id": 457, + "number": 20, + "title": "Test PR", + "state": "closed", + "merged": true, + "merge_commit_sha": "abc123", + "head": { + "ref": "feature-branch", + "sha": "def456" + }, + "base": { + "ref": "main", + "sha": "ghi789" + } + }, + "created_at": ` + referenceTimeStr + ` + }` - input := &ProjectCardMoveOptions{Position: "after:12345"} + testJSONUnmarshalOnly(t, item, want) +} - mux.HandleFunc("/projects/columns/cards/1/moves", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) +func TestProjectV2Item_UnmarshalJSON_DraftIssue(t *testing.T) { + t.Parallel() + + item := ProjectV2Item{ + ID: Ptr(int64(125)), + NodeID: Ptr("PVTI_draft"), + ContentType: Ptr(ProjectV2ItemContentTypeDraftIssue), + Content: &ProjectV2ItemContent{ + DraftIssue: &ProjectV2DraftIssue{ + ID: Ptr(int64(458)), + NodeID: Ptr("DI_test"), + Title: Ptr("Draft Issue Title"), + Body: Ptr("Draft issue body content"), + }, + }, + CreatedAt: &referenceTimestamp, + } - v := &ProjectCardMoveOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + want := `{ + "id": 125, + "node_id": "PVTI_draft", + "content_type": "DraftIssue", + "content": { + "id": 458, + "node_id": "DI_test", + "title": "Draft Issue Title", + "body": "Draft issue body content" + }, + "created_at": ` + referenceTimeStr + ` + }` + + testJSONUnmarshalOnly(t, item, want) +} + +func TestProjectV2Item_UnmarshalJSON_NullContent(t *testing.T) { + t.Parallel() + + item := ProjectV2Item{ + ID: Ptr(int64(126)), + NodeID: Ptr("PVTI_null"), + ContentType: Ptr(ProjectV2ItemContentTypeIssue), + Content: nil, // Content is null + } + + want := `{ + "id": 126, + "node_id": "PVTI_null", + "content_type": "Issue", + "content": null + }` + + testJSONUnmarshalOnly(t, item, want) +} + +func TestProjectV2Item_UnmarshalJSON_MissingContentType(t *testing.T) { + t.Parallel() + + want := `{ + "id": 127, + "node_id": "PVTI_no_type", + "content": { + "id": 459, + "title": "Some content" } - }) + }` - _, err := client.Projects.MoveProjectCard(context.Background(), 1, input) - if err != nil { - t.Errorf("Projects.MoveProjectCard returned error: %v", err) + item := ProjectV2Item{ + ID: Ptr(int64(127)), + NodeID: Ptr("PVTI_no_type"), + Content: nil, + } + + testJSONUnmarshalOnly(t, item, want) +} + +func TestProjectV2Item_UnmarshalJSON_EmptyJSON(t *testing.T) { + t.Parallel() + + item := ProjectV2Item{} + want := "null" + + testJSONUnmarshalOnly(t, item, want) +} + +func TestProjectV2Item_UnmarshalJSON_InvalidJSON(t *testing.T) { + t.Parallel() + + var item ProjectV2Item + if err := json.Unmarshal([]byte("~~~"), &item); err == nil { + t.Error("expected error for invalid JSON, got nil") + } +} + +func TestProjectV2Item_Marshal_Issue(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &ProjectV2Item{}, "{}") + + item := &ProjectV2Item{ + ContentType: Ptr(ProjectV2ItemContentTypeIssue), + Content: &ProjectV2ItemContent{ + Issue: &Issue{ + Number: Ptr(42), + Title: Ptr("Bug report"), + State: Ptr("open"), + }, + }, + ID: Ptr(int64(123)), + } + + want := `{ + "content_type":"Issue", + "content":{ + "number":42, + "state":"open", + "title":"Bug report" + }, + "id":123 + }` + + testJSONMarshal(t, item, want) +} + +func TestProjectV2Item_Marshal_PullRequest(t *testing.T) { + t.Parallel() + testJSONMarshal(t, ProjectV2Item{}, "{}") + + item := ProjectV2Item{ + ContentType: Ptr(ProjectV2ItemContentTypePullRequest), + Content: &ProjectV2ItemContent{ + PullRequest: &PullRequest{ + Number: Ptr(99), + Title: Ptr("Feature addition"), + State: Ptr("closed"), + }, + }, + ID: Ptr(int64(456)), } + + want := `{ + "content_type":"PullRequest", + "content":{ + "number":99, + "state":"closed", + "title":"Feature addition" + }, + "id":456 + }` + + testJSONMarshal(t, item, want) } -func TestProjectsService_AddProjectCollaborator(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectV2Item_Marshal_DraftIssue(t *testing.T) { + t.Parallel() + testJSONMarshal(t, &ProjectV2Item{}, "{}") + + item := &ProjectV2Item{ + ContentType: Ptr(ProjectV2ItemContentTypeDraftIssue), + Content: &ProjectV2ItemContent{ + DraftIssue: &ProjectV2DraftIssue{ + Title: Ptr("Draft task"), + Body: Ptr("Work in progress"), + }, + }, + ID: Ptr(int64(789)), + } + + want := `{ + "content_type":"DraftIssue", + "content":{ + "body":"Work in progress", + "title":"Draft task" + }, + "id":789 + }` + + testJSONMarshal(t, item, want) +} - opt := &ProjectCollaboratorOptions{ - Permission: String("admin"), +func TestProjectV2Item_Marshal_MissingContent(t *testing.T) { + t.Parallel() + + item := &ProjectV2Item{ + ContentType: Ptr(ProjectV2ItemContentTypeIssue), + Content: &ProjectV2ItemContent{}, + ID: Ptr(int64(789)), } - mux.HandleFunc("/projects/1/collaborators/u", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) + want := `{ + "content_type":"Issue", + "content":null, + "id":789 + }` + + testJSONMarshalOnly(t, item, want) +} + +func TestProjectV2ItemContent_Marshal(t *testing.T) { + t.Parallel() + + t.Run("issue", func(t *testing.T) { + t.Parallel() - v := &ProjectCollaboratorOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, opt) { - t.Errorf("Request body = %+v, want %+v", v, opt) + content := ProjectV2ItemContent{ + Issue: &Issue{ + Number: Ptr(42), + Title: Ptr("Bug report"), + State: Ptr("open"), + }, } - w.WriteHeader(http.StatusNoContent) + want := `{ + "number":42, + "state":"open", + "title":"Bug report" + }` + + testJSONMarshalOnly(t, content, want) + testJSONMarshalOnly(t, &content, want) + }) + + t.Run("pull request", func(t *testing.T) { + t.Parallel() + + content := ProjectV2ItemContent{ + PullRequest: &PullRequest{ + Number: Ptr(99), + Title: Ptr("Feature addition"), + State: Ptr("closed"), + }, + } + + want := `{ + "number":99, + "state":"closed", + "title":"Feature addition" + }` + + testJSONMarshalOnly(t, content, want) + testJSONMarshalOnly(t, &content, want) + }) + + t.Run("draft issue", func(t *testing.T) { + t.Parallel() + + content := ProjectV2ItemContent{ + DraftIssue: &ProjectV2DraftIssue{ + Title: Ptr("Draft task"), + Body: Ptr("Work in progress"), + }, + } + + want := `{ + "body":"Work in progress", + "title":"Draft task" + }` + + testJSONMarshalOnly(t, content, want) + testJSONMarshalOnly(t, &content, want) + }) +} + +func TestProjectsService_CreateOrganizationProjectDraftItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateProjectV2DraftItemRequest{Title: "My draft", Body: Ptr("Draft body")} + + mux.HandleFunc("/orgs/o/projectsV2/1/drafts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":42,"node_id":"PVTI_draft","content_type":"DraftIssue"}`) }) - _, err := client.Projects.AddProjectCollaborator(context.Background(), 1, "u", opt) + ctx := t.Context() + item, _, err := client.Projects.CreateOrganizationProjectDraftItem(ctx, "o", 1, input) if err != nil { - t.Errorf("Projects.AddProjectCollaborator returned error: %v", err) + t.Fatalf("Projects.CreateOrganizationProjectDraftItem returned error: %v", err) } + want := &ProjectV2Item{ID: Ptr(int64(42)), NodeID: Ptr("PVTI_draft"), ContentType: Ptr(ProjectV2ItemContentTypeDraftIssue)} + if diff := cmp.Diff(want, item); diff != "" { + t.Errorf("Projects.CreateOrganizationProjectDraftItem mismatch (-want +got):\n%v", diff) + } + + const methodName = "CreateOrganizationProjectDraftItem" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.CreateOrganizationProjectDraftItem(ctx, "\n", 1, input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.CreateOrganizationProjectDraftItem(ctx, "o", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_AddCollaborator_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestProjectsService_CreateUserProjectDraftItem(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateProjectV2DraftItemRequest{Title: "My draft"} + + mux.HandleFunc("/user/12345/projectsV2/1/drafts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":43,"node_id":"PVTI_draft_u","content_type":"DraftIssue"}`) + }) - _, err := client.Projects.AddProjectCollaborator(context.Background(), 1, "%", nil) - testURLParseError(t, err) + ctx := t.Context() + item, _, err := client.Projects.CreateUserProjectDraftItem(ctx, 12345, 1, input) + if err != nil { + t.Fatalf("Projects.CreateUserProjectDraftItem returned error: %v", err) + } + want := &ProjectV2Item{ID: Ptr(int64(43)), NodeID: Ptr("PVTI_draft_u"), ContentType: Ptr(ProjectV2ItemContentTypeDraftIssue)} + if diff := cmp.Diff(want, item); diff != "" { + t.Errorf("Projects.CreateUserProjectDraftItem mismatch (-want +got):\n%v", diff) + } + + const methodName = "CreateUserProjectDraftItem" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.CreateUserProjectDraftItem(ctx, 12345, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_RemoveCollaborator(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_AddOrganizationProjectField(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/1/collaborators/u", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - w.WriteHeader(http.StatusNoContent) + input := AddProjectV2FieldRequest{ + Name: Ptr("Priority"), + DataType: Ptr("single_select"), + SingleSelectOptions: []*ProjectV2FieldSingleSelectOption{ + {Name: "High", Color: Ptr("RED"), Description: Ptr("Urgent")}, + {Name: "Low", Color: Ptr("GRAY")}, + }, + } + + mux.HandleFunc("/orgs/o/projectsV2/1/fields", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":7,"name":"Priority","data_type":"single_select"}`) }) - _, err := client.Projects.RemoveProjectCollaborator(context.Background(), 1, "u") + ctx := t.Context() + field, _, err := client.Projects.AddOrganizationProjectField(ctx, "o", 1, input) if err != nil { - t.Errorf("Projects.RemoveProjectCollaborator returned error: %v", err) + t.Fatalf("Projects.AddOrganizationProjectField returned error: %v", err) } + want := &ProjectV2Field{ID: Ptr(int64(7)), Name: Ptr("Priority"), DataType: Ptr("single_select")} + if diff := cmp.Diff(want, field); diff != "" { + t.Errorf("Projects.AddOrganizationProjectField mismatch (-want +got):\n%v", diff) + } + + const methodName = "AddOrganizationProjectField" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.AddOrganizationProjectField(ctx, "\n", 1, input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.AddOrganizationProjectField(ctx, "o", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_RemoveCollaborator_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestProjectsService_AddUserProjectField(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := AddProjectV2FieldRequest{ + Name: Ptr("Sprint"), + DataType: Ptr("iteration"), + IterationConfiguration: &ProjectV2FieldIterationConfiguration{ + StartDate: Ptr("2026-01-01"), + Duration: Ptr(14), + Iterations: []*ProjectV2FieldIterationConfigurationIteration{ + {Title: Ptr("Sprint 1"), StartDate: Ptr("2026-01-01"), Duration: Ptr(14)}, + }, + }, + } - _, err := client.Projects.RemoveProjectCollaborator(context.Background(), 1, "%") - testURLParseError(t, err) -} + mux.HandleFunc("/users/u/projectsV2/1/fields", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":8,"name":"Sprint","data_type":"iteration"}`) + }) -func TestProjectsService_ListCollaborators(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + ctx := t.Context() + field, _, err := client.Projects.AddUserProjectField(ctx, "u", 1, input) + if err != nil { + t.Fatalf("Projects.AddUserProjectField returned error: %v", err) + } + want := &ProjectV2Field{ID: Ptr(int64(8)), Name: Ptr("Sprint"), DataType: Ptr("iteration")} + if diff := cmp.Diff(want, field); diff != "" { + t.Errorf("Projects.AddUserProjectField mismatch (-want +got):\n%v", diff) + } - mux.HandleFunc("/projects/1/collaborators", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - testFormValues(t, r, values{"page": "2"}) - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + const methodName = "AddUserProjectField" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.AddUserProjectField(ctx, "\n", 1, input) + return err }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.AddUserProjectField(ctx, "u", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - opt := &ListCollaboratorOptions{ - ListOptions: ListOptions{Page: 2}, +func TestProjectsService_CreateOrganizationProjectView(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := CreateProjectV2ViewRequest{ + Name: "My board", + Layout: "board", + Filter: Ptr("is:open"), + VisibleFields: []int64{1, 2, 3}, } - users, _, err := client.Projects.ListProjectCollaborators(context.Background(), 1, opt) + + mux.HandleFunc("/orgs/o/projectsV2/1/views", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + // The first field_id is larger than 2^53 to ensure it is decoded as an + // int64 without the precision loss that a float64 would introduce. + fmt.Fprint(w, `{"id":5,"number":2,"name":"My board","layout":"board","sort_by":[[9007199254740993,"asc"],[456,"desc"]]}`) + }) + + ctx := t.Context() + view, _, err := client.Projects.CreateOrganizationProjectView(ctx, "o", 1, input) if err != nil { - t.Errorf("Projects.ListProjectCollaborators returned error: %v", err) + t.Fatalf("Projects.CreateOrganizationProjectView returned error: %v", err) + } + want := &ProjectV2View{ + ID: 5, + Number: 2, + Name: "My board", + Layout: "board", + SortBy: []*ProjectV2ViewSortBy{ + {FieldID: Ptr(int64(9007199254740993)), Direction: Ptr("asc")}, + {FieldID: Ptr(int64(456)), Direction: Ptr("desc")}, + }, } + if diff := cmp.Diff(want, view); diff != "" { + t.Errorf("Projects.CreateOrganizationProjectView mismatch (-want +got):\n%v", diff) + } + + const methodName = "CreateOrganizationProjectView" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.CreateOrganizationProjectView(ctx, "\n", 1, input) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.CreateOrganizationProjectView(ctx, "o", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestProjectsService_CreateUserProjectView(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(users, want) { - t.Errorf("Projects.ListProjectCollaborators returned %+v, want %+v", users, want) + input := CreateProjectV2ViewRequest{Name: "My table", Layout: "table"} + + mux.HandleFunc("/users/12345/projectsV2/1/views", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":6,"number":3,"name":"My table","layout":"table"}`) + }) + + ctx := t.Context() + view, _, err := client.Projects.CreateUserProjectView(ctx, 12345, 1, input) + if err != nil { + t.Fatalf("Projects.CreateUserProjectView returned error: %v", err) } + want := &ProjectV2View{ID: 6, Number: 3, Name: "My table", Layout: "table"} + if diff := cmp.Diff(want, view); diff != "" { + t.Errorf("Projects.CreateUserProjectView mismatch (-want +got):\n%v", diff) + } + + const methodName = "CreateUserProjectView" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.CreateUserProjectView(ctx, 12345, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_ListCollaborators_withAffiliation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListOrganizationProjectViewItems(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/1/collaborators", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/projectsV2/1/views/2/items", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - testFormValues(t, r, values{"affiliation": "all", "page": "2"}) - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + testFormValues(t, r, values{"after": "2", "before": "1", "per_page": "50", "fields": "10,11", "q": "status:open"}) + fmt.Fprint(w, `[{"id":21,"node_id":"PVTI_view_item"}]`) }) - opt := &ListCollaboratorOptions{ - ListOptions: ListOptions{Page: 2}, - Affiliation: String("all"), - } - users, _, err := client.Projects.ListProjectCollaborators(context.Background(), 1, opt) + opts := &ListProjectItemsOptions{ListProjectsOptions: ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{After: "2", Before: "1", PerPage: 50}, Query: "status:open"}, Fields: []int64{10, 11}} + ctx := t.Context() + items, _, err := client.Projects.ListOrganizationProjectViewItems(ctx, "o", 1, 2, opts) if err != nil { - t.Errorf("Projects.ListProjectCollaborators returned error: %v", err) + t.Fatalf("Projects.ListOrganizationProjectViewItems returned error: %v", err) } - - want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(users, want) { - t.Errorf("Projects.ListProjectCollaborators returned %+v, want %+v", users, want) + want := []*ProjectV2Item{{ID: Ptr(int64(21)), NodeID: Ptr("PVTI_view_item")}} + if diff := cmp.Diff(want, items); diff != "" { + t.Errorf("Projects.ListOrganizationProjectViewItems mismatch (-want +got):\n%v", diff) } + + const methodName = "ListOrganizationProjectViewItems" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListOrganizationProjectViewItems(ctx, "\n", 1, 2, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListOrganizationProjectViewItems(ctx, "o", 1, 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestProjectsService_GetPermissionLevel(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestProjectsService_ListUserProjectViewItems(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/projects/1/collaborators/u/permission", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/users/u/projectsV2/1/views/2/items", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - fmt.Fprintf(w, `{"permission":"admin","user":{"login":"u"}}`) + testFormValues(t, r, values{"per_page": "30"}) + fmt.Fprint(w, `[{"id":22,"node_id":"PVTI_view_item_u"}]`) }) - ppl, _, err := client.Projects.ReviewProjectCollaboratorPermission(context.Background(), 1, "u") + opts := &ListProjectItemsOptions{ListProjectsOptions: ListProjectsOptions{ListProjectsPaginationOptions: ListProjectsPaginationOptions{PerPage: 30}}} + ctx := t.Context() + items, _, err := client.Projects.ListUserProjectViewItems(ctx, "u", 1, 2, opts) if err != nil { - t.Errorf("Projects.ReviewProjectCollaboratorPermission returned error: %v", err) + t.Fatalf("Projects.ListUserProjectViewItems returned error: %v", err) } - - want := &ProjectPermissionLevel{ - Permission: String("admin"), - User: &User{ - Login: String("u"), - }, + want := []*ProjectV2Item{{ID: Ptr(int64(22)), NodeID: Ptr("PVTI_view_item_u")}} + if diff := cmp.Diff(want, items); diff != "" { + t.Errorf("Projects.ListUserProjectViewItems mismatch (-want +got):\n%v", diff) } - if !reflect.DeepEqual(ppl, want) { - t.Errorf("Projects.ReviewProjectCollaboratorPermission returned %+v, want %+v", ppl, want) - } + const methodName = "ListUserProjectViewItems" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Projects.ListUserProjectViewItems(ctx, "\n", 1, 2, opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Projects.ListUserProjectViewItems(ctx, "u", 1, 2, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestProjectV2ViewSortBy_UnmarshalJSON(t *testing.T) { + t.Parallel() + + t.Run("numeric and string field_id", func(t *testing.T) { + t.Parallel() + // The second element uses a string field_id, which the OpenAPI schema + // permits. The first exceeds 2^53 to confirm int64 (not float64) decoding. + want := []*ProjectV2ViewSortBy{ + {FieldID: Ptr(int64(9007199254740993)), Direction: Ptr("asc")}, + {FieldID: Ptr(int64(456)), Direction: Ptr("desc")}, + } + testJSONUnmarshalOnly(t, want, `[[9007199254740993,"asc"],["456","desc"]]`) + }) + + t.Run("wrong tuple length is an error", func(t *testing.T) { + t.Parallel() + var got ProjectV2ViewSortBy + if err := json.Unmarshal([]byte(`[1]`), &got); err == nil { + t.Error("Unmarshal of a 1-element tuple = nil error, want error") + } + }) + + t.Run("non-array is an error", func(t *testing.T) { + t.Parallel() + var got ProjectV2ViewSortBy + if err := json.Unmarshal([]byte(`{"a":1}`), &got); err == nil { + t.Error("Unmarshal of a non-array = nil error, want error") + } + }) + + t.Run("field_id of an unsupported type is an error", func(t *testing.T) { + t.Parallel() + var got ProjectV2ViewSortBy + if err := json.Unmarshal([]byte(`[true,"asc"]`), &got); err == nil { + t.Error("Unmarshal of a boolean field_id = nil error, want error") + } + }) + + t.Run("non-numeric string field_id is an error", func(t *testing.T) { + t.Parallel() + var got ProjectV2ViewSortBy + if err := json.Unmarshal([]byte(`["abc","asc"]`), &got); err == nil { + t.Error(`Unmarshal of field_id "abc" = nil error, want error`) + } + }) + + t.Run("non-string direction is an error", func(t *testing.T) { + t.Parallel() + var got ProjectV2ViewSortBy + if err := json.Unmarshal([]byte(`[123,456]`), &got); err == nil { + t.Error("Unmarshal of a numeric direction = nil error, want error") + } + }) + + t.Run("round trips through MarshalJSON", func(t *testing.T) { + t.Parallel() + in := &ProjectV2ViewSortBy{FieldID: Ptr(int64(9007199254740993)), Direction: Ptr("asc")} + testJSONMarshal(t, in, `[9007199254740993,"asc"]`) + }) } diff --git a/github/pulls.go b/github/pulls.go index e022692d42f..81bc9f89535 100644 --- a/github/pulls.go +++ b/github/pulls.go @@ -8,69 +8,86 @@ package github import ( "bytes" "context" + "errors" "fmt" - "strings" - "time" ) // PullRequestsService handles communication with the pull request related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/pulls/ +// GitHub API docs: https://docs.github.com/rest/pulls?apiVersion=2022-11-28 type PullRequestsService service +// PullRequestAutoMerge represents the "auto_merge" response for a PullRequest. +type PullRequestAutoMerge struct { + EnabledBy *User `json:"enabled_by,omitempty"` + MergeMethod *string `json:"merge_method,omitempty"` + CommitTitle *string `json:"commit_title,omitempty"` + CommitMessage *string `json:"commit_message,omitempty"` +} + // PullRequest represents a GitHub pull request on a repository. type PullRequest struct { - ID *int64 `json:"id,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - Locked *bool `json:"locked,omitempty"` - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - MergedAt *time.Time `json:"merged_at,omitempty"` - Labels []*Label `json:"labels,omitempty"` - User *User `json:"user,omitempty"` - Draft *bool `json:"draft,omitempty"` - Merged *bool `json:"merged,omitempty"` - Mergeable *bool `json:"mergeable,omitempty"` - MergeableState *string `json:"mergeable_state,omitempty"` - MergedBy *User `json:"merged_by,omitempty"` - MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` - Rebaseable *bool `json:"rebaseable,omitempty"` - Comments *int `json:"comments,omitempty"` - Commits *int `json:"commits,omitempty"` - Additions *int `json:"additions,omitempty"` - Deletions *int `json:"deletions,omitempty"` - ChangedFiles *int `json:"changed_files,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - IssueURL *string `json:"issue_url,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - DiffURL *string `json:"diff_url,omitempty"` - PatchURL *string `json:"patch_url,omitempty"` - CommitsURL *string `json:"commits_url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - ReviewCommentsURL *string `json:"review_comments_url,omitempty"` - ReviewCommentURL *string `json:"review_comment_url,omitempty"` - ReviewComments *int `json:"review_comments,omitempty"` - Assignee *User `json:"assignee,omitempty"` - Assignees []*User `json:"assignees,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` - AuthorAssociation *string `json:"author_association,omitempty"` - NodeID *string `json:"node_id,omitempty"` - RequestedReviewers []*User `json:"requested_reviewers,omitempty"` + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + Locked *bool `json:"locked,omitempty"` + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + MergedAt *Timestamp `json:"merged_at,omitempty"` + Labels []*Label `json:"labels,omitempty"` + User *User `json:"user,omitempty"` + Draft *bool `json:"draft,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + IssueURL *string `json:"issue_url,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + DiffURL *string `json:"diff_url,omitempty"` + PatchURL *string `json:"patch_url,omitempty"` + CommitsURL *string `json:"commits_url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + ReviewCommentsURL *string `json:"review_comments_url,omitempty"` + ReviewCommentURL *string `json:"review_comment_url,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Assignees []*User `json:"assignees,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + // AuthorAssociation is the pull request author's relationship to the repository. + // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Pull Requests REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request + AuthorAssociation *string `json:"author_association,omitempty"` + NodeID *string `json:"node_id,omitempty"` + RequestedReviewers []*User `json:"requested_reviewers,omitempty"` + AutoMerge *PullRequestAutoMerge `json:"auto_merge,omitempty"` + + // These fields are not populated by the List operation. + Merged *bool `json:"merged,omitempty"` + Mergeable *bool `json:"mergeable,omitempty"` + MergeableState *string `json:"mergeable_state,omitempty"` + Rebaseable *bool `json:"rebaseable,omitempty"` + MergedBy *User `json:"merged_by,omitempty"` + MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` + Comments *int `json:"comments,omitempty"` + Commits *int `json:"commits,omitempty"` + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + ChangedFiles *int `json:"changed_files,omitempty"` + MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` + ReviewComments *int `json:"review_comments,omitempty"` // RequestedTeams is populated as part of the PullRequestEvent. - // See, https://developer.github.com/v3/activity/events/types/#pullrequestevent for an example. + // See, https://docs.github.com/developers/webhooks-and-events/github-event-types#pullrequestevent for an example. RequestedTeams []*Team `json:"requested_teams,omitempty"` Links *PRLinks `json:"_links,omitempty"` Head *PullRequestBranch `json:"head,omitempty"` Base *PullRequestBranch `json:"base,omitempty"` + Stack *PullRequestStack `json:"stack,omitempty"` // ActiveLockReason is populated only when LockReason is provided while locking the pull request. // Possible values are: "off-topic", "too heated", "resolved", and "spam". @@ -81,12 +98,12 @@ func (p PullRequest) String() string { return Stringify(p) } -// PRLink represents a single link object from Github pull request _links. +// PRLink represents a single link object from GitHub pull request _links. type PRLink struct { HRef *string `json:"href,omitempty"` } -// PRLinks represents the "_links" object in a Github pull request. +// PRLinks represents the "_links" object in a GitHub pull request. type PRLinks struct { Self *PRLink `json:"self,omitempty"` HTML *PRLink `json:"html,omitempty"` @@ -107,6 +124,31 @@ type PullRequestBranch struct { User *User `json:"user,omitempty"` } +// PullRequestStack represents the stack a pull request belongs to, in +// repositories that use stacked pull requests. Base reports the branch the +// entire stack ultimately targets, which can differ from the pull request's own +// Base branch (the branch below it in the stack). +type PullRequestStack struct { + // Base is the base of the stack: the branch the entire stack ultimately targets. + Base *PullRequestStackBase `json:"base"` + // Size is the total number of pull requests in the stack. + Size *int `json:"size,omitempty"` + // Position is the one-based position of this pull request within the stack, + // where 1 is the bottom of the stack. + Position *int `json:"position,omitempty"` + // ID is the ID of the stack that this pull request belongs to. + ID *int64 `json:"id,omitempty"` + // Number is the number of the stack that this pull request belongs to. + Number *int `json:"number,omitempty"` +} + +// PullRequestStackBase represents the base of a stacked pull request's stack: +// the branch the entire stack ultimately targets. +type PullRequestStackBase struct { + Ref string `json:"ref"` + SHA string `json:"sha"` +} + // PullRequestListOptions specifies the optional parameters to the // PullRequestsService.List method. type PullRequestListOptions struct { @@ -126,7 +168,7 @@ type PullRequestListOptions struct { Sort string `url:"sort,omitempty"` // Direction in which to sort pull requests. Possible values are: asc, desc. - // If Sort is "created" or not specified, Default is "desc", otherwise Default + // If Sort is "created" or not specified, Default is "desc"; otherwise, Default // is "asc" Direction string `url:"direction,omitempty"` @@ -135,25 +177,23 @@ type PullRequestListOptions struct { // List the pull requests for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests -func (s *PullRequestsService) List(ctx context.Context, owner string, repo string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests +// +//meta:operation GET /repos/{owner}/{repo}/pulls +func (s *PullRequestsService) List(ctx context.Context, owner, repo string, opts *PullRequestListOptions) ([]*PullRequest, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - var pulls []*PullRequest - resp, err := s.client.Do(ctx, req, &pulls) + resp, err := s.client.Do(req, &pulls) if err != nil { return nil, resp, err } @@ -161,28 +201,31 @@ func (s *PullRequestsService) List(ctx context.Context, owner string, repo strin return pulls, resp, nil } -// ListPullRequestsWithCommit returns pull requests associated with a commit SHA. +// ListPullRequestsWithCommit returns pull requests associated with a commit SHA +// or branch name. // -// The results will include open and closed pull requests. +// The results may include open and closed pull requests. If the commit SHA is +// not present in the repository's default branch, the result will only include +// open pull requests. // -// GitHub API docs: https://developer.github.com/v3/repos/commits/#list-pull-requests-associated-with-commit -func (s *PullRequestsService) ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#list-pull-requests-associated-with-a-commit +// +//meta:operation GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls +func (s *PullRequestsService) ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opts *ListOptions) ([]*PullRequest, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits/%v/pulls", owner, repo, sha) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeListPullsOrBranchesForCommitPreview, mediaTypeDraftPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeListPullsOrBranchesForCommitPreview) var pulls []*PullRequest - resp, err := s.client.Do(ctx, req, &pulls) + resp, err := s.client.Do(req, &pulls) if err != nil { return nil, resp, err } @@ -192,20 +235,18 @@ func (s *PullRequestsService) ListPullRequestsWithCommit(ctx context.Context, ow // Get a single pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request -func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string, number int) (*PullRequest, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request +// +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number} +func (s *PullRequestsService) Get(ctx context.Context, owner, repo string, number int) (*PullRequest, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - pull := new(PullRequest) - resp, err := s.client.Do(ctx, req, pull) + var pull *PullRequest + resp, err := s.client.Do(req, &pull) if err != nil { return nil, resp, err } @@ -214,24 +255,28 @@ func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string } // GetRaw gets a single pull request in raw (diff or patch) format. -func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opt RawOptions) (string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) +// +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request +// +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number} +func (s *PullRequestsService) GetRaw(ctx context.Context, owner, repo string, number int, opts RawOptions) (string, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v", owner, repo, number) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return "", nil, err } - switch opt.Type { + switch opts.Type { case Diff: req.Header.Set("Accept", mediaTypeV3Diff) case Patch: req.Header.Set("Accept", mediaTypeV3Patch) default: - return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) + return "", nil, fmt.Errorf("unsupported raw type %v", opts.Type) } var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) + resp, err := s.client.Do(req, &buf) if err != nil { return "", resp, err } @@ -239,11 +284,19 @@ func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo str return buf.String(), resp, nil } -// NewPullRequest represents a new pull request to be created. -type NewPullRequest struct { - Title *string `json:"title,omitempty"` - Head *string `json:"head,omitempty"` - Base *string `json:"base,omitempty"` +// CreatePullRequest represents a request to create a pull request. +type CreatePullRequest struct { + Title *string `json:"title,omitempty"` + // The name of the branch where your changes are implemented. For + // cross-repository pull requests in the same network, namespace head with + // a user like this: username:branch. + Head string `json:"head"` + HeadRepo *string `json:"head_repo,omitempty"` + // The name of the branch you want the changes pulled into. This should be + // an existing branch on the current repository. You cannot submit a pull + // request to one repository that requests a merge to a base of another + // repository. + Base string `json:"base"` Body *string `json:"body,omitempty"` Issue *int `json:"issue,omitempty"` MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` @@ -252,20 +305,18 @@ type NewPullRequest struct { // Create a new pull request on the specified repository. // -// GitHub API docs: https://developer.github.com/v3/pulls/#create-a-pull-request -func (s *PullRequestsService) Create(ctx context.Context, owner string, repo string, pull *NewPullRequest) (*PullRequest, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#create-a-pull-request +// +//meta:operation POST /repos/{owner}/{repo}/pulls +func (s *PullRequestsService) Create(ctx context.Context, owner, repo string, body CreatePullRequest) (*PullRequest, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) - req, err := s.client.NewRequest("POST", u, pull) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeDraftPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - p := new(PullRequest) - resp, err := s.client.Do(ctx, req, p) + var p *PullRequest + resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } @@ -273,9 +324,9 @@ func (s *PullRequestsService) Create(ctx context.Context, owner string, repo str return p, resp, nil } -// PullReqestBranchUpdateOptions specifies the optional parameters to the +// PullRequestBranchUpdateOptions specifies the optional parameters to the // PullRequestsService.UpdateBranch method. -type PullReqestBranchUpdateOptions struct { +type PullRequestBranchUpdateOptions struct { // ExpectedHeadSHA specifies the most recent commit on the pull request's branch. // Default value is the SHA of the pull request's current HEAD ref. ExpectedHeadSHA *string `json:"expected_head_sha,omitempty"` @@ -295,20 +346,21 @@ type PullRequestBranchUpdateResponse struct { // A follow up request, after a delay of a second or so, should result // in a successful request. // -// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request-branch -func (s *PullRequestsService) UpdateBranch(ctx context.Context, owner, repo string, number int, opts *PullReqestBranchUpdateOptions) (*PullRequestBranchUpdateResponse, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/update-branch", owner, repo, number) +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#update-a-pull-request-branch +// +//meta:operation PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch +func (s *PullRequestsService) UpdateBranch(ctx context.Context, owner, repo string, number int, body *PullRequestBranchUpdateOptions) (*PullRequestBranchUpdateResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/update-branch", owner, repo, number) - req, err := s.client.NewRequest("PUT", u, opts) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeUpdatePullRequestBranchPreview) - p := new(PullRequestBranchUpdateResponse) - resp, err := s.client.Do(ctx, req, p) + var p *PullRequestBranchUpdateResponse + resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } @@ -330,13 +382,15 @@ type pullRequestUpdate struct { // The following fields are editable: Title, Body, State, Base.Ref and MaintainerCanModify. // Base.Ref updates the base branch of the pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request -func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#update-a-pull-request +// +//meta:operation PATCH /repos/{owner}/{repo}/pulls/{pull_number} +func (s *PullRequestsService) Edit(ctx context.Context, owner, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) { if pull == nil { - return nil, nil, fmt.Errorf("pull must be provided") + return nil, nil, errors.New("pull must be provided") } - u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) + u := fmt.Sprintf("repos/%v/%v/pulls/%v", owner, repo, number) update := &pullRequestUpdate{ Title: pull.Title, @@ -344,21 +398,20 @@ func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo strin State: pull.State, MaintainerCanModify: pull.MaintainerCanModify, } - if pull.Base != nil { + // avoid updating the base branch when closing the Pull Request + // - otherwise the GitHub API server returns a "Validation Failed" error: + // "Cannot change base branch of closed pull request". + if pull.Base != nil && pull.GetState() != "closed" { update.Base = pull.Base.Ref } - req, err := s.client.NewRequest("PATCH", u, update) + req, err := s.client.NewRequest(ctx, "PATCH", u, update) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - p := new(PullRequest) - resp, err := s.client.Do(ctx, req, p) + var p *PullRequest + resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } @@ -368,21 +421,23 @@ func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo strin // ListCommits lists the commits in a pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request -func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*RepositoryCommit, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#list-commits-on-a-pull-request +// +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/commits +func (s *PullRequestsService) ListCommits(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*RepositoryCommit, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/commits", owner, repo, number) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var commits []*RepositoryCommit - resp, err := s.client.Do(ctx, req, &commits) + resp, err := s.client.Do(req, &commits) if err != nil { return nil, resp, err } @@ -392,21 +447,23 @@ func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, rep // ListFiles lists the files in a pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests-files -func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*CommitFile, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files +// +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/files +func (s *PullRequestsService) ListFiles(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*CommitFile, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/files", owner, repo, number) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var commitFiles []*CommitFile - resp, err := s.client.Do(ctx, req, &commitFiles) + resp, err := s.client.Do(req, &commitFiles) if err != nil { return nil, resp, err } @@ -416,15 +473,17 @@ func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo // IsMerged checks if a pull request has been merged. // -// GitHub API docs: https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged -func (s *PullRequestsService) IsMerged(ctx context.Context, owner string, repo string, number int) (bool, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#check-if-a-pull-request-has-been-merged +// +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/merge +func (s *PullRequestsService) IsMerged(ctx context.Context, owner, repo string, number int) (bool, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/merge", owner, repo, number) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) merged, err := parseBoolResponse(err) return merged, resp, err } @@ -438,40 +497,51 @@ type PullRequestMergeResult struct { // PullRequestOptions lets you define how a pull request will be merged. type PullRequestOptions struct { - CommitTitle string // Extra detail to append to automatic commit message. (Optional.) + CommitTitle string // Title for the automatic commit message. (Optional.) SHA string // SHA that pull request head must match to allow merge. (Optional.) // The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.) MergeMethod string + + // If false, an empty string commit message will use the default commit message. If true, an empty string commit message will be used. + DontDefaultIfBlank bool } type pullRequestMergeRequest struct { - CommitMessage string `json:"commit_message"` - CommitTitle string `json:"commit_title,omitempty"` - MergeMethod string `json:"merge_method,omitempty"` - SHA string `json:"sha,omitempty"` + CommitMessage *string `json:"commit_message,omitempty"` + CommitTitle string `json:"commit_title,omitempty"` + MergeMethod string `json:"merge_method,omitempty"` + SHA string `json:"sha,omitempty"` } -// Merge a pull request (Merge Button™). -// commitMessage is the title for the automatic commit message. +// Merge a pull request. +// commitMessage is an extra detail to append to automatic commit message. +// +// GitHub API docs: https://docs.github.com/rest/pulls/pulls?apiVersion=2022-11-28#merge-a-pull-request // -// GitHub API docs: https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-buttontrade -func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) +//meta:operation PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge +func (s *PullRequestsService) Merge(ctx context.Context, owner, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/merge", owner, repo, number) - pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage} + var pullRequestBody pullRequestMergeRequest + if commitMessage != "" { + pullRequestBody.CommitMessage = &commitMessage + } if options != nil { pullRequestBody.CommitTitle = options.CommitTitle pullRequestBody.MergeMethod = options.MergeMethod pullRequestBody.SHA = options.SHA + if options.DontDefaultIfBlank && commitMessage == "" { + pullRequestBody.CommitMessage = &commitMessage + } } - req, err := s.client.NewRequest("PUT", u, pullRequestBody) + req, err := s.client.NewRequest(ctx, "PUT", u, &pullRequestBody) if err != nil { return nil, nil, err } - mergeResult := new(PullRequestMergeResult) - resp, err := s.client.Do(ctx, req, mergeResult) + var mergeResult *PullRequestMergeResult + resp, err := s.client.Do(req, &mergeResult) if err != nil { return nil, resp, err } diff --git a/github/pulls_comments.go b/github/pulls_comments.go index 8886c9d124d..1a8016f7272 100644 --- a/github/pulls_comments.go +++ b/github/pulls_comments.go @@ -8,6 +8,7 @@ package github import ( "context" "fmt" + "strings" "time" ) @@ -22,18 +23,30 @@ type PullRequestComment struct { PullRequestReviewID *int64 `json:"pull_request_review_id,omitempty"` Position *int `json:"position,omitempty"` OriginalPosition *int `json:"original_position,omitempty"` + StartLine *int `json:"start_line,omitempty"` + Line *int `json:"line,omitempty"` + OriginalLine *int `json:"original_line,omitempty"` + OriginalStartLine *int `json:"original_start_line,omitempty"` + Side *string `json:"side,omitempty"` + StartSide *string `json:"start_side,omitempty"` CommitID *string `json:"commit_id,omitempty"` OriginalCommitID *string `json:"original_commit_id,omitempty"` User *User `json:"user,omitempty"` Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` // AuthorAssociation is the comment author's relationship to the pull request's repository. // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Pull Request Comments REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#get-a-review-comment-for-a-pull-request AuthorAssociation *string `json:"author_association,omitempty"` URL *string `json:"url,omitempty"` HTMLURL *string `json:"html_url,omitempty"` PullRequestURL *string `json:"pull_request_url,omitempty"` + // Can be one of: LINE, FILE from https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#create-a-review-comment-for-a-pull-request + SubjectType *string `json:"subject_type,omitempty"` } func (p PullRequestComment) String() string { @@ -59,29 +72,34 @@ type PullRequestListCommentsOptions struct { // pull request number of 0 will return all comments on all pull requests for // the repository. // -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request -func (s *PullRequestsService) ListComments(ctx context.Context, owner string, repo string, number int, opt *PullRequestListCommentsOptions) ([]*PullRequestComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#list-review-comments-in-a-repository +// +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#list-review-comments-on-a-pull-request +// +//meta:operation GET /repos/{owner}/{repo}/pulls/comments +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/comments +func (s *PullRequestsService) ListComments(ctx context.Context, owner, repo string, number int, opts *PullRequestListCommentsOptions) ([]*PullRequestComment, *Response, error) { var u string if number == 0 { u = fmt.Sprintf("repos/%v/%v/pulls/comments", owner, repo) } else { - u = fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) + u = fmt.Sprintf("repos/%v/%v/pulls/%v/comments", owner, repo, number) } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var comments []*PullRequestComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -91,19 +109,21 @@ func (s *PullRequestsService) ListComments(ctx context.Context, owner string, re // GetComment fetches the specified pull request comment. // -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment -func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, commentID int64) (*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#get-a-review-comment-for-a-pull-request +// +//meta:operation GET /repos/{owner}/{repo}/pulls/comments/{comment_id} +func (s *PullRequestsService) GetComment(ctx context.Context, owner, repo string, commentID int64) (*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v", owner, repo, commentID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - comment := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, comment) + var comment *PullRequestComment + resp, err := s.client.Do(req, &comment) if err != nil { return nil, resp, err } @@ -113,16 +133,20 @@ func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo // CreateComment creates a new comment on the specified pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment -func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) - req, err := s.client.NewRequest("POST", u, comment) +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#create-a-review-comment-for-a-pull-request +// +//meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/comments +func (s *PullRequestsService) CreateComment(ctx context.Context, owner, repo string, number int, body *PullRequestComment) (*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/comments", owner, repo, number) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - c := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, c) + var c *PullRequestComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -132,8 +156,10 @@ func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, r // CreateCommentInReplyTo creates a new comment as a reply to an existing pull request comment. // -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#alternative-input -func (s *PullRequestsService) CreateCommentInReplyTo(ctx context.Context, owner string, repo string, number int, body string, commentID int64) (*PullRequestComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#create-a-review-comment-for-a-pull-request +// +//meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/comments +func (s *PullRequestsService) CreateCommentInReplyTo(ctx context.Context, owner, repo string, number int, body string, commentID int64) (*PullRequestComment, *Response, error) { comment := &struct { Body string `json:"body,omitempty"` InReplyTo int64 `json:"in_reply_to,omitempty"` @@ -141,14 +167,14 @@ func (s *PullRequestsService) CreateCommentInReplyTo(ctx context.Context, owner Body: body, InReplyTo: commentID, } - u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) - req, err := s.client.NewRequest("POST", u, comment) + u := fmt.Sprintf("repos/%v/%v/pulls/%v/comments", owner, repo, number) + req, err := s.client.NewRequest(ctx, "POST", u, comment) if err != nil { return nil, nil, err } - c := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, c) + var c *PullRequestComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -159,16 +185,18 @@ func (s *PullRequestsService) CreateCommentInReplyTo(ctx context.Context, owner // EditComment updates a pull request comment. // A non-nil comment.Body must be provided. Other comment fields should be left nil. // -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment -func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, commentID int64, comment *PullRequestComment) (*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID) - req, err := s.client.NewRequest("PATCH", u, comment) +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#update-a-review-comment-for-a-pull-request +// +//meta:operation PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id} +func (s *PullRequestsService) EditComment(ctx context.Context, owner, repo string, commentID int64, body *PullRequestComment) (*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v", owner, repo, commentID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - c := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, c) + var c *PullRequestComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -178,12 +206,15 @@ func (s *PullRequestsService) EditComment(ctx context.Context, owner string, rep // DeleteComment deletes a pull request comment. // -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment -func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, commentID int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/pulls/comments?apiVersion=2022-11-28#delete-a-review-comment-for-a-pull-request +// +//meta:operation DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id} +func (s *PullRequestsService) DeleteComment(ctx context.Context, owner, repo string, commentID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v", owner, repo, commentID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } diff --git a/github/pulls_comments_test.go b/github/pulls_comments_test.go index b07b2cd99b0..1171b6bf2d7 100644 --- a/github/pulls_comments_test.go +++ b/github/pulls_comments_test.go @@ -6,126 +6,26 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" + "strings" "testing" - "time" -) - -func TestPullComments_marshall(t *testing.T) { - testJSONMarshal(t, &PullRequestComment{}, "{}") - - createdAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) - updatedAt := time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC) - reactions := &Reactions{ - TotalCount: Int(1), - PlusOne: Int(1), - MinusOne: Int(0), - Laugh: Int(0), - Confused: Int(0), - Heart: Int(0), - Hooray: Int(0), - URL: String("u"), - } - u := &PullRequestComment{ - ID: Int64(10), - InReplyTo: Int64(8), - Body: String("Test comment"), - Path: String("file1.txt"), - DiffHunk: String("@@ -16,33 +16,40 @@ fmt.Println()"), - PullRequestReviewID: Int64(42), - Position: Int(1), - OriginalPosition: Int(4), - CommitID: String("ab"), - OriginalCommitID: String("9c"), - User: &User{ - Login: String("ll"), - ID: Int64(123), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - URL: String("u"), - }, - Reactions: reactions, - CreatedAt: &createdAt, - UpdatedAt: &updatedAt, - URL: String("pullrequestcommentUrl"), - HTMLURL: String("pullrequestcommentHTMLUrl"), - PullRequestURL: String("pullrequestcommentPullRequestURL"), - } - - want := `{ - "id": 10, - "in_reply_to_id": 8, - "body": "Test comment", - "path": "file1.txt", - "diff_hunk": "@@ -16,33 +16,40 @@ fmt.Println()", - "pull_request_review_id": 42, - "position": 1, - "original_position": 4, - "commit_id": "ab", - "original_commit_id": "9c", - "user": { - "login": "ll", - "id": 123, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "url": "u" - }, - "reactions": { - "total_count": 1, - "+1": 1, - "-1": 0, - "laugh": 0, - "confused": 0, - "heart": 0, - "hooray": 0, - "url": "u" - }, - "created_at": "2002-02-10T15:30:00Z", - "updated_at": "2002-02-10T15:30:00Z", - "url": "pullrequestcommentUrl", - "html_url": "pullrequestcommentHTMLUrl", - "pull_request_url": "pullrequestcommentPullRequestURL" - }` - - testJSONMarshal(t, u, want) -} + "github.com/google/go-cmp/cmp" +) func TestPullRequestsService_ListComments_allPulls(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} mux.HandleFunc("/repos/o/r/pulls/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) testFormValues(t, r, values{ "sort": "updated", "direction": "desc", - "since": "2002-02-10T15:30:00Z", + "since": referenceTimeRaw, "page": "2", }) fmt.Fprint(w, `[{"id":1}]`) @@ -134,170 +34,277 @@ func TestPullRequestsService_ListComments_allPulls(t *testing.T) { opt := &PullRequestListCommentsOptions{ Sort: "updated", Direction: "desc", - Since: time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), + Since: referenceTime, ListOptions: ListOptions{Page: 2}, } - pulls, _, err := client.PullRequests.ListComments(context.Background(), "o", "r", 0, opt) + ctx := t.Context() + pulls, _, err := client.PullRequests.ListComments(ctx, "o", "r", 0, opt) if err != nil { t.Errorf("PullRequests.ListComments returned error: %v", err) } - want := []*PullRequestComment{{ID: Int64(1)}} - if !reflect.DeepEqual(pulls, want) { + want := []*PullRequestComment{{ID: Ptr(int64(1))}} + if !cmp.Equal(pulls, want) { t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want) } + + const methodName = "ListComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListComments(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListComments(ctx, "o", "r", 0, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_ListComments_specificPull(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) fmt.Fprint(w, `[{"id":1, "pull_request_review_id":42}]`) }) - pulls, _, err := client.PullRequests.ListComments(context.Background(), "o", "r", 1, nil) + ctx := t.Context() + pulls, _, err := client.PullRequests.ListComments(ctx, "o", "r", 1, nil) if err != nil { t.Errorf("PullRequests.ListComments returned error: %v", err) } - want := []*PullRequestComment{{ID: Int64(1), PullRequestReviewID: Int64(42)}} - if !reflect.DeepEqual(pulls, want) { + want := []*PullRequestComment{{ID: Ptr(int64(1)), PullRequestReviewID: Ptr(int64(42))}} + if !cmp.Equal(pulls, want) { t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want) } } func TestPullRequestsService_ListComments_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.ListComments(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.PullRequests.ListComments(ctx, "%", "r", 1, nil) testURLParseError(t, err) } func TestPullRequestsService_GetComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.PullRequests.GetComment(context.Background(), "o", "r", 1) + ctx := t.Context() + comment, _, err := client.PullRequests.GetComment(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.GetComment returned error: %v", err) } - want := &PullRequestComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &PullRequestComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("PullRequests.GetComment returned %+v, want %+v", comment, want) } + + const methodName = "GetComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.GetComment(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.GetComment(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_GetComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.GetComment(context.Background(), "%", "r", 1) + ctx := t.Context() + _, _, err := client.PullRequests.GetComment(ctx, "%", "r", 1) testURLParseError(t, err) } func TestPullRequestsService_CreateComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &PullRequestComment{Body: String("b")} + input := &PullRequestComment{Body: Ptr("b")} + wantAcceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeMultiLineCommentsPreview} mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { - v := new(PullRequestComment) - json.NewDecoder(r.Body).Decode(v) - + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.PullRequests.CreateComment(context.Background(), "o", "r", 1, input) + ctx := t.Context() + comment, _, err := client.PullRequests.CreateComment(ctx, "o", "r", 1, input) if err != nil { t.Errorf("PullRequests.CreateComment returned error: %v", err) } - want := &PullRequestComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &PullRequestComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("PullRequests.CreateComment returned %+v, want %+v", comment, want) } + + const methodName = "CreateComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.CreateComment(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.CreateComment(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_CreateComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.CreateComment(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.PullRequests.CreateComment(ctx, "%", "r", 1, nil) testURLParseError(t, err) } -func TestPullRequestsService_EditComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestPullRequestsService_CreateCommentInReplyTo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &PullRequestComment{Body: String("b")} + input := &PullRequestComment{Body: Ptr("b")} - mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { - v := new(PullRequestComment) - json.NewDecoder(r.Body).Decode(v) + mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1}`) + }) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + ctx := t.Context() + comment, _, err := client.PullRequests.CreateCommentInReplyTo(ctx, "o", "r", 1, "b", 2) + if err != nil { + t.Errorf("PullRequests.CreateCommentInReplyTo returned error: %v", err) + } + + want := &PullRequestComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { + t.Errorf("PullRequests.CreateCommentInReplyTo returned %+v, want %+v", comment, want) + } + + const methodName = "CreateCommentInReplyTo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.CreateCommentInReplyTo(ctx, "\n", "\n", -1, "\n", -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.CreateCommentInReplyTo(ctx, "o", "r", 1, "b", 2) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} + +func TestPullRequestsService_EditComment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PullRequestComment{Body: Ptr("b")} + mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.PullRequests.EditComment(context.Background(), "o", "r", 1, input) + ctx := t.Context() + comment, _, err := client.PullRequests.EditComment(ctx, "o", "r", 1, input) if err != nil { t.Errorf("PullRequests.EditComment returned error: %v", err) } - want := &PullRequestComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &PullRequestComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("PullRequests.EditComment returned %+v, want %+v", comment, want) } + + const methodName = "EditComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.EditComment(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.EditComment(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_EditComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.EditComment(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.PullRequests.EditComment(ctx, "%", "r", 1, nil) testURLParseError(t, err) } func TestPullRequestsService_DeleteComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/pulls/comments/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.PullRequests.DeleteComment(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.PullRequests.DeleteComment(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.DeleteComment returned error: %v", err) } + + const methodName = "DeleteComment" + testBadOptions(t, methodName, func() (err error) { + _, err = client.PullRequests.DeleteComment(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.PullRequests.DeleteComment(ctx, "o", "r", 1) + }) } func TestPullRequestsService_DeleteComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.PullRequests.DeleteComment(context.Background(), "%", "r", 1) + ctx := t.Context() + _, err := client.PullRequests.DeleteComment(ctx, "%", "r", 1) testURLParseError(t, err) } diff --git a/github/pulls_reviewers.go b/github/pulls_reviewers.go index beae953c4c9..612291a5119 100644 --- a/github/pulls_reviewers.go +++ b/github/pulls_reviewers.go @@ -23,18 +23,27 @@ type Reviewers struct { Teams []*Team `json:"teams,omitempty"` } +type removeReviewersRequest struct { + NodeID *string `json:"node_id,omitempty"` + // Note the lack of omitempty! See comment in RemoveReviewers. + Reviewers []string `json:"reviewers"` + TeamReviewers []string `json:"team_reviewers,omitempty"` +} + // RequestReviewers creates a review request for the provided reviewers for the specified pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#create-a-review-request +// GitHub API docs: https://docs.github.com/rest/pulls/review-requests?apiVersion=2022-11-28#request-reviewers-for-a-pull-request +// +//meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*PullRequest, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) - req, err := s.client.NewRequest("POST", u, &reviewers) + u := fmt.Sprintf("repos/%v/%v/pulls/%v/requested_reviewers", owner, repo, number) + req, err := s.client.NewRequest(ctx, "POST", u, &reviewers) if err != nil { return nil, nil, err } - r := new(PullRequest) - resp, err := s.client.Do(ctx, req, r) + var r *PullRequest + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -44,21 +53,19 @@ func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo // ListReviewers lists reviewers whose reviews have been requested on the specified pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#list-review-requests -func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo string, number int, opt *ListOptions) (*Reviewers, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/requested_reviewers", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } +// GitHub API docs: https://docs.github.com/rest/pulls/review-requests?apiVersion=2022-11-28#get-all-requested-reviewers-for-a-pull-request +// +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers +func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo string, number int) (*Reviewers, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/requested_reviewers", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - reviewers := new(Reviewers) - resp, err := s.client.Do(ctx, req, reviewers) + var reviewers *Reviewers + resp, err := s.client.Do(req, &reviewers) if err != nil { return nil, resp, err } @@ -68,13 +75,25 @@ func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo str // RemoveReviewers removes the review request for the provided reviewers for the specified pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request +// GitHub API docs: https://docs.github.com/rest/pulls/review-requests?apiVersion=2022-11-28#remove-requested-reviewers-from-a-pull-request +// +//meta:operation DELETE /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, &reviewers) + // reviewers.Reviewers may be empty if the caller wants to remove teams, but not users. Unlike AddReviewers, + // "reviewers" is a required param here. Reference: https://github.com/google/go-github/issues/3336 + // The type `removeReviewersRequest` is required because the struct tags are different from `ReviewersRequest`. + removeRequest := removeReviewersRequest(reviewers) + + if removeRequest.Reviewers == nil { + // GitHub accepts the empty list, but rejects null. Removing `omitempty` is not enough - we also have to promote nil to []. + removeRequest.Reviewers = []string{} + } + + u := fmt.Sprintf("repos/%v/%v/pulls/%v/requested_reviewers", owner, repo, number) + req, err := s.client.NewRequest(ctx, "DELETE", u, &removeRequest) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/pulls_reviewers_test.go b/github/pulls_reviewers_test.go index 98d6e656f49..8feaefd309c 100644 --- a/github/pulls_reviewers_test.go +++ b/github/pulls_reviewers_test.go @@ -6,100 +6,108 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestRequestReviewers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + input := ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league", "injustice-league"}} mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testBody(t, r, `{"reviewers":["octocat","googlebot"],"team_reviewers":["justice-league","injustice-league"]}`+"\n") + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":1}`) }) - // This returns a PR, unmarshalling of which is tested elsewhere - ctx := context.Background() - got, _, err := client.PullRequests.RequestReviewers(ctx, "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league", "injustice-league"}}) + // This returns a PR, unmarshaling of which is tested elsewhere + ctx := t.Context() + got, _, err := client.PullRequests.RequestReviewers(ctx, "o", "r", 1, input) if err != nil { t.Errorf("PullRequests.RequestReviewers returned error: %v", err) } - want := &PullRequest{Number: Int(1)} - if !reflect.DeepEqual(got, want) { + want := &PullRequest{Number: Ptr(1)} + if !cmp.Equal(got, want) { t.Errorf("PullRequests.RequestReviewers returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.PullRequests.RequestReviewers(ctx, "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league", "injustice-league"}}) - if got != nil { - t.Errorf("client.BaseURL.Path='' RequestReviewers = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' RequestReviewers resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' RequestReviewers err = nil, want error") - } - - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.PullRequests.RequestReviewers(ctx, "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league", "injustice-league"}}) - if got != nil { - t.Errorf("rate.Reset.Time > now RequestReviewers = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now RequestReviewers resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now RequestReviewers err = nil, want error") - } + const methodName = "RequestReviewers" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.RequestReviewers(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRemoveReviewers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { + input := ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league"}} + + mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testBody(t, r, `{"reviewers":["octocat","googlebot"],"team_reviewers":["justice-league"]}`+"\n") + testJSONBody(t, r, input) }) - ctx := context.Background() - _, err := client.PullRequests.RemoveReviewers(ctx, "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league"}}) + ctx := t.Context() + _, err := client.PullRequests.RemoveReviewers(ctx, "o", "r", 1, input) if err != nil { t.Errorf("PullRequests.RemoveReviewers returned error: %v", err) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - resp, err := client.PullRequests.RemoveReviewers(ctx, "o", "r", 1, ReviewersRequest{Reviewers: []string{"octocat", "googlebot"}, TeamReviewers: []string{"justice-league"}}) - if resp != nil { - t.Errorf("client.BaseURL.Path='' RemoveReviewers resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' RemoveReviewers err = nil, want error") + const methodName = "RemoveReviewers" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.PullRequests.RemoveReviewers(ctx, "o", "r", 1, input) + }) +} + +func TestRemoveReviewers_teamsOnly(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ReviewersRequest{TeamReviewers: []string{"justice-league"}} + + mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + want := ReviewersRequest{ + NodeID: nil, + Reviewers: []string{}, + TeamReviewers: input.TeamReviewers, + } + testJSONBody(t, r, want) + }) + + ctx := t.Context() + _, err := client.PullRequests.RemoveReviewers(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("PullRequests.RemoveReviewers returned error: %v", err) } + + const methodName = "RemoveReviewers" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.PullRequests.RemoveReviewers(ctx, "o", "r", 1, input) + }) } func TestListReviewers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"users":[{"login":"octocat","id":1}],"teams":[{"id":1,"name":"Justice League"}]}`) }) - ctx := context.Background() - got, _, err := client.PullRequests.ListReviewers(ctx, "o", "r", 1, nil) + ctx := t.Context() + got, _, err := client.PullRequests.ListReviewers(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.ListReviewers returned error: %v", err) } @@ -107,70 +115,27 @@ func TestListReviewers(t *testing.T) { want := &Reviewers{ Users: []*User{ { - Login: String("octocat"), - ID: Int64(1), + Login: Ptr("octocat"), + ID: Ptr(int64(1)), }, }, Teams: []*Team{ { - ID: Int64(1), - Name: String("Justice League"), + ID: Ptr(int64(1)), + Name: Ptr("Justice League"), }, }, } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("PullRequests.ListReviewers returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.PullRequests.ListReviewers(ctx, "o", "r", 1, nil) - if got != nil { - t.Errorf("client.BaseURL.Path='' ListReviewers = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListReviewers resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListReviewers err = nil, want error") - } - - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.PullRequests.ListReviewers(ctx, "o", "r", 1, nil) - if got != nil { - t.Errorf("rate.Reset.Time > now ListReviewers = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListReviewers resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListReviewers err = nil, want error") - } -} - -func TestListReviewers_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/pulls/1/requested_reviewers", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{ - "page": "2", - }) - fmt.Fprint(w, `{}`) + const methodName = "ListReviewers" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListReviewers(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err }) - - _, _, err := client.PullRequests.ListReviewers(context.Background(), "o", "r", 1, &ListOptions{Page: 2}) - if err != nil { - t.Errorf("PullRequests.ListReviewers returned error: %v", err) - } - - // Test addOptions failure - _, _, err = client.PullRequests.ListReviewers(context.Background(), "\n", "\n", 1, &ListOptions{Page: 2}) - if err == nil { - t.Error("bad options ListReviewers err = nil, want error") - } - } diff --git a/github/pulls_reviews.go b/github/pulls_reviews.go index 88b82982f6d..f901f32ce5e 100644 --- a/github/pulls_reviews.go +++ b/github/pulls_reviews.go @@ -7,21 +7,30 @@ package github import ( "context" + "errors" "fmt" - "time" ) +var ErrMixedCommentStyles = errors.New("cannot use both position and side/line form comments") + // PullRequestReview represents a review of a pull request. type PullRequestReview struct { ID *int64 `json:"id,omitempty"` NodeID *string `json:"node_id,omitempty"` User *User `json:"user,omitempty"` Body *string `json:"body,omitempty"` - SubmittedAt *time.Time `json:"submitted_at,omitempty"` + SubmittedAt *Timestamp `json:"submitted_at,omitempty"` CommitID *string `json:"commit_id,omitempty"` HTMLURL *string `json:"html_url,omitempty"` PullRequestURL *string `json:"pull_request_url,omitempty"` State *string `json:"state,omitempty"` + // AuthorAssociation is the review author's relationship to the repository. + // Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". + // + // Deprecated: GitHub will remove this field from Events API payloads on October 7, 2025. + // Use the Pull Request Reviews REST API endpoint to retrieve this information. + // See: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#get-a-review-for-a-pull-request + AuthorAssociation *string `json:"author_association,omitempty"` } func (p PullRequestReview) String() string { @@ -33,6 +42,12 @@ type DraftReviewComment struct { Path *string `json:"path,omitempty"` Position *int `json:"position,omitempty"` Body *string `json:"body,omitempty"` + + // The new comfort-fade-preview fields + StartSide *string `json:"start_side,omitempty"` + Side *string `json:"side,omitempty"` + StartLine *int `json:"start_line,omitempty"` + Line *int `json:"line,omitempty"` } func (c DraftReviewComment) String() string { @@ -52,36 +67,67 @@ func (r PullRequestReviewRequest) String() string { return Stringify(r) } -// PullRequestReviewDismissalRequest represents a request to dismiss a review. -type PullRequestReviewDismissalRequest struct { - Message *string `json:"message,omitempty"` +func (r *PullRequestReviewRequest) isComfortFadePreview() (bool, error) { + var isCF *bool + for _, comment := range r.Comments { + if comment == nil { + continue + } + hasPos := comment.Position != nil + hasComfortFade := (comment.StartSide != nil) || (comment.Side != nil) || + (comment.StartLine != nil) || (comment.Line != nil) + + switch { + case hasPos && hasComfortFade: + return false, ErrMixedCommentStyles + case hasPos && isCF != nil && *isCF: + return false, ErrMixedCommentStyles + case hasComfortFade && isCF != nil && !*isCF: + return false, ErrMixedCommentStyles + } + isCF = &hasComfortFade + } + if isCF != nil { + return *isCF, nil + } + return false, nil +} + +// PullRequestDismissReviewRequest represents a request to dismiss a review. +type PullRequestDismissReviewRequest struct { + Message string `json:"message"` + Event *string `json:"event,omitempty"` } -func (r PullRequestReviewDismissalRequest) String() string { +func (r PullRequestDismissReviewRequest) String() string { return Stringify(r) } +// PullRequestSubmitReviewRequest represents a request to submit a review. +type PullRequestSubmitReviewRequest struct { + Body *string `json:"body,omitempty"` + Event string `json:"event"` +} + // ListReviews lists all reviews on the specified pull request. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#list-reviews-for-a-pull-request // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request -func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) - u, err := addOptions(u, opt) +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews +func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opts *ListOptions) ([]*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews", owner, repo, number) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var reviews []*PullRequestReview - resp, err := s.client.Do(ctx, req, &reviews) + resp, err := s.client.Do(req, &reviews) if err != nil { return nil, resp, err } @@ -91,21 +137,19 @@ func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo strin // GetReview fetches the specified pull request review. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#get-a-review-for-a-pull-request // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-a-single-review +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id} func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews/%v", owner, repo, number, reviewID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - review := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, review) + var review *PullRequestReview + resp, err := s.client.Do(req, &review) if err != nil { return nil, resp, err } @@ -115,21 +159,19 @@ func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, // DeletePendingReview deletes the specified pull request pending review. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#delete-a-pending-review-for-a-pull-request // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review +//meta:operation DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id} func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews/%v", owner, repo, number, reviewID) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, nil, err } - review := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, review) + var review *PullRequestReview + resp, err := s.client.Do(req, &review) if err != nil { return nil, resp, err } @@ -139,25 +181,23 @@ func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, re // ListReviewComments lists all the comments for the specified review. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#list-comments-for-a-pull-request-review // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review -func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number int, reviewID int64, opt *ListOptions) ([]*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID) - u, err := addOptions(u, opt) +//meta:operation GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments +func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number int, reviewID int64, opts *ListOptions) ([]*PullRequestComment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews/%v/comments", owner, repo, number, reviewID) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var comments []*PullRequestComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -167,21 +207,62 @@ func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, rep // CreateReview creates a new review on the specified pull request. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// In order to use multi-line comments, you must use the "comfort fade" preview. +// This replaces the use of the "Position" field in comments with 4 new fields: +// +// [Start]Side, and [Start]Line. +// +// These new fields must be used for ALL comments (including single-line), +// with the following restrictions (empirically observed, so subject to change). +// +// For single-line "comfort fade" comments, you must use: // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review -func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) +// Path: &path, // as before +// Body: &body, // as before +// Side: &"RIGHT" (or "LEFT") +// Line: &123, // NOT THE SAME AS POSITION, this is an actual line number. +// +// If StartSide or StartLine is used with single-line comments, a 422 is returned. +// +// For multi-line "comfort fade" comments, you must use: +// +// Path: &path, // as before +// Body: &body, // as before +// StartSide: &"RIGHT" (or "LEFT") +// Side: &"RIGHT" (or "LEFT") +// StartLine: &120, +// Line: &125, +// +// Suggested edits are made by commenting on the lines to replace, and including the +// suggested edit in a block like this (it may be surrounded in non-suggestion markdown): +// +// ```suggestion +// Use this instead. +// It is waaaaaay better. +// ``` +// +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#create-a-review-for-a-pull-request +// +//meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews +func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, body *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews", owner, repo, number) - req, err := s.client.NewRequest("POST", u, review) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - r := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, r) + // Detect which style of review comment is being used. + if isCF, err := body.isComfortFadePreview(); err != nil { + return nil, nil, err + } else if isCF { + // If the review comments are using the comfort fade preview fields, + // then pass the comfort fade header. + req.Header.Set("Accept", mediaTypeMultiLineCommentsPreview) + } + + var r *PullRequestReview + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -191,20 +272,22 @@ func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo stri // UpdateReview updates the review summary on the specified pull request. // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#update-a-pull-request-review +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#update-a-review-for-a-pull-request +// +//meta:operation PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id} func (s *PullRequestsService) UpdateReview(ctx context.Context, owner, repo string, number int, reviewID int64, body string) (*PullRequestReview, *Response, error) { opts := &struct { Body string `json:"body"` }{Body: body} - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews/%v", owner, repo, number, reviewID) - req, err := s.client.NewRequest("PUT", u, opts) + req, err := s.client.NewRequest(ctx, "PUT", u, opts) if err != nil { return nil, nil, err } - review := &PullRequestReview{} - resp, err := s.client.Do(ctx, req, review) + var review *PullRequestReview + resp, err := s.client.Do(req, &review) if err != nil { return nil, resp, err } @@ -214,21 +297,19 @@ func (s *PullRequestsService) UpdateReview(ctx context.Context, owner, repo stri // SubmitReview submits a specified review on the specified pull request. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#submit-a-review-for-a-pull-request // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review -func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID) +//meta:operation POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events +func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number int, reviewID int64, body PullRequestSubmitReviewRequest) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews/%v/events", owner, repo, number, reviewID) - req, err := s.client.NewRequest("POST", u, review) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - r := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, r) + var r *PullRequestReview + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -238,21 +319,19 @@ func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo stri // DismissReview dismisses a specified review on the specified pull request. // -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 +// GitHub API docs: https://docs.github.com/rest/pulls/reviews?apiVersion=2022-11-28#dismiss-a-review-for-a-pull-request // -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review -func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID) +//meta:operation PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals +func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number int, reviewID int64, body PullRequestDismissReviewRequest) (*PullRequestReview, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pulls/%v/reviews/%v/dismissals", owner, repo, number, reviewID) - req, err := s.client.NewRequest("PUT", u, review) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - r := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, r) + var r *PullRequestReview + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } diff --git a/github/pulls_reviews_test.go b/github/pulls_reviews_test.go index 3b78bc9e8f4..8133bc5ca14 100644 --- a/github/pulls_reviews_test.go +++ b/github/pulls_reviews_test.go @@ -6,17 +6,17 @@ package github import ( - "context" - "encoding/json" + "errors" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestPullRequestsService_ListReviews(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,110 +27,173 @@ func TestPullRequestsService_ListReviews(t *testing.T) { }) opt := &ListOptions{Page: 2} - reviews, _, err := client.PullRequests.ListReviews(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + reviews, _, err := client.PullRequests.ListReviews(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("PullRequests.ListReviews returned error: %v", err) } want := []*PullRequestReview{ - {ID: Int64(1)}, - {ID: Int64(2)}, + {ID: Ptr(int64(1))}, + {ID: Ptr(int64(2))}, } - if !reflect.DeepEqual(reviews, want) { + if !cmp.Equal(reviews, want) { t.Errorf("PullRequests.ListReviews returned %+v, want %+v", reviews, want) } + + const methodName = "ListReviews" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListReviews(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListReviews(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_ListReviews_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.ListReviews(context.Background(), "%", "r", 1, nil) + ctx := t.Context() + _, _, err := client.PullRequests.ListReviews(ctx, "%", "r", 1, nil) testURLParseError(t, err) } func TestPullRequestsService_GetReview(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - review, _, err := client.PullRequests.GetReview(context.Background(), "o", "r", 1, 1) + ctx := t.Context() + review, _, err := client.PullRequests.GetReview(ctx, "o", "r", 1, 1) if err != nil { t.Errorf("PullRequests.GetReview returned error: %v", err) } - want := &PullRequestReview{ID: Int64(1)} - if !reflect.DeepEqual(review, want) { + want := &PullRequestReview{ID: Ptr(int64(1))} + if !cmp.Equal(review, want) { t.Errorf("PullRequests.GetReview returned %+v, want %+v", review, want) } + + const methodName = "GetReview" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.GetReview(ctx, "\n", "\n", -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.GetReview(ctx, "o", "r", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_GetReview_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.GetReview(context.Background(), "%", "r", 1, 1) + ctx := t.Context() + _, _, err := client.PullRequests.GetReview(ctx, "%", "r", 1, 1) testURLParseError(t, err) } func TestPullRequestsService_DeletePendingReview(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") fmt.Fprint(w, `{"id":1}`) }) - review, _, err := client.PullRequests.DeletePendingReview(context.Background(), "o", "r", 1, 1) + ctx := t.Context() + review, _, err := client.PullRequests.DeletePendingReview(ctx, "o", "r", 1, 1) if err != nil { t.Errorf("PullRequests.DeletePendingReview returned error: %v", err) } - want := &PullRequestReview{ID: Int64(1)} - if !reflect.DeepEqual(review, want) { + want := &PullRequestReview{ID: Ptr(int64(1))} + if !cmp.Equal(review, want) { t.Errorf("PullRequests.DeletePendingReview returned %+v, want %+v", review, want) } + + const methodName = "DeletePendingReview" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.DeletePendingReview(ctx, "\n", "\n", -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.DeletePendingReview(ctx, "o", "r", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_DeletePendingReview_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.DeletePendingReview(context.Background(), "%", "r", 1, 1) + ctx := t.Context() + _, _, err := client.PullRequests.DeletePendingReview(ctx, "%", "r", 1, 1) testURLParseError(t, err) } func TestPullRequestsService_ListReviewComments(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) - comments, _, err := client.PullRequests.ListReviewComments(context.Background(), "o", "r", 1, 1, nil) + ctx := t.Context() + comments, _, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, nil) if err != nil { t.Errorf("PullRequests.ListReviewComments returned error: %v", err) } want := []*PullRequestComment{ - {ID: Int64(1)}, - {ID: Int64(2)}, + {ID: Ptr(int64(1))}, + {ID: Ptr(int64(2))}, } - if !reflect.DeepEqual(comments, want) { + if !cmp.Equal(comments, want) { t.Errorf("PullRequests.ListReviewComments returned %+v, want %+v", comments, want) } + + const methodName = "ListReviewComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListReviewComments(ctx, "\n", "\n", -1, -1, nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_ListReviewComments_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -140,154 +203,404 @@ func TestPullRequestsService_ListReviewComments_withOptions(t *testing.T) { fmt.Fprint(w, `[]`) }) - _, _, err := client.PullRequests.ListReviewComments(context.Background(), "o", "r", 1, 1, &ListOptions{Page: 2}) + ctx := t.Context() + _, _, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, &ListOptions{Page: 2}) if err != nil { t.Errorf("PullRequests.ListReviewComments returned error: %v", err) } + + const methodName = "ListReviewComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListReviewComments(ctx, "\n", "\n", -1, -1, &ListOptions{Page: 2}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListReviewComments(ctx, "o", "r", 1, 1, &ListOptions{Page: 2}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestPullRequestReviewRequest_isComfortFadePreview(t *testing.T) { + t.Parallel() + path := "path/to/file.go" + body := "this is a comment body" + left, right := "LEFT", "RIGHT" + pos1, pos2, pos3 := 1, 2, 3 + line1, line2, line3 := 11, 22, 33 + + tests := []struct { + name string + review *PullRequestReviewRequest + wantErr error + wantBool bool + }{{ + name: "empty review", + review: &PullRequestReviewRequest{}, + wantBool: false, + }, { + name: "nil comment", + review: &PullRequestReviewRequest{Comments: []*DraftReviewComment{nil}}, + wantBool: false, + }, { + name: "old-style review", + review: &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Position: &pos1, + }, { + Path: &path, + Body: &body, + Position: &pos2, + }, { + Path: &path, + Body: &body, + Position: &pos3, + }}, + }, + wantBool: false, + }, { + name: "new-style review", + review: &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Side: &right, + Line: &line1, + }, { + Path: &path, + Body: &body, + Side: &left, + Line: &line2, + }, { + Path: &path, + Body: &body, + Side: &right, + Line: &line3, + }}, + }, + wantBool: true, + }, { + name: "blended comment", + review: &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Position: &pos1, // can't have both styles. + Side: &right, + Line: &line1, + }}, + }, + wantErr: ErrMixedCommentStyles, + }, { + name: "position then line", + review: &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Position: &pos1, + }, { + Path: &path, + Body: &body, + Side: &right, + Line: &line1, + }}, + }, + wantErr: ErrMixedCommentStyles, + }, { + name: "line then position", + review: &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Side: &right, + Line: &line1, + }, { + Path: &path, + Body: &body, + Position: &pos1, + }}, + }, + wantErr: ErrMixedCommentStyles, + }} + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + gotBool, gotErr := tc.review.isComfortFadePreview() + if tc.wantErr != nil { + if !errors.Is(gotErr, tc.wantErr) { + t.Errorf("isComfortFadePreview() = %v, wanted %v", gotErr, tc.wantErr) + } + } else { + if gotBool != tc.wantBool { + t.Errorf("isComfortFadePreview() = %v, wanted %v", gotBool, tc.wantBool) + } + } + }) + } } func TestPullRequestsService_ListReviewComments_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.ListReviewComments(context.Background(), "%", "r", 1, 1, nil) + ctx := t.Context() + _, _, err := client.PullRequests.ListReviewComments(ctx, "%", "r", 1, 1, nil) testURLParseError(t, err) } func TestPullRequestsService_CreateReview(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := &PullRequestReviewRequest{ - CommitID: String("commit_id"), - Body: String("b"), - Event: String("APPROVE"), + CommitID: Ptr("commit_id"), + Body: Ptr("b"), + Event: Ptr("APPROVE"), } mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) { - v := new(PullRequestReviewRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - review, _, err := client.PullRequests.CreateReview(context.Background(), "o", "r", 1, input) + ctx := t.Context() + review, _, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, input) if err != nil { t.Errorf("PullRequests.CreateReview returned error: %v", err) } - want := &PullRequestReview{ID: Int64(1)} - if !reflect.DeepEqual(review, want) { + want := &PullRequestReview{ID: Ptr(int64(1))} + if !cmp.Equal(review, want) { t.Errorf("PullRequests.CreateReview returned %+v, want %+v", review, want) } + + const methodName = "CreateReview" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.CreateReview(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_CreateReview_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.CreateReview(context.Background(), "%", "r", 1, &PullRequestReviewRequest{}) + ctx := t.Context() + _, _, err := client.PullRequests.CreateReview(ctx, "%", "r", 1, &PullRequestReviewRequest{}) testURLParseError(t, err) } +func TestPullRequestsService_CreateReview_badReview(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + path := "path/to/file.go" + body := "this is a comment body" + badReview := &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Side: Ptr("RIGHT"), + Line: Ptr(11), + }, { + Path: &path, + Body: &body, + Position: Ptr(1), + }}, + } + + _, _, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, badReview) + if err == nil { + t.Error("CreateReview badReview err = nil, want err") + } +} + +func TestPullRequestsService_CreateReview_addHeader(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + path := "path/to/file.go" + body := "this is a comment body" + left, right := "LEFT", "RIGHT" + line1, line2, line3 := 11, 22, 33 + input := &PullRequestReviewRequest{ + Comments: []*DraftReviewComment{{ + Path: &path, + Body: &body, + Side: &right, + Line: &line1, + }, { + Path: &path, + Body: &body, + Side: &left, + Line: &line2, + }, { + Path: &path, + Body: &body, + Side: &right, + Line: &line3, + }}, + } + + mux.HandleFunc("/repos/o/r/pulls/1/reviews", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + + _, _, err := client.PullRequests.CreateReview(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("CreateReview addHeader err = %v, want nil", err) + } +} + func TestPullRequestsService_UpdateReview(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/reviews/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - fmt.Fprintf(w, `{"id":1}`) + fmt.Fprint(w, `{"id":1}`) }) - got, _, err := client.PullRequests.UpdateReview(context.Background(), "o", "r", 1, 1, "updated_body") + ctx := t.Context() + got, _, err := client.PullRequests.UpdateReview(ctx, "o", "r", 1, 1, "updated_body") if err != nil { t.Errorf("PullRequests.UpdateReview returned error: %v", err) } - want := &PullRequestReview{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { + want := &PullRequestReview{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { t.Errorf("PullRequests.UpdateReview = %+v, want %+v", got, want) } + + const methodName = "UpdateReview" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.UpdateReview(ctx, "\n", "\n", -1, -1, "updated_body") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.UpdateReview(ctx, "o", "r", 1, 1, "updated_body") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_SubmitReview(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &PullRequestReviewRequest{ - Body: String("b"), - Event: String("APPROVE"), + input := PullRequestSubmitReviewRequest{ + Body: Ptr("b"), + Event: "APPROVE", } mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/events", func(w http.ResponseWriter, r *http.Request) { - v := new(PullRequestReviewRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - review, _, err := client.PullRequests.SubmitReview(context.Background(), "o", "r", 1, 1, input) + ctx := t.Context() + review, _, err := client.PullRequests.SubmitReview(ctx, "o", "r", 1, 1, input) if err != nil { t.Errorf("PullRequests.SubmitReview returned error: %v", err) } - want := &PullRequestReview{ID: Int64(1)} - if !reflect.DeepEqual(review, want) { + want := &PullRequestReview{ID: Ptr(int64(1))} + if !cmp.Equal(review, want) { t.Errorf("PullRequests.SubmitReview returned %+v, want %+v", review, want) } + + const methodName = "SubmitReview" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.SubmitReview(ctx, "\n", "\n", -1, -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.SubmitReview(ctx, "o", "r", 1, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_SubmitReview_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.SubmitReview(context.Background(), "%", "r", 1, 1, &PullRequestReviewRequest{}) + ctx := t.Context() + _, _, err := client.PullRequests.SubmitReview(ctx, "%", "r", 1, 1, PullRequestSubmitReviewRequest{}) testURLParseError(t, err) } func TestPullRequestsService_DismissReview(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &PullRequestReviewDismissalRequest{Message: String("m")} + input := PullRequestDismissReviewRequest{Message: "m", Event: Ptr("DISMISS")} mux.HandleFunc("/repos/o/r/pulls/1/reviews/1/dismissals", func(w http.ResponseWriter, r *http.Request) { - v := new(PullRequestReviewDismissalRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - review, _, err := client.PullRequests.DismissReview(context.Background(), "o", "r", 1, 1, input) + ctx := t.Context() + review, _, err := client.PullRequests.DismissReview(ctx, "o", "r", 1, 1, input) if err != nil { t.Errorf("PullRequests.DismissReview returned error: %v", err) } - want := &PullRequestReview{ID: Int64(1)} - if !reflect.DeepEqual(review, want) { + want := &PullRequestReview{ID: Ptr(int64(1))} + if !cmp.Equal(review, want) { t.Errorf("PullRequests.DismissReview returned %+v, want %+v", review, want) } + + const methodName = "ListReviews" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.DismissReview(ctx, "\n", "\n", -1, -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.DismissReview(ctx, "o", "r", 1, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_DismissReview_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.DismissReview(context.Background(), "%", "r", 1, 1, &PullRequestReviewDismissalRequest{}) + ctx := t.Context() + _, _, err := client.PullRequests.DismissReview(ctx, "%", "r", 1, 1, PullRequestDismissReviewRequest{}) testURLParseError(t, err) } diff --git a/github/pulls_test.go b/github/pulls_test.go index 09d60e3a3e4..926e0d17866 100644 --- a/github/pulls_test.go +++ b/github/pulls_test.go @@ -6,24 +6,21 @@ package github import ( - "context" - "encoding/json" "fmt" "io" "net/http" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestPullRequestsService_List(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview} mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) testFormValues(t, r, values{ "state": "closed", "head": "h", @@ -35,82 +32,120 @@ func TestPullRequestsService_List(t *testing.T) { fmt.Fprint(w, `[{"number":1}]`) }) - opt := &PullRequestListOptions{"closed", "h", "b", "created", "desc", ListOptions{Page: 2}} - pulls, _, err := client.PullRequests.List(context.Background(), "o", "r", opt) + opts := &PullRequestListOptions{"closed", "h", "b", "created", "desc", ListOptions{Page: 2}} + ctx := t.Context() + pulls, _, err := client.PullRequests.List(ctx, "o", "r", opts) if err != nil { t.Errorf("PullRequests.List returned error: %v", err) } - want := []*PullRequest{{Number: Int(1)}} - if !reflect.DeepEqual(pulls, want) { + want := []*PullRequest{{Number: Ptr(1)}} + if !cmp.Equal(pulls, want) { t.Errorf("PullRequests.List returned %+v, want %+v", pulls, want) } + + const methodName = "List" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.List(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.List(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_ListPullRequestsWithCommit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeListPullsOrBranchesForCommitPreview, mediaTypeDraftPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/repos/o/r/commits/sha/pulls", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeListPullsOrBranchesForCommitPreview) testFormValues(t, r, values{ - "state": "closed", - "head": "h", - "base": "b", - "sort": "created", - "direction": "desc", - "page": "2", + "page": "2", }) fmt.Fprint(w, `[{"number":1}]`) }) - opt := &PullRequestListOptions{"closed", "h", "b", "created", "desc", ListOptions{Page: 2}} - pulls, _, err := client.PullRequests.ListPullRequestsWithCommit(context.Background(), "o", "r", "sha", opt) + opts := &ListOptions{Page: 2} + ctx := t.Context() + pulls, _, err := client.PullRequests.ListPullRequestsWithCommit(ctx, "o", "r", "sha", opts) if err != nil { t.Errorf("PullRequests.ListPullRequestsWithCommit returned error: %v", err) } - want := []*PullRequest{{Number: Int(1)}} - if !reflect.DeepEqual(pulls, want) { + want := []*PullRequest{{Number: Ptr(1)}} + if !cmp.Equal(pulls, want) { t.Errorf("PullRequests.ListPullRequestsWithCommit returned %+v, want %+v", pulls, want) } + + const methodName = "ListPullRequestsWithCommit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListPullRequestsWithCommit(ctx, "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListPullRequestsWithCommit(ctx, "o", "r", "sha", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_List_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.List(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.PullRequests.List(ctx, "%", "r", nil) testURLParseError(t, err) } func TestPullRequestsService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview} mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) fmt.Fprint(w, `{"number":1}`) }) - pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + ctx := t.Context() + pull, _, err := client.PullRequests.Get(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.Get returned error: %v", err) } - want := &PullRequest{Number: Int(1)} - if !reflect.DeepEqual(pull, want) { + want := &PullRequest{Number: Ptr(1)} + if !cmp.Equal(pull, want) { t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.Get(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.Get(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_GetRaw_diff(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) const rawStr = "@@diff content" @@ -120,19 +155,34 @@ func TestPullRequestsService_GetRaw_diff(t *testing.T) { fmt.Fprint(w, rawStr) }) - got, _, err := client.PullRequests.GetRaw(context.Background(), "o", "r", 1, RawOptions{Diff}) + ctx := t.Context() + got, _, err := client.PullRequests.GetRaw(ctx, "o", "r", 1, RawOptions{Diff}) if err != nil { t.Fatalf("PullRequests.GetRaw returned error: %v", err) } want := rawStr if got != want { - t.Errorf("PullRequests.GetRaw returned %s want %s", got, want) + t.Errorf("PullRequests.GetRaw returned %v want %v", got, want) } + + const methodName = "GetRaw" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.GetRaw(ctx, "\n", "\n", -1, RawOptions{Diff}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.GetRaw(ctx, "o", "r", 1, RawOptions{Diff}) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_GetRaw_patch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) const rawStr = "@@patch content" @@ -142,21 +192,23 @@ func TestPullRequestsService_GetRaw_patch(t *testing.T) { fmt.Fprint(w, rawStr) }) - got, _, err := client.PullRequests.GetRaw(context.Background(), "o", "r", 1, RawOptions{Patch}) + ctx := t.Context() + got, _, err := client.PullRequests.GetRaw(ctx, "o", "r", 1, RawOptions{Patch}) if err != nil { t.Fatalf("PullRequests.GetRaw returned error: %v", err) } want := rawStr if got != want { - t.Errorf("PullRequests.GetRaw returned %s want %s", got, want) + t.Errorf("PullRequests.GetRaw returned %v want %v", got, want) } } func TestPullRequestsService_GetRaw_invalid(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.GetRaw(context.Background(), "o", "r", 1, RawOptions{100}) + ctx := t.Context() + _, _, err := client.PullRequests.GetRaw(ctx, "o", "r", 1, RawOptions{100}) if err == nil { t.Fatal("PullRequests.GetRaw should return error") } @@ -166,8 +218,8 @@ func TestPullRequestsService_GetRaw_invalid(t *testing.T) { } func TestPullRequestsService_Get_links(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -186,71 +238,73 @@ func TestPullRequestsService_Get_links(t *testing.T) { }`) }) - pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + ctx := t.Context() + pull, _, err := client.PullRequests.Get(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.Get returned error: %v", err) } want := &PullRequest{ - Number: Int(1), + Number: Ptr(1), Links: &PRLinks{ Self: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/1347"), }, HTML: &PRLink{ - HRef: String("https://github.com/octocat/Hello-World/pull/1347"), + HRef: Ptr("https://github.com/octocat/Hello-World/pull/1347"), }, Issue: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/issues/1347"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/issues/1347"), }, Comments: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/issues/1347/comments"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/issues/1347/comments"), }, ReviewComments: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments"), }, ReviewComment: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"), }, Commits: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/1347/commits"), }, Statuses: &PRLink{ - HRef: String("https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"), + HRef: Ptr("https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"), }, }, } - if !reflect.DeepEqual(pull, want) { + if !cmp.Equal(pull, want) { t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) } } func TestPullRequestsService_Get_headAndBase(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"number":1,"head":{"ref":"r2","repo":{"id":2}},"base":{"ref":"r1","repo":{"id":1}}}`) }) - pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + ctx := t.Context() + pull, _, err := client.PullRequests.Get(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.Get returned error: %v", err) } want := &PullRequest{ - Number: Int(1), + Number: Ptr(1), Head: &PullRequestBranch{ - Ref: String("r2"), - Repo: &Repository{ID: Int64(2)}, + Ref: Ptr("r2"), + Repo: &Repository{ID: Ptr(int64(2))}, }, Base: &PullRequestBranch{ - Ref: String("r1"), - Repo: &Repository{ID: Int64(1)}, + Ref: Ptr("r1"), + Repo: &Repository{ID: Ptr(int64(1))}, }, } - if !reflect.DeepEqual(pull, want) { + if !cmp.Equal(pull, want) { t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) } } func TestPullRequestsService_Get_urlFields(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -265,78 +319,89 @@ func TestPullRequestsService_Get_urlFields(t *testing.T) { "review_comment_url": "https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"}`) }) - pull, _, err := client.PullRequests.Get(context.Background(), "o", "r", 1) + ctx := t.Context() + pull, _, err := client.PullRequests.Get(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.Get returned error: %v", err) } want := &PullRequest{ - Number: Int(1), - URL: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347"), - HTMLURL: String("https://github.com/octocat/Hello-World/pull/1347"), - IssueURL: String("https://api.github.com/repos/octocat/Hello-World/issues/1347"), - StatusesURL: String("https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"), - DiffURL: String("https://github.com/octocat/Hello-World/pull/1347.diff"), - PatchURL: String("https://github.com/octocat/Hello-World/pull/1347.patch"), - ReviewCommentsURL: String("https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments"), - ReviewCommentURL: String("https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"), - } - - if !reflect.DeepEqual(pull, want) { + Number: Ptr(1), + URL: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/1347"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/pull/1347"), + IssueURL: Ptr("https://api.github.com/repos/octocat/Hello-World/issues/1347"), + StatusesURL: Ptr("https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e"), + DiffURL: Ptr("https://github.com/octocat/Hello-World/pull/1347.diff"), + PatchURL: Ptr("https://github.com/octocat/Hello-World/pull/1347.patch"), + ReviewCommentsURL: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/1347/comments"), + ReviewCommentURL: Ptr("https://api.github.com/repos/octocat/Hello-World/pulls/comments{/number}"), + } + + if !cmp.Equal(pull, want) { t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) } } func TestPullRequestsService_Get_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.Get(context.Background(), "%", "r", 1) + ctx := t.Context() + _, _, err := client.PullRequests.Get(ctx, "%", "r", 1) testURLParseError(t, err) } func TestPullRequestsService_Create(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &NewPullRequest{Title: String("t")} + input := CreatePullRequest{Title: Ptr("t")} mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { - v := new(NewPullRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeDraftPreview} - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":1}`) }) - pull, _, err := client.PullRequests.Create(context.Background(), "o", "r", input) + ctx := t.Context() + pull, _, err := client.PullRequests.Create(ctx, "o", "r", input) if err != nil { t.Errorf("PullRequests.Create returned error: %v", err) } - want := &PullRequest{Number: Int(1)} - if !reflect.DeepEqual(pull, want) { + want := &PullRequest{Number: Ptr(1)} + if !cmp.Equal(pull, want) { t.Errorf("PullRequests.Create returned %+v, want %+v", pull, want) } + + const methodName = "Create" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.Create(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.Create(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_Create_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.Create(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.PullRequests.Create(ctx, "%", "r", CreatePullRequest{}) testURLParseError(t, err) } func TestPullRequestsService_UpdateBranch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/update-branch", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -348,91 +413,123 @@ func TestPullRequestsService_UpdateBranch(t *testing.T) { }`) }) - opts := &PullReqestBranchUpdateOptions{ - ExpectedHeadSHA: String("s"), + opts := &PullRequestBranchUpdateOptions{ + ExpectedHeadSHA: Ptr("s"), } - pull, _, err := client.PullRequests.UpdateBranch(context.Background(), "o", "r", 1, opts) + ctx := t.Context() + pull, _, err := client.PullRequests.UpdateBranch(ctx, "o", "r", 1, opts) if err != nil { t.Errorf("PullRequests.UpdateBranch returned error: %v", err) } want := &PullRequestBranchUpdateResponse{ - Message: String("Updating pull request branch."), - URL: String("https://github.com/repos/o/r/pulls/1"), + Message: Ptr("Updating pull request branch."), + URL: Ptr("https://github.com/repos/o/r/pulls/1"), } - if !reflect.DeepEqual(pull, want) { + if !cmp.Equal(pull, want) { t.Errorf("PullRequests.UpdateBranch returned %+v, want %+v", pull, want) } + + const methodName = "UpdateBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.UpdateBranch(ctx, "\n", "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.UpdateBranch(ctx, "o", "r", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) tests := []struct { input *PullRequest sendResponse string - - wantUpdate string - want *PullRequest + want *PullRequest + wantUpdate *pullRequestUpdate }{ { - input: &PullRequest{Title: String("t")}, + input: &PullRequest{Title: Ptr("t")}, sendResponse: `{"number":1}`, - wantUpdate: `{"title":"t"}`, - want: &PullRequest{Number: Int(1)}, + want: &PullRequest{Number: Ptr(1)}, + wantUpdate: &pullRequestUpdate{ + Title: Ptr("t"), + }, }, { // base update - input: &PullRequest{Base: &PullRequestBranch{Ref: String("master")}}, + input: &PullRequest{Base: &PullRequestBranch{Ref: Ptr("master")}}, sendResponse: `{"number":1,"base":{"ref":"master"}}`, - wantUpdate: `{"base":"master"}`, want: &PullRequest{ - Number: Int(1), - Base: &PullRequestBranch{Ref: String("master")}, + Number: Ptr(1), + Base: &PullRequestBranch{Ref: Ptr("master")}, + }, + wantUpdate: &pullRequestUpdate{ + Base: Ptr("master"), }, }, } for i, tt := range tests { madeRequest := false - wantAcceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/%v", i), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - testBody(t, r, tt.wantUpdate+"\n") - io.WriteString(w, tt.sendResponse) + testJSONBody(t, r, tt.wantUpdate) + _, err := io.WriteString(w, tt.sendResponse) + assertNilError(t, err) madeRequest = true }) - pull, _, err := client.PullRequests.Edit(context.Background(), "o", "r", i, tt.input) + ctx := t.Context() + pull, _, err := client.PullRequests.Edit(ctx, "o", "r", i, tt.input) if err != nil { - t.Errorf("%d: PullRequests.Edit returned error: %v", i, err) + t.Errorf("%v: PullRequests.Edit returned error: %v", i, err) } - if !reflect.DeepEqual(pull, tt.want) { - t.Errorf("%d: PullRequests.Edit returned %+v, want %+v", i, pull, tt.want) + if !cmp.Equal(pull, tt.want) { + t.Errorf("%v: PullRequests.Edit returned %+v, want %+v", i, pull, tt.want) } if !madeRequest { - t.Errorf("%d: PullRequest.Edit did not make the expected request", i) + t.Errorf("%v: PullRequest.Edit did not make the expected request", i) } + + const methodName = "Edit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.Edit(ctx, "\n", "\n", -i, tt.input) + return err + }) } + testNewRequestAndDoFailure(t, "Edit", client, func() (*Response, error) { + got, resp, err := client.PullRequests.Edit(t.Context(), "o", "r", 1, &PullRequest{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", "Edit", got) + } + return resp, err + }) } func TestPullRequestsService_Edit_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.PullRequests.Edit(context.Background(), "%", "r", 1, &PullRequest{}) + ctx := t.Context() + _, _, err := client.PullRequests.Edit(ctx, "%", "r", 1, &PullRequest{}) testURLParseError(t, err) } func TestPullRequestsService_ListCommits(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/commits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -458,38 +555,53 @@ func TestPullRequestsService_ListCommits(t *testing.T) { ]`) }) - opt := &ListOptions{Page: 2} - commits, _, err := client.PullRequests.ListCommits(context.Background(), "o", "r", 1, opt) + opts := &ListOptions{Page: 2} + ctx := t.Context() + commits, _, err := client.PullRequests.ListCommits(ctx, "o", "r", 1, opts) if err != nil { t.Errorf("PullRequests.ListCommits returned error: %v", err) } want := []*RepositoryCommit{ { - SHA: String("3"), - Parents: []Commit{ + SHA: Ptr("3"), + Parents: []*Commit{ { - SHA: String("2"), + SHA: Ptr("2"), }, }, }, { - SHA: String("2"), - Parents: []Commit{ + SHA: Ptr("2"), + Parents: []*Commit{ { - SHA: String("1"), + SHA: Ptr("1"), }, }, }, } - if !reflect.DeepEqual(commits, want) { + if !cmp.Equal(commits, want) { t.Errorf("PullRequests.ListCommits returned %+v, want %+v", commits, want) } + + const methodName = "ListCommits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListCommits(ctx, "\n", "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListCommits(ctx, "o", "r", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_ListFiles(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/files", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -517,61 +629,91 @@ func TestPullRequestsService_ListFiles(t *testing.T) { ]`) }) - opt := &ListOptions{Page: 2} - commitFiles, _, err := client.PullRequests.ListFiles(context.Background(), "o", "r", 1, opt) + opts := &ListOptions{Page: 2} + ctx := t.Context() + commitFiles, _, err := client.PullRequests.ListFiles(ctx, "o", "r", 1, opts) if err != nil { t.Errorf("PullRequests.ListFiles returned error: %v", err) } want := []*CommitFile{ { - SHA: String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), - Filename: String("file1.txt"), - Additions: Int(103), - Deletions: Int(21), - Changes: Int(124), - Status: String("added"), - Patch: String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), + SHA: Ptr("6dcb09b5b57875f334f61aebed695e2e4193db5e"), + Filename: Ptr("file1.txt"), + Additions: Ptr(103), + Deletions: Ptr(21), + Changes: Ptr(124), + Status: Ptr("added"), + Patch: Ptr("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), }, { - SHA: String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), - Filename: String("file2.txt"), - Additions: Int(5), - Deletions: Int(3), - Changes: Int(103), - Status: String("modified"), - Patch: String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), + SHA: Ptr("f61aebed695e2e4193db5e6dcb09b5b57875f334"), + Filename: Ptr("file2.txt"), + Additions: Ptr(5), + Deletions: Ptr(3), + Changes: Ptr(103), + Status: Ptr("modified"), + Patch: Ptr("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), }, } - if !reflect.DeepEqual(commitFiles, want) { + if !cmp.Equal(commitFiles, want) { t.Errorf("PullRequests.ListFiles returned %+v, want %+v", commitFiles, want) } + + const methodName = "ListFiles" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.ListFiles(ctx, "\n", "\n", -1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.ListFiles(ctx, "o", "r", 1, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_IsMerged(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - isMerged, _, err := client.PullRequests.IsMerged(context.Background(), "o", "r", 1) + ctx := t.Context() + isMerged, _, err := client.PullRequests.IsMerged(ctx, "o", "r", 1) if err != nil { t.Errorf("PullRequests.IsMerged returned error: %v", err) } want := true - if !reflect.DeepEqual(isMerged, want) { + if !cmp.Equal(isMerged, want) { t.Errorf("PullRequests.IsMerged returned %+v, want %+v", isMerged, want) } + + const methodName = "IsMerged" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.IsMerged(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.IsMerged(ctx, "o", "r", 1) + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestPullRequestsService_Merge(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -584,45 +726,70 @@ func TestPullRequestsService_Merge(t *testing.T) { }) options := &PullRequestOptions{MergeMethod: "rebase"} - merge, _, err := client.PullRequests.Merge(context.Background(), "o", "r", 1, "merging pull request", options) + ctx := t.Context() + merge, _, err := client.PullRequests.Merge(ctx, "o", "r", 1, "merging pull request", options) if err != nil { t.Errorf("PullRequests.Merge returned error: %v", err) } want := &PullRequestMergeResult{ - SHA: String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), - Merged: Bool(true), - Message: String("Pull Request successfully merged"), + SHA: Ptr("6dcb09b5b57875f334f61aebed695e2e4193db5e"), + Merged: Ptr(true), + Message: Ptr("Pull Request successfully merged"), } - if !reflect.DeepEqual(merge, want) { + if !cmp.Equal(merge, want) { t.Errorf("PullRequests.Merge returned %+v, want %+v", merge, want) } + + const methodName = "Merge" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.PullRequests.Merge(ctx, "\n", "\n", -1, "\n", options) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.PullRequests.Merge(ctx, "o", "r", 1, "merging pull request", options) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } // Test that different merge options produce expected PUT requests. See issue https://github.com/google/go-github/issues/500. func TestPullRequestsService_Merge_options(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) tests := []struct { - options *PullRequestOptions - wantBody string + options *PullRequestOptions + want pullRequestMergeRequest }{ { - options: nil, - wantBody: `{"commit_message":"merging pull request"}`, + options: nil, + want: pullRequestMergeRequest{ + CommitMessage: Ptr("merging pull request"), + }, }, { - options: &PullRequestOptions{}, - wantBody: `{"commit_message":"merging pull request"}`, + options: &PullRequestOptions{}, + want: pullRequestMergeRequest{ + CommitMessage: Ptr("merging pull request"), + }, }, { - options: &PullRequestOptions{MergeMethod: "rebase"}, - wantBody: `{"commit_message":"merging pull request","merge_method":"rebase"}`, + options: &PullRequestOptions{MergeMethod: "rebase"}, + want: pullRequestMergeRequest{ + CommitMessage: Ptr("merging pull request"), + MergeMethod: "rebase", + }, }, { - options: &PullRequestOptions{SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e"}, - wantBody: `{"commit_message":"merging pull request","sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}`, + options: &PullRequestOptions{SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e"}, + want: pullRequestMergeRequest{ + CommitMessage: Ptr("merging pull request"), + SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e", + }, }, { options: &PullRequestOptions{ @@ -630,20 +797,65 @@ func TestPullRequestsService_Merge_options(t *testing.T) { SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e", MergeMethod: "squash", }, - wantBody: `{"commit_message":"merging pull request","commit_title":"Extra detail","merge_method":"squash","sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}`, + want: pullRequestMergeRequest{ + CommitMessage: Ptr("merging pull request"), + SHA: "6dcb09b5b57875f334f61aebed695e2e4193db5e", + CommitTitle: "Extra detail", + MergeMethod: "squash", + }, + }, + { + options: &PullRequestOptions{ + DontDefaultIfBlank: true, + }, + want: pullRequestMergeRequest{ + CommitMessage: Ptr("merging pull request"), + }, }, } for i, test := range tests { madeRequest := false - mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/%d/merge", i), func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/%v/merge", i), func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testBody(t, r, test.wantBody+"\n") + testJSONBody(t, r, test.want) madeRequest = true }) - _, _, _ = client.PullRequests.Merge(context.Background(), "o", "r", i, "merging pull request", test.options) + ctx := t.Context() + _, _, _ = client.PullRequests.Merge(ctx, "o", "r", i, "merging pull request", test.options) if !madeRequest { - t.Errorf("%d: PullRequests.Merge(%#v): expected request was not made", i, test.options) + t.Errorf("%v: PullRequests.Merge(%#v): expected request was not made", i, test.options) } } } + +func TestPullRequestsService_Merge_Blank_Message(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + madeRequest := false + want := &pullRequestMergeRequest{} + mux.HandleFunc("/repos/o/r/pulls/1/merge", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, want) + madeRequest = true + }) + + ctx := t.Context() + _, _, _ = client.PullRequests.Merge(ctx, "o", "r", 1, "", nil) + if !madeRequest { + t.Error("TestPullRequestsService_Merge_Blank_Message #1 did not make request") + } + + madeRequest = false + opts := PullRequestOptions{ + DontDefaultIfBlank: true, + } + want = &pullRequestMergeRequest{ + CommitMessage: Ptr(""), + } + _, _, _ = client.PullRequests.Merge(ctx, "o", "r", 1, "", &opts) + if !madeRequest { + t.Error("TestPullRequestsService_Merge_Blank_Message #2 did not make request") + } +} diff --git a/github/pulls_threads.go b/github/pulls_threads.go new file mode 100644 index 00000000000..23e924d88fb --- /dev/null +++ b/github/pulls_threads.go @@ -0,0 +1,17 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +// PullRequestThread represents a thread of comments on a pull request. +type PullRequestThread struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Comments []*PullRequestComment `json:"comments,omitempty"` +} + +func (p PullRequestThread) String() string { + return Stringify(p) +} diff --git a/github/rate_limit.go b/github/rate_limit.go new file mode 100644 index 00000000000..a63f8382d00 --- /dev/null +++ b/github/rate_limit.go @@ -0,0 +1,140 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import "context" + +// RateLimitService provides access to rate limit functions in the GitHub API. +type RateLimitService service + +// Rate represents the rate limit for the current client. +type Rate struct { + // The maximum number of requests that you can make per hour. + Limit int `json:"limit"` + + // The number of requests remaining in the current rate limit window. + Remaining int `json:"remaining"` + + // The number of requests you have made in the current rate limit window. + Used int `json:"used"` + + // The time at which the current rate limit window resets, in UTC epoch seconds. + Reset Timestamp `json:"reset"` + + // The rate limit resource that the request counted against. + // For more information about the different resources, see REST API endpoints for rate limits. + // GitHub API docs: https://docs.github.com/rest/rate-limit/rate-limit?apiVersion=2022-11-28#get-rate-limit-status-for-the-authenticated-user + Resource string `json:"resource,omitempty"` +} + +func (r Rate) String() string { + return Stringify(r) +} + +// RateLimits represents the rate limits for the current client. +type RateLimits struct { + // The rate limit for non-search API requests. Unauthenticated + // requests are limited to 60 per hour. Authenticated requests are + // limited to 5,000 per hour. + // + // GitHub API docs: https://docs.github.com/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limiting + Core *Rate `json:"core"` + + // The rate limit for search API requests. Unauthenticated requests + // are limited to 10 requests per minutes. Authenticated requests are + // limited to 30 per minute. + // + // GitHub API docs: https://docs.github.com/rest/search?apiVersion=2022-11-28#rate-limit + Search *Rate `json:"search"` + + // GitHub API docs: https://docs.github.com/en/graphql/overview/resource-limitations#rate-limit + GraphQL *Rate `json:"graphql"` + + // GitHub API docs: https://docs.github.com/rest/rate-limit?apiVersion=2022-11-28 + IntegrationManifest *Rate `json:"integration_manifest"` + + SourceImport *Rate `json:"source_import"` + CodeScanningUpload *Rate `json:"code_scanning_upload"` + ActionsRunnerRegistration *Rate `json:"actions_runner_registration"` + SCIM *Rate `json:"scim"` + DependencySnapshots *Rate `json:"dependency_snapshots"` + CodeSearch *Rate `json:"code_search"` + AuditLog *Rate `json:"audit_log"` + DependencySBOM *Rate `json:"dependency_sbom"` +} + +func (r RateLimits) String() string { + return Stringify(r) +} + +// Get returns the rate limits for the current client. +// +// GitHub API docs: https://docs.github.com/rest/rate-limit/rate-limit?apiVersion=2022-11-28#get-rate-limit-status-for-the-authenticated-user +// +//meta:operation GET /rate_limit +func (s *RateLimitService) Get(ctx context.Context) (*RateLimits, *Response, error) { + // This resource is not subject to rate limits. + if !s.client.disableRateLimitCheck { + ctx = context.WithValue(ctx, BypassRateLimitCheck, true) + } + + req, err := s.client.NewRequest(ctx, "GET", "rate_limit", nil) + if err != nil { + return nil, nil, err + } + + response := new(struct { + Resources *RateLimits `json:"resources"` + }) + + resp, err := s.client.Do(req, response) + if err != nil { + return nil, resp, err + } + + if response.Resources != nil { + s.client.rateMu.Lock() + if response.Resources.Core != nil { + s.client.rateLimits[CoreCategory] = *response.Resources.Core + } + if response.Resources.Search != nil { + s.client.rateLimits[SearchCategory] = *response.Resources.Search + } + if response.Resources.GraphQL != nil { + s.client.rateLimits[GraphqlCategory] = *response.Resources.GraphQL + } + if response.Resources.IntegrationManifest != nil { + s.client.rateLimits[IntegrationManifestCategory] = *response.Resources.IntegrationManifest + } + if response.Resources.SourceImport != nil { + s.client.rateLimits[SourceImportCategory] = *response.Resources.SourceImport + } + if response.Resources.CodeScanningUpload != nil { + s.client.rateLimits[CodeScanningUploadCategory] = *response.Resources.CodeScanningUpload + } + if response.Resources.ActionsRunnerRegistration != nil { + s.client.rateLimits[ActionsRunnerRegistrationCategory] = *response.Resources.ActionsRunnerRegistration + } + if response.Resources.SCIM != nil { + s.client.rateLimits[ScimCategory] = *response.Resources.SCIM + } + if response.Resources.DependencySnapshots != nil { + s.client.rateLimits[DependencySnapshotsCategory] = *response.Resources.DependencySnapshots + } + if response.Resources.CodeSearch != nil { + s.client.rateLimits[CodeSearchCategory] = *response.Resources.CodeSearch + } + if response.Resources.AuditLog != nil { + s.client.rateLimits[AuditLogCategory] = *response.Resources.AuditLog + } + if response.Resources.DependencySBOM != nil { + s.client.rateLimits[DependencySBOMCategory] = *response.Resources.DependencySBOM + } + s.client.rateMu.Unlock() + } + + return response.Resources, resp, nil +} diff --git a/github/rate_limit_test.go b/github/rate_limit_test.go new file mode 100644 index 00000000000..67fde2194c4 --- /dev/null +++ b/github/rate_limit_test.go @@ -0,0 +1,429 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestRateLimits_String(t *testing.T) { + t.Parallel() + v := RateLimits{ + Core: &Rate{}, + Search: &Rate{}, + GraphQL: &Rate{}, + IntegrationManifest: &Rate{}, + SourceImport: &Rate{}, + CodeScanningUpload: &Rate{}, + ActionsRunnerRegistration: &Rate{}, + SCIM: &Rate{}, + DependencySnapshots: &Rate{}, + CodeSearch: &Rate{}, + AuditLog: &Rate{}, + DependencySBOM: &Rate{}, + } + want := `github.RateLimits{Core:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, Search:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, GraphQL:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, IntegrationManifest:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, SourceImport:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, CodeScanningUpload:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, ActionsRunnerRegistration:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, SCIM:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, DependencySnapshots:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, CodeSearch:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, AuditLog:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}, DependencySBOM:github.Rate{Limit:0, Remaining:0, Used:0, Reset:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, Resource:""}}` + if got := v.String(); got != want { + t.Errorf("RateLimits.String = %v, want %v", got, want) + } +} + +func TestRateLimits(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"resources":{ + "core": {"limit":2,"remaining":1,"used":1,"reset":1372700873}, + "search": {"limit":3,"remaining":2,"used":1,"reset":1372700874}, + "graphql": {"limit":4,"remaining":3,"used":1,"reset":1372700875}, + "integration_manifest": {"limit":5,"remaining":4,"used":1,"reset":1372700876}, + "source_import": {"limit":6,"remaining":5,"used":1,"reset":1372700877}, + "code_scanning_upload": {"limit":7,"remaining":6,"used":1,"reset":1372700878}, + "actions_runner_registration": {"limit":8,"remaining":7,"used":1,"reset":1372700879}, + "scim": {"limit":9,"remaining":8,"used":1,"reset":1372700880}, + "dependency_snapshots": {"limit":10,"remaining":9,"used":1,"reset":1372700881}, + "code_search": {"limit":11,"remaining":10,"used":1,"reset":1372700882}, + "audit_log": {"limit": 12,"remaining":11,"used":1,"reset":1372700883}, + "dependency_sbom": {"limit": 100,"remaining":100,"used":0,"reset":1372700884} + }}`) + }) + + ctx := t.Context() + rate, _, err := client.RateLimit.Get(ctx) + if err != nil { + t.Errorf("RateLimit.Get returned error: %v", err) + } + + want := &RateLimits{ + Core: &Rate{ + Limit: 2, + Remaining: 1, + Used: 1, + Reset: *refTimestamp(1372700873), + }, + Search: &Rate{ + Limit: 3, + Remaining: 2, + Used: 1, + Reset: *refTimestamp(1372700874), + }, + GraphQL: &Rate{ + Limit: 4, + Remaining: 3, + Used: 1, + Reset: *refTimestamp(1372700875), + }, + IntegrationManifest: &Rate{ + Limit: 5, + Remaining: 4, + Used: 1, + Reset: *refTimestamp(1372700876), + }, + SourceImport: &Rate{ + Limit: 6, + Remaining: 5, + Used: 1, + Reset: *refTimestamp(1372700877), + }, + CodeScanningUpload: &Rate{ + Limit: 7, + Remaining: 6, + Used: 1, + Reset: *refTimestamp(1372700878), + }, + ActionsRunnerRegistration: &Rate{ + Limit: 8, + Remaining: 7, + Used: 1, + Reset: *refTimestamp(1372700879), + }, + SCIM: &Rate{ + Limit: 9, + Remaining: 8, + Used: 1, + Reset: *refTimestamp(1372700880), + }, + DependencySnapshots: &Rate{ + Limit: 10, + Remaining: 9, + Used: 1, + Reset: *refTimestamp(1372700881), + }, + CodeSearch: &Rate{ + Limit: 11, + Remaining: 10, + Used: 1, + Reset: *refTimestamp(1372700882), + }, + AuditLog: &Rate{ + Limit: 12, + Remaining: 11, + Used: 1, + Reset: *refTimestamp(1372700883), + }, + DependencySBOM: &Rate{ + Limit: 100, + Remaining: 100, + Used: 0, + Reset: *refTimestamp(1372700884), + }, + } + if !cmp.Equal(rate, want) { + t.Errorf("RateLimits returned %+v, want %+v", rate, want) + } + tests := []struct { + category RateLimitCategory + rate *Rate + }{ + { + category: CoreCategory, + rate: want.Core, + }, + { + category: SearchCategory, + rate: want.Search, + }, + { + category: GraphqlCategory, + rate: want.GraphQL, + }, + { + category: IntegrationManifestCategory, + rate: want.IntegrationManifest, + }, + { + category: SourceImportCategory, + rate: want.SourceImport, + }, + { + category: CodeScanningUploadCategory, + rate: want.CodeScanningUpload, + }, + { + category: ActionsRunnerRegistrationCategory, + rate: want.ActionsRunnerRegistration, + }, + { + category: ScimCategory, + rate: want.SCIM, + }, + { + category: DependencySnapshotsCategory, + rate: want.DependencySnapshots, + }, + { + category: CodeSearchCategory, + rate: want.CodeSearch, + }, + { + category: AuditLogCategory, + rate: want.AuditLog, + }, + { + category: DependencySBOMCategory, + rate: want.DependencySBOM, + }, + } + + for _, tt := range tests { + if got, want := client.rateLimits[tt.category], *tt.rate; got != want { + t.Errorf("client.rateLimits[%v] is %+v, want %+v", tt.category, got, want) + } + } +} + +func TestRateLimits_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "RateLimits" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.RateLimit.Get(ctx) + return resp, err + }) +} + +func TestRateLimits_overQuota(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + client.rateLimits[CoreCategory] = Rate{ + Limit: 1, + Remaining: 0, + Used: 1, + Reset: Timestamp{time.Now().Add(time.Hour).Local()}, + } + mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, `{"resources":{ + "core": {"limit":2,"remaining":1,"used":1,"reset":1372700873}, + "search": {"limit":3,"remaining":2,"used":1,"reset":1372700874}, + "graphql": {"limit":4,"remaining":3,"used":1,"reset":1372700875}, + "integration_manifest": {"limit":5,"remaining":4,"used":1,"reset":1372700876}, + "source_import": {"limit":6,"remaining":5,"used":1,"reset":1372700877}, + "code_scanning_upload": {"limit":7,"remaining":6,"used":1,"reset":1372700878}, + "actions_runner_registration": {"limit":8,"remaining":7,"used":1,"reset":1372700879}, + "scim": {"limit":9,"remaining":8,"used":1,"reset":1372700880}, + "dependency_snapshots": {"limit":10,"remaining":9,"used":1,"reset":1372700881}, + "code_search": {"limit":11,"remaining":10,"used":1,"reset":1372700882}, + "audit_log": {"limit":12,"remaining":11,"used":1,"reset":1372700883}, + "dependency_sbom": {"limit":13,"remaining":12,"used":1,"reset":1372700884} + }}`) + }) + + ctx := t.Context() + rate, _, err := client.RateLimit.Get(ctx) + if err != nil { + t.Errorf("RateLimit.Get returned error: %v", err) + } + + want := &RateLimits{ + Core: &Rate{ + Limit: 2, + Remaining: 1, + Used: 1, + Reset: *refTimestamp(1372700873), + }, + Search: &Rate{ + Limit: 3, + Remaining: 2, + Used: 1, + Reset: *refTimestamp(1372700874), + }, + GraphQL: &Rate{ + Limit: 4, + Remaining: 3, + Used: 1, + Reset: *refTimestamp(1372700875), + }, + IntegrationManifest: &Rate{ + Limit: 5, + Remaining: 4, + Used: 1, + Reset: *refTimestamp(1372700876), + }, + SourceImport: &Rate{ + Limit: 6, + Remaining: 5, + Used: 1, + Reset: *refTimestamp(1372700877), + }, + CodeScanningUpload: &Rate{ + Limit: 7, + Remaining: 6, + Used: 1, + Reset: *refTimestamp(1372700878), + }, + ActionsRunnerRegistration: &Rate{ + Limit: 8, + Remaining: 7, + Used: 1, + Reset: *refTimestamp(1372700879), + }, + SCIM: &Rate{ + Limit: 9, + Remaining: 8, + Used: 1, + Reset: *refTimestamp(1372700880), + }, + DependencySnapshots: &Rate{ + Limit: 10, + Remaining: 9, + Used: 1, + Reset: *refTimestamp(1372700881), + }, + CodeSearch: &Rate{ + Limit: 11, + Remaining: 10, + Used: 1, + Reset: *refTimestamp(1372700882), + }, + AuditLog: &Rate{ + Limit: 12, + Remaining: 11, + Used: 1, + Reset: *refTimestamp(1372700883), + }, + DependencySBOM: &Rate{ + Limit: 13, + Remaining: 12, + Used: 1, + Reset: *refTimestamp(1372700884), + }, + } + if !cmp.Equal(rate, want) { + t.Errorf("RateLimits returned %+v, want %+v", rate, want) + } + + tests := []struct { + category RateLimitCategory + rate *Rate + }{ + { + category: CoreCategory, + rate: want.Core, + }, + { + category: SearchCategory, + rate: want.Search, + }, + { + category: GraphqlCategory, + rate: want.GraphQL, + }, + { + category: IntegrationManifestCategory, + rate: want.IntegrationManifest, + }, + { + category: SourceImportCategory, + rate: want.SourceImport, + }, + { + category: CodeScanningUploadCategory, + rate: want.CodeScanningUpload, + }, + { + category: ActionsRunnerRegistrationCategory, + rate: want.ActionsRunnerRegistration, + }, + { + category: ScimCategory, + rate: want.SCIM, + }, + { + category: DependencySnapshotsCategory, + rate: want.DependencySnapshots, + }, + { + category: CodeSearchCategory, + rate: want.CodeSearch, + }, + { + category: AuditLogCategory, + rate: want.AuditLog, + }, + { + category: DependencySBOMCategory, + rate: want.DependencySBOM, + }, + } + for _, tt := range tests { + if got, want := client.rateLimits[tt.category], *tt.rate; got != want { + t.Errorf("client.rateLimits[%v] is %+v, want %+v", tt.category, got, want) + } + } +} + +func TestRateLimits_bypassRateLimitCheckContext(t *testing.T) { + t.Parallel() + tests := []struct { + name string + disableRateLimitCheck bool + wantBypassValue any + }{ + { + name: "DisableRateLimitCheck disabled", + disableRateLimitCheck: false, + wantBypassValue: true, + }, + { + name: "DisableRateLimitCheck enabled", + disableRateLimitCheck: true, + wantBypassValue: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.disableRateLimitCheck = tt.disableRateLimitCheck + + mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"resources":{}}`) + }) + + _, resp, err := client.RateLimit.Get(t.Context()) + if err != nil { + t.Errorf("RateLimit.Get returned error: %v", err) + } + + if got, want := resp.Request.Context().Value(BypassRateLimitCheck), tt.wantBypassValue; got != want { + t.Errorf("Request context bypass value = %v, want %v", got, want) + } + }) + } +} diff --git a/github/reactions.go b/github/reactions.go index 0865f8cdca4..85b6e831940 100644 --- a/github/reactions.go +++ b/github/reactions.go @@ -13,7 +13,7 @@ import ( // ReactionsService provides access to the reactions-related functions in the // GitHub API. // -// GitHub API docs: https://developer.github.com/v3/reactions/ +// GitHub API docs: https://docs.github.com/rest/reactions?apiVersion=2022-11-28 type ReactionsService service // Reaction represents a GitHub reaction. @@ -24,8 +24,9 @@ type Reaction struct { NodeID *string `json:"node_id,omitempty"` // Content is the type of reaction. // Possible values are: - // "+1", "-1", "laugh", "confused", "heart", "hooray". - Content *string `json:"content,omitempty"` + // "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". + Content *string `json:"content,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` } // Reactions represents a summary of GitHub reactions. @@ -37,6 +38,8 @@ type Reactions struct { Confused *int `json:"confused,omitempty"` Heart *int `json:"heart,omitempty"` Hooray *int `json:"hooray,omitempty"` + Rocket *int `json:"rocket,omitempty"` + Eyes *int `json:"eyes,omitempty"` URL *string `json:"url,omitempty"` } @@ -44,26 +47,37 @@ func (r Reaction) String() string { return Stringify(r) } +// ListReactionOptions specifies the optional parameters to the list reactions endpoints. +type ListReactionOptions struct { + // Content restricts the returned comment reactions to only those with the given type. + // Omit this parameter to list all reactions to a commit comment. + // Possible values are: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". + Content string `url:"content,omitempty"` + + ListOptions +} + // ListCommentReactions lists the reactions for a commit comment. // -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment -func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-a-commit-comment +// +//meta:operation GET /repos/{owner}/{repo}/comments/{comment_id}/reactions +func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -74,23 +88,24 @@ func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo // CreateCommentReaction creates a reaction for a commit comment. // Note that if you have already created a reaction of type content, the // previously created reaction will be returned with Status: 200 OK. -// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-a-commit-comment // -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment -func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { +//meta:operation POST /repos/{owner}/{repo}/comments/{comment_id}/reactions +func (s *ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) + var m *Reaction + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -98,26 +113,49 @@ func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo return m, resp, nil } +// DeleteCommentReaction deletes the reaction for a commit comment. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-commit-comment-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteCommentReaction(ctx context.Context, owner, repo string, commentID, reactionID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions/%v", owner, repo, commentID, reactionID) + + return s.deleteReaction(ctx, u) +} + +// DeleteCommentReactionByID deletes the reaction for a commit comment by repository ID. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-commit-comment-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteCommentReactionByID(ctx context.Context, repoID, commentID, reactionID int64) (*Response, error) { + u := fmt.Sprintf("repositories/%v/comments/%v/reactions/%v", repoID, commentID, reactionID) + + return s.deleteReaction(ctx, u) +} + // ListIssueReactions lists the reactions for an issue. // -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue -func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Reaction, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-an-issue +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/reactions +func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -128,23 +166,24 @@ func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo s // CreateIssueReaction creates a reaction for an issue. // Note that if you have already created a reaction of type content, the // previously created reaction will be returned with Status: 200 OK. -// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue // -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue -func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) { +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/reactions +func (s *ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) + var m *Reaction + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -152,26 +191,49 @@ func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo s return m, resp, nil } +// DeleteIssueReaction deletes the reaction to an issue. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id} +func (s *ReactionsService) DeleteIssueReaction(ctx context.Context, owner, repo string, issueNumber int, reactionID int64) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/issues/%v/reactions/%v", owner, repo, issueNumber, reactionID) + + return s.deleteReaction(ctx, url) +} + +// DeleteIssueReactionByID deletes the reaction to an issue by repository ID. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id} +func (s *ReactionsService) DeleteIssueReactionByID(ctx context.Context, repoID, issueNumber int, reactionID int64) (*Response, error) { + url := fmt.Sprintf("repositories/%v/issues/%v/reactions/%v", repoID, issueNumber, reactionID) + + return s.deleteReaction(ctx, url) +} + // ListIssueCommentReactions lists the reactions for an issue comment. // -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment -func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-an-issue-comment +// +//meta:operation GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions +func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -182,23 +244,24 @@ func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, // CreateIssueCommentReaction creates a reaction for an issue comment. // Note that if you have already created a reaction of type content, the // previously created reaction will be returned with Status: 200 OK. -// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-an-issue-comment // -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment -func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { +//meta:operation POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions +func (s *ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) + var m *Reaction + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -206,26 +269,49 @@ func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, return m, resp, nil } +// DeleteIssueCommentReaction deletes the reaction to an issue comment. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-comment-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteIssueCommentReaction(ctx context.Context, owner, repo string, commentID, reactionID int64) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions/%v", owner, repo, commentID, reactionID) + + return s.deleteReaction(ctx, url) +} + +// DeleteIssueCommentReactionByID deletes the reaction to an issue comment by repository ID. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-comment-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteIssueCommentReactionByID(ctx context.Context, repoID, commentID, reactionID int64) (*Response, error) { + url := fmt.Sprintf("repositories/%v/issues/comments/%v/reactions/%v", repoID, commentID, reactionID) + + return s.deleteReaction(ctx, url) +} + // ListPullRequestCommentReactions lists the reactions for a pull request review comment. // -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment -func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-a-pull-request-review-comment +// +//meta:operation GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions +func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -236,23 +322,24 @@ func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, // CreatePullRequestCommentReaction creates a reaction for a pull request review comment. // Note that if you have already created a reaction of type content, the // previously created reaction will be returned with Status: 200 OK. -// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-a-pull-request-review-comment // -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment -func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { +//meta:operation POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions +func (s *ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. req.Header.Set("Accept", mediaTypeReactionsPreview) - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) + var m *Reaction + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -260,17 +347,43 @@ func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, return m, resp, nil } +// DeletePullRequestCommentReaction deletes the reaction to a pull request review comment. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-pull-request-comment-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id} +func (s *ReactionsService) DeletePullRequestCommentReaction(ctx context.Context, owner, repo string, commentID, reactionID int64) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions/%v", owner, repo, commentID, reactionID) + + return s.deleteReaction(ctx, url) +} + +// DeletePullRequestCommentReactionByID deletes the reaction to a pull request review comment by repository ID. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-pull-request-comment-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id} +func (s *ReactionsService) DeletePullRequestCommentReactionByID(ctx context.Context, repoID, commentID, reactionID int64) (*Response, error) { + url := fmt.Sprintf("repositories/%v/pulls/comments/%v/reactions/%v", repoID, commentID, reactionID) + + return s.deleteReaction(ctx, url) +} + // ListTeamDiscussionReactions lists the reactions for a team discussion. // -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-team-discussion -func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, teamID int64, discussionNumber int, opt *ListOptions) ([]*Reaction, *Response, error) { +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#list-reactions-for-a-team-discussion-legacy +// +//meta:operation GET /teams/{team_id}/discussions/{discussion_number}/reactions +func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, teamID int64, discussionNumber int, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } @@ -278,7 +391,7 @@ func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, team req.Header.Set("Accept", mediaTypeReactionsPreview) var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -287,22 +400,26 @@ func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, team } // CreateTeamDiscussionReaction creates a reaction for a team discussion. -// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". // -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-team-discussion +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion-legacy +// +//meta:operation POST /teams/{team_id}/discussions/{discussion_number}/reactions func (s *ReactionsService) CreateTeamDiscussionReaction(ctx context.Context, teamID int64, discussionNumber int, content string) (*Reaction, *Response, error) { u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber) - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeReactionsPreview) - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) + var m *Reaction + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -310,17 +427,47 @@ func (s *ReactionsService) CreateTeamDiscussionReaction(ctx context.Context, tea return m, resp, nil } +// DeleteTeamDiscussionReaction deletes the reaction to a team discussion. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#delete-team-discussion-reaction +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions/{reaction_id} +func (s *ReactionsService) DeleteTeamDiscussionReaction(ctx context.Context, org, teamSlug string, discussionNumber int, reactionID int64) (*Response, error) { + url := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/reactions/%v", org, teamSlug, discussionNumber, reactionID) + + return s.deleteReaction(ctx, url) +} + +// DeleteTeamDiscussionReactionByOrgIDAndTeamID deletes the reaction to a team discussion by organization ID and team ID. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion +// +//meta:operation POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions +func (s *ReactionsService) DeleteTeamDiscussionReactionByOrgIDAndTeamID(ctx context.Context, orgID, teamID, discussionNumber int, reactionID int64) (*Response, error) { + url := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/reactions/%v", orgID, teamID, discussionNumber, reactionID) + + return s.deleteReaction(ctx, url) +} + // ListTeamDiscussionCommentReactions lists the reactions for a team discussion comment. // -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-team-discussion-comment -func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Context, teamID int64, discussionNumber, commentNumber int, opt *ListOptions) ([]*Reaction, *Response, error) { +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#list-reactions-for-a-team-discussion-comment-legacy +// +//meta:operation GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions +func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Context, teamID int64, discussionNumber, commentNumber int, opts *ListReactionOptions) ([]*Reaction, *Response, error) { u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } @@ -328,30 +475,34 @@ func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Contex req.Header.Set("Accept", mediaTypeReactionsPreview) var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) + resp, err := s.client.Do(req, &m) if err != nil { - return nil, nil, err + return nil, resp, err } return m, resp, nil } // CreateTeamDiscussionCommentReaction creates a reaction for a team discussion comment. -// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion-comment-legacy // -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-team-discussion-comment +//meta:operation POST /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions func (s *ReactionsService) CreateTeamDiscussionCommentReaction(ctx context.Context, teamID int64, discussionNumber, commentNumber int, content string) (*Reaction, *Response, error) { u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber) - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeReactionsPreview) - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) + var m *Reaction + resp, err := s.client.Do(req, &m) if err != nil { return nil, resp, err } @@ -359,19 +510,117 @@ func (s *ReactionsService) CreateTeamDiscussionCommentReaction(ctx context.Conte return m, resp, nil } -// DeleteReaction deletes a reaction. +// DeleteTeamDiscussionCommentReaction deletes the reaction to a team discussion comment. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#delete-team-discussion-comment-reaction +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions/{reaction_id} +func (s *ReactionsService) DeleteTeamDiscussionCommentReaction(ctx context.Context, org, teamSlug string, discussionNumber, commentNumber int, reactionID int64) (*Response, error) { + url := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments/%v/reactions/%v", org, teamSlug, discussionNumber, commentNumber, reactionID) + + return s.deleteReaction(ctx, url) +} + +// DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID deletes the reaction to a team discussion comment by organization ID and team ID. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion-comment // -// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive -func (s *ReactionsService) DeleteReaction(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("reactions/%v", id) +//meta:operation POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions +func (s *ReactionsService) DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID(ctx context.Context, orgID, teamID, discussionNumber, commentNumber int, reactionID int64) (*Response, error) { + url := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/comments/%v/reactions/%v", orgID, teamID, discussionNumber, commentNumber, reactionID) - req, err := s.client.NewRequest("DELETE", u, nil) + return s.deleteReaction(ctx, url) +} + +func (s *ReactionsService) deleteReaction(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest(ctx, "DELETE", url, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeReactionsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// CreateReleaseReaction creates a reaction to a release. +// Note that a response with a Status: 200 OK means that you already +// added the reaction type to this release. +// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#create-reaction-for-a-release +// +//meta:operation POST /repos/{owner}/{repo}/releases/{release_id}/reactions +func (s *ReactionsService) CreateReleaseReaction(ctx context.Context, owner, repo string, releaseID int64, content string) (*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v/reactions", owner, repo, releaseID) + + body := &Reaction{Content: &content} + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m *Reaction + resp, err := s.client.Do(req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// ListReleaseReactions lists the reactions for a release. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#list-reactions-for-a-release +// +//meta:operation GET /repos/{owner}/{repo}/releases/{release_id}/reactions +func (s *ReactionsService) ListReleaseReactions(ctx context.Context, owner, repo string, releaseID int64, opts *ListReactionOptions) ([]*Reaction, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v/reactions", owner, repo, releaseID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeReactionsPreview) + + var m []*Reaction + resp, err := s.client.Do(req, &m) + if err != nil { + return nil, resp, err + } + + return m, resp, nil +} + +// DeleteReleaseReaction deletes the reaction for a release. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-release-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteReleaseReaction(ctx context.Context, owner, repo string, releaseID, reactionID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v/reactions/%v", owner, repo, releaseID, reactionID) + + return s.deleteReaction(ctx, u) +} + +// DeleteReleaseReactionByID deletes the reaction for a release by repository ID. +// +// GitHub API docs: https://docs.github.com/rest/reactions/reactions?apiVersion=2022-11-28#delete-a-release-reaction +// +//meta:operation DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id} +func (s *ReactionsService) DeleteReleaseReactionByID(ctx context.Context, repoID, releaseID, reactionID int64) (*Response, error) { + u := fmt.Sprintf("repositories/%v/releases/%v/reactions/%v", repoID, releaseID, reactionID) + + return s.deleteReaction(ctx, u) } diff --git a/github/reactions_test.go b/github/reactions_test.go index e1baec3f92d..cd3a32b8d35 100644 --- a/github/reactions_test.go +++ b/github/reactions_test.go @@ -6,288 +6,961 @@ package github import ( - "context" + "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestReactionsService_ListCommentReactions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) - w.WriteHeader(http.StatusOK) - w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + testFormValues(t, r, values{"content": "+1"}) + fmt.Fprint(w, `[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`) }) - got, _, err := client.Reactions.ListCommentReactions(context.Background(), "o", "r", 1, nil) + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + reactions, _, err := client.Reactions.ListCommentReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListCommentReactions returned error: %v", err) } - want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}} - if !reflect.DeepEqual(got, want) { - t.Errorf("ListCommentReactions = %+v, want %+v", got, want) + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(reactions, want) { + t.Errorf("ListCommentReactions = %+v, want %+v", reactions, want) } + + const methodName = "ListCommentReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListCommentReactions(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListCommentReactions(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestReactionsService_CreateCommentReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) }) - got, _, err := client.Reactions.CreateCommentReaction(context.Background(), "o", "r", 1, "+1") + ctx := t.Context() + got, _, err := client.Reactions.CreateCommentReaction(ctx, "o", "r", 1, "+1") if err != nil { t.Errorf("CreateCommentReaction returned error: %v", err) } - want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} - if !reflect.DeepEqual(got, want) { + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")} + if !cmp.Equal(got, want) { t.Errorf("CreateCommentReaction = %+v, want %+v", got, want) } + + const methodName = "CreateCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreateCommentReaction(ctx, "\n", "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreateCommentReaction(ctx, "o", "r", 1, "+1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestReactionsService_ListIssueReactions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) - w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) - got, _, err := client.Reactions.ListIssueReactions(context.Background(), "o", "r", 1, nil) + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + got, _, err := client.Reactions.ListIssueReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListIssueReactions returned error: %v", err) } - want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}} - if !reflect.DeepEqual(got, want) { + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { t.Errorf("ListIssueReactions = %+v, want %+v", got, want) } } +func TestReactionsService_ListIssueReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "ListIssueReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListIssueReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListIssueReactions(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestReactionsService_CreateIssueReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) }) - got, _, err := client.Reactions.CreateIssueReaction(context.Background(), "o", "r", 1, "+1") + ctx := t.Context() + got, _, err := client.Reactions.CreateIssueReaction(ctx, "o", "r", 1, "+1") if err != nil { t.Errorf("CreateIssueReaction returned error: %v", err) } - want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} - if !reflect.DeepEqual(got, want) { + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")} + if !cmp.Equal(got, want) { t.Errorf("CreateIssueReaction = %+v, want %+v", got, want) } + + const methodName = "CreateIssueReaction" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreateIssueReaction(ctx, "\n", "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreateIssueReaction(ctx, "o", "r", 1, "+1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestReactionsService_ListIssueCommentReactions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) - w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) - got, _, err := client.Reactions.ListIssueCommentReactions(context.Background(), "o", "r", 1, nil) + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + got, _, err := client.Reactions.ListIssueCommentReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListIssueCommentReactions returned error: %v", err) } - want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}} - if !reflect.DeepEqual(got, want) { + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { t.Errorf("ListIssueCommentReactions = %+v, want %+v", got, want) } } +func TestReactionsService_ListIssueCommentReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "ListIssueCommentReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListIssueCommentReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListIssueCommentReactions(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestReactionsService_CreateIssueCommentReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) }) - got, _, err := client.Reactions.CreateIssueCommentReaction(context.Background(), "o", "r", 1, "+1") + ctx := t.Context() + got, _, err := client.Reactions.CreateIssueCommentReaction(ctx, "o", "r", 1, "+1") if err != nil { t.Errorf("CreateIssueCommentReaction returned error: %v", err) } - want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} - if !reflect.DeepEqual(got, want) { + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")} + if !cmp.Equal(got, want) { t.Errorf("CreateIssueCommentReaction = %+v, want %+v", got, want) } + + const methodName = "CreateIssueCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreateIssueCommentReaction(ctx, "\n", "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreateIssueCommentReaction(ctx, "o", "r", 1, "+1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestReactionsService_ListPullRequestCommentReactions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) - w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) - got, _, err := client.Reactions.ListPullRequestCommentReactions(context.Background(), "o", "r", 1, nil) + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + got, _, err := client.Reactions.ListPullRequestCommentReactions(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("ListPullRequestCommentReactions returned error: %v", err) } - want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}} - if !reflect.DeepEqual(got, want) { + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { t.Errorf("ListPullRequestCommentReactions = %+v, want %+v", got, want) } } +func TestReactionsService_ListPullRequestCommentReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "ListPullRequestCommentReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListPullRequestCommentReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListPullRequestCommentReactions(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestReactionsService_CreatePullRequestCommentReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) }) - got, _, err := client.Reactions.CreatePullRequestCommentReaction(context.Background(), "o", "r", 1, "+1") + ctx := t.Context() + got, _, err := client.Reactions.CreatePullRequestCommentReaction(ctx, "o", "r", 1, "+1") if err != nil { t.Errorf("CreatePullRequestCommentReaction returned error: %v", err) } - want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} - if !reflect.DeepEqual(got, want) { + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")} + if !cmp.Equal(got, want) { t.Errorf("CreatePullRequestCommentReaction = %+v, want %+v", got, want) } + + const methodName = "CreatePullRequestCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreatePullRequestCommentReaction(ctx, "\n", "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreatePullRequestCommentReaction(ctx, "o", "r", 1, "+1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestReactionsService_ListTeamDiscussionReactions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/teams/1/discussions/2/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) - w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) - got, _, err := client.Reactions.ListTeamDiscussionReactions(context.Background(), 1, 2, nil) + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + got, _, err := client.Reactions.ListTeamDiscussionReactions(ctx, 1, 2, opt) if err != nil { t.Errorf("ListTeamDiscussionReactions returned error: %v", err) } - want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}} - if !reflect.DeepEqual(got, want) { + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { t.Errorf("ListTeamDiscussionReactions = %+v, want %+v", got, want) } } +func TestReactionsService_ListTeamDiscussionReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "ListTeamDiscussionReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListTeamDiscussionReactions(ctx, -1, -2, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListTeamDiscussionReactions(ctx, 1, 2, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestReactionsService_CreateTeamDiscussionReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/teams/1/discussions/2/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) }) - got, _, err := client.Reactions.CreateTeamDiscussionReaction(context.Background(), 1, 2, "+1") + ctx := t.Context() + got, _, err := client.Reactions.CreateTeamDiscussionReaction(ctx, 1, 2, "+1") if err != nil { t.Errorf("CreateTeamDiscussionReaction returned error: %v", err) } - want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} - if !reflect.DeepEqual(got, want) { + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")} + if !cmp.Equal(got, want) { t.Errorf("CreateTeamDiscussionReaction = %+v, want %+v", got, want) } + + const methodName = "CreateTeamDiscussionReaction" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreateTeamDiscussionReaction(ctx, -1, -2, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreateTeamDiscussionReaction(ctx, 1, 2, "+1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestReactionService_ListTeamDiscussionCommentReactions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/teams/1/discussions/2/comments/3/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) w.WriteHeader(http.StatusOK) - w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) }) - got, _, err := client.Reactions.ListTeamDiscussionCommentReactions(context.Background(), 1, 2, 3, nil) + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + got, _, err := client.Reactions.ListTeamDiscussionCommentReactions(ctx, 1, 2, 3, opt) if err != nil { t.Errorf("ListTeamDiscussionCommentReactions returned error: %v", err) } - want := []*Reaction{{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")}} - if !reflect.DeepEqual(got, want) { + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { t.Errorf("ListTeamDiscussionCommentReactions = %+v, want %+v", got, want) } } +func TestReactionService_ListTeamDiscussionCommentReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "ListTeamDiscussionCommentReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListTeamDiscussionCommentReactions(ctx, -1, -2, -3, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListTeamDiscussionCommentReactions(ctx, 1, 2, 3, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestReactionService_CreateTeamDiscussionCommentReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/teams/1/discussions/2/comments/3/reactions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusCreated) - w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) }) - got, _, err := client.Reactions.CreateTeamDiscussionCommentReaction(context.Background(), 1, 2, 3, "+1") + ctx := t.Context() + got, _, err := client.Reactions.CreateTeamDiscussionCommentReaction(ctx, 1, 2, 3, "+1") if err != nil { t.Errorf("CreateTeamDiscussionCommentReaction returned error: %v", err) } - want := &Reaction{ID: Int64(1), User: &User{Login: String("l"), ID: Int64(2)}, Content: String("+1")} - if !reflect.DeepEqual(got, want) { + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")} + if !cmp.Equal(got, want) { t.Errorf("CreateTeamDiscussionCommentReaction = %+v, want %+v", got, want) } + + const methodName = "CreateTeamDiscussionCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreateTeamDiscussionCommentReaction(ctx, -1, -2, -3, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreateTeamDiscussionCommentReaction(ctx, 1, 2, 3, "+1") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestReactionsService_DeleteCommentReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/comments/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteCommentReaction(ctx, "o", "r", 1, 2); err != nil { + t.Errorf("DeleteCommentReaction returned error: %v", err) + } + + const methodName = "DeleteCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteCommentReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteCommentReaction(ctx, "o", "r", 1, 2) + }) +} + +func TestReactionsService_DeleteCommentReactionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repositories/1/comments/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteCommentReactionByID(ctx, 1, 2, 3); err != nil { + t.Errorf("Reactions.DeleteCommentReactionByID returned error: %v", err) + } + + const methodName = "DeleteCommentReactionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteCommentReactionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteCommentReactionByID(ctx, 1, 2, 3) + }) +} + +func TestReactionsService_DeleteIssueReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteIssueReaction(ctx, "o", "r", 1, 2); err != nil { + t.Errorf("DeleteIssueReaction returned error: %v", err) + } + + const methodName = "DeleteIssueReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteIssueReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteIssueReaction(ctx, "o", "r", 1, 2) + }) +} + +func TestReactionsService_DeleteIssueReactionByRepoID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repositories/1/issues/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteIssueReactionByID(ctx, 1, 2, 3); err != nil { + t.Errorf("Reactions.DeleteIssueReactionByID returned error: %v", err) + } + + const methodName = "DeleteIssueReactionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteIssueReactionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteIssueReactionByID(ctx, 1, 2, 3) + }) +} + +func TestReactionsService_DeleteIssueCommentReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/comments/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteIssueCommentReaction(ctx, "o", "r", 1, 2); err != nil { + t.Errorf("DeleteIssueCommentReaction returned error: %v", err) + } + + const methodName = "DeleteIssueCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteIssueCommentReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteIssueCommentReaction(ctx, "o", "r", 1, 2) + }) } -func TestReactionsService_DeleteReaction(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestReactionsService_DeleteIssueCommentReactionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/reactions/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repositories/1/issues/comments/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") testHeader(t, r, "Accept", mediaTypeReactionsPreview) w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Reactions.DeleteReaction(context.Background(), 1); err != nil { - t.Errorf("DeleteReaction returned error: %v", err) + ctx := t.Context() + if _, err := client.Reactions.DeleteIssueCommentReactionByID(ctx, 1, 2, 3); err != nil { + t.Errorf("Reactions.DeleteIssueCommentReactionByID returned error: %v", err) } + + const methodName = "DeleteIssueCommentReactionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteIssueCommentReactionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteIssueCommentReactionByID(ctx, 1, 2, 3) + }) +} + +func TestReactionsService_DeletePullRequestCommentReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeletePullRequestCommentReaction(ctx, "o", "r", 1, 2); err != nil { + t.Errorf("DeletePullRequestCommentReaction returned error: %v", err) + } + + const methodName = "DeletePullRequestCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeletePullRequestCommentReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeletePullRequestCommentReaction(ctx, "o", "r", 1, 2) + }) +} + +func TestReactionsService_DeletePullRequestCommentReactionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repositories/1/pulls/comments/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeletePullRequestCommentReactionByID(ctx, 1, 2, 3); err != nil { + t.Errorf("Reactions.DeletePullRequestCommentReactionByID returned error: %v", err) + } + + const methodName = "DeletePullRequestCommentReactionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeletePullRequestCommentReactionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeletePullRequestCommentReactionByID(ctx, 1, 2, 3) + }) +} + +func TestReactionsService_DeleteTeamDiscussionReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/discussions/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteTeamDiscussionReaction(ctx, "o", "s", 1, 2); err != nil { + t.Errorf("Reactions.DeleteTeamDiscussionReaction returned error: %v", err) + } + + const methodName = "DeleteTeamDiscussionReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteTeamDiscussionReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteTeamDiscussionReaction(ctx, "o", "s", 1, 2) + }) +} + +func TestReactionsService_DeleteTeamDiscussionReactionByTeamIDAndOrgID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/discussions/3/reactions/4", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteTeamDiscussionReactionByOrgIDAndTeamID(ctx, 1, 2, 3, 4); err != nil { + t.Errorf("Reactions.DeleteTeamDiscussionReactionByOrgIDAndTeamID returned error: %v", err) + } + + const methodName = "DeleteTeamDiscussionReactionByOrgIDAndTeamID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteTeamDiscussionReactionByOrgIDAndTeamID(ctx, -1, -2, -3, -4) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteTeamDiscussionReactionByOrgIDAndTeamID(ctx, 1, 2, 3, 4) + }) +} + +func TestReactionsService_DeleteTeamDiscussionCommentReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/discussions/1/comments/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteTeamDiscussionCommentReaction(ctx, "o", "s", 1, 2, 3); err != nil { + t.Errorf("Reactions.DeleteTeamDiscussionCommentReaction returned error: %v", err) + } + + const methodName = "DeleteTeamDiscussionCommentReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteTeamDiscussionCommentReaction(ctx, "\n", "\n", -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteTeamDiscussionCommentReaction(ctx, "o", "s", 1, 2, 3) + }) +} + +func TestReactionsService_DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/discussions/3/comments/4/reactions/5", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID(ctx, 1, 2, 3, 4, 5); err != nil { + t.Errorf("Reactions.DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID returned error: %v", err) + } + + const methodName = "DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID(ctx, -1, -2, -3, -4, -5) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteTeamDiscussionCommentReactionByOrgIDAndTeamID(ctx, 1, 2, 3, 4, 5) + }) +} + +func TestReactionService_CreateReleaseReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusCreated) + assertWrite(t, w, []byte(`{"id":1,"user":{"login":"l","id":2},"content":"rocket"}`)) + }) + + const methodName = "CreateReleaseReaction" + ctx := t.Context() + got, _, err := client.Reactions.CreateReleaseReaction(ctx, "o", "r", 1, "rocket") + if err != nil { + t.Errorf("%v returned error: %v", methodName, err) + } + + want := &Reaction{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("rocket")} + if !cmp.Equal(got, want) { + t.Errorf("%v = %+v, want %+v", methodName, got, want) + } + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.CreateReleaseReaction(ctx, "\n", "\n", -1, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.CreateReleaseReaction(ctx, "o", "r", 1, "rocket") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestReactionsService_ListReleaseReactions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/1/reactions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + testFormValues(t, r, values{"content": "+1"}) + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) + }) + + opt := &ListReactionOptions{Content: "+1"} + ctx := t.Context() + got, _, err := client.Reactions.ListReleaseReactions(ctx, "o", "r", 1, opt) + if err != nil { + t.Errorf("ListReleaseReactions returned error: %v", err) + } + want := []*Reaction{{ID: Ptr(int64(1)), User: &User{Login: Ptr("l"), ID: Ptr(int64(2))}, Content: Ptr("+1")}} + if !cmp.Equal(got, want) { + t.Errorf("ListReleaseReactions = %+v, want %+v", got, want) + } +} + +func TestReactionsService_ListReleaseReactions_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "ListReleaseReactions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Reactions.ListReleaseReactions(ctx, "\n", "\n", -1, &ListReactionOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Reactions.ListReleaseReactions(ctx, "o", "r", 1, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestReactionsService_DeleteReleaseReaction(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/1/reactions/2", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteReleaseReaction(ctx, "o", "r", 1, 2); err != nil { + t.Errorf("DeleteReleaseReaction returned error: %v", err) + } + + const methodName = "DeleteReleaseReaction" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteReleaseReaction(ctx, "\n", "\n", -1, -2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteReleaseReaction(ctx, "o", "r", 1, 2) + }) +} + +func TestReactionsService_DeleteReleaseReactionByRepoID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repositories/1/releases/2/reactions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeReactionsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Reactions.DeleteReleaseReactionByID(ctx, 1, 2, 3); err != nil { + t.Errorf("Reactions.DeleteReleaseReactionByID returned error: %v", err) + } + + const methodName = "DeleteReleaseReactionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Reactions.DeleteIssueReactionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Reactions.DeleteIssueReactionByID(ctx, 1, 2, 3) + }) } diff --git a/github/repos.go b/github/repos.go index a380f3028cd..588b0c66fbe 100644 --- a/github/repos.go +++ b/github/repos.go @@ -7,72 +7,99 @@ package github import ( "context" + "encoding/json" + "errors" "fmt" + "net/http" + "net/url" "strings" ) +const githubBranchNotProtected string = "Branch not protected" + +var ErrBranchNotProtected = errors.New("branch is not protected") + // RepositoriesService handles communication with the repository related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/repos/ +// GitHub API docs: https://docs.github.com/rest/repos?apiVersion=2022-11-28 type RepositoriesService service // Repository represents a GitHub repository. type Repository struct { - ID *int64 `json:"id,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Owner *User `json:"owner,omitempty"` - Name *string `json:"name,omitempty"` - FullName *string `json:"full_name,omitempty"` - Description *string `json:"description,omitempty"` - Homepage *string `json:"homepage,omitempty"` - CodeOfConduct *CodeOfConduct `json:"code_of_conduct,omitempty"` - DefaultBranch *string `json:"default_branch,omitempty"` - MasterBranch *string `json:"master_branch,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PushedAt *Timestamp `json:"pushed_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CloneURL *string `json:"clone_url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - MirrorURL *string `json:"mirror_url,omitempty"` - SSHURL *string `json:"ssh_url,omitempty"` - SVNURL *string `json:"svn_url,omitempty"` - Language *string `json:"language,omitempty"` - Fork *bool `json:"fork,omitempty"` - ForksCount *int `json:"forks_count,omitempty"` - NetworkCount *int `json:"network_count,omitempty"` - OpenIssuesCount *int `json:"open_issues_count,omitempty"` - StargazersCount *int `json:"stargazers_count,omitempty"` - SubscribersCount *int `json:"subscribers_count,omitempty"` - WatchersCount *int `json:"watchers_count,omitempty"` - Size *int `json:"size,omitempty"` - AutoInit *bool `json:"auto_init,omitempty"` - Parent *Repository `json:"parent,omitempty"` - Source *Repository `json:"source,omitempty"` - TemplateRepository *Repository `json:"template_repository,omitempty"` - Organization *Organization `json:"organization,omitempty"` - Permissions *map[string]bool `json:"permissions,omitempty"` - AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` - AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` - AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` - Topics []string `json:"topics,omitempty"` - Archived *bool `json:"archived,omitempty"` - Disabled *bool `json:"disabled,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Owner *User `json:"owner,omitempty"` + Name *string `json:"name,omitempty"` + FullName *string `json:"full_name,omitempty"` + Description *string `json:"description,omitempty"` + Homepage *string `json:"homepage,omitempty"` + CodeOfConduct *CodeOfConduct `json:"code_of_conduct,omitempty"` + DefaultBranch *string `json:"default_branch,omitempty"` + MasterBranch *string `json:"master_branch,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PushedAt *Timestamp `json:"pushed_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CloneURL *string `json:"clone_url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + MirrorURL *string `json:"mirror_url,omitempty"` + SSHURL *string `json:"ssh_url,omitempty"` + SVNURL *string `json:"svn_url,omitempty"` + Language *string `json:"language,omitempty"` + Fork *bool `json:"fork,omitempty"` + ForksCount *int `json:"forks_count,omitempty"` + NetworkCount *int `json:"network_count,omitempty"` + OpenIssuesCount *int `json:"open_issues_count,omitempty"` + OpenIssues *int `json:"open_issues,omitempty"` // Deprecated: Replaced by OpenIssuesCount. For backward compatibility OpenIssues is still populated. + StargazersCount *int `json:"stargazers_count,omitempty"` + SubscribersCount *int `json:"subscribers_count,omitempty"` + WatchersCount *int `json:"watchers_count,omitempty"` // Deprecated: Replaced by StargazersCount. For backward compatibility WatchersCount is still populated. + Watchers *int `json:"watchers,omitempty"` // Deprecated: Replaced by StargazersCount. For backward compatibility Watchers is still populated. + Size *int `json:"size,omitempty"` + AutoInit *bool `json:"auto_init,omitempty"` + Parent *Repository `json:"parent,omitempty"` + Source *Repository `json:"source,omitempty"` + TemplateRepository *Repository `json:"template_repository,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Permissions *RepositoryPermissions `json:"permissions,omitempty"` + AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` + AllowUpdateBranch *bool `json:"allow_update_branch,omitempty"` + AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` + AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` + AllowAutoMerge *bool `json:"allow_auto_merge,omitempty"` + AllowForking *bool `json:"allow_forking,omitempty"` + WebCommitSignoffRequired *bool `json:"web_commit_signoff_required,omitempty"` + DeleteBranchOnMerge *bool `json:"delete_branch_on_merge,omitempty"` + UseSquashPRTitleAsDefault *bool `json:"use_squash_pr_title_as_default,omitempty"` + SquashMergeCommitTitle *string `json:"squash_merge_commit_title,omitempty"` // Can be one of: "PR_TITLE", "COMMIT_OR_PR_TITLE" + SquashMergeCommitMessage *string `json:"squash_merge_commit_message,omitempty"` // Can be one of: "PR_BODY", "COMMIT_MESSAGES", "BLANK" + MergeCommitTitle *string `json:"merge_commit_title,omitempty"` // Can be one of: "PR_TITLE", "MERGE_MESSAGE" + MergeCommitMessage *string `json:"merge_commit_message,omitempty"` // Can be one of: "PR_BODY", "PR_TITLE", "BLANK" + Topics []string `json:"topics,omitempty"` + CustomProperties map[string]any `json:"custom_properties,omitempty"` + Archived *bool `json:"archived,omitempty"` + Disabled *bool `json:"disabled,omitempty"` // Only provided when using RepositoriesService.Get while in preview License *License `json:"license,omitempty"` // Additional mutable fields when creating and editing a repository - Private *bool `json:"private,omitempty"` - HasIssues *bool `json:"has_issues,omitempty"` - HasWiki *bool `json:"has_wiki,omitempty"` - HasPages *bool `json:"has_pages,omitempty"` - HasProjects *bool `json:"has_projects,omitempty"` - HasDownloads *bool `json:"has_downloads,omitempty"` - IsTemplate *bool `json:"is_template,omitempty"` - LicenseTemplate *string `json:"license_template,omitempty"` - GitignoreTemplate *string `json:"gitignore_template,omitempty"` + Private *bool `json:"private,omitempty"` + HasIssues *bool `json:"has_issues,omitempty"` + HasWiki *bool `json:"has_wiki,omitempty"` + HasPages *bool `json:"has_pages,omitempty"` + HasProjects *bool `json:"has_projects,omitempty"` + HasDownloads *bool `json:"has_downloads,omitempty"` + HasDiscussions *bool `json:"has_discussions,omitempty"` + HasPullRequests *bool `json:"has_pull_requests,omitempty"` + PullRequestCreationPolicy *string `json:"pull_request_creation_policy,omitempty"` // Can be one of: "all", "collaborators_only" + IsTemplate *bool `json:"is_template,omitempty"` + LicenseTemplate *string `json:"license_template,omitempty"` + GitignoreTemplate *string `json:"gitignore_template,omitempty"` + + // Options for configuring Advanced Security and Secret Scanning + SecurityAndAnalysis *SecurityAndAnalysis `json:"security_and_analysis,omitempty"` // Creating an organization repository. Required for non-owners. TeamID *int64 `json:"team_id,omitempty"` @@ -117,19 +144,228 @@ type Repository struct { TeamsURL *string `json:"teams_url,omitempty"` // TextMatches is only populated from search results that request text matches - // See: search.go and https://developer.github.com/v3/search/#text-match-metadata - TextMatches []TextMatch `json:"text_matches,omitempty"` + // See: search.go and https://docs.github.com/rest/search?apiVersion=2022-11-28#text-match-metadata + TextMatches []*TextMatch `json:"text_matches,omitempty"` + + // Visibility is only used for Create and Edit endpoints. The visibility field + // overrides the field parameter when both are used. + // Can be one of public, private or internal. + Visibility *string `json:"visibility,omitempty"` + + // RoleName is only returned by the API 'check team permissions for a repository'. + // See: teams.go (IsTeamRepoByID) https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#check-team-permissions-for-a-repository + RoleName *string `json:"role_name,omitempty"` } func (r Repository) String() string { return Stringify(r) } +// BranchListOptions specifies the optional parameters to the +// RepositoriesService.ListBranches method. +type BranchListOptions struct { + // Setting to true returns only protected branches. + // When set to false, only unprotected branches are returned. + // Omitting this parameter returns all branches. + // Default: nil + Protected *bool `url:"protected,omitempty"` + + ListOptions +} + // RepositoryListOptions specifies the optional parameters to the // RepositoriesService.List method. type RepositoryListOptions struct { - // Visibility of repositories to list. Can be one of all, public, or private. + // See RepositoryListByAuthenticatedUserOptions.Visibility + Visibility string `url:"visibility,omitempty"` + + // See RepositoryListByAuthenticatedUserOptions.Affiliation + Affiliation string `url:"affiliation,omitempty"` + + // See RepositoryListByUserOptions.Type or RepositoryListByAuthenticatedUserOptions.Type + Type string `url:"type,omitempty"` + + // See RepositoryListByUserOptions.Sort or RepositoryListByAuthenticatedUserOptions.Sort + Sort string `url:"sort,omitempty"` + + // See RepositoryListByUserOptions.Direction or RepositoryListByAuthenticatedUserOptions.Direction + Direction string `url:"direction,omitempty"` + + ListOptions +} + +// SecurityAndAnalysis specifies the optional advanced security features +// that are enabled on a given repository. +type SecurityAndAnalysis struct { + AdvancedSecurity *AdvancedSecurity `json:"advanced_security,omitempty"` + SecretScanning *SecretScanning `json:"secret_scanning,omitempty"` + SecretScanningPushProtection *SecretScanningPushProtection `json:"secret_scanning_push_protection,omitempty"` + DependabotSecurityUpdates *DependabotSecurityUpdates `json:"dependabot_security_updates,omitempty"` + SecretScanningValidityChecks *SecretScanningValidityChecks `json:"secret_scanning_validity_checks,omitempty"` + CodeSecurity *CodeSecurity `json:"code_security,omitempty"` +} + +// RepositoryPermissions represents the permissions a user has for a repository. +type RepositoryPermissions struct { + Admin *bool `json:"admin,omitempty"` + Maintain *bool `json:"maintain,omitempty"` + Push *bool `json:"push,omitempty"` + Triage *bool `json:"triage,omitempty"` + Pull *bool `json:"pull,omitempty"` +} + +func (s SecurityAndAnalysis) String() string { + return Stringify(s) +} + +// AdvancedSecurity specifies the state of advanced security on a repository. +// +// GitHub API docs: https://docs.github.com/github/getting-started-with-github/learning-about-github/about-github-advanced-security +type AdvancedSecurity struct { + Status *string `json:"status,omitempty"` +} + +func (a AdvancedSecurity) String() string { + return Stringify(a) +} + +// SecretScanning specifies the state of secret scanning on a repository. +// +// GitHub API docs: https://docs.github.com/code-security/secret-security/about-secret-scanning +type SecretScanning struct { + Status *string `json:"status,omitempty"` +} + +func (s SecretScanning) String() string { + return Stringify(s) +} + +// SecretScanningPushProtection specifies the state of secret scanning push protection on a repository. +// +// GitHub API docs: https://docs.github.com/code-security/secret-scanning/about-secret-scanning#about-secret-scanning-for-partner-patterns +type SecretScanningPushProtection struct { + Status *string `json:"status,omitempty"` +} + +func (s SecretScanningPushProtection) String() string { + return Stringify(s) +} + +// DependabotSecurityUpdates specifies the state of Dependabot security updates on a repository. +// +// GitHub API docs: https://docs.github.com/code-security/dependabot/dependabot-security-updates/about-dependabot-security-updates +type DependabotSecurityUpdates struct { + Status *string `json:"status,omitempty"` +} + +func (d DependabotSecurityUpdates) String() string { + return Stringify(d) +} + +// SecretScanningValidityChecks represents the state of secret scanning validity checks on a repository. +// +// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository#allowing-validity-checks-for-partner-patterns-in-a-repository +type SecretScanningValidityChecks struct { + Status *string `json:"status,omitempty"` +} + +// CodeSecurity represents the state of code security on a repository. +// +// GitHub API docs: https://docs.github.com/en/code-security/getting-started/github-security-features#available-with-github-code-security +type CodeSecurity struct { + Status *string `json:"status,omitempty"` +} + +func (c CodeSecurity) String() string { + return Stringify(c) +} + +// List calls either RepositoriesService.ListByUser or RepositoriesService.ListByAuthenticatedUser +// depending on whether user is empty. +// +// Deprecated: Use RepositoriesService.ListByUser or RepositoriesService.ListByAuthenticatedUser instead. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user +// +//meta:operation GET /user/repos +//meta:operation GET /users/{username}/repos +func (s *RepositoriesService) List(ctx context.Context, user string, opts *RepositoryListOptions) ([]*Repository, *Response, error) { + if opts == nil { + opts = &RepositoryListOptions{} + } + if user != "" { + return s.ListByUser(ctx, user, &RepositoryListByUserOptions{ + Type: opts.Type, + Sort: opts.Sort, + Direction: opts.Direction, + ListOptions: opts.ListOptions, + }) + } + return s.ListByAuthenticatedUser(ctx, &RepositoryListByAuthenticatedUserOptions{ + Visibility: opts.Visibility, + Affiliation: opts.Affiliation, + Type: opts.Type, + Sort: opts.Sort, + Direction: opts.Direction, + ListOptions: opts.ListOptions, + }) +} + +// RepositoryListByUserOptions specifies the optional parameters to the +// RepositoriesService.ListByUser method. +type RepositoryListByUserOptions struct { + // Limit results to repositories of the specified type. + // Default: owner + // Can be one of: all, owner, member + Type string `url:"type,omitempty"` + + // The property to sort the results by. + // Default: full_name + // Can be one of: created, updated, pushed, full_name + Sort string `url:"sort,omitempty"` + + // The order to sort by. + // Default: asc when using full_name; otherwise, desc. + // Can be one of: asc, desc + Direction string `url:"direction,omitempty"` + + ListOptions +} + +// ListByUser lists public repositories for the specified user. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-a-user +// +//meta:operation GET /users/{username}/repos +func (s *RepositoriesService) ListByUser(ctx context.Context, user string, opts *RepositoryListByUserOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("users/%v/repos", user) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var repos []*Repository + resp, err := s.client.Do(req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// RepositoryListByAuthenticatedUserOptions specifies the optional parameters to the +// RepositoriesService.ListByAuthenticatedUser method. +type RepositoryListByAuthenticatedUserOptions struct { + // Limit results to repositories with the specified visibility. // Default: all + // Can be one of: all, public, private Visibility string `url:"visibility,omitempty"` // List repos of given affiliation[s]. @@ -143,14 +379,15 @@ type RepositoryListOptions struct { // Default: owner,collaborator,organization_member Affiliation string `url:"affiliation,omitempty"` - // Type of repositories to list. - // Can be one of all, owner, public, private, member. Default: all - // Will cause a 422 error if used in the same request as visibility or - // affiliation. + // Limit results to repositories of the specified type. Will cause a 422 error if + // used in the same request as visibility or affiliation. + // Default: all + // Can be one of: all, owner, public, private, member Type string `url:"type,omitempty"` - // How to sort the repository list. Can be one of created, updated, pushed, - // full_name. Default: full_name + // The property to sort the results by. + // Default: full_name + // Can be one of: created, updated, pushed, full_name Sort string `url:"sort,omitempty"` // Direction in which to sort repositories. Can be one of asc or desc. @@ -160,33 +397,25 @@ type RepositoryListOptions struct { ListOptions } -// List the repositories for a user. Passing the empty string will list -// repositories for the authenticated user. +// ListByAuthenticatedUser lists repositories for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-user-repositories -func (s *RepositoriesService) List(ctx context.Context, user string, opt *RepositoryListOptions) ([]*Repository, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/repos", user) - } else { - u = "user/repos" - } - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user +// +//meta:operation GET /user/repos +func (s *RepositoriesService) ListByAuthenticatedUser(ctx context.Context, opts *RepositoryListByAuthenticatedUserOptions) ([]*Repository, *Response, error) { + u := "user/repos" + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeTopicsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) + resp, err := s.client.Do(req, &repos) if err != nil { return nil, resp, err } @@ -201,30 +430,39 @@ type RepositoryListByOrgOptions struct { // forks, sources, member. Default is "all". Type string `url:"type,omitempty"` + // How to sort the repository list. Can be one of created, updated, pushed, + // full_name. Default is "created". + Sort string `url:"sort,omitempty"` + + // Direction in which to sort repositories. Can be one of asc or desc. + // Default when using full_name: asc; otherwise desc. + Direction string `url:"direction,omitempty"` + ListOptions } // ListByOrg lists the repositories for an organization. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-organization-repositories -func (s *RepositoriesService) ListByOrg(ctx context.Context, org string, opt *RepositoryListByOrgOptions) ([]*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-organization-repositories +// +//meta:operation GET /orgs/{org}/repos +func (s *RepositoriesService) ListByOrg(ctx context.Context, org string, opts *RepositoryListByOrgOptions) ([]*Repository, *Response, error) { u := fmt.Sprintf("orgs/%v/repos", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeTopicsPreview} + acceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeRepositoryVisibilityPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) + resp, err := s.client.Do(req, &repos) if err != nil { return nil, resp, err } @@ -241,20 +479,22 @@ type RepositoryListAllOptions struct { // ListAll lists all GitHub repositories in the order that they were created. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-all-public-repositories -func (s *RepositoriesService) ListAll(ctx context.Context, opt *RepositoryListAllOptions) ([]*Repository, *Response, error) { - u, err := addOptions("repositories", opt) +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-public-repositories +// +//meta:operation GET /repositories +func (s *RepositoriesService) ListAll(ctx context.Context, opts *RepositoryListAllOptions) ([]*Repository, *Response, error) { + u, err := addOptions("repositories", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) + resp, err := s.client.Do(req, &repos) if err != nil { return nil, resp, err } @@ -273,21 +513,33 @@ type createRepoRequest struct { Description *string `json:"description,omitempty"` Homepage *string `json:"homepage,omitempty"` - Private *bool `json:"private,omitempty"` - HasIssues *bool `json:"has_issues,omitempty"` - HasProjects *bool `json:"has_projects,omitempty"` - HasWiki *bool `json:"has_wiki,omitempty"` - IsTemplate *bool `json:"is_template,omitempty"` + Private *bool `json:"private,omitempty"` + Visibility *string `json:"visibility,omitempty"` + HasIssues *bool `json:"has_issues,omitempty"` + HasProjects *bool `json:"has_projects,omitempty"` + HasWiki *bool `json:"has_wiki,omitempty"` + HasDiscussions *bool `json:"has_discussions,omitempty"` + IsTemplate *bool `json:"is_template,omitempty"` // Creating an organization repository. Required for non-owners. TeamID *int64 `json:"team_id,omitempty"` - AutoInit *bool `json:"auto_init,omitempty"` - GitignoreTemplate *string `json:"gitignore_template,omitempty"` - LicenseTemplate *string `json:"license_template,omitempty"` - AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` - AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` - AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` + AutoInit *bool `json:"auto_init,omitempty"` + GitignoreTemplate *string `json:"gitignore_template,omitempty"` + LicenseTemplate *string `json:"license_template,omitempty"` + AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` + AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` + AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` + AllowUpdateBranch *bool `json:"allow_update_branch,omitempty"` + AllowAutoMerge *bool `json:"allow_auto_merge,omitempty"` + AllowForking *bool `json:"allow_forking,omitempty"` + DeleteBranchOnMerge *bool `json:"delete_branch_on_merge,omitempty"` + UseSquashPRTitleAsDefault *bool `json:"use_squash_pr_title_as_default,omitempty"` + SquashMergeCommitTitle *string `json:"squash_merge_commit_title,omitempty"` + SquashMergeCommitMessage *string `json:"squash_merge_commit_message,omitempty"` + MergeCommitTitle *string `json:"merge_commit_title,omitempty"` + MergeCommitMessage *string `json:"merge_commit_message,omitempty"` + CustomProperties map[string]any `json:"custom_properties,omitempty"` } // Create a new repository. If an organization is specified, the new @@ -297,8 +549,22 @@ type createRepoRequest struct { // Note that only a subset of the repo fields are used and repo must // not be nil. // -// GitHub API docs: https://developer.github.com/v3/repos/#create +// Also note that this method will return the response without actually +// waiting for GitHub to finish creating the repository and letting the +// changes propagate throughout its servers. You may set up a loop with +// exponential back-off to verify repository's creation. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#create-a-repository-for-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#create-an-organization-repository +// +//meta:operation POST /orgs/{org}/repos +//meta:operation POST /user/repos func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repository) (*Repository, *Response, error) { + if repo == nil { + return nil, nil, errors.New("repository must be provided") + } + var u string if org != "" { u = fmt.Sprintf("orgs/%v/repos", org) @@ -307,31 +573,44 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo } repoReq := &createRepoRequest{ - Name: repo.Name, - Description: repo.Description, - Homepage: repo.Homepage, - Private: repo.Private, - HasIssues: repo.HasIssues, - HasProjects: repo.HasProjects, - HasWiki: repo.HasWiki, - IsTemplate: repo.IsTemplate, - TeamID: repo.TeamID, - AutoInit: repo.AutoInit, - GitignoreTemplate: repo.GitignoreTemplate, - LicenseTemplate: repo.LicenseTemplate, - AllowSquashMerge: repo.AllowSquashMerge, - AllowMergeCommit: repo.AllowMergeCommit, - AllowRebaseMerge: repo.AllowRebaseMerge, - } - - req, err := s.client.NewRequest("POST", u, repoReq) + Name: repo.Name, + Description: repo.Description, + Homepage: repo.Homepage, + Private: repo.Private, + Visibility: repo.Visibility, + HasIssues: repo.HasIssues, + HasProjects: repo.HasProjects, + HasWiki: repo.HasWiki, + HasDiscussions: repo.HasDiscussions, + IsTemplate: repo.IsTemplate, + TeamID: repo.TeamID, + AutoInit: repo.AutoInit, + GitignoreTemplate: repo.GitignoreTemplate, + LicenseTemplate: repo.LicenseTemplate, + AllowSquashMerge: repo.AllowSquashMerge, + AllowMergeCommit: repo.AllowMergeCommit, + AllowRebaseMerge: repo.AllowRebaseMerge, + AllowUpdateBranch: repo.AllowUpdateBranch, + AllowAutoMerge: repo.AllowAutoMerge, + AllowForking: repo.AllowForking, + DeleteBranchOnMerge: repo.DeleteBranchOnMerge, + UseSquashPRTitleAsDefault: repo.UseSquashPRTitleAsDefault, + SquashMergeCommitTitle: repo.SquashMergeCommitTitle, + SquashMergeCommitMessage: repo.SquashMergeCommitMessage, + MergeCommitTitle: repo.MergeCommitTitle, + MergeCommitMessage: repo.MergeCommitMessage, + CustomProperties: repo.CustomProperties, + } + + req, err := s.client.NewRequest(ctx, "POST", u, repoReq) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeRepositoryTemplatePreview) - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) + acceptHeaders := []string{mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + var r *Repository + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -341,28 +620,29 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo // TemplateRepoRequest represents a request to create a repository from a template. type TemplateRepoRequest struct { - // Name is required when creating a repo. - Name *string `json:"name,omitempty"` - Owner *string `json:"owner,omitempty"` - Description *string `json:"description,omitempty"` - - Private *bool `json:"private,omitempty"` + Name string `json:"name"` + Owner *string `json:"owner,omitempty"` + Description *string `json:"description,omitempty"` + IncludeAllBranches *bool `json:"include_all_branches,omitempty"` + Private *bool `json:"private,omitempty"` } // CreateFromTemplate generates a repository from a template. // -// GitHub API docs: https://developer.github.com/v3/repos/#create-repository-using-a-repository-template -func (s *RepositoriesService) CreateFromTemplate(ctx context.Context, templateOwner, templateRepo string, templateRepoReq *TemplateRepoRequest) (*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#create-a-repository-using-a-template +// +//meta:operation POST /repos/{template_owner}/{template_repo}/generate +func (s *RepositoriesService) CreateFromTemplate(ctx context.Context, templateOwner, templateRepo string, body TemplateRepoRequest) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v/generate", templateOwner, templateRepo) - req, err := s.client.NewRequest("POST", u, templateRepoReq) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } req.Header.Set("Accept", mediaTypeRepositoryTemplatePreview) - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) + var r *Repository + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -372,21 +652,27 @@ func (s *RepositoriesService) CreateFromTemplate(ctx context.Context, templateOw // Get fetches a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#get +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#get-a-repository +// +//meta:operation GET /repos/{owner}/{repo} func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when the license support fully launches - // https://developer.github.com/v3/licenses/#get-a-repositorys-license - acceptHeaders := []string{mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview, mediaTypeRepositoryTemplatePreview} + // https://docs.github.com/rest/licenses?apiVersion=2022-11-28#get-a-repositorys-license + acceptHeaders := []string{ + mediaTypeCodesOfConductPreview, + mediaTypeTopicsPreview, + mediaTypeRepositoryTemplatePreview, + mediaTypeRepositoryVisibilityPreview, + } req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - repository := new(Repository) - resp, err := s.client.Do(ctx, req, repository) + var repository *Repository + resp, err := s.client.Do(req, &repository) if err != nil { return nil, resp, err } @@ -395,39 +681,44 @@ func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Rep } // GetCodeOfConduct gets the contents of a repository's code of conduct. +// Note that https://docs.github.com/rest/codes-of-conduct?apiVersion=2022-11-28#about-the-codes-of-conduct-api +// says to use the GET /repos/{owner}/{repo} endpoint. // -// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#get-the-contents-of-a-repositorys-code-of-conduct +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#get-a-repository +// +//meta:operation GET /repos/{owner}/{repo} func (s *RepositoriesService) GetCodeOfConduct(ctx context.Context, owner, repo string) (*CodeOfConduct, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/community/code_of_conduct", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v", owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - coc := new(CodeOfConduct) - resp, err := s.client.Do(ctx, req, coc) + var r *Repository + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return coc, resp, nil + return r.GetCodeOfConduct(), resp, nil } // GetByID fetches a repository. // -// Note: GetByID uses the undocumented GitHub API endpoint /repositories/:id. +// Note: GetByID uses the undocumented GitHub API endpoint "GET /repositories/{repository_id}". +// +//meta:operation GET /repositories/{repository_id} func (s *RepositoriesService) GetByID(ctx context.Context, id int64) (*Repository, *Response, error) { - u := fmt.Sprintf("repositories/%d", id) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repositories/%v", id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - repository := new(Repository) - resp, err := s.client.Do(ctx, req, repository) + var repository *Repository + resp, err := s.client.Do(req, &repository) if err != nil { return nil, resp, err } @@ -437,17 +728,20 @@ func (s *RepositoriesService) GetByID(ctx context.Context, id int64) (*Repositor // Edit updates a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#edit -func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, repository *Repository) (*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#update-a-repository +// +//meta:operation PATCH /repos/{owner}/{repo} +func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, body *Repository) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v", owner, repo) - req, err := s.client.NewRequest("PATCH", u, repository) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeRepositoryTemplatePreview) - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) + acceptHeaders := []string{mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + var r *Repository + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -457,21 +751,24 @@ func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, repo // Delete a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#delete-a-repository +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#delete-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo} func (s *RepositoriesService) Delete(ctx context.Context, owner, repo string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } -// Contributor represents a repository contributor +// Contributor represents a repository contributor. type Contributor struct { Login *string `json:"login,omitempty"` ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` AvatarURL *string `json:"avatar_url,omitempty"` GravatarID *string `json:"gravatar_id,omitempty"` URL *string `json:"url,omitempty"` @@ -488,6 +785,8 @@ type Contributor struct { Type *string `json:"type,omitempty"` SiteAdmin *bool `json:"site_admin,omitempty"` Contributions *int `json:"contributions,omitempty"` + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` } // ListContributorsOptions specifies the optional parameters to the @@ -499,93 +798,136 @@ type ListContributorsOptions struct { ListOptions } +// GetVulnerabilityAlerts checks if vulnerability alerts are enabled for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#check-if-vulnerability-alerts-are-enabled-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/vulnerability-alerts +func (s *RepositoriesService) GetVulnerabilityAlerts(ctx context.Context, owner, repository string) (bool, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/vulnerability-alerts", owner, repository) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return false, nil, err + } + + req.Header.Set("Accept", mediaTypeRequiredVulnerabilityAlertsPreview) + + resp, err := s.client.Do(req, nil) + vulnerabilityAlertsEnabled, err := parseBoolResponse(err) + return vulnerabilityAlertsEnabled, resp, err +} + // EnableVulnerabilityAlerts enables vulnerability alerts and the dependency graph for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#enable-vulnerability-alerts +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#enable-vulnerability-alerts +// +//meta:operation PUT /repos/{owner}/{repo}/vulnerability-alerts func (s *RepositoriesService) EnableVulnerabilityAlerts(ctx context.Context, owner, repository string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/vulnerability-alerts", owner, repository) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredVulnerabilityAlertsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // DisableVulnerabilityAlerts disables vulnerability alerts and the dependency graph for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#disable-vulnerability-alerts +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#disable-vulnerability-alerts +// +//meta:operation DELETE /repos/{owner}/{repo}/vulnerability-alerts func (s *RepositoriesService) DisableVulnerabilityAlerts(ctx context.Context, owner, repository string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/vulnerability-alerts", owner, repository) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredVulnerabilityAlertsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// GetAutomatedSecurityFixes checks if the automated security fixes for a repository are enabled. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#check-if-dependabot-security-updates-are-enabled-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/automated-security-fixes +func (s *RepositoriesService) GetAutomatedSecurityFixes(ctx context.Context, owner, repository string) (*AutomatedSecurityFixes, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/automated-security-fixes", owner, repository) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var p *AutomatedSecurityFixes + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + return p, resp, nil } // EnableAutomatedSecurityFixes enables the automated security fixes for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#enable-automated-security-fixes +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#enable-dependabot-security-updates +// +//meta:operation PUT /repos/{owner}/{repo}/automated-security-fixes func (s *RepositoriesService) EnableAutomatedSecurityFixes(ctx context.Context, owner, repository string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/automated-security-fixes", owner, repository) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredAutomatedSecurityFixesPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // DisableAutomatedSecurityFixes disables vulnerability alerts and the dependency graph for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#disable-automated-security-fixes +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#disable-dependabot-security-updates +// +//meta:operation DELETE /repos/{owner}/{repo}/automated-security-fixes func (s *RepositoriesService) DisableAutomatedSecurityFixes(ctx context.Context, owner, repository string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/automated-security-fixes", owner, repository) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredAutomatedSecurityFixesPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListContributors lists contributors for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-contributors -func (s *RepositoriesService) ListContributors(ctx context.Context, owner string, repository string, opt *ListContributorsOptions) ([]*Contributor, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repository-contributors +// +//meta:operation GET /repos/{owner}/{repo}/contributors +func (s *RepositoriesService) ListContributors(ctx context.Context, owner, repository string, opts *ListContributorsOptions) ([]*Contributor, *Response, error) { u := fmt.Sprintf("repos/%v/%v/contributors", owner, repository) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var contributor []*Contributor - resp, err := s.client.Do(ctx, req, &contributor) + resp, err := s.client.Do(req, &contributor) if err != nil { - return nil, nil, err + return nil, resp, err } return contributor, resp, nil @@ -595,21 +937,23 @@ func (s *RepositoriesService) ListContributors(ctx context.Context, owner string // specifies the languages and the number of bytes of code written in that // language. For example: // -// { -// "C": 78769, -// "Python": 7769 -// } +// { +// "C": 78769, +// "Python": 7769 +// } +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repository-languages // -// GitHub API docs: https://developer.github.com/v3/repos/#list-languages -func (s *RepositoriesService) ListLanguages(ctx context.Context, owner string, repo string) (map[string]int, *Response, error) { +//meta:operation GET /repos/{owner}/{repo}/languages +func (s *RepositoriesService) ListLanguages(ctx context.Context, owner, repo string) (map[string]int, *Response, error) { u := fmt.Sprintf("repos/%v/%v/languages", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } languages := make(map[string]int) - resp, err := s.client.Do(ctx, req, &languages) + resp, err := s.client.Do(req, &languages) if err != nil { return nil, resp, err } @@ -619,23 +963,23 @@ func (s *RepositoriesService) ListLanguages(ctx context.Context, owner string, r // ListTeams lists the teams for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-teams -func (s *RepositoriesService) ListTeams(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Team, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repository-teams +// +//meta:operation GET /repos/{owner}/{repo}/teams +func (s *RepositoriesService) ListTeams(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Team, *Response, error) { u := fmt.Sprintf("repos/%v/%v/teams", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) + resp, err := s.client.Do(req, &teams) if err != nil { return nil, resp, err } @@ -653,21 +997,23 @@ type RepositoryTag struct { // ListTags lists tags for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-tags -func (s *RepositoriesService) ListTags(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*RepositoryTag, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repository-tags +// +//meta:operation GET /repos/{owner}/{repo}/tags +func (s *RepositoriesService) ListTags(ctx context.Context, owner, repo string, opts *ListOptions) ([]*RepositoryTag, *Response, error) { u := fmt.Sprintf("repos/%v/%v/tags", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var tags []*RepositoryTag - resp, err := s.client.Do(ctx, req, &tags) + resp, err := s.client.Do(req, &tags) if err != nil { return nil, resp, err } @@ -675,19 +1021,183 @@ func (s *RepositoriesService) ListTags(ctx context.Context, owner string, repo s return tags, resp, nil } -// Branch represents a repository branch +// Branch represents a repository branch. type Branch struct { Name *string `json:"name,omitempty"` Commit *RepositoryCommit `json:"commit,omitempty"` Protected *bool `json:"protected,omitempty"` + + // Protection will always be included in APIs which return the + // 'Branch With Protection' schema such as 'Get a branch', but may + // not be included in APIs that return the `Short Branch` schema + // such as 'List branches'. In such cases, if branch protection is + // enabled, Protected will be `true` but this will be nil, and + // additional protection details can be obtained by calling GetBranch(). + Protection *Protection `json:"protection,omitempty"` + ProtectionURL *string `json:"protection_url,omitempty"` } // Protection represents a repository branch's protection. type Protection struct { - RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` - RequiredPullRequestReviews *PullRequestReviewsEnforcement `json:"required_pull_request_reviews"` - EnforceAdmins *AdminEnforcement `json:"enforce_admins"` - Restrictions *BranchRestrictions `json:"restrictions"` + RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` + RequiredPullRequestReviews *PullRequestReviewsEnforcement `json:"required_pull_request_reviews"` + EnforceAdmins *AdminEnforcement `json:"enforce_admins"` + Restrictions *BranchRestrictions `json:"restrictions"` + RequireLinearHistory *RequireLinearHistory `json:"required_linear_history"` + AllowForcePushes *AllowForcePushes `json:"allow_force_pushes"` + AllowDeletions *AllowDeletions `json:"allow_deletions"` + RequiredConversationResolution *RequiredConversationResolution `json:"required_conversation_resolution"` + BlockCreations *BlockCreations `json:"block_creations,omitempty"` + LockBranch *LockBranch `json:"lock_branch,omitempty"` + AllowForkSyncing *AllowForkSyncing `json:"allow_fork_syncing,omitempty"` + RequiredSignatures *SignaturesProtectedBranch `json:"required_signatures,omitempty"` + URL *string `json:"url,omitempty"` +} + +// BlockCreations represents whether users can push changes that create branches. If this is true, this +// setting blocks pushes that create new branches, unless the push is initiated by a user, team, or app +// which has the ability to push. +type BlockCreations struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// LockBranch represents if the branch is marked as read-only. If this is true, users will not be able to push to the branch. +type LockBranch struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// AllowForkSyncing represents whether users can pull changes from upstream when the branch is locked. +type AllowForkSyncing struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// BranchProtectionRule represents the rule applied to a repositories branch. +type BranchProtectionRule struct { + ID *int64 `json:"id,omitempty"` + RepositoryID *int64 `json:"repository_id,omitempty"` + Name *string `json:"name,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + PullRequestReviewsEnforcementLevel *string `json:"pull_request_reviews_enforcement_level,omitempty"` + RequiredApprovingReviewCount *int `json:"required_approving_review_count,omitempty"` + DismissStaleReviewsOnPush *bool `json:"dismiss_stale_reviews_on_push,omitempty"` + AuthorizedDismissalActorsOnly *bool `json:"authorized_dismissal_actors_only,omitempty"` + IgnoreApprovalsFromContributors *bool `json:"ignore_approvals_from_contributors,omitempty"` + RequireCodeOwnerReview *bool `json:"require_code_owner_review,omitempty"` + RequiredStatusChecks []string `json:"required_status_checks,omitempty"` + RequiredStatusChecksEnforcementLevel *string `json:"required_status_checks_enforcement_level,omitempty"` + StrictRequiredStatusChecksPolicy *bool `json:"strict_required_status_checks_policy,omitempty"` + SignatureRequirementEnforcementLevel *string `json:"signature_requirement_enforcement_level,omitempty"` + LinearHistoryRequirementEnforcementLevel *string `json:"linear_history_requirement_enforcement_level,omitempty"` + AdminEnforced *bool `json:"admin_enforced,omitempty"` + AllowForcePushesEnforcementLevel *string `json:"allow_force_pushes_enforcement_level,omitempty"` + AllowDeletionsEnforcementLevel *string `json:"allow_deletions_enforcement_level,omitempty"` + MergeQueueEnforcementLevel *string `json:"merge_queue_enforcement_level,omitempty"` + RequiredDeploymentsEnforcementLevel *string `json:"required_deployments_enforcement_level,omitempty"` + RequiredConversationResolutionLevel *string `json:"required_conversation_resolution_level,omitempty"` + AuthorizedActorsOnly *bool `json:"authorized_actors_only,omitempty"` + AuthorizedActorNames []string `json:"authorized_actor_names,omitempty"` + RequireLastPushApproval *bool `json:"require_last_push_approval,omitempty"` +} + +// ProtectionChanges represents the changes to the rule if the BranchProtection was edited. +type ProtectionChanges struct { + AdminEnforced *AdminEnforcedChanges `json:"admin_enforced,omitempty"` + AllowDeletionsEnforcementLevel *AllowDeletionsEnforcementLevelChanges `json:"allow_deletions_enforcement_level,omitempty"` + AuthorizedActorNames *AuthorizedActorNames `json:"authorized_actor_names,omitempty"` + AuthorizedActorsOnly *AuthorizedActorsOnly `json:"authorized_actors_only,omitempty"` + AuthorizedDismissalActorsOnly *AuthorizedDismissalActorsOnlyChanges `json:"authorized_dismissal_actors_only,omitempty"` + CreateProtected *CreateProtectedChanges `json:"create_protected,omitempty"` + DismissStaleReviewsOnPush *DismissStaleReviewsOnPushChanges `json:"dismiss_stale_reviews_on_push,omitempty"` + LinearHistoryRequirementEnforcementLevel *LinearHistoryRequirementEnforcementLevelChanges `json:"linear_history_requirement_enforcement_level,omitempty"` + PullRequestReviewsEnforcementLevel *PullRequestReviewsEnforcementLevelChanges `json:"pull_request_reviews_enforcement_level,omitempty"` + RequireCodeOwnerReview *RequireCodeOwnerReviewChanges `json:"require_code_owner_review,omitempty"` + RequiredConversationResolutionLevel *RequiredConversationResolutionLevelChanges `json:"required_conversation_resolution_level,omitempty"` + RequiredDeploymentsEnforcementLevel *RequiredDeploymentsEnforcementLevelChanges `json:"required_deployments_enforcement_level,omitempty"` + RequiredStatusChecks *RequiredStatusChecksChanges `json:"required_status_checks,omitempty"` + RequiredStatusChecksEnforcementLevel *RequiredStatusChecksEnforcementLevelChanges `json:"required_status_checks_enforcement_level,omitempty"` + SignatureRequirementEnforcementLevel *SignatureRequirementEnforcementLevelChanges `json:"signature_requirement_enforcement_level,omitempty"` + RequireLastPushApproval *RequireLastPushApprovalChanges `json:"require_last_push_approval,omitempty"` +} + +// AdminEnforcedChanges represents the changes made to the AdminEnforced policy. +type AdminEnforcedChanges struct { + From *bool `json:"from,omitempty"` +} + +// AllowDeletionsEnforcementLevelChanges represents the changes made to the AllowDeletionsEnforcementLevel policy. +type AllowDeletionsEnforcementLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// AuthorizedActorNames represents who are authorized to edit the branch protection rules. +type AuthorizedActorNames struct { + From []string `json:"from,omitempty"` +} + +// AuthorizedActorsOnly represents if the branch rule can be edited by authorized actors only. +type AuthorizedActorsOnly struct { + From *bool `json:"from,omitempty"` +} + +// AuthorizedDismissalActorsOnlyChanges represents the changes made to the AuthorizedDismissalActorsOnly policy. +type AuthorizedDismissalActorsOnlyChanges struct { + From *bool `json:"from,omitempty"` +} + +// CreateProtectedChanges represents the changes made to the CreateProtected policy. +type CreateProtectedChanges struct { + From *bool `json:"from,omitempty"` +} + +// DismissStaleReviewsOnPushChanges represents the changes made to the DismissStaleReviewsOnPushChanges policy. +type DismissStaleReviewsOnPushChanges struct { + From *bool `json:"from,omitempty"` +} + +// LinearHistoryRequirementEnforcementLevelChanges represents the changes made to the LinearHistoryRequirementEnforcementLevel policy. +type LinearHistoryRequirementEnforcementLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// PullRequestReviewsEnforcementLevelChanges represents the changes made to the PullRequestReviewsEnforcementLevel policy. +type PullRequestReviewsEnforcementLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// RequireCodeOwnerReviewChanges represents the changes made to the RequireCodeOwnerReview policy. +type RequireCodeOwnerReviewChanges struct { + From *bool `json:"from,omitempty"` +} + +// RequiredConversationResolutionLevelChanges represents the changes made to the RequiredConversationResolutionLevel policy. +type RequiredConversationResolutionLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// RequiredDeploymentsEnforcementLevelChanges represents the changes made to the RequiredDeploymentsEnforcementLevel policy. +type RequiredDeploymentsEnforcementLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// RequiredStatusChecksChanges represents the changes made to the RequiredStatusChecks policy. +type RequiredStatusChecksChanges struct { + From []string `json:"from,omitempty"` +} + +// RequiredStatusChecksEnforcementLevelChanges represents the changes made to the RequiredStatusChecksEnforcementLevel policy. +type RequiredStatusChecksEnforcementLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// SignatureRequirementEnforcementLevelChanges represents the changes made to the SignatureRequirementEnforcementLevel policy. +type SignatureRequirementEnforcementLevelChanges struct { + From *string `json:"from,omitempty"` +} + +// RequireLastPushApprovalChanges represents the changes made to the RequireLastPushApproval policy. +type RequireLastPushApprovalChanges struct { + From *bool `json:"from,omitempty"` } // ProtectionRequest represents a request to create/edit a branch's protection. @@ -696,27 +1206,67 @@ type ProtectionRequest struct { RequiredPullRequestReviews *PullRequestReviewsEnforcementRequest `json:"required_pull_request_reviews"` EnforceAdmins bool `json:"enforce_admins"` Restrictions *BranchRestrictionsRequest `json:"restrictions"` -} - -// RequiredStatusChecks represents the protection status of a individual branch. + // Enforces a linear commit Git history, which prevents anyone from pushing merge commits to a branch. + RequireLinearHistory *bool `json:"required_linear_history,omitempty"` + // Permits force pushes to the protected branch by anyone with write access to the repository. + AllowForcePushes *bool `json:"allow_force_pushes,omitempty"` + // Allows deletion of the protected branch by anyone with write access to the repository. + AllowDeletions *bool `json:"allow_deletions,omitempty"` + // RequiredConversationResolution, if set to true, requires all comments + // on the pull request to be resolved before it can be merged to a protected branch. + RequiredConversationResolution *bool `json:"required_conversation_resolution,omitempty"` + // BlockCreations, if set to true, will cause the restrictions setting to also block pushes + // which create new branches, unless initiated by a user, team, app with the ability to push. + BlockCreations *bool `json:"block_creations,omitempty"` + // LockBranch, if set to true, will prevent users from pushing to the branch. + LockBranch *bool `json:"lock_branch,omitempty"` + // AllowForkSyncing, if set to true, will allow users to pull changes from upstream + // when the branch is locked. + AllowForkSyncing *bool `json:"allow_fork_syncing,omitempty"` +} + +// RequiredStatusChecks represents the protection status of an individual branch. type RequiredStatusChecks struct { // Require branches to be up to date before merging. (Required.) Strict bool `json:"strict"` // The list of status checks to require in order to merge into this - // branch. (Required; use []string{} instead of nil for empty list.) - Contexts []string `json:"contexts"` + // branch. An empty slice is valid. (Deprecated. Note: only one of + // Contexts/Checks can be populated, but at least one must be populated). + Contexts *[]string `json:"contexts,omitempty"` + // The list of status checks to require in order to merge into this + // branch. An empty slice is valid. + Checks *[]*RequiredStatusCheck `json:"checks,omitempty"` + ContextsURL *string `json:"contexts_url,omitempty"` + URL *string `json:"url,omitempty"` } // RequiredStatusChecksRequest represents a request to edit a protected branch's status checks. type RequiredStatusChecksRequest struct { - Strict *bool `json:"strict,omitempty"` - Contexts []string `json:"contexts,omitempty"` + Strict *bool `json:"strict,omitempty"` + // Deprecated. Note: if both Contexts and Checks are populated, + // the GitHub API will only use Checks. + Contexts []string `json:"contexts,omitempty"` + Checks []*RequiredStatusCheck `json:"checks,omitempty"` +} + +// RequiredStatusCheck represents a status check of a protected branch. +type RequiredStatusCheck struct { + // The name of the required check. + Context string `json:"context"` + // The ID of the GitHub App that must provide this check. + // Omit this field to automatically select the GitHub App + // that has recently provided this check, + // or any app if it was not set by a GitHub App. + // Pass -1 to explicitly allow any app to set the status. + AppID *int64 `json:"app_id,omitempty"` } // PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch. type PullRequestReviewsEnforcement struct { - // Specifies which users and teams can dismiss pull request reviews. - DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions"` + // Allow specific users, teams, or apps to bypass pull request requirements. + BypassPullRequestAllowances *BypassPullRequestAllowances `json:"bypass_pull_request_allowances,omitempty"` + // Specifies which users, teams and apps can dismiss pull request reviews. + DismissalRestrictions *DismissalRestrictions `json:"dismissal_restrictions,omitempty"` // Specifies if approved reviews are dismissed automatically, when a new commit is pushed. DismissStaleReviews bool `json:"dismiss_stale_reviews"` // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. @@ -724,14 +1274,18 @@ type PullRequestReviewsEnforcement struct { // RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged. // Valid values are 1-6. RequiredApprovingReviewCount int `json:"required_approving_review_count"` + // RequireLastPushApproval specifies whether the last pusher to a pull request branch can approve it. + RequireLastPushApproval bool `json:"require_last_push_approval"` } // PullRequestReviewsEnforcementRequest represents request to set the pull request review // enforcement of a protected branch. It is separate from PullRequestReviewsEnforcement above // because the request structure is different from the response structure. type PullRequestReviewsEnforcementRequest struct { - // Specifies which users and teams should be allowed to dismiss pull request reviews. - // User and team dismissal restrictions are only available for + // Allow specific users, teams, or apps to bypass pull request requirements. + BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"` + // Specifies which users, teams and apps should be allowed to dismiss pull request reviews. + // User, team and app dismissal restrictions are only available for // organization-owned repositories. Must be nil for personal repositories. DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"` // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. (Required) @@ -741,21 +1295,48 @@ type PullRequestReviewsEnforcementRequest struct { // RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged. // Valid values are 1-6. RequiredApprovingReviewCount int `json:"required_approving_review_count"` + // RequireLastPushApproval specifies whether the last pusher to a pull request branch can approve it. + RequireLastPushApproval *bool `json:"require_last_push_approval,omitempty"` } // PullRequestReviewsEnforcementUpdate represents request to patch the pull request review // enforcement of a protected branch. It is separate from PullRequestReviewsEnforcementRequest above // because the patch request does not require all fields to be initialized. type PullRequestReviewsEnforcementUpdate struct { - // Specifies which users and teams can dismiss pull request reviews. Can be omitted. + // Allow specific users, teams, or apps to bypass pull request requirements. + BypassPullRequestAllowancesRequest *BypassPullRequestAllowancesRequest `json:"bypass_pull_request_allowances,omitempty"` + // Specifies which users, teams and apps can dismiss pull request reviews. Can be omitted. DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"` // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted. DismissStaleReviews *bool `json:"dismiss_stale_reviews,omitempty"` - // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. - RequireCodeOwnerReviews bool `json:"require_code_owner_reviews,omitempty"` + // RequireCodeOwnerReviews specifies if merging pull requests is blocked until code owners have reviewed. + RequireCodeOwnerReviews *bool `json:"require_code_owner_reviews,omitempty"` // RequiredApprovingReviewCount specifies the number of approvals required before the pull request can be merged. - // Valid values are 1 - 6. + // Valid values are 1 - 6 or 0 to not require reviewers. RequiredApprovingReviewCount int `json:"required_approving_review_count"` + // RequireLastPushApproval specifies whether the last pusher to a pull request branch can approve it. + RequireLastPushApproval *bool `json:"require_last_push_approval,omitempty"` +} + +// RequireLinearHistory represents the configuration to enforce branches with no merge commit. +type RequireLinearHistory struct { + Enabled bool `json:"enabled"` +} + +// AllowDeletions represents the configuration to accept deletion of protected branches. +type AllowDeletions struct { + Enabled bool `json:"enabled"` +} + +// AllowForcePushes represents the configuration to accept forced pushes on protected branches. +type AllowForcePushes struct { + Enabled bool `json:"enabled"` +} + +// RequiredConversationResolution requires all comments on the pull request to be resolved before it can be +// merged to a protected branch when enabled. +type RequiredConversationResolution struct { + Enabled bool `json:"enabled"` } // AdminEnforcement represents the configuration to enforce required status checks for repository administrators. @@ -771,6 +1352,8 @@ type BranchRestrictions struct { Users []*User `json:"users"` // The list of team slugs with push access. Teams []*Team `json:"teams"` + // The list of app slugs with push access. + Apps []*App `json:"apps"` } // BranchRestrictionsRequest represents the request to create/edit the @@ -782,18 +1365,45 @@ type BranchRestrictionsRequest struct { Users []string `json:"users"` // The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.) Teams []string `json:"teams"` + // The list of app slugs with push access. + Apps []string `json:"apps"` +} + +// BypassPullRequestAllowances represents the people, teams, or apps who are allowed to bypass required pull requests. +type BypassPullRequestAllowances struct { + // The list of users allowed to bypass pull request requirements. + Users []*User `json:"users"` + // The list of teams allowed to bypass pull request requirements. + Teams []*Team `json:"teams"` + // The list of apps allowed to bypass pull request requirements. + Apps []*App `json:"apps"` +} + +// BypassPullRequestAllowancesRequest represents the people, teams, or apps who are +// allowed to bypass required pull requests. +// It is separate from BypassPullRequestAllowances above because the request structure is +// different from the response structure. +type BypassPullRequestAllowancesRequest struct { + // The list of user logins allowed to bypass pull request requirements. + Users []string `json:"users"` + // The list of team slugs allowed to bypass pull request requirements. + Teams []string `json:"teams"` + // The list of app slugs allowed to bypass pull request requirements. + Apps []string `json:"apps"` } // DismissalRestrictions specifies which users and teams can dismiss pull request reviews. type DismissalRestrictions struct { - // The list of users who can dimiss pull request reviews. + // The list of users who can dismiss pull request reviews. Users []*User `json:"users"` // The list of teams which can dismiss pull request reviews. Teams []*Team `json:"teams"` + // The list of apps which can dismiss pull request reviews. + Apps []*App `json:"apps"` } // DismissalRestrictionsRequest represents the request to create/edit the -// restriction to allows only specific users or teams to dimiss pull request reviews. It is +// restriction to allows only specific users, teams or apps to dismiss pull request reviews. It is // separate from DismissalRestrictions above because the request structure is // different from the response structure. // Note: Both Users and Teams must be nil, or both must be non-nil. @@ -802,6 +1412,8 @@ type DismissalRestrictionsRequest struct { Users *[]string `json:"users,omitempty"` // The list of team slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.) Teams *[]string `json:"teams,omitempty"` + // The list of app slugs which can dismiss pull request reviews. (Required; use nil to disable dismissal_restrictions or &[]string{} otherwise.) + Apps *[]string `json:"apps,omitempty"` } // SignaturesProtectedBranch represents the protection status of an individual branch. @@ -811,26 +1423,31 @@ type SignaturesProtectedBranch struct { Enabled *bool `json:"enabled,omitempty"` } +// AutomatedSecurityFixes represents their status. +type AutomatedSecurityFixes struct { + Enabled *bool `json:"enabled"` + Paused *bool `json:"paused"` +} + // ListBranches lists branches for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-branches -func (s *RepositoriesService) ListBranches(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Branch, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/branches/branches?apiVersion=2022-11-28#list-branches +// +//meta:operation GET /repos/{owner}/{repo}/branches +func (s *RepositoriesService) ListBranches(ctx context.Context, owner, repo string, opts *BranchListOptions) ([]*Branch, *Response, error) { u := fmt.Sprintf("repos/%v/%v/branches", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - var branches []*Branch - resp, err := s.client.Do(ctx, req, &branches) + resp, err := s.client.Do(req, &branches) if err != nil { return nil, resp, err } @@ -840,19 +1457,54 @@ func (s *RepositoriesService) ListBranches(ctx context.Context, owner string, re // GetBranch gets the specified branch for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#get-branch -func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch string) (*Branch, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branches?apiVersion=2022-11-28#get-a-branch +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch} +func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch string, maxRedirects int) (*Branch, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, url.PathEscape(branch)) + + resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err } + defer resp.Body.Close() - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) + if resp.StatusCode != http.StatusOK { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) + } + + var b *Branch + err = json.NewDecoder(resp.Body).Decode(&b) + return b, newResponse(resp), err +} + +// renameBranchRequest represents a request to rename a branch. +type renameBranchRequest struct { + NewName string `json:"new_name"` +} + +// RenameBranch renames a branch in a repository. +// +// To rename a non-default branch: Users must have push access. GitHub Apps must have the `contents:write` repository permission. +// To rename the default branch: Users must have admin or owner permissions. GitHub Apps must have the `administration:write` repository permission. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branches?apiVersion=2022-11-28#rename-a-branch +// +//meta:operation POST /repos/{owner}/{repo}/branches/{branch}/rename +func (s *RepositoriesService) RenameBranch(ctx context.Context, owner, repo, branch, newName string) (*Branch, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/rename", owner, repo, url.PathEscape(branch)) + r := &renameBranchRequest{NewName: newName} + req, err := s.client.NewRequest(ctx, "POST", u, r) + if err != nil { + return nil, nil, err + } - b := new(Branch) - resp, err := s.client.Do(ctx, req, b) + var b *Branch + resp, err := s.client.Do(req, &b) if err != nil { return nil, resp, err } @@ -862,20 +1514,26 @@ func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch // GetBranchProtection gets the protection of a given branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-branch-protection +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-branch-protection +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*Protection, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - p := new(Protection) - resp, err := s.client.Do(ctx, req, p) + var p *Protection + resp, err := s.client.Do(req, &p) if err != nil { + if isBranchNotProtected(err) { + err = ErrBranchNotProtected + } return nil, resp, err } @@ -884,20 +1542,24 @@ func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, re // GetRequiredStatusChecks gets the required status checks for a given protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-status-checks-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-status-checks-protection +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks func (s *RepositoriesService) GetRequiredStatusChecks(ctx context.Context, owner, repo, branch string) (*RequiredStatusChecks, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - p := new(RequiredStatusChecks) - resp, err := s.client.Do(ctx, req, p) + var p *RequiredStatusChecks + resp, err := s.client.Do(req, &p) if err != nil { + if isBranchNotProtected(err) { + err = ErrBranchNotProtected + } return nil, resp, err } @@ -906,19 +1568,23 @@ func (s *RepositoriesService) GetRequiredStatusChecks(ctx context.Context, owner // ListRequiredStatusChecksContexts lists the required status checks contexts for a given protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-required-status-checks-contexts-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-all-status-check-contexts +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts func (s *RepositoriesService) ListRequiredStatusChecksContexts(ctx context.Context, owner, repo, branch string) (contexts []string, resp *Response, err error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks/contexts", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks/contexts", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - resp, err = s.client.Do(ctx, req, &contexts) + resp, err = s.client.Do(req, &contexts) if err != nil { + if isBranchNotProtected(err) { + err = ErrBranchNotProtected + } return nil, resp, err } @@ -927,19 +1593,22 @@ func (s *RepositoriesService) ListRequiredStatusChecksContexts(ctx context.Conte // UpdateBranchProtection updates the protection of a given branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-branch-protection -func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *ProtectionRequest) (*Protection, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) - req, err := s.client.NewRequest("PUT", u, preq) +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#update-branch-protection +// +//meta:operation PUT /repos/{owner}/{repo}/branches/{branch}/protection +func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, body *ProtectionRequest) (*Protection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - p := new(Protection) - resp, err := s.client.Do(ctx, req, p) + var p *Protection + resp, err := s.client.Do(req, &p) if err != nil { return nil, resp, err } @@ -949,36 +1618,43 @@ func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, // RemoveBranchProtection removes the protection of a given branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-branch-protection +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#delete-branch-protection +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection func (s *RepositoriesService) RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // GetSignaturesProtectedBranch gets required signatures of protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-signatures-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-commit-signature-protection +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures func (s *RepositoriesService) GetSignaturesProtectedBranch(ctx context.Context, owner, repo, branch string) (*SignaturesProtectedBranch, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeSignaturePreview) - p := new(SignaturesProtectedBranch) - resp, err := s.client.Do(ctx, req, p) + var p *SignaturesProtectedBranch + resp, err := s.client.Do(req, &p) if err != nil { + if isBranchNotProtected(err) { + err = ErrBranchNotProtected + } return nil, resp, err } @@ -988,54 +1664,64 @@ func (s *RepositoriesService) GetSignaturesProtectedBranch(ctx context.Context, // RequireSignaturesOnProtectedBranch makes signed commits required on a protected branch. // It requires admin access and branch protection to be enabled. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-required-signatures-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#create-commit-signature-protection +// +//meta:operation POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures func (s *RepositoriesService) RequireSignaturesOnProtectedBranch(ctx context.Context, owner, repo, branch string) (*SignaturesProtectedBranch, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, branch) - req, err := s.client.NewRequest("POST", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeSignaturePreview) - r := new(SignaturesProtectedBranch) - resp, err := s.client.Do(ctx, req, r) + var r *SignaturesProtectedBranch + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return r, resp, err + return r, resp, nil } // OptionalSignaturesOnProtectedBranch removes required signed commits on a given branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-required-signatures-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#delete-commit-signature-protection +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures func (s *RepositoriesService) OptionalSignaturesOnProtectedBranch(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_signatures", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeSignaturePreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UpdateRequiredStatusChecks updates the required status checks for a given protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-required-status-checks-of-protected-branch -func (s *RepositoriesService) UpdateRequiredStatusChecks(ctx context.Context, owner, repo, branch string, sreq *RequiredStatusChecksRequest) (*RequiredStatusChecks, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch) - req, err := s.client.NewRequest("PATCH", u, sreq) +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#update-status-check-protection +// +//meta:operation PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks +func (s *RepositoriesService) UpdateRequiredStatusChecks(ctx context.Context, owner, repo, branch string, body *RequiredStatusChecksRequest) (*RequiredStatusChecks, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - sc := new(RequiredStatusChecks) - resp, err := s.client.Do(ctx, req, sc) + var sc *RequiredStatusChecks + resp, err := s.client.Do(req, &sc) if err != nil { return nil, resp, err } @@ -1043,18 +1729,37 @@ func (s *RepositoriesService) UpdateRequiredStatusChecks(ctx context.Context, ow return sc, resp, nil } +// RemoveRequiredStatusChecks removes the required status checks for a given protected branch. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#remove-status-check-protection +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks +func (s *RepositoriesService) RemoveRequiredStatusChecks(ctx context.Context, owner, repo, branch string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + // License gets the contents of a repository's license if one is detected. // -// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license +// GitHub API docs: https://docs.github.com/rest/licenses/licenses?apiVersion=2022-11-28#get-the-license-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/license func (s *RepositoriesService) License(ctx context.Context, owner, repo string) (*RepositoryLicense, *Response, error) { u := fmt.Sprintf("repos/%v/%v/license", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - r := &RepositoryLicense{} - resp, err := s.client.Do(ctx, req, r) + var r *RepositoryLicense + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -1064,19 +1769,22 @@ func (s *RepositoriesService) License(ctx context.Context, owner, repo string) ( // GetPullRequestReviewEnforcement gets pull request review enforcement of a protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-pull-request-review-enforcement-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-pull-request-review-protection +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews func (s *RepositoriesService) GetPullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - r := new(PullRequestReviewsEnforcement) - resp, err := s.client.Do(ctx, req, r) + var r *PullRequestReviewsEnforcement + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -1087,85 +1795,93 @@ func (s *RepositoriesService) GetPullRequestReviewEnforcement(ctx context.Contex // UpdatePullRequestReviewEnforcement patches pull request review enforcement of a protected branch. // It requires admin access and branch protection to be enabled. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch -func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string, patch *PullRequestReviewsEnforcementUpdate) (*PullRequestReviewsEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - req, err := s.client.NewRequest("PATCH", u, patch) +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#update-pull-request-review-protection +// +//meta:operation PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews +func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string, body *PullRequestReviewsEnforcementUpdate) (*PullRequestReviewsEnforcement, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - r := new(PullRequestReviewsEnforcement) - resp, err := s.client.Do(ctx, req, r) + var r *PullRequestReviewsEnforcement + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return r, resp, err + return r, resp, nil } // DisableDismissalRestrictions disables dismissal restrictions of a protected branch. // It requires admin access and branch protection to be enabled. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#update-pull-request-review-protection +// +//meta:operation PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews func (s *RepositoriesService) DisableDismissalRestrictions(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, url.PathEscape(branch)) - data := struct { - R []interface{} `json:"dismissal_restrictions"` - }{[]interface{}{}} + data := new(struct { + DismissalRestrictionsRequest `json:"dismissal_restrictions"` + }) - req, err := s.client.NewRequest("PATCH", u, data) + req, err := s.client.NewRequest(ctx, "PATCH", u, data) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - r := new(PullRequestReviewsEnforcement) - resp, err := s.client.Do(ctx, req, r) + var r *PullRequestReviewsEnforcement + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return r, resp, err + return r, resp, nil } // RemovePullRequestReviewEnforcement removes pull request enforcement of a protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-pull-request-review-enforcement-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#delete-pull-request-review-protection +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews func (s *RepositoriesService) RemovePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // GetAdminEnforcement gets admin enforcement information of a protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-admin-enforcement-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-admin-branch-protection +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins func (s *RepositoriesService) GetAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - r := new(AdminEnforcement) - resp, err := s.client.Do(ctx, req, r) + var r *AdminEnforcement + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } @@ -1176,40 +1892,42 @@ func (s *RepositoriesService) GetAdminEnforcement(ctx context.Context, owner, re // AddAdminEnforcement adds admin enforcement to a protected branch. // It requires admin access and branch protection to be enabled. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-admin-enforcement-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#set-admin-branch-protection +// +//meta:operation POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins func (s *RepositoriesService) AddAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) - req, err := s.client.NewRequest("POST", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - r := new(AdminEnforcement) - resp, err := s.client.Do(ctx, req, r) + var r *AdminEnforcement + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } - return r, resp, err + return r, resp, nil } // RemoveAdminEnforcement removes admin enforcement from a protected branch. // -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-admin-enforcement-of-protected-branch +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#delete-admin-branch-protection +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins func (s *RepositoriesService) RemoveAdminEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeRequiredApprovingReviewsPreview) - - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // repositoryTopics represents a collection of repository topics. @@ -1219,19 +1937,25 @@ type repositoryTopics struct { // ListAllTopics lists topics for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/#list-all-topics-for-a-repository -func (s *RepositoriesService) ListAllTopics(ctx context.Context, owner, repo string) ([]string, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#get-all-repository-topics +// +//meta:operation GET /repos/{owner}/{repo}/topics +func (s *RepositoriesService) ListAllTopics(ctx context.Context, owner, repo string, opts *ListOptions) ([]string, *Response, error) { u := fmt.Sprintf("repos/%v/%v/topics", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeTopicsPreview) - topics := new(repositoryTopics) - resp, err := s.client.Do(ctx, req, topics) + var topics *repositoryTopics + resp, err := s.client.Do(req, &topics) if err != nil { return nil, resp, err } @@ -1239,9 +1963,11 @@ func (s *RepositoriesService) ListAllTopics(ctx context.Context, owner, repo str return topics.Names, resp, nil } -// ReplaceAllTopics replaces topics for a repository. +// ReplaceAllTopics replaces all repository topics. // -// GitHub API docs: https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#replace-all-repository-topics +// +//meta:operation PUT /repos/{owner}/{repo}/topics func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo string, topics []string) ([]string, *Response, error) { u := fmt.Sprintf("repos/%v/%v/topics", owner, repo) t := &repositoryTopics{ @@ -1250,16 +1976,15 @@ func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo if t.Names == nil { t.Names = []string{} } - req, err := s.client.NewRequest("PUT", u, t) + req, err := s.client.NewRequest(ctx, "PUT", u, t) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeTopicsPreview) t = new(repositoryTopics) - resp, err := s.client.Do(ctx, req, t) + resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err } @@ -1267,9 +1992,333 @@ func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo return t.Names, resp, nil } +// ListApps lists the GitHub apps that have push access to a given protected branch. +// It requires the GitHub apps to have `write` access to the `content` permission. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// Deprecated: Please use ListAppRestrictions instead. +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-apps-with-access-to-the-protected-branch +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps +func (s *RepositoriesService) ListApps(ctx context.Context, owner, repo, branch string) ([]*App, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var apps []*App + resp, err := s.client.Do(req, &apps) + if err != nil { + return nil, resp, err + } + + return apps, resp, nil +} + +// ListAppRestrictions lists the GitHub apps that have push access to a given protected branch. +// It requires the GitHub apps to have `write` access to the `content` permission. +// +// Note: This is a wrapper around ListApps so a naming convention with ListUserRestrictions and ListTeamRestrictions is preserved. +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-apps-with-access-to-the-protected-branch +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps +func (s *RepositoriesService) ListAppRestrictions(ctx context.Context, owner, repo, branch string) ([]*App, *Response, error) { + return s.ListApps(ctx, owner, repo, branch) +} + +// ReplaceAppRestrictions replaces the apps that have push access to a given protected branch. +// It removes all apps that previously had push access and grants push access to the new list of apps. +// It requires the GitHub apps to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#set-app-access-restrictions +// +//meta:operation PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps +func (s *RepositoriesService) ReplaceAppRestrictions(ctx context.Context, owner, repo, branch string, body []string) ([]*App, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var newApps []*App + resp, err := s.client.Do(req, &newApps) + if err != nil { + return nil, resp, err + } + + return newApps, resp, nil +} + +// AddAppRestrictions grants the specified apps push access to a given protected branch. +// It requires the GitHub apps to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#add-app-access-restrictions +// +//meta:operation POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps +func (s *RepositoriesService) AddAppRestrictions(ctx context.Context, owner, repo, branch string, body []string) ([]*App, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var newApps []*App + resp, err := s.client.Do(req, &newApps) + if err != nil { + return nil, resp, err + } + + return newApps, resp, nil +} + +// RemoveAppRestrictions removes the restrictions of an app from pushing to this branch. +// It requires the GitHub apps to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#remove-app-access-restrictions +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps +func (s *RepositoriesService) RemoveAppRestrictions(ctx context.Context, owner, repo, branch string, apps []string) ([]*App, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/apps", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, apps) + if err != nil { + return nil, nil, err + } + + var newApps []*App + resp, err := s.client.Do(req, &newApps) + if err != nil { + return nil, resp, err + } + + return newApps, resp, nil +} + +// ListTeamRestrictions lists the GitHub teams that have push access to a given protected branch. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-teams-with-access-to-the-protected-branch +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams +func (s *RepositoriesService) ListTeamRestrictions(ctx context.Context, owner, repo, branch string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var teams []*Team + resp, err := s.client.Do(req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// ReplaceTeamRestrictions replaces the team that have push access to a given protected branch. +// This removes all teams that previously had push access and grants push access to the new list of teams. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#set-team-access-restrictions +// +//meta:operation PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams +func (s *RepositoriesService) ReplaceTeamRestrictions(ctx context.Context, owner, repo, branch string, body []string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var newTeams []*Team + resp, err := s.client.Do(req, &newTeams) + if err != nil { + return nil, resp, err + } + + return newTeams, resp, nil +} + +// AddTeamRestrictions grants the specified teams push access to a given protected branch. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#add-team-access-restrictions +// +//meta:operation POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams +func (s *RepositoriesService) AddTeamRestrictions(ctx context.Context, owner, repo, branch string, body []string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var newTeams []*Team + resp, err := s.client.Do(req, &newTeams) + if err != nil { + return nil, resp, err + } + + return newTeams, resp, nil +} + +// RemoveTeamRestrictions removes the restrictions of a team from pushing to this branch. +// It requires the GitHub teams to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#remove-team-access-restrictions +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams +func (s *RepositoriesService) RemoveTeamRestrictions(ctx context.Context, owner, repo, branch string, teams []string) ([]*Team, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/teams", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, teams) + if err != nil { + return nil, nil, err + } + + var newTeams []*Team + resp, err := s.client.Do(req, &newTeams) + if err != nil { + return nil, resp, err + } + + return newTeams, resp, nil +} + +// ListUserRestrictions lists the GitHub users that have push access to a given protected branch. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#get-users-with-access-to-the-protected-branch +// +//meta:operation GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users +func (s *RepositoriesService) ListUserRestrictions(ctx context.Context, owner, repo, branch string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var users []*User + resp, err := s.client.Do(req, &users) + if err != nil { + return nil, resp, err + } + + return users, resp, nil +} + +// ReplaceUserRestrictions replaces the user that have push access to a given protected branch. +// It removes all users that previously had push access and grants push access to the new list of users. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#set-user-access-restrictions +// +//meta:operation PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users +func (s *RepositoriesService) ReplaceUserRestrictions(ctx context.Context, owner, repo, branch string, body []string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var newUsers []*User + resp, err := s.client.Do(req, &newUsers) + if err != nil { + return nil, resp, err + } + + return newUsers, resp, nil +} + +// AddUserRestrictions grants the specified users push access to a given protected branch. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#add-user-access-restrictions +// +//meta:operation POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users +func (s *RepositoriesService) AddUserRestrictions(ctx context.Context, owner, repo, branch string, body []string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var newUsers []*User + resp, err := s.client.Do(req, &newUsers) + if err != nil { + return nil, resp, err + } + + return newUsers, resp, nil +} + +// RemoveUserRestrictions removes the restrictions of a user from pushing to this branch. +// It requires the GitHub users to have `write` access to the `content` permission. +// +// Note: The list of users, apps, and teams in total is limited to 100 items. +// +// Note: the branch name is URL path escaped for you. See: https://pkg.go.dev/net/url#PathEscape . +// +// GitHub API docs: https://docs.github.com/rest/branches/branch-protection?apiVersion=2022-11-28#remove-user-access-restrictions +// +//meta:operation DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users +func (s *RepositoriesService) RemoveUserRestrictions(ctx context.Context, owner, repo, branch string, users []string) ([]*User, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/restrictions/users", owner, repo, url.PathEscape(branch)) + req, err := s.client.NewRequest(ctx, "DELETE", u, users) + if err != nil { + return nil, nil, err + } + + var newUsers []*User + resp, err := s.client.Do(req, &newUsers) + if err != nil { + return nil, resp, err + } + + return newUsers, resp, nil +} + // TransferRequest represents a request to transfer a repository. type TransferRequest struct { NewOwner string `json:"new_owner"` + NewName *string `json:"new_name,omitempty"` TeamID []int64 `json:"team_ids,omitempty"` } @@ -1281,23 +2330,224 @@ type TransferRequest struct { // A follow up request, after a delay of a second or so, should result // in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/#transfer-a-repository +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#transfer-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/transfer func (s *RepositoriesService) Transfer(ctx context.Context, owner, repo string, transfer TransferRequest) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v/transfer", owner, repo) - req, err := s.client.NewRequest("POST", u, &transfer) + req, err := s.client.NewRequest(ctx, "POST", u, &transfer) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryTransferPreview) + var r *Repository + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + +// DispatchRequestOptions represents a request to trigger a repository_dispatch event. +type DispatchRequestOptions struct { + // EventType is a custom webhook event name. (Required.) + EventType string `json:"event_type"` + // ClientPayload is a custom JSON payload with extra information about the webhook event. + // Defaults to an empty JSON object. + ClientPayload *json.RawMessage `json:"client_payload,omitempty"` +} + +// Dispatch triggers a repository_dispatch event in a GitHub Actions workflow. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#create-a-repository-dispatch-event +// +//meta:operation POST /repos/{owner}/{repo}/dispatches +func (s *RepositoriesService) Dispatch(ctx context.Context, owner, repo string, opts DispatchRequestOptions) (*Repository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/dispatches", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, &opts) + if err != nil { + return nil, nil, err + } - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) + var r *Repository + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } return r, resp, nil } + +// isBranchNotProtected determines whether a branch is not protected +// based on the error message returned by GitHub API. +func isBranchNotProtected(err error) bool { + var errorResponse *ErrorResponse + return errors.As(err, &errorResponse) && errorResponse.Message == githubBranchNotProtected +} + +// EnablePrivateReporting enables private reporting of vulnerabilities for a +// repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#enable-private-vulnerability-reporting-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/private-vulnerability-reporting +func (s *RepositoriesService) EnablePrivateReporting(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/private-vulnerability-reporting", owner, repo) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DisablePrivateReporting disables private reporting of vulnerabilities for a +// repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#disable-private-vulnerability-reporting-for-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/private-vulnerability-reporting +func (s *RepositoriesService) DisablePrivateReporting(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/private-vulnerability-reporting", owner, repo) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// checkPrivateReporting represents whether private vulnerability reporting is enabled. +type checkPrivateReporting struct { + Enabled bool `json:"enabled,omitempty"` +} + +// IsPrivateReportingEnabled checks if private vulnerability reporting is enabled +// for the repository and returns a boolean indicating the status. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#check-if-private-vulnerability-reporting-is-enabled-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/private-vulnerability-reporting +func (s *RepositoriesService) IsPrivateReportingEnabled(ctx context.Context, owner, repo string) (bool, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/private-vulnerability-reporting", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return false, nil, err + } + + var privateReporting checkPrivateReporting + resp, err := s.client.Do(req, &privateReporting) + return privateReporting.Enabled, resp, err +} + +// ListRepositoryActivityOptions specifies the optional parameters to the +// RepositoriesService.ListRepositoryActivities method. +type ListRepositoryActivityOptions struct { + // The direction to sort the results by. + // Default: desc + // Can be one of: asc, desc + Direction string `url:"direction,omitempty"` + + // For paginated result sets, The number of results per page (max 100). + PerPage int `url:"per_page,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for events before this cursor. + Before string `url:"before,omitempty"` + + // A cursor, as given in the Link header. If specified, the query only searches for events after this cursor. + After string `url:"after,omitempty"` + + // The Git reference for the activities you want to list. + // The ref for a branch can be formatted either as refs/heads/BRANCH_NAME or BRANCH_NAME, where BRANCH_NAME is the name of your branch. + Ref string `url:"ref,omitempty"` + + // The GitHub username to use to filter by the actor who performed the activity. + Actor string `url:"actor,omitempty"` + + // The time period to filter by. + // For example, day will filter for activity that occurred in the past 24 hours, and week will filter for activity that occurred in the past 7 days (168 hours). + // Can be one of: day, week, month, quarter, year + TimePeriod string `url:"time_period,omitempty"` + + // The activity type to filter by. + // For example, you can choose to filter by "force_push", to see all force pushes to the repository. + // Can be one of: push, force_push, branch_creation, branch_deletion, pr_merge, merge_queue_merge + ActivityType string `url:"activity_type,omitempty"` +} + +// RepositoryActor represents a repository actor. +type RepositoryActor struct { + Login *string `json:"login,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + AvatarURL *string `json:"avatar_url,omitempty"` + GravatarID *string `json:"gravatar_id,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + FollowersURL *string `json:"followers_url,omitempty"` + FollowingURL *string `json:"following_url,omitempty"` + GistsURL *string `json:"gists_url,omitempty"` + StarredURL *string `json:"starred_url,omitempty"` + SubscriptionsURL *string `json:"subscriptions_url,omitempty"` + OrganizationsURL *string `json:"organizations_url,omitempty"` + ReposURL *string `json:"repos_url,omitempty"` + EventsURL *string `json:"events_url,omitempty"` + ReceivedEventsURL *string `json:"received_events_url,omitempty"` + Type *string `json:"type,omitempty"` + UserViewType *string `json:"user_view_type,omitempty"` + SiteAdmin *bool `json:"site_admin,omitempty"` +} + +// RepositoryActivity represents a repository activity. +type RepositoryActivity struct { + ID int64 `json:"id"` + NodeID string `json:"node_id"` + Before string `json:"before"` + After string `json:"after"` + Ref string `json:"ref"` + Timestamp *Timestamp `json:"timestamp"` + ActivityType string `json:"activity_type"` + Actor *RepositoryActor `json:"actor,omitempty"` +} + +// ListRepositoryActivities lists the activities for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-repository-activities +// +//meta:operation GET /repos/{owner}/{repo}/activity +func (s *RepositoriesService) ListRepositoryActivities(ctx context.Context, owner, repo string, opts *ListRepositoryActivityOptions) ([]*RepositoryActivity, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/activity", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var activities []*RepositoryActivity + resp, err := s.client.Do(req, &activities) + if err != nil { + return nil, resp, err + } + + return activities, resp, nil +} diff --git a/github/repos_actions_access.go b/github/repos_actions_access.go new file mode 100644 index 00000000000..705d868d2b7 --- /dev/null +++ b/github/repos_actions_access.go @@ -0,0 +1,59 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepositoryActionsAccessLevel represents the repository actions access level. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-the-level-of-access-for-workflows-outside-of-the-repository +type RepositoryActionsAccessLevel struct { + // AccessLevel specifies the level of access that workflows outside of the repository have + // to actions and reusable workflows within the repository. + // Possible values are: "none", "organization" "enterprise". + AccessLevel *string `json:"access_level,omitempty"` +} + +// GetActionsAccessLevel gets the level of access that workflows outside of the repository have +// to actions and reusable workflows in the repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-the-level-of-access-for-workflows-outside-of-the-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions/access +func (s *RepositoriesService) GetActionsAccessLevel(ctx context.Context, owner, repo string) (*RepositoryActionsAccessLevel, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/access", owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var raal *RepositoryActionsAccessLevel + resp, err := s.client.Do(req, &raal) + if err != nil { + return nil, resp, err + } + + return raal, resp, nil +} + +// EditActionsAccessLevel sets the level of access that workflows outside of the repository have +// to actions and reusable workflows in the repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-the-level-of-access-for-workflows-outside-of-the-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/access +func (s *RepositoriesService) EditActionsAccessLevel(ctx context.Context, owner, repo string, body RepositoryActionsAccessLevel) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/access", owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_actions_access_test.go b/github/repos_actions_access_test.go new file mode 100644 index 00000000000..7f35de5188f --- /dev/null +++ b/github/repos_actions_access_test.go @@ -0,0 +1,77 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetActionsAccessLevel(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions/access", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"access_level": "none"}`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.GetActionsAccessLevel(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetActionsAccessLevel returned error: %v", err) + } + want := &RepositoryActionsAccessLevel{AccessLevel: Ptr("none")} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.GetActionsAccessLevel returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsAccessLevel" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetActionsAccessLevel(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetActionsAccessLevel(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_EditActionsAccessLevel(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &RepositoryActionsAccessLevel{AccessLevel: Ptr("organization")} + + mux.HandleFunc("/repos/o/r/actions/permissions/access", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + _, err := client.Repositories.EditActionsAccessLevel(ctx, "o", "r", *input) + if err != nil { + t.Errorf("Repositories.EditActionsAccessLevel returned error: %v", err) + } + + const methodName = "EditActionsAccessLevel" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EditActionsAccessLevel(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Repositories.EditActionsAccessLevel(ctx, "o", "r", *input) + return resp, err + }) +} diff --git a/github/repos_actions_allowed.go b/github/repos_actions_allowed.go new file mode 100644 index 00000000000..c1536d0c7e5 --- /dev/null +++ b/github/repos_actions_allowed.go @@ -0,0 +1,53 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetActionsAllowed gets the allowed actions and reusable workflows for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-allowed-actions-and-reusable-workflows-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions/selected-actions +func (s *RepositoriesService) GetActionsAllowed(ctx context.Context, org, repo string) (*ActionsAllowed, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/selected-actions", org, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var actionsAllowed *ActionsAllowed + resp, err := s.client.Do(req, &actionsAllowed) + if err != nil { + return nil, resp, err + } + + return actionsAllowed, resp, nil +} + +// EditActionsAllowed sets the allowed actions and reusable workflows for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-allowed-actions-and-reusable-workflows-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/selected-actions +func (s *RepositoriesService) EditActionsAllowed(ctx context.Context, org, repo string, body ActionsAllowed) (*ActionsAllowed, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/selected-actions", org, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *ActionsAllowed + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} diff --git a/github/repos_actions_allowed_test.go b/github/repos_actions_allowed_test.go new file mode 100644 index 00000000000..0882ea95335 --- /dev/null +++ b/github/repos_actions_allowed_test.go @@ -0,0 +1,86 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoryService_GetActionsAllowed(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.GetActionsAllowed(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetActionsAllowed returned error: %v", err) + } + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.GetActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetActionsAllowed(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetActionsAllowed(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_EditActionsAllowed(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + + mux.HandleFunc("/repos/o/r/actions/permissions/selected-actions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"github_owned_allowed":true, "verified_allowed":false, "patterns_allowed":["a/b"]}`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.EditActionsAllowed(ctx, "o", "r", *input) + if err != nil { + t.Errorf("Repositories.EditActionsAllowed returned error: %v", err) + } + + want := &ActionsAllowed{GithubOwnedAllowed: Ptr(true), VerifiedAllowed: Ptr(false), PatternsAllowed: []string{"a/b"}} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.EditActionsAllowed returned %+v, want %+v", org, want) + } + + const methodName = "EditActionsAllowed" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.EditActionsAllowed(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.EditActionsAllowed(ctx, "o", "r", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/repos_actions_permissions.go b/github/repos_actions_permissions.go new file mode 100644 index 00000000000..20e21b06d0a --- /dev/null +++ b/github/repos_actions_permissions.go @@ -0,0 +1,230 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ActionsPermissionsRepository represents a policy for repositories and allowed actions in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28 +type ActionsPermissionsRepository struct { + Enabled *bool `json:"enabled,omitempty"` + AllowedActions *string `json:"allowed_actions,omitempty"` + SelectedActionsURL *string `json:"selected_actions_url,omitempty"` + SHAPinningRequired *bool `json:"sha_pinning_required,omitempty"` +} + +func (a ActionsPermissionsRepository) String() string { + return Stringify(a) +} + +// DefaultWorkflowPermissionRepository represents the default permissions for GitHub Actions workflows for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28 +type DefaultWorkflowPermissionRepository struct { + DefaultWorkflowPermissions *string `json:"default_workflow_permissions,omitempty"` + CanApprovePullRequestReviews *bool `json:"can_approve_pull_request_reviews,omitempty"` +} + +// GetActionsPermissions gets the GitHub Actions permissions policy for repositories and allowed actions in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-github-actions-permissions-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions +func (s *RepositoriesService) GetActionsPermissions(ctx context.Context, owner, repo string) (*ActionsPermissionsRepository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *ActionsPermissionsRepository + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateActionsPermissions sets the permissions policy for repositories and allowed actions in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-github-actions-permissions-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions +func (s *RepositoriesService) UpdateActionsPermissions(ctx context.Context, owner, repo string, body ActionsPermissionsRepository) (*ActionsPermissionsRepository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions", owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var permissions *ActionsPermissionsRepository + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// GetDefaultWorkflowPermissions gets the GitHub Actions default workflow permissions in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-default-workflow-permissions-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions/workflow +func (s *RepositoriesService) GetDefaultWorkflowPermissions(ctx context.Context, owner, repo string) (*DefaultWorkflowPermissionRepository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/workflow", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *DefaultWorkflowPermissionRepository + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdateDefaultWorkflowPermissions sets the GitHub Actions default workflow permissions in a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-default-workflow-permissions-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/workflow +func (s *RepositoriesService) UpdateDefaultWorkflowPermissions(ctx context.Context, owner, repo string, body DefaultWorkflowPermissionRepository) (*DefaultWorkflowPermissionRepository, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/workflow", owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var p *DefaultWorkflowPermissionRepository + resp, err := s.client.Do(req, &p) + if err != nil { + return nil, resp, err + } + + return p, resp, nil +} + +// GetArtifactAndLogRetentionPeriod gets the artifact and log retention period for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-artifact-and-log-retention-settings-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention +func (s *RepositoriesService) GetArtifactAndLogRetentionPeriod(ctx context.Context, owner, repo string) (*ArtifactPeriod, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/artifact-and-log-retention", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var arp *ArtifactPeriod + resp, err := s.client.Do(req, &arp) + if err != nil { + return nil, resp, err + } + + return arp, resp, nil +} + +// UpdateArtifactAndLogRetentionPeriod sets the artifact and log retention period for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-artifact-and-log-retention-settings-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention +func (s *RepositoriesService) UpdateArtifactAndLogRetentionPeriod(ctx context.Context, owner, repo string, body ArtifactPeriodOpt) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/artifact-and-log-retention", owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetPrivateRepoForkPRWorkflowSettings gets the settings for whether workflows from fork pull requests can run on a private repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-private-repo-fork-pr-workflow-settings-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions/fork-pr-workflows-private-repos +func (s *RepositoriesService) GetPrivateRepoForkPRWorkflowSettings(ctx context.Context, owner, repo string) (*WorkflowsPermissions, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/fork-pr-workflows-private-repos", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var permissions *WorkflowsPermissions + resp, err := s.client.Do(req, &permissions) + if err != nil { + return nil, resp, err + } + + return permissions, resp, nil +} + +// UpdatePrivateRepoForkPRWorkflowSettings sets the settings for whether workflows from fork pull requests can run on a private repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-private-repo-fork-pr-workflow-settings-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/fork-pr-workflows-private-repos +func (s *RepositoriesService) UpdatePrivateRepoForkPRWorkflowSettings(ctx context.Context, owner, repo string, body *WorkflowsPermissionsOpt) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/fork-pr-workflows-private-repos", owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// GetForkPRContributorApprovalPermissions gets the fork PR contributor approval policy for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#get-fork-pr-contributor-approval-permissions-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/actions/permissions/fork-pr-contributor-approval +func (s *ActionsService) GetForkPRContributorApprovalPermissions(ctx context.Context, owner, repo string) (*ContributorApprovalPermissions, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/fork-pr-contributor-approval", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var policy *ContributorApprovalPermissions + resp, err := s.client.Do(req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// UpdateForkPRContributorApprovalPermissions sets the fork PR contributor approval policy for a repository. +// +// GitHub API docs: https://docs.github.com/rest/actions/permissions?apiVersion=2022-11-28#set-fork-pr-contributor-approval-permissions-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/actions/permissions/fork-pr-contributor-approval +func (s *ActionsService) UpdateForkPRContributorApprovalPermissions(ctx context.Context, owner, repo string, body ContributorApprovalPermissions) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/actions/permissions/fork-pr-contributor-approval", owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_actions_permissions_test.go b/github/repos_actions_permissions_test.go new file mode 100644 index 00000000000..f32ed1dd2b3 --- /dev/null +++ b/github/repos_actions_permissions_test.go @@ -0,0 +1,372 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetActionsPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled": true, "allowed_actions": "all", "sha_pinning_required": true}`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.GetActionsPermissions(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetActionsPermissions returned error: %v", err) + } + want := &ActionsPermissionsRepository{Enabled: Ptr(true), AllowedActions: Ptr("all"), SHAPinningRequired: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.GetActionsPermissions returned %+v, want %+v", org, want) + } + + const methodName = "GetActionsPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetActionsPermissions(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetActionsPermissions(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateActionsPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ActionsPermissionsRepository{Enabled: Ptr(true), AllowedActions: Ptr("selected"), SHAPinningRequired: Ptr(true)} + + mux.HandleFunc("/repos/o/r/actions/permissions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"enabled": true, "allowed_actions": "selected", "sha_pinning_required": true}`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.UpdateActionsPermissions(ctx, "o", "r", *input) + if err != nil { + t.Errorf("Repositories.UpdateActionsPermissions returned error: %v", err) + } + + want := &ActionsPermissionsRepository{Enabled: Ptr(true), AllowedActions: Ptr("selected"), SHAPinningRequired: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.UpdateActionsPermissions returned %+v, want %+v", org, want) + } + + const methodName = "UpdateActionsPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateActionsPermissions(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateActionsPermissions(ctx, "o", "r", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetDefaultWorkflowPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.GetDefaultWorkflowPermissions(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetDefaultWorkflowPermissions returned error: %v", err) + } + want := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.GetDefaultWorkflowPermissions returned %+v, want %+v", org, want) + } + + const methodName = "GetDefaultWorkflowPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetDefaultWorkflowPermissions(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetDefaultWorkflowPermissions(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateDefaultWorkflowPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + + mux.HandleFunc("/repos/o/r/actions/permissions/workflow", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ "default_workflow_permissions": "read", "can_approve_pull_request_reviews": true }`) + }) + + ctx := t.Context() + org, _, err := client.Repositories.UpdateDefaultWorkflowPermissions(ctx, "o", "r", *input) + if err != nil { + t.Errorf("Repositories.UpdateDefaultWorkflowPermissions returned error: %v", err) + } + + want := &DefaultWorkflowPermissionRepository{DefaultWorkflowPermissions: Ptr("read"), CanApprovePullRequestReviews: Ptr(true)} + if !cmp.Equal(org, want) { + t.Errorf("Repositories.UpdateDefaultWorkflowPermissions returned %+v, want %+v", org, want) + } + + const methodName = "UpdateDefaultWorkflowPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateDefaultWorkflowPermissions(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateDefaultWorkflowPermissions(ctx, "o", "r", *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetArtifactAndLogRetentionPeriod(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions/artifact-and-log-retention", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"days": 90, "maximum_allowed_days": 365}`) + }) + + ctx := t.Context() + period, _, err := client.Repositories.GetArtifactAndLogRetentionPeriod(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetArtifactAndLogRetentionPeriod returned error: %v", err) + } + + want := &ArtifactPeriod{ + Days: Ptr(90), + MaximumAllowedDays: Ptr(365), + } + if !cmp.Equal(period, want) { + t.Errorf("Repositories.GetArtifactAndLogRetentionPeriod = %+v, want %+v", period, want) + } + + const methodName = "GetArtifactAndLogRetentionPeriod" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetArtifactAndLogRetentionPeriod(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetArtifactAndLogRetentionPeriod(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateArtifactAndLogRetentionPeriod(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ArtifactPeriodOpt{Days: Ptr(90)} + + mux.HandleFunc("/repos/o/r/actions/permissions/artifact-and-log-retention", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Repositories.UpdateArtifactAndLogRetentionPeriod(ctx, "o", "r", *input) + if err != nil { + t.Errorf("Repositories.UpdateArtifactAndLogRetentionPeriod returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Repositories.UpdateArtifactAndLogRetentionPeriod = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateArtifactAndLogRetentionPeriod" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.UpdateArtifactAndLogRetentionPeriod(ctx, "\n", "\n", *input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.UpdateArtifactAndLogRetentionPeriod(ctx, "o", "r", *input) + }) +} + +func TestRepositoriesService_GetPrivateRepoForkPRWorkflowSettings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions/fork-pr-workflows-private-repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"run_workflows_from_fork_pull_requests": true, "send_write_tokens_to_workflows": false, "send_secrets_and_variables": true, "require_approval_for_fork_pr_workflows": false}`) + }) + + ctx := t.Context() + permissions, _, err := client.Repositories.GetPrivateRepoForkPRWorkflowSettings(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetPrivateRepoForkPRWorkflowSettings returned error: %v", err) + } + want := &WorkflowsPermissions{ + RunWorkflowsFromForkPullRequests: Ptr(true), + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(true), + RequireApprovalForForkPRWorkflows: Ptr(false), + } + if !cmp.Equal(permissions, want) { + t.Errorf("Repositories.GetPrivateRepoForkPRWorkflowSettings returned %+v, want %+v", permissions, want) + } + + const methodName = "GetPrivateRepoForkPRWorkflowSettings" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPrivateRepoForkPRWorkflowSettings(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPrivateRepoForkPRWorkflowSettings(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdatePrivateRepoForkPRWorkflowSettings(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &WorkflowsPermissionsOpt{ + RunWorkflowsFromForkPullRequests: true, + SendWriteTokensToWorkflows: Ptr(false), + SendSecretsAndVariables: Ptr(true), + } + + mux.HandleFunc("/repos/o/r/actions/permissions/fork-pr-workflows-private-repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Repositories.UpdatePrivateRepoForkPRWorkflowSettings(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.UpdatePrivateRepoForkPRWorkflowSettings returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Repositories.UpdatePrivateRepoForkPRWorkflowSettings = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdatePrivateRepoForkPRWorkflowSettings" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.UpdatePrivateRepoForkPRWorkflowSettings(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.UpdatePrivateRepoForkPRWorkflowSettings(ctx, "o", "r", input) + }) +} + +func TestActionsService_GetForkPRContributorApprovalPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/actions/permissions/fork-pr-contributor-approval", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"approval_policy": "require_approval"}`) + }) + + ctx := t.Context() + policy, _, err := client.Actions.GetForkPRContributorApprovalPermissions(ctx, "o", "r") + if err != nil { + t.Errorf("Actions.GetForkPRContributorApprovalPermissions returned error: %v", err) + } + want := &ContributorApprovalPermissions{ApprovalPolicy: "require_approval"} + if !cmp.Equal(policy, want) { + t.Errorf("Actions.GetForkPRContributorApprovalPermissions returned %+v, want %+v", policy, want) + } + + const methodName = "GetForkPRContributorApprovalPermissions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetForkPRContributorApprovalPermissions(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetForkPRContributorApprovalPermissions(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_UpdateForkPRContributorApprovalPermissions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := ContributorApprovalPermissions{ApprovalPolicy: "require_approval"} + + mux.HandleFunc("/repos/o/r/actions/permissions/fork-pr-contributor-approval", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Actions.UpdateForkPRContributorApprovalPermissions(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.UpdateForkPRContributorApprovalPermissions returned error: %v", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Errorf("Actions.UpdateForkPRContributorApprovalPermissions = %v, want %v", resp.StatusCode, http.StatusNoContent) + } + + const methodName = "UpdateForkPRContributorApprovalPermissions" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateForkPRContributorApprovalPermissions(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateForkPRContributorApprovalPermissions(ctx, "o", "r", input) + }) +} diff --git a/github/repos_attestations.go b/github/repos_attestations.go new file mode 100644 index 00000000000..edca5dc664b --- /dev/null +++ b/github/repos_attestations.go @@ -0,0 +1,39 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListAttestations returns a collection of artifact attestations +// with a given subject digest that are associated with a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/attestations?apiVersion=2022-11-28#list-attestations +// +//meta:operation GET /repos/{owner}/{repo}/attestations/{subject_digest} +func (s *RepositoriesService) ListAttestations(ctx context.Context, owner, repo, subjectDigest string, opts *ListOptions) (*AttestationsResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/attestations/%v", owner, repo, subjectDigest) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var attestations *AttestationsResponse + resp, err := s.client.Do(req, &attestations) + if err != nil { + return nil, resp, err + } + + return attestations, resp, nil +} diff --git a/github/repos_attestations_test.go b/github/repos_attestations_test.go new file mode 100644 index 00000000000..cc522c3ce86 --- /dev/null +++ b/github/repos_attestations_test.go @@ -0,0 +1,71 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListAttestations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/attestations/digest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "attestations": [ + { + "repository_id": 1, + "bundle": {} + }, + { + "repository_id": 2, + "bundle": {} + } + ] + }`) + }) + ctx := t.Context() + attestations, _, err := client.Repositories.ListAttestations(ctx, "o", "r", "digest", &ListOptions{}) + if err != nil { + t.Errorf("Repositories.ListAttestations returned error: %v", err) + } + + want := &AttestationsResponse{ + Attestations: []*Attestation{ + { + RepositoryID: 1, + Bundle: []byte(`{}`), + }, + { + RepositoryID: 2, + Bundle: []byte(`{}`), + }, + }, + } + + if !cmp.Equal(attestations, want) { + t.Errorf("Repositories.ListAttestations = %+v, want %+v", attestations, want) + } + const methodName = "ListAttestations" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListAttestations(ctx, "\n", "\n", "\n", &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListAttestations(ctx, "o", "r", "digest", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/repos_autolinks.go b/github/repos_autolinks.go new file mode 100644 index 00000000000..c097f601504 --- /dev/null +++ b/github/repos_autolinks.go @@ -0,0 +1,108 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CreateAutolinkRequest specifies parameters for RepositoriesService.CreateAutolink method. +type CreateAutolinkRequest struct { + KeyPrefix string `json:"key_prefix"` + URLTemplate string `json:"url_template"` + IsAlphanumeric *bool `json:"is_alphanumeric,omitempty"` +} + +// Autolink represents autolinks to external resources like Jira issues and Zendesk tickets. +type Autolink struct { + ID *int64 `json:"id,omitempty"` + KeyPrefix *string `json:"key_prefix,omitempty"` + URLTemplate *string `json:"url_template,omitempty"` + IsAlphanumeric *bool `json:"is_alphanumeric,omitempty"` +} + +// ListAutolinks returns a list of autolinks configured for the given repository. +// Information about autolinks are only available to repository administrators. +// +// GitHub API docs: https://docs.github.com/rest/repos/autolinks?apiVersion=2022-11-28#get-all-autolinks-of-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/autolinks +func (s *RepositoriesService) ListAutolinks(ctx context.Context, owner, repo string) ([]*Autolink, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/autolinks", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var autolinks []*Autolink + resp, err := s.client.Do(req, &autolinks) + if err != nil { + return nil, resp, err + } + + return autolinks, resp, nil +} + +// CreateAutolink creates an autolink reference for a repository. +// Users with admin access to the repository can create an autolink. +// +// GitHub API docs: https://docs.github.com/rest/repos/autolinks?apiVersion=2022-11-28#create-an-autolink-reference-for-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/autolinks +func (s *RepositoriesService) CreateAutolink(ctx context.Context, owner, repo string, body CreateAutolinkRequest) (*Autolink, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/autolinks", owner, repo) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var al *Autolink + resp, err := s.client.Do(req, &al) + if err != nil { + return nil, resp, err + } + return al, resp, nil +} + +// GetAutolink returns a single autolink reference by ID that was configured for the given repository. +// Information about autolinks are only available to repository administrators. +// +// GitHub API docs: https://docs.github.com/rest/repos/autolinks?apiVersion=2022-11-28#get-an-autolink-reference-of-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/autolinks/{autolink_id} +func (s *RepositoriesService) GetAutolink(ctx context.Context, owner, repo string, id int64) (*Autolink, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/autolinks/%v", owner, repo, id) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var autolink *Autolink + resp, err := s.client.Do(req, &autolink) + if err != nil { + return nil, resp, err + } + + return autolink, resp, nil +} + +// DeleteAutolink deletes a single autolink reference by ID that was configured for the given repository. +// Information about autolinks are only available to repository administrators. +// +// GitHub API docs: https://docs.github.com/rest/repos/autolinks?apiVersion=2022-11-28#delete-an-autolink-reference-from-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/autolinks/{autolink_id} +func (s *RepositoriesService) DeleteAutolink(ctx context.Context, owner, repo string, id int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/autolinks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(req, nil) +} diff --git a/github/repos_autolinks_test.go b/github/repos_autolinks_test.go new file mode 100644 index 00000000000..a5fb2b12a80 --- /dev/null +++ b/github/repos_autolinks_test.go @@ -0,0 +1,155 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListAutolinks(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/autolinks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1, "key_prefix": "TICKET-", "url_template": "https://example.com/TICKET?query="}, {"id":2, "key_prefix": "STORY-", "url_template": "https://example.com/STORY?query="}]`) + }) + + ctx := t.Context() + autolinks, _, err := client.Repositories.ListAutolinks(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.ListAutolinks returned error: %v", err) + } + + want := []*Autolink{ + {ID: Ptr(int64(1)), KeyPrefix: Ptr("TICKET-"), URLTemplate: Ptr("https://example.com/TICKET?query=")}, + {ID: Ptr(int64(2)), KeyPrefix: Ptr("STORY-"), URLTemplate: Ptr("https://example.com/STORY?query=")}, + } + + if !cmp.Equal(autolinks, want) { + t.Errorf("Repositories.ListAutolinks returned %+v, want %+v", autolinks, want) + } + + const methodName = "ListAutolinks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListAutolinks(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListAutolinks(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateAutolink(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + body := CreateAutolinkRequest{ + KeyPrefix: "TICKET-", + URLTemplate: "https://example.com/TICKET?query=", + IsAlphanumeric: Ptr(true), + } + mux.HandleFunc("/repos/o/r/autolinks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, body) + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(` + { + "key_prefix": "TICKET-", + "url_template": "https://example.com/TICKET?query=", + "is_alphanumeric": true + } + `)) + }) + ctx := t.Context() + autolink, _, err := client.Repositories.CreateAutolink(ctx, "o", "r", body) + if err != nil { + t.Errorf("Repositories.CreateAutolink returned error: %v", err) + } + want := &Autolink{ + KeyPrefix: Ptr("TICKET-"), + URLTemplate: Ptr("https://example.com/TICKET?query="), + IsAlphanumeric: Ptr(true), + } + + if !cmp.Equal(autolink, want) { + t.Errorf("CreateAutolink returned %+v, want %+v", autolink, want) + } + + const methodName = "CreateAutolink" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateAutolink(ctx, "\n", "\n", body) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateAutolink(ctx, "o", "r", body) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetAutolink(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/autolinks/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1, "key_prefix": "TICKET-", "url_template": "https://example.com/TICKET?query="}`) + }) + + ctx := t.Context() + autolink, _, err := client.Repositories.GetAutolink(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetAutolink returned error: %v", err) + } + + want := &Autolink{ID: Ptr(int64(1)), KeyPrefix: Ptr("TICKET-"), URLTemplate: Ptr("https://example.com/TICKET?query=")} + if !cmp.Equal(autolink, want) { + t.Errorf("Repositories.GetAutolink returned %+v, want %+v", autolink, want) + } + + const methodName = "GetAutolink" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAutolink(ctx, "o", "r", 2) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteAutolink(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/autolinks/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.DeleteAutolink(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteAutolink returned error: %v", err) + } + + const methodName = "DeleteAutolink" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteAutolink(ctx, "o", "r", 2) + }) +} diff --git a/github/repos_codeowners.go b/github/repos_codeowners.go new file mode 100644 index 00000000000..d178294f77e --- /dev/null +++ b/github/repos_codeowners.go @@ -0,0 +1,61 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetCodeownersErrorsOptions specifies the optional parameters to the +// RepositoriesService.GetCodeownersErrors method. +type GetCodeownersErrorsOptions struct { + // A branch, tag or commit name used to determine which version of the CODEOWNERS file to use. + // Default: the repository's default branch (e.g. main). + Ref string `url:"ref,omitempty"` +} + +// CodeownersErrors represents a list of syntax errors detected in the CODEOWNERS file. +type CodeownersErrors struct { + Errors []*CodeownersError `json:"errors"` +} + +// CodeownersError represents a syntax error detected in the CODEOWNERS file. +type CodeownersError struct { + Line int `json:"line"` + Column int `json:"column"` + Kind string `json:"kind"` + Source string `json:"source"` + Suggestion *string `json:"suggestion,omitempty"` + Message string `json:"message"` + Path string `json:"path"` +} + +// GetCodeownersErrors lists any syntax errors that are detected in the CODEOWNERS file. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#list-codeowners-errors +// +//meta:operation GET /repos/{owner}/{repo}/codeowners/errors +func (s *RepositoriesService) GetCodeownersErrors(ctx context.Context, owner, repo string, opts *GetCodeownersErrorsOptions) (*CodeownersErrors, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/codeowners/errors", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var codeownersErrors *CodeownersErrors + resp, err := s.client.Do(req, &codeownersErrors) + if err != nil { + return nil, resp, err + } + + return codeownersErrors, resp, nil +} diff --git a/github/repos_codeowners_test.go b/github/repos_codeowners_test.go new file mode 100644 index 00000000000..87b2a7a12aa --- /dev/null +++ b/github/repos_codeowners_test.go @@ -0,0 +1,138 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetCodeownersErrors_noRef(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/codeowners/errors", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3) + fmt.Fprint(w, `{ + "errors": [ + { + "line": 1, + "column": 1, + "kind": "Invalid pattern", + "source": "***/*.rb @monalisa", + "suggestion": "Did you mean **/*.rb?", + "message": "Invalid pattern on line 3: Did you mean **/*.rb?\n\n ***/*.rb @monalisa\n ^", + "path": ".github/CODEOWNERS" + } + ] + } + `) + }) + + ctx := t.Context() + codeownersErrors, _, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r", nil) + if err != nil { + t.Errorf("Repositories.GetCodeownersErrors returned error: %v", err) + } + + want := &CodeownersErrors{ + Errors: []*CodeownersError{ + { + Line: 1, + Column: 1, + Kind: "Invalid pattern", + Source: "***/*.rb @monalisa", + Suggestion: Ptr("Did you mean **/*.rb?"), + Message: "Invalid pattern on line 3: Did you mean **/*.rb?\n\n ***/*.rb @monalisa\n ^", + Path: ".github/CODEOWNERS", + }, + }, + } + if !cmp.Equal(codeownersErrors, want) { + t.Errorf("Repositories.GetCodeownersErrors returned %+v, want %+v", codeownersErrors, want) + } + + const methodName = "GetCodeownersErrors" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCodeownersErrors(ctx, "\n", "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetCodeownersErrors_specificRef(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/codeowners/errors", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3) + testFormValues(t, r, values{"ref": "mybranch"}) + fmt.Fprint(w, `{ + "errors": [ + { + "line": 1, + "column": 1, + "kind": "Invalid pattern", + "source": "***/*.rb @monalisa", + "suggestion": "Did you mean **/*.rb?", + "message": "Invalid pattern on line 3: Did you mean **/*.rb?\n\n ***/*.rb @monalisa\n ^", + "path": ".github/CODEOWNERS" + } + ] + } + `) + }) + + opts := &GetCodeownersErrorsOptions{Ref: "mybranch"} + ctx := t.Context() + codeownersErrors, _, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Repositories.GetCodeownersErrors returned error: %v", err) + } + + want := &CodeownersErrors{ + Errors: []*CodeownersError{ + { + Line: 1, + Column: 1, + Kind: "Invalid pattern", + Source: "***/*.rb @monalisa", + Suggestion: Ptr("Did you mean **/*.rb?"), + Message: "Invalid pattern on line 3: Did you mean **/*.rb?\n\n ***/*.rb @monalisa\n ^", + Path: ".github/CODEOWNERS", + }, + }, + } + if !cmp.Equal(codeownersErrors, want) { + t.Errorf("Repositories.GetCodeownersErrors returned %+v, want %+v", codeownersErrors, want) + } + + const methodName = "GetCodeownersErrors" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCodeownersErrors(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCodeownersErrors(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/repos_collaborators.go b/github/repos_collaborators.go index 757e9f39e44..451381bf47c 100644 --- a/github/repos_collaborators.go +++ b/github/repos_collaborators.go @@ -23,28 +23,48 @@ type ListCollaboratorsOptions struct { // Default value is "all". Affiliation string `url:"affiliation,omitempty"` + // Permission specifies how collaborators should be filtered by the permissions they have on the repository. + // Possible values are: + // "pull", "triage", "push", "maintain", "admin" + // + // If not specified, all collaborators will be returned. + Permission string `url:"permission,omitempty"` + ListOptions } +// CollaboratorInvitation represents an invitation created when adding a collaborator. +// GitHub API docs: https://docs.github.com/rest/collaborators/collaborators?apiVersion=2022-11-28#add-a-repository-collaborator +type CollaboratorInvitation struct { + ID *int64 `json:"id,omitempty"` + Repo *Repository `json:"repository,omitempty"` + Invitee *User `json:"invitee,omitempty"` + Inviter *User `json:"inviter,omitempty"` + Permissions *string `json:"permissions,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` +} + // ListCollaborators lists the GitHub users that have access to the repository. // -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#list-collaborators -func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo string, opt *ListCollaboratorsOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/collaborators/collaborators?apiVersion=2022-11-28#list-repository-collaborators +// +//meta:operation GET /repos/{owner}/{repo}/collaborators +func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo string, opts *ListCollaboratorsOptions) ([]*User, *Response, error) { u := fmt.Sprintf("repos/%v/%v/collaborators", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - var users []*User - resp, err := s.client.Do(ctx, req, &users) + resp, err := s.client.Do(req, &users) if err != nil { return nil, resp, err } @@ -57,15 +77,17 @@ func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo // Note: This will return false if the user is not a collaborator OR the user // is not a GitHub user. // -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#get +// GitHub API docs: https://docs.github.com/rest/collaborators/collaborators?apiVersion=2022-11-28#check-if-a-user-is-a-repository-collaborator +// +//meta:operation GET /repos/{owner}/{repo}/collaborators/{username} func (s *RepositoriesService) IsCollaborator(ctx context.Context, owner, repo, user string) (bool, *Response, error) { u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) isCollab, err := parseBoolResponse(err) return isCollab, resp, err } @@ -77,22 +99,28 @@ type RepositoryPermissionLevel struct { Permission *string `json:"permission,omitempty"` User *User `json:"user,omitempty"` + + RoleName *string `json:"role_name,omitempty"` } // GetPermissionLevel retrieves the specific permission level a collaborator has for a given repository. -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level +// +// GitHub API docs: https://docs.github.com/rest/collaborators/collaborators?apiVersion=2022-11-28#get-repository-permissions-for-a-user +// +//meta:operation GET /repos/{owner}/{repo}/collaborators/{username}/permission func (s *RepositoriesService) GetPermissionLevel(ctx context.Context, owner, repo, user string) (*RepositoryPermissionLevel, *Response, error) { u := fmt.Sprintf("repos/%v/%v/collaborators/%v/permission", owner, repo, user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - rpl := new(RepositoryPermissionLevel) - resp, err := s.client.Do(ctx, req, rpl) + var rpl *RepositoryPermissionLevel + resp, err := s.client.Do(req, &rpl) if err != nil { return nil, resp, err } + return rpl, resp, nil } @@ -102,8 +130,10 @@ type RepositoryAddCollaboratorOptions struct { // Permission specifies the permission to grant the user on this repository. // Possible values are: // pull - team members can pull, but not push to or administer this repository - // push - team members can pull and push, but not administer this repository + // push - team members can push and pull, but not administer this repository // admin - team members can pull, push and administer this repository + // maintain - team members can manage the repository without access to sensitive or destructive actions. + // triage - team members can proactively manage issues and pull requests without write access. // // Default value is "push". This option is only valid for organization-owned repositories. Permission string `json:"permission,omitempty"` @@ -112,26 +142,37 @@ type RepositoryAddCollaboratorOptions struct { // AddCollaborator sends an invitation to the specified GitHub user // to become a collaborator to the given repo. // -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator -func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/collaborators/collaborators?apiVersion=2022-11-28#add-a-repository-collaborator +// +//meta:operation PUT /repos/{owner}/{repo}/collaborators/{username} +func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, body *RepositoryAddCollaboratorOptions) (*CollaboratorInvitation, *Response, error) { u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) - req, err := s.client.NewRequest("PUT", u, opt) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { - return nil, err + return nil, nil, err + } + + var acr *CollaboratorInvitation + resp, err := s.client.Do(req, &acr) + if err != nil { + return nil, resp, err } - return s.client.Do(ctx, req, nil) + return acr, resp, nil } // RemoveCollaborator removes the specified GitHub user as collaborator from the given repo. // Note: Does not return error if a valid user that is not a collaborator is removed. // -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#remove-collaborator +// GitHub API docs: https://docs.github.com/rest/collaborators/collaborators?apiVersion=2022-11-28#remove-a-repository-collaborator +// +//meta:operation DELETE /repos/{owner}/{repo}/collaborators/{username} func (s *RepositoriesService) RemoveCollaborator(ctx context.Context, owner, repo, user string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } diff --git a/github/repos_collaborators_test.go b/github/repos_collaborators_test.go index a4d3210c8ff..7c3d9cab0dd 100644 --- a/github/repos_collaborators_test.go +++ b/github/repos_collaborators_test.go @@ -6,195 +6,352 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListCollaborators(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) testFormValues(t, r, values{"page": "2"}) - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) }) opt := &ListCollaboratorsOptions{ ListOptions: ListOptions{Page: 2}, } - users, _, err := client.Repositories.ListCollaborators(context.Background(), "o", "r", opt) + ctx := t.Context() + users, _, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListCollaborators returned error: %v", err) } - want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(users, want) { t.Errorf("Repositories.ListCollaborators returned %+v, want %+v", users, want) } + + const methodName = "ListCollaborators" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCollaborators(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListCollaborators_withAffiliation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"affiliation": "all", "page": "2"}) - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) }) opt := &ListCollaboratorsOptions{ ListOptions: ListOptions{Page: 2}, Affiliation: "all", } - users, _, err := client.Repositories.ListCollaborators(context.Background(), "o", "r", opt) + ctx := t.Context() + users, _, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListCollaborators returned error: %v", err) + } + + want := []*User{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(users, want) { + t.Errorf("Repositories.ListCollaborators returned %+v, want %+v", users, want) + } + + const methodName = "ListCollaborators" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCollaborators(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListCollaborators_withPermission(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"permission": "pull", "page": "2"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListCollaboratorsOptions{ + ListOptions: ListOptions{Page: 2}, + Permission: "pull", + } + ctx := t.Context() + users, _, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListCollaborators returned error: %v", err) } - want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(users, want) { t.Errorf("Repositories.ListCollaborators returned %+v, want %+v", users, want) } + + const methodName = "ListCollaborators" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCollaborators(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListCollaborators_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListCollaborators(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListCollaborators(ctx, "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_IsCollaborator_True(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - isCollab, _, err := client.Repositories.IsCollaborator(context.Background(), "o", "r", "u") + ctx := t.Context() + isCollab, _, err := client.Repositories.IsCollaborator(ctx, "o", "r", "u") if err != nil { t.Errorf("Repositories.IsCollaborator returned error: %v", err) } if !isCollab { - t.Errorf("Repositories.IsCollaborator returned false, want true") + t.Error("Repositories.IsCollaborator returned false, want true") } + + const methodName = "IsCollaborator" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.IsCollaborator(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.IsCollaborator(ctx, "o", "r", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_IsCollaborator_False(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - isCollab, _, err := client.Repositories.IsCollaborator(context.Background(), "o", "r", "u") + ctx := t.Context() + isCollab, _, err := client.Repositories.IsCollaborator(ctx, "o", "r", "u") if err != nil { t.Errorf("Repositories.IsCollaborator returned error: %v", err) } if isCollab { - t.Errorf("Repositories.IsCollaborator returned true, want false") + t.Error("Repositories.IsCollaborator returned true, want false") } + + const methodName = "IsCollaborator" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.IsCollaborator(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.IsCollaborator(ctx, "o", "r", "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_IsCollaborator_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.IsCollaborator(context.Background(), "%", "%", "%") + ctx := t.Context() + _, _, err := client.Repositories.IsCollaborator(ctx, "%", "%", "%") testURLParseError(t, err) } func TestRepositoryService_GetPermissionLevel(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/collaborators/u/permission", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `{"permission":"admin","user":{"login":"u"}}`) + fmt.Fprint(w, `{"permission":"admin","user":{"login":"u"}}`) }) - rpl, _, err := client.Repositories.GetPermissionLevel(context.Background(), "o", "r", "u") + ctx := t.Context() + rpl, _, err := client.Repositories.GetPermissionLevel(ctx, "o", "r", "u") if err != nil { t.Errorf("Repositories.GetPermissionLevel returned error: %v", err) } want := &RepositoryPermissionLevel{ - Permission: String("admin"), + Permission: Ptr("admin"), User: &User{ - Login: String("u"), + Login: Ptr("u"), }, } - if !reflect.DeepEqual(rpl, want) { + if !cmp.Equal(rpl, want) { t.Errorf("Repositories.GetPermissionLevel returned %+v, want %+v", rpl, want) } + + const methodName = "GetPermissionLevel" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPermissionLevel(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPermissionLevel(ctx, "o", "r", "u") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_AddCollaborator(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) opt := &RepositoryAddCollaboratorOptions{Permission: "admin"} - mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { - v := new(RepositoryAddCollaboratorOptions) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, opt) { - t.Errorf("Request body = %+v, want %+v", v, opt) - } - - w.WriteHeader(http.StatusNoContent) + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`{"permissions": "write","url": "https://api.github.com/user/repository_invitations/1296269","html_url": "https://github.com/octocat/Hello-World/invitations","id":1,"permissions":"write","repository":{"url":"s","name":"r","id":1},"invitee":{"login":"u"},"inviter":{"login":"o"}}`)) }) - - _, err := client.Repositories.AddCollaborator(context.Background(), "o", "r", "u", opt) + ctx := t.Context() + collaboratorInvitation, _, err := client.Repositories.AddCollaborator(ctx, "o", "r", "u", opt) if err != nil { t.Errorf("Repositories.AddCollaborator returned error: %v", err) } + want := &CollaboratorInvitation{ + ID: Ptr(int64(1)), + Repo: &Repository{ + ID: Ptr(int64(1)), + URL: Ptr("s"), + Name: Ptr("r"), + }, + Invitee: &User{ + Login: Ptr("u"), + }, + Inviter: &User{ + Login: Ptr("o"), + }, + Permissions: Ptr("write"), + URL: Ptr("https://api.github.com/user/repository_invitations/1296269"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/invitations"), + } + + if !cmp.Equal(collaboratorInvitation, want) { + t.Errorf("AddCollaborator returned %+v, want %+v", collaboratorInvitation, want) + } + + const methodName = "AddCollaborator" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddCollaborator(ctx, "\n", "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddCollaborator(ctx, "o", "r", "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_AddCollaborator_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.AddCollaborator(context.Background(), "%", "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.AddCollaborator(ctx, "%", "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_RemoveCollaborator(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Repositories.RemoveCollaborator(context.Background(), "o", "r", "u") + ctx := t.Context() + _, err := client.Repositories.RemoveCollaborator(ctx, "o", "r", "u") if err != nil { t.Errorf("Repositories.RemoveCollaborator returned error: %v", err) } + + const methodName = "RemoveCollaborator" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.RemoveCollaborator(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.RemoveCollaborator(ctx, "o", "r", "u") + }) } func TestRepositoriesService_RemoveCollaborator_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.RemoveCollaborator(context.Background(), "%", "%", "%") + ctx := t.Context() + _, err := client.Repositories.RemoveCollaborator(ctx, "%", "%", "%") testURLParseError(t, err) } diff --git a/github/repos_comments.go b/github/repos_comments.go index 380ca3d3b43..e5da15f02a4 100644 --- a/github/repos_comments.go +++ b/github/repos_comments.go @@ -8,7 +8,6 @@ package github import ( "context" "fmt" - "time" ) // RepositoryComment represents a comment for a commit, file, or line in a repository. @@ -20,8 +19,8 @@ type RepositoryComment struct { CommitID *string `json:"commit_id,omitempty"` User *User `json:"user,omitempty"` Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` // User-mutable fields Body *string `json:"body"` @@ -36,24 +35,25 @@ func (r RepositoryComment) String() string { // ListComments lists all the comments for the repository. // -// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository -func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/comments?apiVersion=2022-11-28#list-commit-comments-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/comments +func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo string, opts *ListOptions) ([]*RepositoryComment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/comments", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeReactionsPreview) var comments []*RepositoryComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -63,24 +63,25 @@ func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo stri // ListCommitComments lists all the comments for a given commit SHA. // -// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit -func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, repo, sha string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/comments?apiVersion=2022-11-28#list-commit-comments +// +//meta:operation GET /repos/{owner}/{repo}/commits/{commit_sha}/comments +func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, repo, sha string, opts *ListOptions) ([]*RepositoryComment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeReactionsPreview) var comments []*RepositoryComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -91,16 +92,18 @@ func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, rep // CreateComment creates a comment for the given commit. // Note: GitHub allows for comments to be created for non-existing files and positions. // -// GitHub API docs: https://developer.github.com/v3/repos/comments/#create-a-commit-comment -func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sha string, comment *RepositoryComment) (*RepositoryComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/comments?apiVersion=2022-11-28#create-a-commit-comment +// +//meta:operation POST /repos/{owner}/{repo}/commits/{commit_sha}/comments +func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sha string, body *RepositoryComment) (*RepositoryComment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) - req, err := s.client.NewRequest("POST", u, comment) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - c := new(RepositoryComment) - resp, err := s.client.Do(ctx, req, c) + var c *RepositoryComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -110,19 +113,20 @@ func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sh // GetComment gets a single comment from a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment +// GitHub API docs: https://docs.github.com/rest/commits/comments?apiVersion=2022-11-28#get-a-commit-comment +// +//meta:operation GET /repos/{owner}/{repo}/comments/{comment_id} func (s *RepositoriesService) GetComment(ctx context.Context, owner, repo string, id int64) (*RepositoryComment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeReactionsPreview) - c := new(RepositoryComment) - resp, err := s.client.Do(ctx, req, c) + var c *RepositoryComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -132,16 +136,18 @@ func (s *RepositoriesService) GetComment(ctx context.Context, owner, repo string // UpdateComment updates the body of a single comment. // -// GitHub API docs: https://developer.github.com/v3/repos/comments/#update-a-commit-comment -func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo string, id int64, comment *RepositoryComment) (*RepositoryComment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/comments?apiVersion=2022-11-28#update-a-commit-comment +// +//meta:operation PATCH /repos/{owner}/{repo}/comments/{comment_id} +func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo string, id int64, body *RepositoryComment) (*RepositoryComment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, comment) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - c := new(RepositoryComment) - resp, err := s.client.Do(ctx, req, c) + var c *RepositoryComment + resp, err := s.client.Do(req, &c) if err != nil { return nil, resp, err } @@ -151,12 +157,15 @@ func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo str // DeleteComment deletes a single comment from a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/comments/#delete-a-commit-comment +// GitHub API docs: https://docs.github.com/rest/commits/comments?apiVersion=2022-11-28#delete-a-commit-comment +// +//meta:operation DELETE /repos/{owner}/{repo}/comments/{comment_id} func (s *RepositoriesService) DeleteComment(ctx context.Context, owner, repo string, id int64) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } diff --git a/github/repos_comments_test.go b/github/repos_comments_test.go index 0eb54eba2ee..7cebb99dad9 100644 --- a/github/repos_comments_test.go +++ b/github/repos_comments_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListComments(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -26,28 +25,44 @@ func TestRepositoriesService_ListComments(t *testing.T) { }) opt := &ListOptions{Page: 2} - comments, _, err := client.Repositories.ListComments(context.Background(), "o", "r", opt) + ctx := t.Context() + comments, _, err := client.Repositories.ListComments(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListComments returned error: %v", err) } - want := []*RepositoryComment{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(comments, want) { + want := []*RepositoryComment{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(comments, want) { t.Errorf("Repositories.ListComments returned %+v, want %+v", comments, want) } + + const methodName = "ListComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListComments(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListComments(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListComments_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListComments(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListComments(ctx, "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_ListCommitComments(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/s/comments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -57,65 +72,91 @@ func TestRepositoriesService_ListCommitComments(t *testing.T) { }) opt := &ListOptions{Page: 2} - comments, _, err := client.Repositories.ListCommitComments(context.Background(), "o", "r", "s", opt) + ctx := t.Context() + comments, _, err := client.Repositories.ListCommitComments(ctx, "o", "r", "s", opt) if err != nil { t.Errorf("Repositories.ListCommitComments returned error: %v", err) } - want := []*RepositoryComment{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(comments, want) { + want := []*RepositoryComment{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(comments, want) { t.Errorf("Repositories.ListCommitComments returned %+v, want %+v", comments, want) } + + const methodName = "ListCommitComments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCommitComments(ctx, "\n", "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCommitComments(ctx, "o", "r", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListCommitComments_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListCommitComments(context.Background(), "%", "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListCommitComments(ctx, "%", "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_CreateComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &RepositoryComment{Body: String("b")} + input := &RepositoryComment{Body: Ptr("b")} mux.HandleFunc("/repos/o/r/commits/s/comments", func(w http.ResponseWriter, r *http.Request) { - v := new(RepositoryComment) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Repositories.CreateComment(context.Background(), "o", "r", "s", input) + ctx := t.Context() + comment, _, err := client.Repositories.CreateComment(ctx, "o", "r", "s", input) if err != nil { t.Errorf("Repositories.CreateComment returned error: %v", err) } - want := &RepositoryComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &RepositoryComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Repositories.CreateComment returned %+v, want %+v", comment, want) } + + const methodName = "CreateComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateComment(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateComment(ctx, "o", "r", "s", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_CreateComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.CreateComment(context.Background(), "%", "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.CreateComment(ctx, "%", "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_GetComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -123,80 +164,119 @@ func TestRepositoriesService_GetComment(t *testing.T) { fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Repositories.GetComment(context.Background(), "o", "r", 1) + ctx := t.Context() + comment, _, err := client.Repositories.GetComment(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetComment returned error: %v", err) } - want := &RepositoryComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &RepositoryComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Repositories.GetComment returned %+v, want %+v", comment, want) } + + const methodName = "GetComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetComment(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetComment(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.GetComment(context.Background(), "%", "%", 1) + ctx := t.Context() + _, _, err := client.Repositories.GetComment(ctx, "%", "%", 1) testURLParseError(t, err) } func TestRepositoriesService_UpdateComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &RepositoryComment{Body: String("b")} + input := &RepositoryComment{Body: Ptr("b")} mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { - v := new(RepositoryComment) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - comment, _, err := client.Repositories.UpdateComment(context.Background(), "o", "r", 1, input) + ctx := t.Context() + comment, _, err := client.Repositories.UpdateComment(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Repositories.UpdateComment returned error: %v", err) } - want := &RepositoryComment{ID: Int64(1)} - if !reflect.DeepEqual(comment, want) { + want := &RepositoryComment{ID: Ptr(int64(1))} + if !cmp.Equal(comment, want) { t.Errorf("Repositories.UpdateComment returned %+v, want %+v", comment, want) } + + const methodName = "UpdateComment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateComment(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateComment(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_UpdateComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.UpdateComment(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Repositories.UpdateComment(ctx, "%", "%", 1, nil) testURLParseError(t, err) } func TestRepositoriesService_DeleteComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/comments/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Repositories.DeleteComment(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteComment(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.DeleteComment returned error: %v", err) } + + const methodName = "DeleteComment" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteComment(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteComment(ctx, "o", "r", 1) + }) } func TestRepositoriesService_DeleteComment_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.DeleteComment(context.Background(), "%", "%", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteComment(ctx, "%", "%", 1) testURLParseError(t, err) } diff --git a/github/repos_commits.go b/github/repos_commits.go index 95096923432..cf380af1ae3 100644 --- a/github/repos_commits.go +++ b/github/repos_commits.go @@ -17,20 +17,20 @@ import ( // Note that it's wrapping a Commit, so author/committer information is in two places, // but contain different details about them: in RepositoryCommit "github details", in Commit - "git details". type RepositoryCommit struct { - NodeID *string `json:"node_id,omitempty"` - SHA *string `json:"sha,omitempty"` - Commit *Commit `json:"commit,omitempty"` - Author *User `json:"author,omitempty"` - Committer *User `json:"committer,omitempty"` - Parents []Commit `json:"parents,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - URL *string `json:"url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` + SHA *string `json:"sha,omitempty"` + Commit *Commit `json:"commit,omitempty"` + Author *User `json:"author,omitempty"` + Committer *User `json:"committer,omitempty"` + Parents []*Commit `json:"parents,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + URL *string `json:"url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` // Details about how many changes were made in this commit. Only filled in during GetCommit! Stats *CommitStats `json:"stats,omitempty"` // Details about which files, and how this commit touched. Only filled in during GetCommit! - Files []CommitFile `json:"files,omitempty"` + Files []*CommitFile `json:"files,omitempty"` } func (r RepositoryCommit) String() string { @@ -79,9 +79,9 @@ type CommitsComparison struct { BehindBy *int `json:"behind_by,omitempty"` TotalCommits *int `json:"total_commits,omitempty"` - Commits []RepositoryCommit `json:"commits,omitempty"` + Commits []*RepositoryCommit `json:"commits,omitempty"` - Files []CommitFile `json:"files,omitempty"` + Files []*CommitFile `json:"files,omitempty"` HTMLURL *string `json:"html_url,omitempty"` PermalinkURL *string `json:"permalink_url,omitempty"` @@ -119,26 +119,28 @@ type CommitsListOptions struct { type BranchCommit struct { Name *string `json:"name,omitempty"` Commit *Commit `json:"commit,omitempty"` - Protected *string `json:"protected,omitempty"` + Protected *bool `json:"protected,omitempty"` } // ListCommits lists the commits of a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/commits/#list -func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#list-commits +// +//meta:operation GET /repos/{owner}/{repo}/commits +func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opts *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var commits []*RepositoryCommit - resp, err := s.client.Do(ctx, req, &commits) + resp, err := s.client.Do(req, &commits) if err != nil { return nil, resp, err } @@ -148,18 +150,23 @@ func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo strin // GetCommit fetches the specified commit, including all details about it. // -// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit -// See also: https://developer.github.com/v3/git/commits/#get-a-single-commit provides the same functionality -func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#get-a-commit +// +//meta:operation GET /repos/{owner}/{repo}/commits/{ref} +func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string, opts *ListOptions) (*RepositoryCommit, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - commit := new(RepositoryCommit) - resp, err := s.client.Do(ctx, req, commit) + var commit *RepositoryCommit + resp, err := s.client.Do(req, &commit) if err != nil { return nil, resp, err } @@ -168,24 +175,28 @@ func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha st } // GetCommitRaw fetches the specified commit in raw (diff or patch) format. -func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opt RawOptions) (string, *Response, error) { +// +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#get-a-commit +// +//meta:operation GET /repos/{owner}/{repo}/commits/{ref} +func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner, repo, sha string, opts RawOptions) (string, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return "", nil, err } - switch opt.Type { + switch opts.Type { case Diff: req.Header.Set("Accept", mediaTypeV3Diff) case Patch: req.Header.Set("Accept", mediaTypeV3Patch) default: - return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) + return "", nil, fmt.Errorf("unsupported raw type %v", opts.Type) } var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) + resp, err := s.client.Do(req, &buf) if err != nil { return "", resp, err } @@ -196,11 +207,13 @@ func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, re // GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is // supplied and no new commits have occurred, a 304 Unmodified response is returned. // -// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#get-a-commit +// +//meta:operation GET /repos/{owner}/{repo}/commits/{ref} func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, url.QueryEscape(ref)) + u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, refURLEscape(ref)) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return "", nil, err } @@ -211,7 +224,7 @@ func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, re req.Header.Set("Accept", mediaTypeV3SHA) var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) + resp, err := s.client.Do(req, &buf) if err != nil { return "", resp, err } @@ -220,19 +233,27 @@ func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, re } // CompareCommits compares a range of commits with each other. -// todo: support media formats - https://github.com/google/go-github/issues/6 // -// GitHub API docs: https://developer.github.com/v3/repos/commits/#compare-two-commits -func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head) +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits +// +//meta:operation GET /repos/{owner}/{repo}/compare/{basehead} +func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo, base, head string, opts *ListOptions) (*CommitsComparison, *Response, error) { + escapedBase := url.QueryEscape(base) + escapedHead := url.QueryEscape(head) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, escapedBase, escapedHead) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - comp := new(CommitsComparison) - resp, err := s.client.Do(ctx, req, comp) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var comp *CommitsComparison + resp, err := s.client.Do(req, &comp) if err != nil { return nil, resp, err } @@ -240,22 +261,61 @@ func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo st return comp, resp, nil } +// CompareCommitsRaw compares a range of commits with each other in raw (diff or patch) format. +// +// Both "base" and "head" must be branch names in "repo". +// To compare branches across other repositories in the same network as "repo", +// use the format ":branch". +// +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#compare-two-commits +// +//meta:operation GET /repos/{owner}/{repo}/compare/{basehead} +func (s *RepositoriesService) CompareCommitsRaw(ctx context.Context, owner, repo, base, head string, opts RawOptions) (string, *Response, error) { + escapedBase := url.QueryEscape(base) + escapedHead := url.QueryEscape(head) + + u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, escapedBase, escapedHead) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return "", nil, err + } + + switch opts.Type { + case Diff: + req.Header.Set("Accept", mediaTypeV3Diff) + case Patch: + req.Header.Set("Accept", mediaTypeV3Patch) + default: + return "", nil, fmt.Errorf("unsupported raw type %v", opts.Type) + } + + var buf bytes.Buffer + resp, err := s.client.Do(req, &buf) + if err != nil { + return "", resp, err + } + + return buf.String(), resp, nil +} + // ListBranchesHeadCommit gets all branches where the given commit SHA is the HEAD, // or latest commit for the branch. // -// GitHub API docs: https://developer.github.com/v3/repos/commits/#list-branches-for-head-commit +// GitHub API docs: https://docs.github.com/rest/commits/commits?apiVersion=2022-11-28#list-branches-for-head-commit +// +//meta:operation GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head func (s *RepositoriesService) ListBranchesHeadCommit(ctx context.Context, owner, repo, sha string) ([]*BranchCommit, *Response, error) { u := fmt.Sprintf("repos/%v/%v/commits/%v/branches-where-head", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeListPullsOrBranchesForCommitPreview) var branchCommits []*BranchCommit - resp, err := s.client.Do(ctx, req, &branchCommits) + resp, err := s.client.Do(req, &branchCommits) if err != nil { return nil, resp, err } diff --git a/github/repos_commits_test.go b/github/repos_commits_test.go index feed2b95d04..1ca3c1f4338 100644 --- a/github/repos_commits_test.go +++ b/github/repos_commits_test.go @@ -6,18 +6,19 @@ package github import ( - "context" "fmt" "net/http" - "reflect" + "net/url" "strings" "testing" "time" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListCommits(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) // given mux.HandleFunc("/repos/o/r/commits", func(w http.ResponseWriter, r *http.Request) { @@ -27,37 +28,53 @@ func TestRepositoriesService_ListCommits(t *testing.T) { "sha": "s", "path": "p", "author": "a", - "since": "2013-08-01T00:00:00Z", - "until": "2013-09-03T00:00:00Z", + "since": referenceTimeRaw, + "until": "2007-03-04T15:04:05Z", }) - fmt.Fprintf(w, `[{"sha": "s"}]`) + fmt.Fprint(w, `[{"sha": "s"}]`) }) opt := &CommitsListOptions{ SHA: "s", Path: "p", Author: "a", - Since: time.Date(2013, time.August, 1, 0, 0, 0, 0, time.UTC), - Until: time.Date(2013, time.September, 3, 0, 0, 0, 0, time.UTC), + Since: referenceTime, + Until: time.Date(2007, time.March, 4, 15, 4, 5, 0, time.UTC), } - commits, _, err := client.Repositories.ListCommits(context.Background(), "o", "r", opt) + ctx := t.Context() + commits, _, err := client.Repositories.ListCommits(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListCommits returned error: %v", err) } - want := []*RepositoryCommit{{SHA: String("s")}} - if !reflect.DeepEqual(commits, want) { + want := []*RepositoryCommit{{SHA: Ptr("s")}} + if !cmp.Equal(commits, want) { t.Errorf("Repositories.ListCommits returned %+v, want %+v", commits, want) } + + const methodName = "ListCommits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCommits(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCommits(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetCommit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `{ + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{ "sha": "s", "commit": { "message": "m" }, "author": { "login": "l" }, @@ -80,54 +97,70 @@ func TestRepositoriesService_GetCommit(t *testing.T) { }`) }) - commit, _, err := client.Repositories.GetCommit(context.Background(), "o", "r", "s") + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + commit, _, err := client.Repositories.GetCommit(ctx, "o", "r", "s", opts) if err != nil { t.Errorf("Repositories.GetCommit returned error: %v", err) } want := &RepositoryCommit{ - SHA: String("s"), + SHA: Ptr("s"), Commit: &Commit{ - Message: String("m"), + Message: Ptr("m"), }, Author: &User{ - Login: String("l"), + Login: Ptr("l"), }, Committer: &User{ - Login: String("l"), + Login: Ptr("l"), }, - Parents: []Commit{ + Parents: []*Commit{ { - SHA: String("s"), + SHA: Ptr("s"), }, }, Stats: &CommitStats{ - Additions: Int(104), - Deletions: Int(4), - Total: Int(108), + Additions: Ptr(104), + Deletions: Ptr(4), + Total: Ptr(108), }, - Files: []CommitFile{ + Files: []*CommitFile{ { - Filename: String("f"), - Additions: Int(10), - Deletions: Int(2), - Changes: Int(12), - Status: String("s"), - Patch: String("p"), - BlobURL: String("b"), - RawURL: String("r"), - ContentsURL: String("c"), + Filename: Ptr("f"), + Additions: Ptr(10), + Deletions: Ptr(2), + Changes: Ptr(12), + Status: Ptr("s"), + Patch: Ptr("p"), + BlobURL: Ptr("b"), + RawURL: Ptr("r"), + ContentsURL: Ptr("c"), }, }, } - if !reflect.DeepEqual(commit, want) { + if !cmp.Equal(commit, want) { t.Errorf("Repositories.GetCommit returned \n%+v, want \n%+v", commit, want) } + + const methodName = "GetCommit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCommit(ctx, "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCommit(ctx, "o", "r", "s", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetCommitRaw_diff(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) const rawStr = "@@diff content" @@ -137,19 +170,34 @@ func TestRepositoriesService_GetCommitRaw_diff(t *testing.T) { fmt.Fprint(w, rawStr) }) - got, _, err := client.Repositories.GetCommitRaw(context.Background(), "o", "r", "s", RawOptions{Type: Diff}) + ctx := t.Context() + got, _, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{Type: Diff}) if err != nil { t.Fatalf("Repositories.GetCommitRaw returned error: %v", err) } want := rawStr if got != want { - t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want) + t.Errorf("Repositories.GetCommitRaw returned %v want %v", got, want) } + + const methodName = "GetCommitRaw" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCommitRaw(ctx, "\n", "\n", "\n", RawOptions{Type: Diff}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{Type: Diff}) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetCommitRaw_patch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) const rawStr = "@@patch content" @@ -159,21 +207,23 @@ func TestRepositoriesService_GetCommitRaw_patch(t *testing.T) { fmt.Fprint(w, rawStr) }) - got, _, err := client.Repositories.GetCommitRaw(context.Background(), "o", "r", "s", RawOptions{Type: Patch}) + ctx := t.Context() + got, _, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{Type: Patch}) if err != nil { t.Fatalf("Repositories.GetCommitRaw returned error: %v", err) } want := rawStr if got != want { - t.Errorf("Repositories.GetCommitRaw returned %s want %s", got, want) + t.Errorf("Repositories.GetCommitRaw returned %v want %v", got, want) } } func TestRepositoriesService_GetCommitRaw_invalid(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.GetCommitRaw(context.Background(), "o", "r", "s", RawOptions{100}) + ctx := t.Context() + _, _, err := client.Repositories.GetCommitRaw(ctx, "o", "r", "s", RawOptions{100}) if err == nil { t.Fatal("Repositories.GetCommitRaw should return error") } @@ -183,18 +233,20 @@ func TestRepositoriesService_GetCommitRaw_invalid(t *testing.T) { } func TestRepositoriesService_GetCommitSHA1(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + const sha1 = "01234abcde" mux.HandleFunc("/repos/o/r/commits/master", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeV3SHA) - fmt.Fprintf(w, sha1) + fmt.Fprint(w, sha1) }) - got, _, err := client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "master", "") + ctx := t.Context() + got, _, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "master", "") if err != nil { t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err) } @@ -212,30 +264,46 @@ func TestRepositoriesService_GetCommitSHA1(t *testing.T) { w.WriteHeader(http.StatusNotModified) }) - got, _, err = client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "tag", sha1) + got, _, err = client.Repositories.GetCommitSHA1(ctx, "o", "r", "tag", sha1) if err == nil { - t.Errorf("Expected HTTP 304 response") + t.Error("Expected HTTP 304 response") } want = "" if got != want { t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) } + + const methodName = "GetCommitSHA1" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCommitSHA1(ctx, "\n", "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "master", "") + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_NonAlphabetCharacter_GetCommitSHA1(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + const sha1 = "01234abcde" - mux.HandleFunc("/repos/o/r/commits/master%20hash", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/commits/master%2520hash", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeV3SHA) - fmt.Fprintf(w, sha1) + fmt.Fprint(w, sha1) }) - got, _, err := client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "master%20hash", "") + ctx := t.Context() + got, _, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "master%20hash", "") if err != nil { t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err) } @@ -252,9 +320,9 @@ func TestRepositoriesService_NonAlphabetCharacter_GetCommitSHA1(t *testing.T) { w.WriteHeader(http.StatusNotModified) }) - got, _, err = client.Repositories.GetCommitSHA1(context.Background(), "o", "r", "tag", sha1) + got, _, err = client.Repositories.GetCommitSHA1(ctx, "o", "r", "tag", sha1) if err == nil { - t.Errorf("Expected HTTP 304 response") + t.Error("Expected HTTP 304 response") } if want := ""; got != want { @@ -262,121 +330,358 @@ func TestRepositoriesService_NonAlphabetCharacter_GetCommitSHA1(t *testing.T) { } } -func TestRepositoriesService_CompareCommits(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_TrailingPercent_GetCommitSHA1(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/compare/b...h", func(w http.ResponseWriter, r *http.Request) { + const sha1 = "01234abcde" + + mux.HandleFunc("/repos/o/r/commits/comm%", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `{ - "base_commit": { - "sha": "s", - "commit": { - "author": { "name": "n" }, - "committer": { "name": "n" }, - "message": "m", - "tree": { "sha": "t" } - }, - "author": { "login": "l" }, - "committer": { "login": "l" }, - "parents": [ { "sha": "s" } ] - }, - "status": "s", - "ahead_by": 1, - "behind_by": 2, - "total_commits": 1, - "commits": [ - { - "sha": "s", - "commit": { "author": { "name": "n" } }, - "author": { "login": "l" }, - "committer": { "login": "l" }, - "parents": [ { "sha": "s" } ] - } - ], - "files": [ { "filename": "f" } ], - "html_url": "https://github.com/o/r/compare/b...h", - "permalink_url": "https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17", - "diff_url": "https://github.com/o/r/compare/b...h.diff", - "patch_url": "https://github.com/o/r/compare/b...h.patch", - "url": "https://api.github.com/repos/o/r/compare/b...h" - }`) + testHeader(t, r, "Accept", mediaTypeV3SHA) + + fmt.Fprint(w, sha1) }) - got, _, err := client.Repositories.CompareCommits(context.Background(), "o", "r", "b", "h") + ctx := t.Context() + got, _, err := client.Repositories.GetCommitSHA1(ctx, "o", "r", "comm%", "") if err != nil { - t.Errorf("Repositories.CompareCommits returned error: %v", err) + t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err) } - want := &CommitsComparison{ - BaseCommit: &RepositoryCommit{ - SHA: String("s"), - Commit: &Commit{ - Author: &CommitAuthor{Name: String("n")}, - Committer: &CommitAuthor{Name: String("n")}, - Message: String("m"), - Tree: &Tree{SHA: String("t")}, - }, - Author: &User{Login: String("l")}, - Committer: &User{Login: String("l")}, - Parents: []Commit{ - { - SHA: String("s"), + if want := sha1; got != want { + t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) + } + + mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3SHA) + testHeader(t, r, "If-None-Match", `"`+sha1+`"`) + + w.WriteHeader(http.StatusNotModified) + }) + + got, _, err = client.Repositories.GetCommitSHA1(ctx, "o", "r", "tag", sha1) + if err == nil { + t.Error("Expected HTTP 304 response") + } + + if want := ""; got != want { + t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) + } +} + +func TestRepositoriesService_CompareCommits(t *testing.T) { + t.Parallel() + testCases := []struct { + base string + head string + }{ + {base: "b", head: "h"}, + {base: "123base", head: "head123"}, + {base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"}, + } + + for i, sample := range testCases { + t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + base := sample.base + head := sample.head + + encodedBase := url.PathEscape(base) + encodedHead := url.PathEscape(head) + + escapedBase := url.QueryEscape(base) + escapedHead := url.QueryEscape(head) + + pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", encodedBase, encodedHead) + + mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprintf(w, `{ + "base_commit": { + "sha": "s", + "commit": { + "author": { "name": "n" }, + "committer": { "name": "n" }, + "message": "m", + "tree": { "sha": "t" } + }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ] + }, + "status": "s", + "ahead_by": 1, + "behind_by": 2, + "total_commits": 1, + "commits": [ + { + "sha": "s", + "commit": { "author": { "name": "n" } }, + "author": { "login": "l" }, + "committer": { "login": "l" }, + "parents": [ { "sha": "s" } ] + } + ], + "files": [ { "filename": "f" } ], + "html_url": "https://github.com/o/r/compare/%[1]v...%[2]v", + "permalink_url": "https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17", + "diff_url": "https://github.com/o/r/compare/%[1]v...%[2]v.diff", + "patch_url": "https://github.com/o/r/compare/%[1]v...%[2]v.patch", + "url": "https://api.github.com/repos/o/r/compare/%[1]v...%[2]v" +}`, escapedBase, escapedHead) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := t.Context() + got, _, err := client.Repositories.CompareCommits(ctx, "o", "r", base, head, opts) + if err != nil { + t.Errorf("Repositories.CompareCommits returned error: %v", err) + } + + want := &CommitsComparison{ + BaseCommit: &RepositoryCommit{ + SHA: Ptr("s"), + Commit: &Commit{ + Author: &CommitAuthor{Name: Ptr("n")}, + Committer: &CommitAuthor{Name: Ptr("n")}, + Message: Ptr("m"), + Tree: &Tree{SHA: Ptr("t")}, + }, + Author: &User{Login: Ptr("l")}, + Committer: &User{Login: Ptr("l")}, + Parents: []*Commit{ + { + SHA: Ptr("s"), + }, + }, }, - }, - }, - Status: String("s"), - AheadBy: Int(1), - BehindBy: Int(2), - TotalCommits: Int(1), - Commits: []RepositoryCommit{ - { - SHA: String("s"), - Commit: &Commit{ - Author: &CommitAuthor{Name: String("n")}, + Status: Ptr("s"), + AheadBy: Ptr(1), + BehindBy: Ptr(2), + TotalCommits: Ptr(1), + Commits: []*RepositoryCommit{ + { + SHA: Ptr("s"), + Commit: &Commit{ + Author: &CommitAuthor{Name: Ptr("n")}, + }, + Author: &User{Login: Ptr("l")}, + Committer: &User{Login: Ptr("l")}, + Parents: []*Commit{ + { + SHA: Ptr("s"), + }, + }, + }, }, - Author: &User{Login: String("l")}, - Committer: &User{Login: String("l")}, - Parents: []Commit{ + Files: []*CommitFile{ { - SHA: String("s"), + Filename: Ptr("f"), }, }, - }, - }, - Files: []CommitFile{ - { - Filename: String("f"), - }, - }, - HTMLURL: String("https://github.com/o/r/compare/b...h"), - PermalinkURL: String("https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17"), - DiffURL: String("https://github.com/o/r/compare/b...h.diff"), - PatchURL: String("https://github.com/o/r/compare/b...h.patch"), - URL: String("https://api.github.com/repos/o/r/compare/b...h"), + HTMLURL: Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v", escapedBase, escapedHead)), + PermalinkURL: Ptr("https://github.com/o/r/compare/o:bbcd538c8e72b8c175046e27cc8f907076331401...o:0328041d1152db8ae77652d1618a02e57f745f17"), + DiffURL: Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v.diff", escapedBase, escapedHead)), + PatchURL: Ptr(fmt.Sprintf("https://github.com/o/r/compare/%v...%v.patch", escapedBase, escapedHead)), + URL: Ptr(fmt.Sprintf("https://api.github.com/repos/o/r/compare/%v...%v", escapedBase, escapedHead)), + } + + if !cmp.Equal(got, want) { + t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want) + } + + const methodName = "CompareCommits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CompareCommits(ctx, "\n", "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CompareCommits(ctx, "o", "r", base, head, opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want) +func TestRepositoriesService_CompareCommitsRaw_diff(t *testing.T) { + t.Parallel() + testCases := []struct { + base string + head string + }{ + {base: "b", head: "h"}, + {base: "123base", head: "head123"}, + {base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"}, + } + + for i, sample := range testCases { + t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + base := sample.base + head := sample.head + + encodedBase := url.PathEscape(base) + encodedHead := url.PathEscape(head) + + pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", encodedBase, encodedHead) + const rawStr = "@@diff content" + + mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3Diff) + fmt.Fprint(w, rawStr) + }) + + ctx := t.Context() + got, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Diff}) + if err != nil { + t.Fatalf("Repositories.CompareCommitsRaw returned error: %v", err) + } + want := rawStr + if got != want { + t.Errorf("Repositories.CompareCommitsRaw returned %v want %v", got, want) + } + + const methodName = "CompareCommitsRaw" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CompareCommitsRaw(ctx, "\n", "\n", "\n", "\n", RawOptions{Type: Diff}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Diff}) + if got != "" { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want ''", methodName, got) + } + return resp, err + }) + }) + } +} + +func TestRepositoriesService_CompareCommitsRaw_patch(t *testing.T) { + t.Parallel() + testCases := []struct { + base string + head string + }{ + {base: "b", head: "h"}, + {base: "123base", head: "head123"}, + {base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"}, + } + + for i, sample := range testCases { + t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + base := sample.base + head := sample.head + + encodedBase := url.PathEscape(base) + encodedHead := url.PathEscape(head) + + pattern := fmt.Sprintf("/repos/o/r/compare/%v...%v", encodedBase, encodedHead) + const rawStr = "@@patch content" + + mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeV3Patch) + fmt.Fprint(w, rawStr) + }) + + ctx := t.Context() + got, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", base, head, RawOptions{Type: Patch}) + if err != nil { + t.Fatalf("Repositories.CompareCommitsRaw returned error: %v", err) + } + want := rawStr + if got != want { + t.Errorf("Repositories.CompareCommitsRaw returned %v want %v", got, want) + } + }) + } +} + +func TestRepositoriesService_CompareCommitsRaw_invalid(t *testing.T) { + t.Parallel() + ctx := t.Context() + + testCases := []struct { + base string + head string + }{ + {base: "b", head: "h"}, + {base: "123base", head: "head123"}, + {base: "`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+123base", head: "head123`~!@#$%^&*()_+-=[]\\{}|;':\",./<>?/*-+"}, + } + + for i, sample := range testCases { + t.Run(fmt.Sprintf("case #%v", i+1), func(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + _, _, err := client.Repositories.CompareCommitsRaw(ctx, "o", "r", sample.base, sample.head, RawOptions{100}) + if err == nil { + t.Fatal("Repositories.GetCommitRaw should return error") + } + if !strings.Contains(err.Error(), "unsupported raw type") { + t.Error("Repositories.GetCommitRaw should return unsupported raw type error") + } + }) } } func TestRepositoriesService_ListBranchesHeadCommit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/s/branches-where-head", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `[{"name": "b"}]`) + fmt.Fprint(w, `[{"name": "b","commit":{"sha":"2e90302801c870f17b6152327d9b9a03c8eca0e2","url":"https://api.github.com/repos/google/go-github/commits/2e90302801c870f17b6152327d9b9a03c8eca0e2"},"protected":true}]`) }) - branches, _, err := client.Repositories.ListBranchesHeadCommit(context.Background(), "o", "r", "s") + ctx := t.Context() + branches, _, err := client.Repositories.ListBranchesHeadCommit(ctx, "o", "r", "s") if err != nil { t.Errorf("Repositories.ListBranchesHeadCommit returned error: %v", err) } - want := []*BranchCommit{{Name: String("b")}} - if !reflect.DeepEqual(branches, want) { + want := []*BranchCommit{ + { + Name: Ptr("b"), + Commit: &Commit{ + SHA: Ptr("2e90302801c870f17b6152327d9b9a03c8eca0e2"), + URL: Ptr("https://api.github.com/repos/google/go-github/commits/2e90302801c870f17b6152327d9b9a03c8eca0e2"), + }, + Protected: Ptr(true), + }, + } + if !cmp.Equal(branches, want) { t.Errorf("Repositories.ListBranchesHeadCommit returned %+v, want %+v", branches, want) } + + const methodName = "ListBranchesHeadCommit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListBranchesHeadCommit(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListBranchesHeadCommit(ctx, "o", "r", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_community_health.go b/github/repos_community_health.go index 73d1d573bff..143da8c4e06 100644 --- a/github/repos_community_health.go +++ b/github/repos_community_health.go @@ -8,20 +8,22 @@ package github import ( "context" "fmt" - "time" ) // Metric represents the different fields for one file in community health files. type Metric struct { Name *string `json:"name"` Key *string `json:"key"` + SPDXID *string `json:"spdx_id"` URL *string `json:"url"` HTMLURL *string `json:"html_url"` + NodeID *string `json:"node_id"` } // CommunityHealthFiles represents the different files in the community health metrics response. type CommunityHealthFiles struct { CodeOfConduct *Metric `json:"code_of_conduct"` + CodeOfConductFile *Metric `json:"code_of_conduct_file"` Contributing *Metric `json:"contributing"` IssueTemplate *Metric `json:"issue_template"` PullRequestTemplate *Metric `json:"pull_request_template"` @@ -31,26 +33,28 @@ type CommunityHealthFiles struct { // CommunityHealthMetrics represents a response containing the community metrics of a repository. type CommunityHealthMetrics struct { - HealthPercentage *int `json:"health_percentage"` - Files *CommunityHealthFiles `json:"files"` - UpdatedAt *time.Time `json:"updated_at"` + HealthPercentage *int `json:"health_percentage"` + Description *string `json:"description"` + Documentation *string `json:"documentation"` + Files *CommunityHealthFiles `json:"files"` + UpdatedAt *Timestamp `json:"updated_at"` + ContentReportsEnabled *bool `json:"content_reports_enabled"` } // GetCommunityHealthMetrics retrieves all the community health metrics for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/community/#retrieve-community-health-metrics +// GitHub API docs: https://docs.github.com/rest/metrics/community?apiVersion=2022-11-28#get-community-profile-metrics +// +//meta:operation GET /repos/{owner}/{repo}/community/profile func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, owner, repo string) (*CommunityHealthMetrics, *Response, error) { u := fmt.Sprintf("repos/%v/%v/community/profile", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) - - metrics := &CommunityHealthMetrics{} - resp, err := s.client.Do(ctx, req, metrics) + var metrics *CommunityHealthMetrics + resp, err := s.client.Do(req, &metrics) if err != nil { return nil, resp, err } diff --git a/github/repos_community_health_test.go b/github/repos_community_health_test.go index 6092af0b199..19705d5ee6c 100644 --- a/github/repos_community_health_test.go +++ b/github/repos_community_health_test.go @@ -6,23 +6,23 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_GetCommunityHealthMetrics(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/community/profile", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) - fmt.Fprintf(w, `{ + fmt.Fprint(w, `{ "health_percentage": 100, + "description": "My first repository on GitHub!", + "documentation": null, "files": { "code_of_conduct": { "name": "Contributor Covenant", @@ -30,57 +30,102 @@ func TestRepositoriesService_GetCommunityHealthMetrics(t *testing.T) { "url": null, "html_url": "https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md" }, + "code_of_conduct_file": { + "url": "https://api.github.com/repos/octocat/Hello-World/contents/CODE_OF_CONDUCT.md", + "html_url": "https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md" + }, "contributing": { "url": "https://api.github.com/repos/octocat/Hello-World/contents/CONTRIBUTING", "html_url": "https://github.com/octocat/Hello-World/blob/master/CONTRIBUTING" }, + "issue_template": { + "url": "https://api.github.com/repos/octocat/Hello-World/contents/ISSUE_TEMPLATE", + "html_url": "https://github.com/octocat/Hello-World/blob/master/ISSUE_TEMPLATE" + }, + "pull_request_template": { + "url": "https://api.github.com/repos/octocat/Hello-World/contents/PULL_REQUEST_TEMPLATE", + "html_url": "https://github.com/octocat/Hello-World/blob/master/PULL_REQUEST_TEMPLATE" + }, "license": { "name": "MIT License", "key": "mit", + "spdx_id": "MIT", "url": "https://api.github.com/licenses/mit", - "html_url": "https://github.com/octocat/Hello-World/blob/master/LICENSE" + "html_url": "https://github.com/octocat/Hello-World/blob/master/LICENSE", + "node_id": "MDc6TGljZW5zZW1pdA==" }, "readme": { "url": "https://api.github.com/repos/octocat/Hello-World/contents/README.md", "html_url": "https://github.com/octocat/Hello-World/blob/master/README.md" } }, - "updated_at": "2017-02-28T00:00:00Z" + "updated_at": `+referenceTimeStr+`, + "content_reports_enabled": true }`) }) - got, _, err := client.Repositories.GetCommunityHealthMetrics(context.Background(), "o", "r") + ctx := t.Context() + got, _, err := client.Repositories.GetCommunityHealthMetrics(ctx, "o", "r") if err != nil { t.Errorf("Repositories.GetCommunityHealthMetrics returned error: %v", err) } - updatedAt := time.Date(2017, time.February, 28, 0, 0, 0, 0, time.UTC) want := &CommunityHealthMetrics{ - HealthPercentage: Int(100), - UpdatedAt: &updatedAt, + HealthPercentage: Ptr(100), + Description: Ptr("My first repository on GitHub!"), + UpdatedAt: &referenceTimestamp, + ContentReportsEnabled: Ptr(true), Files: &CommunityHealthFiles{ CodeOfConduct: &Metric{ - Name: String("Contributor Covenant"), - Key: String("contributor_covenant"), - HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md"), + Name: Ptr("Contributor Covenant"), + Key: Ptr("contributor_covenant"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md"), + }, + CodeOfConductFile: &Metric{ + URL: Ptr("https://api.github.com/repos/octocat/Hello-World/contents/CODE_OF_CONDUCT.md"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/CODE_OF_CONDUCT.md"), }, Contributing: &Metric{ - URL: String("https://api.github.com/repos/octocat/Hello-World/contents/CONTRIBUTING"), - HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/CONTRIBUTING"), + URL: Ptr("https://api.github.com/repos/octocat/Hello-World/contents/CONTRIBUTING"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/CONTRIBUTING"), + }, + IssueTemplate: &Metric{ + URL: Ptr("https://api.github.com/repos/octocat/Hello-World/contents/ISSUE_TEMPLATE"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/ISSUE_TEMPLATE"), + }, + PullRequestTemplate: &Metric{ + URL: Ptr("https://api.github.com/repos/octocat/Hello-World/contents/PULL_REQUEST_TEMPLATE"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/PULL_REQUEST_TEMPLATE"), }, License: &Metric{ - Name: String("MIT License"), - Key: String("mit"), - URL: String("https://api.github.com/licenses/mit"), - HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/LICENSE"), + Name: Ptr("MIT License"), + Key: Ptr("mit"), + SPDXID: Ptr("MIT"), + URL: Ptr("https://api.github.com/licenses/mit"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/LICENSE"), + NodeID: Ptr("MDc6TGljZW5zZW1pdA=="), }, Readme: &Metric{ - URL: String("https://api.github.com/repos/octocat/Hello-World/contents/README.md"), - HTMLURL: String("https://github.com/octocat/Hello-World/blob/master/README.md"), + URL: Ptr("https://api.github.com/repos/octocat/Hello-World/contents/README.md"), + HTMLURL: Ptr("https://github.com/octocat/Hello-World/blob/master/README.md"), }, }, } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Repositories.GetCommunityHealthMetrics:\ngot:\n%v\nwant:\n%v", Stringify(got), Stringify(want)) } + + const methodName = "GetCommunityHealthMetrics" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCommunityHealthMetrics(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCommunityHealthMetrics(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_contents.go b/github/repos_contents.go index bf6cabc5c0b..4c9cb27f5b7 100644 --- a/github/repos_contents.go +++ b/github/repos_contents.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // Repository contents API methods. -// GitHub API docs: https://developer.github.com/v3/repos/contents/ +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28 package github @@ -12,13 +12,23 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "net/http" "net/url" - "path" + "strings" ) +// ErrContentsDirectory indicates that the contents are not available for a directory. +var ErrContentsDirectory = errors.New("contents not available for directory") + +// ErrContentsSubmodule indicates that the contents are not available for a submodule. +var ErrContentsSubmodule = errors.New("contents not available for submodule") + +// ErrContentsNoDownloadURL indicates that the contents download URL is empty, which may occur when file size > 100 MB. +var ErrContentsNoDownloadURL = errors.New("contents download url is empty") + // RepositoryContent represents a file or directory in a github repository. type RepositoryContent struct { Type *string `json:"type,omitempty"` @@ -32,24 +42,25 @@ type RepositoryContent struct { // Content contains the actual file content, which may be encoded. // Callers should call GetContent which will decode the content if // necessary. - Content *string `json:"content,omitempty"` - SHA *string `json:"sha,omitempty"` - URL *string `json:"url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - DownloadURL *string `json:"download_url,omitempty"` + Content *string `json:"content,omitempty"` + SHA *string `json:"sha,omitempty"` + URL *string `json:"url,omitempty"` + GitURL *string `json:"git_url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + DownloadURL *string `json:"download_url,omitempty"` + SubmoduleGitURL *string `json:"submodule_git_url,omitempty"` } // RepositoryContentResponse holds the parsed response from CreateFile, UpdateFile, and DeleteFile. type RepositoryContentResponse struct { Content *RepositoryContent `json:"content,omitempty"` - Commit `json:"commit,omitempty"` + Commit `json:"commit"` } // RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile. type RepositoryContentFileOptions struct { Message *string `json:"message,omitempty"` - Content []byte `json:"content,omitempty"` // unencoded + Content []byte `json:"content"` // unencoded SHA *string `json:"sha,omitempty"` Branch *string `json:"branch,omitempty"` Author *CommitAuthor `json:"author,omitempty"` @@ -57,7 +68,7 @@ type RepositoryContentFileOptions struct { } // RepositoryContentGetOptions represents an optional ref parameter, which can be a SHA, -// branch, or tag +// branch, or tag. E.g., `6540c41b`, `heads/main`, `tags/v1.0`. type RepositoryContentGetOptions struct { Ref string `url:"ref,omitempty"` } @@ -76,6 +87,9 @@ func (r *RepositoryContent) GetContent() (string, error) { switch encoding { case "base64": + if r.Content == nil { + return "", errors.New("malformed response: base64 encoding of null content") + } c, err := base64.StdEncoding.DecodeString(*r.Content) return string(c), err case "": @@ -83,6 +97,8 @@ func (r *RepositoryContent) GetContent() (string, error) { return "", nil } return *r.Content, nil + case "none": + return "", errors.New("unsupported content encoding: none, this may occur when file size > 1 MB, if that is the case consider using DownloadContents") default: return "", fmt.Errorf("unsupported content encoding: %v", encoding) } @@ -90,22 +106,27 @@ func (r *RepositoryContent) GetContent() (string, error) { // GetReadme gets the Readme file for the repository. // -// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-the-readme -func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, opt *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#get-a-repository-readme +// +//meta:operation GET /repos/{owner}/{repo}/readme +func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, opts *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) { u := fmt.Sprintf("repos/%v/%v/readme", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - readme := new(RepositoryContent) - resp, err := s.client.Do(ctx, req, readme) + + var readme *RepositoryContent + resp, err := s.client.Do(req, &readme) if err != nil { return nil, resp, err } + return readme, resp, nil } @@ -113,26 +134,75 @@ func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, // specified file. This function will work with files of any size, as opposed // to GetContents which is limited to 1 Mb files. It is the caller's // responsibility to close the ReadCloser. -func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, filepath string, opt *RepositoryContentGetOptions) (io.ReadCloser, error) { - dir := path.Dir(filepath) - filename := path.Base(filepath) - _, dirContents, _, err := s.GetContents(ctx, owner, repo, dir, opt) +// +// It is possible for the download to result in a failed response when the +// returned error is nil. Callers should check the returned Response status +// code to verify the content is from a successful response. +// +// DownloadContents returns [ErrContentsDirectory] if the path references a +// directory, [ErrContentsSubmodule] if the path references a submodule, and +// [ErrContentsNoDownloadURL] if the file's download URL is empty. +// +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#get-repository-content +// +//meta:operation GET /repos/{owner}/{repo}/contents/{path} +func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, filepath string, opts *RepositoryContentGetOptions) (io.ReadCloser, *Response, error) { + rc, _, resp, err := s.DownloadContentsWithMeta(ctx, owner, repo, filepath, opts) + return rc, resp, err +} + +// DownloadContentsWithMeta is identical to DownloadContents but additionally +// returns the RepositoryContent of the requested file. This additional data +// is useful for future operations involving the requested file. For merely +// reading the content of a file, DownloadContents is perfectly adequate. +// +// It is possible for the download to result in a failed response when the +// returned error is nil. Callers should check the returned Response status +// code to verify the content is from a successful response. +// +// DownloadContentsWithMeta returns [ErrContentsDirectory] if the path +// references a directory, [ErrContentsSubmodule] if the path references a +// submodule, and [ErrContentsNoDownloadURL] if the file's download URL is +// empty. +// +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#get-repository-content +// +//meta:operation GET /repos/{owner}/{repo}/contents/{path} +func (s *RepositoriesService) DownloadContentsWithMeta(ctx context.Context, owner, repo, filepath string, opts *RepositoryContentGetOptions) (io.ReadCloser, *RepositoryContent, *Response, error) { + fileContent, _, resp, err := s.GetContents(ctx, owner, repo, filepath, opts) if err != nil { - return nil, err + return nil, nil, resp, err } - for _, contents := range dirContents { - if *contents.Name == filename { - if contents.DownloadURL == nil || *contents.DownloadURL == "" { - return nil, fmt.Errorf("No download link found for %s", filepath) - } - resp, err := s.client.client.Get(*contents.DownloadURL) - if err != nil { - return nil, err - } - return resp.Body, nil - } + + if fileContent == nil { + return nil, nil, resp, ErrContentsDirectory + } + + if fileContent.GetType() == "submodule" { + return nil, fileContent, resp, ErrContentsSubmodule + } + + content, err := fileContent.GetContent() + if err == nil && content != "" { + return io.NopCloser(strings.NewReader(content)), fileContent, resp, nil + } + + downloadURL := fileContent.GetDownloadURL() + if downloadURL == "" { + return nil, fileContent, resp, ErrContentsNoDownloadURL } - return nil, fmt.Errorf("No file named %s found in %s", filename, dir) + + dlReq, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil) + if err != nil { + return nil, fileContent, resp, err + } + + dlResp, err := s.client.client.Do(dlReq) + if err != nil { + return nil, fileContent, &Response{Response: dlResp}, err + } + + return dlResp.Body, fileContent, &Response{Response: dlResp}, nil } // GetContents can return either the metadata and content of a single file @@ -142,128 +212,176 @@ func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, // as possible, both result types will be returned but only one will contain a // value and the other will be nil. // -// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-contents -func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path string, opt *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) { - escapedPath := (&url.URL{Path: path}).String() - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, escapedPath) - u, err = addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#get-repository-content +// +//meta:operation GET /repos/{owner}/{repo}/contents/{path} +func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path string, opts *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) { + escapedPath := (&url.URL{Path: strings.TrimSuffix(path, "/")}).String() + u := fmt.Sprintf("repos/%v/%v/contents/%v", owner, repo, escapedPath) + u, err = addOptions(u, opts) if err != nil { return nil, nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, nil, err } + var rawJSON json.RawMessage - resp, err = s.client.Do(ctx, req, &rawJSON) + resp, err = s.client.Do(req, &rawJSON) if err != nil { return nil, nil, resp, err } + fileUnmarshalError := json.Unmarshal(rawJSON, &fileContent) if fileUnmarshalError == nil { return fileContent, nil, resp, nil } + directoryUnmarshalError := json.Unmarshal(rawJSON, &directoryContent) if directoryUnmarshalError == nil { return nil, directoryContent, resp, nil } - return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError) + + return nil, nil, resp, fmt.Errorf("unmarshaling failed for both file and directory content: %v and %v", fileUnmarshalError, directoryUnmarshalError) } // CreateFile creates a new file in a repository at the given path and returns // the commit and file metadata. // -// GitHub API docs: https://developer.github.com/v3/repos/contents/#create-a-file -func (s *RepositoriesService) CreateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) - req, err := s.client.NewRequest("PUT", u, opt) +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#create-or-update-file-contents +// +//meta:operation PUT /repos/{owner}/{repo}/contents/{path} +func (s *RepositoriesService) CreateFile(ctx context.Context, owner, repo, path string, body *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/contents/%v", owner, repo, path) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - createResponse := new(RepositoryContentResponse) - resp, err := s.client.Do(ctx, req, createResponse) + + var createResponse *RepositoryContentResponse + resp, err := s.client.Do(req, &createResponse) if err != nil { return nil, resp, err } + return createResponse, resp, nil } // UpdateFile updates a file in a repository at the given path and returns the // commit and file metadata. Requires the blob SHA of the file being updated. // -// GitHub API docs: https://developer.github.com/v3/repos/contents/#update-a-file -func (s *RepositoriesService) UpdateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) - req, err := s.client.NewRequest("PUT", u, opt) +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#create-or-update-file-contents +// +//meta:operation PUT /repos/{owner}/{repo}/contents/{path} +func (s *RepositoriesService) UpdateFile(ctx context.Context, owner, repo, path string, body *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/contents/%v", owner, repo, path) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - updateResponse := new(RepositoryContentResponse) - resp, err := s.client.Do(ctx, req, updateResponse) + + var updateResponse *RepositoryContentResponse + resp, err := s.client.Do(req, &updateResponse) if err != nil { return nil, resp, err } + return updateResponse, resp, nil } // DeleteFile deletes a file from a repository and returns the commit. // Requires the blob SHA of the file to be deleted. // -// GitHub API docs: https://developer.github.com/v3/repos/contents/#delete-a-file -func (s *RepositoriesService) DeleteFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) - req, err := s.client.NewRequest("DELETE", u, opt) +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#delete-a-file +// +//meta:operation DELETE /repos/{owner}/{repo}/contents/{path} +func (s *RepositoriesService) DeleteFile(ctx context.Context, owner, repo, path string, opts *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/contents/%v", owner, repo, path) + req, err := s.client.NewRequest(ctx, "DELETE", u, opts) if err != nil { return nil, nil, err } - deleteResponse := new(RepositoryContentResponse) - resp, err := s.client.Do(ctx, req, deleteResponse) + + var deleteResponse *RepositoryContentResponse + resp, err := s.client.Do(req, &deleteResponse) if err != nil { return nil, resp, err } + return deleteResponse, resp, nil } -// archiveFormat is used to define the archive type when calling GetArchiveLink. -type archiveFormat string +// ArchiveFormat is used to define the archive type when calling GetArchiveLink. +type ArchiveFormat string const ( // Tarball specifies an archive in gzipped tar format. - Tarball archiveFormat = "tarball" + Tarball ArchiveFormat = "tarball" // Zipball specifies an archive in zip format. - Zipball archiveFormat = "zipball" + Zipball ArchiveFormat = "zipball" ) // GetArchiveLink returns an URL to download a tarball or zipball archive for a // repository. The archiveFormat can be specified by either the github.Tarball // or github.Zipball constant. // -// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-archive-link -func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat) - if opt != nil && opt.Ref != "" { - u += fmt.Sprintf("/%s", opt.Ref) +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#download-a-repository-archive-tar +// +// GitHub API docs: https://docs.github.com/rest/repos/contents?apiVersion=2022-11-28#download-a-repository-archive-zip +// +//meta:operation GET /repos/{owner}/{repo}/tarball/{ref} +//meta:operation GET /repos/{owner}/{repo}/zipball/{ref} +func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat ArchiveFormat, opts *RepositoryContentGetOptions, maxRedirects int) (*url.URL, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/%v", owner, repo, archiveformat) + if opts != nil && opts.Ref != "" { + u += fmt.Sprintf("/%v", opts.Ref) + } + + if s.client.rateLimitRedirectionalEndpoints { + return s.getArchiveLinkWithRateLimit(ctx, u, maxRedirects) } - req, err := s.client.NewRequest("GET", u, nil) + + return s.getArchiveLinkWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *RepositoriesService) getArchiveLinkWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err } - var resp *http.Response - // Use http.DefaultTransport if no custom Transport is configured - req = withContext(ctx, req) - if s.client.client.Transport == nil { - resp, err = http.DefaultTransport.RoundTrip(req) - } else { - resp, err = s.client.client.Transport.RoundTrip(req) + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound { + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) } + + parsedURL, err := url.Parse(resp.Header.Get("Location")) + if err != nil { + return nil, newResponse(resp), err + } + + return parsedURL, newResponse(resp), nil +} + +func (s *RepositoriesService) getArchiveLinkWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - resp.Body.Close() - if resp.StatusCode != http.StatusFound { - return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) + + url, resp, err := s.client.bareDoUntilFound(req, maxRedirects) + if err != nil { + return nil, resp, err } - parsedURL, err := url.Parse(resp.Header.Get("Location")) - return parsedURL, newResponse(resp), err + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil } diff --git a/github/repos_contents_test.go b/github/repos_contents_test.go index 29bab8d270e..b2890684a4d 100644 --- a/github/repos_contents_test.go +++ b/github/repos_contents_test.go @@ -6,29 +6,32 @@ package github import ( - "context" + "errors" "fmt" - "io/ioutil" + "io" "net/http" - "reflect" + "net/url" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoryContent_GetContent(t *testing.T) { + t.Parallel() tests := []struct { encoding, content *string // input encoding and content want string // desired output wantErr bool // whether an error is expected }{ { - encoding: String(""), - content: String("hello"), + encoding: Ptr(""), + content: Ptr("hello"), want: "hello", wantErr: false, }, { encoding: nil, - content: String("hello"), + content: Ptr("hello"), want: "hello", wantErr: false, }, @@ -39,14 +42,20 @@ func TestRepositoryContent_GetContent(t *testing.T) { wantErr: false, }, { - encoding: String("base64"), - content: String("aGVsbG8="), + encoding: Ptr("base64"), + content: Ptr("aGVsbG8="), want: "hello", wantErr: false, }, { - encoding: String("bad"), - content: String("aGVsbG8="), + encoding: Ptr("bad"), + content: Ptr("aGVsbG8="), + want: "", + wantErr: true, + }, + { + encoding: Ptr("none"), + content: nil, want: "", wantErr: true, }, @@ -56,11 +65,11 @@ func TestRepositoryContent_GetContent(t *testing.T) { r := RepositoryContent{Encoding: tt.encoding, Content: tt.content} got, err := r.GetContent() if err != nil && !tt.wantErr { - t.Errorf("RepositoryContent(%s, %s) returned unexpected error: %v", + t.Errorf("RepositoryContent(%v, %v) returned unexpected error: %v", stringOrNil(tt.encoding), stringOrNil(tt.content), err) } if err == nil && tt.wantErr { - t.Errorf("RepositoryContent(%s, %s) did not return unexpected error", + t.Errorf("RepositoryContent(%v, %v) did not return unexpected error", stringOrNil(tt.encoding), stringOrNil(tt.content)) } if want := tt.want; got != want { @@ -79,8 +88,9 @@ func stringOrNil(s *string) string { } func TestRepositoriesService_GetReadme(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ @@ -91,38 +101,110 @@ func TestRepositoriesService_GetReadme(t *testing.T) { "path": "README.md" }`) }) - readme, _, err := client.Repositories.GetReadme(context.Background(), "o", "r", &RepositoryContentGetOptions{}) + ctx := t.Context() + readme, _, err := client.Repositories.GetReadme(ctx, "o", "r", &RepositoryContentGetOptions{}) if err != nil { t.Errorf("Repositories.GetReadme returned error: %v", err) } - want := &RepositoryContent{Type: String("file"), Name: String("README.md"), Size: Int(5362), Encoding: String("base64"), Path: String("README.md")} - if !reflect.DeepEqual(readme, want) { + want := &RepositoryContent{Type: Ptr("file"), Name: Ptr("README.md"), Size: Ptr(5362), Encoding: Ptr("base64"), Path: Ptr("README.md")} + if !cmp.Equal(readme, want) { t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want) } + + const methodName = "GetReadme" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetReadme(ctx, "\n", "\n", &RepositoryContentGetOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetReadme(ctx, "o", "r", &RepositoryContentGetOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_DownloadContents_Success(t *testing.T) { - client, mux, serverURL, teardown := setup() - defer teardown() - mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { +func TestRepositoriesService_DownloadContents_SuccessWithContent(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `[{ - "type": "file", - "name": "f", - "download_url": "`+serverURL+baseURLPath+`/download/f" - }]`) + fmt.Fprintf(w, `{ + "type": "file", + "name": "f", + "content": "foo", + "download_url": "%v/download/f" +}`, serverURL+baseURLPath) + }) + + ctx := t.Context() + r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) + if err != nil { + t.Errorf("Repositories.DownloadContents returned error: %v", err) + } + + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want) + } + + bytes, err := io.ReadAll(r) + if err != nil { + t.Errorf("Error reading response body: %v", err) + } + r.Close() + + if got, want := string(bytes), "foo"; got != want { + t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) + } + + const methodName = "DownloadContents" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.DownloadContents(ctx, "\n", "\n", "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DownloadContents_SuccessByDownload(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "type": "file", + "name": "f", + "content": "", + "download_url": "%v/download/f" +}`, serverURL+baseURLPath) }) + mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, "foo") }) - r, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil) + ctx := t.Context() + r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) if err != nil { t.Errorf("Repositories.DownloadContents returned error: %v", err) } - bytes, err := ioutil.ReadAll(r) + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want) + } + + bytes, err := io.ReadAll(r) if err != nil { t.Errorf("Error reading response body: %v", err) } @@ -131,42 +213,456 @@ func TestRepositoriesService_DownloadContents_Success(t *testing.T) { if got, want := string(bytes), "foo"; got != want { t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) } + + const methodName = "DownloadContents" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.DownloadContents(ctx, "\n", "\n", "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_DownloadContents_FailedDownloadResponse(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "type": "file", + "name": "f", + "content": "", + "download_url": "%v/download/f" +}`, serverURL+baseURLPath) + }) + mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, "foo error") + }) + + ctx := t.Context() + r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) + if err != nil { + t.Errorf("Repositories.DownloadContents returned error: %v", err) + } + + if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want { + t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want) + } + + bytes, err := io.ReadAll(r) + if err != nil { + t.Errorf("Error reading response body: %v", err) + } + r.Close() + + if got, want := string(bytes), "foo error"; got != want { + t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) + } +} + +func TestRepositoriesService_DownloadContents_NotFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) + if err == nil { + t.Error("Repositories.DownloadContents did not return expected error") + } + + if reader != nil { + t.Error("Repositories.DownloadContents did not return expected reader") + } + + if resp == nil || resp.Response.StatusCode != http.StatusNotFound { + t.Error("Repositories.DownloadContents did not return expected response") + } +} + +func TestRepositoriesService_DownloadContents_NotFile(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{ - "type": "file", - "name": "f", - }]`) + "type": "file", + "name": "f", + "content": "" +}]`) }) - _, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil) - if err == nil { - t.Errorf("Repositories.DownloadContents did not return expected error") + ctx := t.Context() + reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d", nil) + if err == nil || !errors.Is(err, ErrContentsDirectory) { + t.Error("Repositories.DownloadContents did not return expected error") + } + + if resp == nil || resp.Response.StatusCode != http.StatusOK { + t.Error("Repositories.DownloadContents did not return expected response") + } + + if reader != nil { + t.Error("Repositories.DownloadContents did not return expected reader") } } -func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_DownloadContents_Submodule(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `[]`) + fmt.Fprint(w, `{ + "type": "submodule", + "name": "f", + "content": "", + "download_url": "", + "submodule_git_url": "http://example.com/submodule.git" +}`) + }) + + ctx := t.Context() + reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d", nil) + if err == nil || !errors.Is(err, ErrContentsSubmodule) { + t.Error("Repositories.DownloadContents did not return expected error") + } + + if resp == nil || resp.Response.StatusCode != http.StatusOK { + t.Error("Repositories.DownloadContents did not return expected response") + } + + if reader != nil { + t.Error("Repositories.DownloadContents did not return expected reader") + } +} + +func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "type": "file", + "name": "f", + "content": "" +}`) + }) + + ctx := t.Context() + reader, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil) + if err == nil || !errors.Is(err, ErrContentsNoDownloadURL) { + t.Error("Repositories.DownloadContents did not return expected error") + } + + if resp == nil || resp.Response.StatusCode != http.StatusOK { + t.Error("Repositories.DownloadContents did not return expected response") + } + + if reader != nil { + t.Error("Repositories.DownloadContents did not return expected reader") + } +} + +func TestRepositoriesService_DownloadContentsWithMeta_SuccessWithContent(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "type": "file", + "name": "f", + "content": "foo", + "download_url": "%v/download/f" +}`, serverURL+baseURLPath) + }) + + ctx := t.Context() + r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) + if err != nil { + t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err) + } + + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want) + } + + bytes, err := io.ReadAll(r) + if err != nil { + t.Errorf("Error reading response body: %v", err) + } + r.Close() + + if got, want := string(bytes), "foo"; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want) + } + + if c != nil && c.Name != nil { + if got, want := *c.Name, "f"; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want) + } + } else { + t.Error("Returned RepositoryContent is null") + } + + const methodName = "DownloadContentsWithMeta" + testBadOptions(t, methodName, func() (err error) { + _, _, _, err = client.Repositories.DownloadContentsWithMeta(ctx, "\n", "\n", "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, cot, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + if cot != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, cot) + } + return resp, err + }) +} + +func TestRepositoriesService_DownloadContentsWithMeta_SuccessByDownload(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "type": "file", + "name": "f", + "download_url": "%v/download/f" +}`, serverURL+baseURLPath) + }) + + mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, "foo") + }) + + ctx := t.Context() + r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) + if err != nil { + t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err) + } + + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want) + } + + bytes, err := io.ReadAll(r) + if err != nil { + t.Errorf("Error reading response body: %v", err) + } + r.Close() + + if got, want := string(bytes), "foo"; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want) + } + + if c != nil && c.Name != nil { + if got, want := *c.Name, "f"; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want) + } + } else { + t.Error("Returned RepositoryContent is null") + } +} + +func TestRepositoriesService_DownloadContentsWithMeta_FailedDownloadResponse(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{ + "type": "file", + "name": "f", + "download_url": "%v/download/f" +}`, serverURL+baseURLPath) + }) + mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(w, "foo error") + }) + + ctx := t.Context() + r, c, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) + if err != nil { + t.Errorf("Repositories.DownloadContentsWithMeta returned error: %v", err) + } + + if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned status code %v, want %v", got, want) + } + + bytes, err := io.ReadAll(r) + if err != nil { + t.Errorf("Error reading response body: %v", err) + } + r.Close() + + if got, want := string(bytes), "foo error"; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned %v, want %v", got, want) + } + + if c != nil && c.Name != nil { + if got, want := *c.Name, "f"; got != want { + t.Errorf("Repositories.DownloadContentsWithMeta returned content name %v, want %v", got, want) + } + } else { + t.Error("Returned RepositoryContent is null") + } +} + +func TestRepositoriesService_DownloadContentsWithMeta_NotFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) }) - _, err := client.Repositories.DownloadContents(context.Background(), "o", "r", "d/f", nil) + ctx := t.Context() + reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) if err == nil { - t.Errorf("Repositories.DownloadContents did not return expected error") + t.Error("Repositories.DownloadContentsWithMeta did not return expected error") + } + + if reader != nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected reader") + } + + if contents != nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected content") + } + + if resp == nil || resp.Response.StatusCode != http.StatusNotFound { + t.Error("Repositories.DownloadContentsWithMeta did not return expected response") + } +} + +func TestRepositoriesService_DownloadContentsWithMeta_NotFile(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "type": "file", + "name": "f", + "content": "" +}]`) + }) + + ctx := t.Context() + reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d", nil) + if err == nil || !errors.Is(err, ErrContentsDirectory) { + t.Error("Repositories.DownloadContentsWithMeta did not return expected error") + } + + if reader != nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected reader") + } + + if contents != nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected content") + } + + if resp == nil || resp.Response.StatusCode != http.StatusOK { + t.Error("Repositories.DownloadContentsWithMeta did not return expected response") + } +} + +func TestRepositoriesService_DownloadContentsWithMeta_Submodule(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "type": "submodule", + "name": "f", + "content": "", + "download_url": "", + "submodule_git_url": "http://example.com/submodule.git" +}`) + }) + + ctx := t.Context() + reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d", nil) + if err == nil || !errors.Is(err, ErrContentsSubmodule) { + t.Error("Repositories.DownloadContentsWithMeta did not return expected error") + } + + if reader != nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected reader") + } + + if contents == nil || contents.GetType() != "submodule" { + t.Error("Repositories.DownloadContentsWithMeta did not return expected content") + } + + if resp == nil || resp.Response.StatusCode != http.StatusOK { + t.Error("Repositories.DownloadContentsWithMeta did not return expected response") + } +} + +func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "type": "file", + "name": "f", + "content": "" +}`) + }) + + ctx := t.Context() + reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil) + if err == nil || !errors.Is(err, ErrContentsNoDownloadURL) { + t.Error("Repositories.DownloadContentsWithMeta did not return expected error") + } + + if reader != nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected reader") + } + + if contents == nil { + t.Error("Repositories.DownloadContentsWithMeta did not return expected content") + } + + if resp == nil || resp.Response.StatusCode != http.StatusOK { + t.Error("Repositories.DownloadContentsWithMeta did not return expected response") } } func TestRepositoriesService_GetContents_File(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{ @@ -177,58 +673,95 @@ func TestRepositoriesService_GetContents_File(t *testing.T) { "path": "LICENSE" }`) }) - fileContents, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{}) + ctx := t.Context() + fileContents, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{}) if err != nil { t.Errorf("Repositories.GetContents returned error: %v", err) } - want := &RepositoryContent{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Encoding: String("base64"), Path: String("LICENSE")} - if !reflect.DeepEqual(fileContents, want) { + want := &RepositoryContent{Type: Ptr("file"), Name: Ptr("LICENSE"), Size: Ptr(20678), Encoding: Ptr("base64"), Path: Ptr("LICENSE")} + if !cmp.Equal(fileContents, want) { t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want) } + + const methodName = "GetContents" + testBadOptions(t, methodName, func() (err error) { + _, _, _, err = client.Repositories.GetContents(ctx, "\n", "\n", "\n", &RepositoryContentGetOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, _, resp, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{}`) }) - _, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{}) + ctx := t.Context() + _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "p#?%/中.go", &RepositoryContentGetOptions{}) if err != nil { t.Fatalf("Repositories.GetContents returned error: %v", err) } } func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/repos/o/r/contents/some directory/file.go", func(w http.ResponseWriter, r *http.Request) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/some%20directory/file.go", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{}`) }) - _, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory/file.go", &RepositoryContentGetOptions{}) + ctx := t.Context() + _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some directory/file.go", &RepositoryContentGetOptions{}) if err != nil { t.Fatalf("Repositories.GetContents returned error: %v", err) } } +func TestRepositoriesService_GetContents_PathWithParent(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/some/../directory/file.go", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{}`) + }) + ctx := t.Context() + _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some/../directory/file.go", &RepositoryContentGetOptions{}) + if err == nil { + t.Fatal("Repositories.GetContents expected error but got none") + } +} + func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/repos/o/r/contents/some directory+name/file.go", func(w http.ResponseWriter, r *http.Request) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/some%20directory%2Bname/file.go", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{}`) }) - _, _, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{}) + ctx := t.Context() + _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{}) if err != nil { t.Fatalf("Repositories.GetContents returned error: %v", err) } } func TestRepositoriesService_GetContents_Directory(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{ @@ -243,20 +776,24 @@ func TestRepositoriesService_GetContents_Directory(t *testing.T) { "path": "LICENSE" }]`) }) - _, directoryContents, _, err := client.Repositories.GetContents(context.Background(), "o", "r", "p", &RepositoryContentGetOptions{}) + ctx := t.Context() + _, directoryContents, _, err := client.Repositories.GetContents(ctx, "o", "r", "p", &RepositoryContentGetOptions{}) if err != nil { t.Errorf("Repositories.GetContents returned error: %v", err) } - want := []*RepositoryContent{{Type: String("dir"), Name: String("lib"), Path: String("lib")}, - {Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Path: String("LICENSE")}} - if !reflect.DeepEqual(directoryContents, want) { + want := []*RepositoryContent{ + {Type: Ptr("dir"), Name: Ptr("lib"), Path: Ptr("lib")}, + {Type: Ptr("file"), Name: Ptr("LICENSE"), Size: Ptr(20678), Path: Ptr("LICENSE")}, + } + if !cmp.Equal(directoryContents, want) { t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want) } } func TestRepositoriesService_CreateFile(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") fmt.Fprint(w, `{ @@ -269,32 +806,46 @@ func TestRepositoriesService_CreateFile(t *testing.T) { } }`) }) - message := "m" - content := []byte("c") repositoryContentsOptions := &RepositoryContentFileOptions{ - Message: &message, - Content: content, - Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, + Message: Ptr("m"), + Content: []byte("c"), + Committer: &CommitAuthor{Name: Ptr("n"), Email: Ptr("e")}, } - createResponse, _, err := client.Repositories.CreateFile(context.Background(), "o", "r", "p", repositoryContentsOptions) + ctx := t.Context() + createResponse, _, err := client.Repositories.CreateFile(ctx, "o", "r", "p", repositoryContentsOptions) if err != nil { t.Errorf("Repositories.CreateFile returned error: %v", err) } want := &RepositoryContentResponse{ - Content: &RepositoryContent{Name: String("p")}, + Content: &RepositoryContent{Name: Ptr("p")}, Commit: Commit{ - Message: String("m"), - SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), + Message: Ptr("m"), + SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), }, } - if !reflect.DeepEqual(createResponse, want) { + if !cmp.Equal(createResponse, want) { t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want) } + + const methodName = "CreateFile" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateFile(ctx, "\n", "\n", "\n", repositoryContentsOptions) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateFile(ctx, "o", "r", "p", repositoryContentsOptions) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_UpdateFile(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") fmt.Fprint(w, `{ @@ -307,34 +858,47 @@ func TestRepositoriesService_UpdateFile(t *testing.T) { } }`) }) - message := "m" - content := []byte("c") - sha := "f5f369044773ff9c6383c087466d12adb6fa0828" repositoryContentsOptions := &RepositoryContentFileOptions{ - Message: &message, - Content: content, - SHA: &sha, - Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, + Message: Ptr("m"), + Content: []byte("c"), + SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), + Committer: &CommitAuthor{Name: Ptr("n"), Email: Ptr("e")}, } - updateResponse, _, err := client.Repositories.UpdateFile(context.Background(), "o", "r", "p", repositoryContentsOptions) + ctx := t.Context() + updateResponse, _, err := client.Repositories.UpdateFile(ctx, "o", "r", "p", repositoryContentsOptions) if err != nil { t.Errorf("Repositories.UpdateFile returned error: %v", err) } want := &RepositoryContentResponse{ - Content: &RepositoryContent{Name: String("p")}, + Content: &RepositoryContent{Name: Ptr("p")}, Commit: Commit{ - Message: String("m"), - SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), + Message: Ptr("m"), + SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), }, } - if !reflect.DeepEqual(updateResponse, want) { + if !cmp.Equal(updateResponse, want) { t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want) } + + const methodName = "UpdateFile" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateFile(ctx, "\n", "\n", "\n", repositoryContentsOptions) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateFile(ctx, "o", "r", "p", repositoryContentsOptions) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_DeleteFile(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") fmt.Fprint(w, `{ @@ -345,45 +909,196 @@ func TestRepositoriesService_DeleteFile(t *testing.T) { } }`) }) - message := "m" - sha := "f5f369044773ff9c6383c087466d12adb6fa0828" repositoryContentsOptions := &RepositoryContentFileOptions{ - Message: &message, - SHA: &sha, - Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, + Message: Ptr("m"), + SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), + Committer: &CommitAuthor{Name: Ptr("n"), Email: Ptr("e")}, } - deleteResponse, _, err := client.Repositories.DeleteFile(context.Background(), "o", "r", "p", repositoryContentsOptions) + ctx := t.Context() + deleteResponse, _, err := client.Repositories.DeleteFile(ctx, "o", "r", "p", repositoryContentsOptions) if err != nil { t.Errorf("Repositories.DeleteFile returned error: %v", err) } want := &RepositoryContentResponse{ Content: nil, Commit: Commit{ - Message: String("m"), - SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), + Message: Ptr("m"), + SHA: Ptr("f5f369044773ff9c6383c087466d12adb6fa0828"), }, } - if !reflect.DeepEqual(deleteResponse, want) { + if !cmp.Equal(deleteResponse, want) { t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want) } + + const methodName = "DeleteFile" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.DeleteFile(ctx, "\n", "\n", "\n", repositoryContentsOptions) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.DeleteFile(ctx, "o", "r", "p", repositoryContentsOptions) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetArchiveLink(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/tarball/yo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + ctx := t.Context() + url, resp, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{Ref: "yo"}, 1) + if err != nil { + t.Errorf("Repositories.GetArchiveLink returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Repositories.GetArchiveLink returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url, want) + } + + const methodName = "GetArchiveLink" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetArchiveLink(ctx, "\n", "\n", Tarball, &RepositoryContentGetOptions{}, 1) + return err + }) + + // Add custom round tripper + client.client.Transport = roundTripperFunc(func(*http.Request) (*http.Response, error) { + return nil, errors.New("failed to get archive link") + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 1) + return err + }) + }) + } +} + +func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusMovedPermanently) + }) + ctx := t.Context() + _, resp, _ := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 0) + if resp.StatusCode != http.StatusMovedPermanently { + t.Errorf("Repositories.GetArchiveLink returned status: %v, want %v", resp.StatusCode, http.StatusMovedPermanently) + } + }) + } +} + +func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_followRedirects(t *testing.T) { + t.Parallel() + tcs := []struct { + name string + respectRateLimits bool + }{ + { + name: "withoutRateLimits", + respectRateLimits: false, + }, + { + name: "withRateLimits", + respectRateLimits: true, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) + client.rateLimitRedirectionalEndpoints = tc.respectRateLimits + + // Mock a redirect link, which leads to an archive link + mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) + }) + mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Redirect(w, r, "https://github.com/a", http.StatusFound) + }) + ctx := t.Context() + url, resp, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, 1) + if err != nil { + t.Errorf("Repositories.GetArchiveLink returned error: %v", err) + } + if resp.StatusCode != http.StatusFound { + t.Errorf("Repositories.GetArchiveLink returned status: %v, want %v", resp.StatusCode, http.StatusFound) + } + want := "https://github.com/a" + if url.String() != want { + t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url, want) + } + }) + } +} + +func TestRepositoriesService_GetContents_NoTrailingSlashInDirectoryApiPath(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/contents/.github", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - http.Redirect(w, r, "http://github.com/a", http.StatusFound) + testFormValues(t, r, values{"ref": "mybranch"}) + fmt.Fprint(w, `{}`) + }) + ctx := t.Context() + _, _, _, err := client.Repositories.GetContents(ctx, "o", "r", ".github/", &RepositoryContentGetOptions{ + Ref: "mybranch", }) - url, resp, err := client.Repositories.GetArchiveLink(context.Background(), "o", "r", Tarball, &RepositoryContentGetOptions{}) if err != nil { - t.Errorf("Repositories.GetArchiveLink returned error: %v", err) - } - if resp.StatusCode != http.StatusFound { - t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound) - } - want := "http://github.com/a" - if url.String() != want { - t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want) + t.Fatalf("Repositories.GetContents returned error: %v", err) } } diff --git a/github/repos_deployment_branch_policies.go b/github/repos_deployment_branch_policies.go new file mode 100644 index 00000000000..20d176c5926 --- /dev/null +++ b/github/repos_deployment_branch_policies.go @@ -0,0 +1,144 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// DeploymentBranchPolicy represents a single deployment branch policy for an environment. +type DeploymentBranchPolicy struct { + Name *string `json:"name,omitempty"` + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Type *string `json:"type,omitempty"` +} + +// DeploymentBranchPolicyResponse represents the slightly different format of response that comes back when you list deployment branch policies. +type DeploymentBranchPolicyResponse struct { + TotalCount *int `json:"total_count,omitempty"` + BranchPolicies []*DeploymentBranchPolicy `json:"branch_policies,omitempty"` +} + +// CreateDeploymentBranchPolicyRequest represents a request to create a deployment branch policy. +type CreateDeploymentBranchPolicyRequest struct { + Name string `json:"name"` + Type *string `json:"type,omitempty"` +} + +// UpdateDeploymentBranchPolicyRequest represents a request to update a deployment branch policy. +type UpdateDeploymentBranchPolicyRequest struct { + Name string `json:"name"` +} + +// ListDeploymentBranchPolicies lists the deployment branch policies for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/branch-policies?apiVersion=2022-11-28#list-deployment-branch-policies +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies +func (s *RepositoriesService) ListDeploymentBranchPolicies(ctx context.Context, owner, repo, environment string, opts *ListOptions) (*DeploymentBranchPolicyResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies", owner, repo, environment) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var list *DeploymentBranchPolicyResponse + resp, err := s.client.Do(req, &list) + if err != nil { + return nil, resp, err + } + + return list, resp, nil +} + +// GetDeploymentBranchPolicy gets a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/branch-policies?apiVersion=2022-11-28#get-a-deployment-branch-policy +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id} +func (s *RepositoriesService) GetDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, branchPolicyID int64) (*DeploymentBranchPolicy, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies/%v", owner, repo, environment, branchPolicyID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var policy *DeploymentBranchPolicy + resp, err := s.client.Do(req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// CreateDeploymentBranchPolicy creates a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/branch-policies?apiVersion=2022-11-28#create-a-deployment-branch-policy +// +//meta:operation POST /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies +func (s *RepositoriesService) CreateDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, body CreateDeploymentBranchPolicyRequest) (*DeploymentBranchPolicy, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies", owner, repo, environment) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var policy *DeploymentBranchPolicy + resp, err := s.client.Do(req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// UpdateDeploymentBranchPolicy updates a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/branch-policies?apiVersion=2022-11-28#update-a-deployment-branch-policy +// +//meta:operation PUT /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id} +func (s *RepositoriesService) UpdateDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, branchPolicyID int64, body UpdateDeploymentBranchPolicyRequest) (*DeploymentBranchPolicy, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies/%v", owner, repo, environment, branchPolicyID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var policy *DeploymentBranchPolicy + resp, err := s.client.Do(req, &policy) + if err != nil { + return nil, resp, err + } + + return policy, resp, nil +} + +// DeleteDeploymentBranchPolicy deletes a deployment branch policy for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/branch-policies?apiVersion=2022-11-28#delete-a-deployment-branch-policy +// +//meta:operation DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id} +func (s *RepositoriesService) DeleteDeploymentBranchPolicy(ctx context.Context, owner, repo, environment string, branchPolicyID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment-branch-policies/%v", owner, repo, environment, branchPolicyID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_deployment_branch_policies_test.go b/github/repos_deployment_branch_policies_test.go new file mode 100644 index 00000000000..2da27aa5b42 --- /dev/null +++ b/github/repos_deployment_branch_policies_test.go @@ -0,0 +1,168 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListDeploymentBranchPolicies(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies", func(w http.ResponseWriter, r *http.Request) { + testFormValues(t, r, values{ + "page": "1", + "per_page": "30", + }) + fmt.Fprint(w, `{"total_count":2, "branch_policies":[{"id":1}, {"id": 2}]}`) + }) + + opts := &ListOptions{Page: 1, PerPage: 30} + ctx := t.Context() + got, _, err := client.Repositories.ListDeploymentBranchPolicies(ctx, "o", "r", "e", opts) + if err != nil { + t.Errorf("Repositories.ListDeploymentBranchPolicies returned error: %v", err) + } + + want := &DeploymentBranchPolicyResponse{ + BranchPolicies: []*DeploymentBranchPolicy{ + {ID: Ptr(int64(1))}, + {ID: Ptr(int64(2))}, + }, + TotalCount: Ptr(2), + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ListDeploymentBranchPolicies = %+v, want %+v", got, want) + } + + const methodName = "ListDeploymentBranchPolicies" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListDeploymentBranchPolicies(ctx, "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListDeploymentBranchPolicies(ctx, "o", "r", "e", opts) + if got != nil { + t.Errorf("got non-nil Repositories.ListDeploymentBranchPolicies response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetDeploymentBranchPolicy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies/1", func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.GetDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + if err != nil { + t.Errorf("Repositories.GetDeploymentBranchPolicy returned error: %v", err) + } + + want := &DeploymentBranchPolicy{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.GetDeploymentBranchPolicy = %+v, want %+v", got, want) + } + + const methodName = "GetDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + if got != nil { + t.Errorf("got non-nil Repositories.GetDeploymentBranchPolicy response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateDeploymentBranchPolicy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1, "type":"branch"}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, "o", "r", "e", CreateDeploymentBranchPolicyRequest{Name: "n", Type: Ptr("branch")}) + if err != nil { + t.Errorf("Repositories.CreateDeploymentBranchPolicy returned error: %v", err) + } + + want := &DeploymentBranchPolicy{ID: Ptr(int64(1)), Type: Ptr("branch")} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.CreateDeploymentBranchPolicy = %+v, want %+v", got, want) + } + + const methodName = "CreateDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateDeploymentBranchPolicy(ctx, "o", "r", "e", CreateDeploymentBranchPolicyRequest{Name: "n"}) + if got != nil { + t.Errorf("got non-nil Repositories.CreateDeploymentBranchPolicy response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateDeploymentBranchPolicy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, "o", "r", "e", 1, UpdateDeploymentBranchPolicyRequest{Name: "n"}) + if err != nil { + t.Errorf("Repositories.UpdateDeploymentBranchPolicy returned error: %v", err) + } + + want := &DeploymentBranchPolicy{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.UpdateDeploymentBranchPolicy = %+v, want %+v", got, want) + } + + const methodName = "UpdateDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateDeploymentBranchPolicy(ctx, "o", "r", "e", 1, UpdateDeploymentBranchPolicyRequest{Name: "n"}) + if got != nil { + t.Errorf("got non-nil Repositories.UpdateDeploymentBranchPolicy response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteDeploymentBranchPolicy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment-branch-policies/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Repositories.DeleteDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + if err != nil { + t.Errorf("Repositories.DeleteDeploymentBranchPolicy returned error: %v", err) + } + + const methodName = "DeleteDeploymentBranchPolicy" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteDeploymentBranchPolicy(ctx, "o", "r", "e", 1) + }) +} diff --git a/github/repos_deployment_protection_rules.go b/github/repos_deployment_protection_rules.go new file mode 100644 index 00000000000..10b0f86e579 --- /dev/null +++ b/github/repos_deployment_protection_rules.go @@ -0,0 +1,152 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// CustomDeploymentProtectionRuleApp represents a single deployment protection rule app for an environment. +type CustomDeploymentProtectionRuleApp struct { + ID *int64 `json:"id,omitempty"` + Slug *string `json:"slug,omitempty"` + IntegrationURL *string `json:"integration_url,omitempty"` + NodeID *string `json:"node_id,omitempty"` +} + +// CustomDeploymentProtectionRule represents a single deployment protection rule for an environment. +type CustomDeploymentProtectionRule struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + App *CustomDeploymentProtectionRuleApp `json:"app,omitempty"` +} + +// ListDeploymentProtectionRuleResponse represents the response that comes back when you list deployment protection rules. +type ListDeploymentProtectionRuleResponse struct { + TotalCount *int `json:"total_count,omitempty"` + ProtectionRules []*CustomDeploymentProtectionRule `json:"custom_deployment_protection_rules,omitempty"` +} + +// ListCustomDeploymentRuleIntegrationsResponse represents the slightly different response that comes back when you list custom deployment rule integrations. +type ListCustomDeploymentRuleIntegrationsResponse struct { + TotalCount *int `json:"total_count,omitempty"` + AvailableIntegrations []*CustomDeploymentProtectionRuleApp `json:"available_custom_deployment_protection_rule_integrations,omitempty"` +} + +// CustomDeploymentProtectionRuleRequest represents a deployment protection rule request. +type CustomDeploymentProtectionRuleRequest struct { + IntegrationID *int64 `json:"integration_id,omitempty"` +} + +// GetAllDeploymentProtectionRules gets all the deployment protection rules for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules?apiVersion=2022-11-28#get-all-deployment-protection-rules-for-an-environment +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules +func (s *RepositoriesService) GetAllDeploymentProtectionRules(ctx context.Context, owner, repo, environment string) (*ListDeploymentProtectionRuleResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules", owner, repo, environment) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var list *ListDeploymentProtectionRuleResponse + resp, err := s.client.Do(req, &list) + if err != nil { + return nil, resp, err + } + + return list, resp, nil +} + +// CreateCustomDeploymentProtectionRule creates a custom deployment protection rule on an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules?apiVersion=2022-11-28#create-a-custom-deployment-protection-rule-on-an-environment +// +//meta:operation POST /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules +func (s *RepositoriesService) CreateCustomDeploymentProtectionRule(ctx context.Context, owner, repo, environment string, body *CustomDeploymentProtectionRuleRequest) (*CustomDeploymentProtectionRule, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules", owner, repo, environment) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var protectionRule *CustomDeploymentProtectionRule + resp, err := s.client.Do(req, &protectionRule) + if err != nil { + return nil, resp, err + } + + return protectionRule, resp, nil +} + +// ListCustomDeploymentRuleIntegrations lists the custom deployment rule integrations for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules?apiVersion=2022-11-28#list-custom-deployment-rule-integrations-available-for-an-environment +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps +func (s *RepositoriesService) ListCustomDeploymentRuleIntegrations(ctx context.Context, owner, repo, environment string, opts *ListOptions) (*ListCustomDeploymentRuleIntegrationsResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules/apps", owner, repo, environment) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var list *ListCustomDeploymentRuleIntegrationsResponse + resp, err := s.client.Do(req, &list) + if err != nil { + return nil, resp, err + } + + return list, resp, nil +} + +// GetCustomDeploymentProtectionRule gets a custom deployment protection rule for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules?apiVersion=2022-11-28#get-a-custom-deployment-protection-rule +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id} +func (s *RepositoriesService) GetCustomDeploymentProtectionRule(ctx context.Context, owner, repo, environment string, protectionRuleID int64) (*CustomDeploymentProtectionRule, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules/%v", owner, repo, environment, protectionRuleID) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var protectionRule *CustomDeploymentProtectionRule + resp, err := s.client.Do(req, &protectionRule) + if err != nil { + return nil, resp, err + } + + return protectionRule, resp, nil +} + +// DisableCustomDeploymentProtectionRule disables a custom deployment protection rule for an environment. +// +// GitHub API docs: https://docs.github.com/rest/deployments/protection-rules?apiVersion=2022-11-28#disable-a-custom-protection-rule-for-an-environment +// +//meta:operation DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id} +func (s *RepositoriesService) DisableCustomDeploymentProtectionRule(ctx context.Context, owner, repo, environment string, protectionRuleID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v/deployment_protection_rules/%v", owner, repo, environment, protectionRuleID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_deployment_protection_rules_test.go b/github/repos_deployment_protection_rules_test.go new file mode 100644 index 00000000000..d3179bc69df --- /dev/null +++ b/github/repos_deployment_protection_rules_test.go @@ -0,0 +1,212 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetAllDeploymentProtectionRules(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":2, "custom_deployment_protection_rules":[{ "id": 3, "node_id": "IEH37kRlcGxveW1lbnRTdGF0ddiv", "enabled": true, "app": { "id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}}, { "id": 4, "node_id": "MDE2OkRlcGxveW1lbnRTdHJ41128", "enabled": true, "app": { "id": 1, "node_id": "UHVE67RlcGxveW1lbnRTdTY!jfeuy", "slug": "another-custom-app", "integration_url": "https://api.github.com/apps/another-custom-app"}}]}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.GetAllDeploymentProtectionRules(ctx, "o", "r", "e") + if err != nil { + t.Errorf("Repositories.GetAllDeploymentProtectionRules returned error: %v", err) + } + + want := &ListDeploymentProtectionRuleResponse{ + ProtectionRules: []*CustomDeploymentProtectionRule{ + {ID: Ptr(int64(3)), NodeID: Ptr("IEH37kRlcGxveW1lbnRTdGF0ddiv"), Enabled: Ptr(true), App: &CustomDeploymentProtectionRuleApp{ID: Ptr(int64(1)), NodeID: Ptr("GHT58kRlcGxveW1lbnRTdTY!bbcy"), Slug: Ptr("a-custom-app"), IntegrationURL: Ptr("https://api.github.com/apps/a-custom-app")}}, + {ID: Ptr(int64(4)), NodeID: Ptr("MDE2OkRlcGxveW1lbnRTdHJ41128"), Enabled: Ptr(true), App: &CustomDeploymentProtectionRuleApp{ID: Ptr(int64(1)), NodeID: Ptr("UHVE67RlcGxveW1lbnRTdTY!jfeuy"), Slug: Ptr("another-custom-app"), IntegrationURL: Ptr("https://api.github.com/apps/another-custom-app")}}, + }, + TotalCount: Ptr(2), + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.GetAllDeploymentProtectionRules = %+v, want %+v", got, want) + } + + const methodName = "GetAllDeploymentProtectionRules" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAllDeploymentProtectionRules(ctx, "o", "r", "e") + if got != nil { + t.Errorf("got non-nil Repositories.GetAllDeploymentProtectionRules response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateCustomDeploymentProtectionRule(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CustomDeploymentProtectionRuleRequest{ + IntegrationID: Ptr(int64(5)), + } + + mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":3, "node_id": "IEH37kRlcGxveW1lbnRTdGF0ddiv", "enabled": true, "app": {"id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.CreateCustomDeploymentProtectionRule(ctx, "o", "r", "e", input) + if err != nil { + t.Errorf("Repositories.CreateCustomDeploymentProtectionRule returned error: %v", err) + } + + want := &CustomDeploymentProtectionRule{ + ID: Ptr(int64(3)), + NodeID: Ptr("IEH37kRlcGxveW1lbnRTdGF0ddiv"), + Enabled: Ptr(true), + App: &CustomDeploymentProtectionRuleApp{ + ID: Ptr(int64(1)), + NodeID: Ptr("GHT58kRlcGxveW1lbnRTdTY!bbcy"), + Slug: Ptr("a-custom-app"), + IntegrationURL: Ptr("https://api.github.com/apps/a-custom-app"), + }, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.CreateCustomDeploymentProtectionRule = %+v, want %+v", got, want) + } + + const methodName = "CreateCustomDeploymentProtectionRule" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateCustomDeploymentProtectionRule(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateCustomDeploymentProtectionRule(ctx, "o", "r", "e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListCustomDeploymentRuleIntegrations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules/apps", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "1", + "per_page": "30", + }) + fmt.Fprint(w, `{"total_count": 2, "available_custom_deployment_protection_rule_integrations": [{"id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}, {"id": 2, "node_id": "UHVE67RlcGxveW1lbnRTdTY!jfeuy", "slug": "another-custom-app", "integration_url": "https://api.github.com/apps/another-custom-app"}]}`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 30} + got, _, err := client.Repositories.ListCustomDeploymentRuleIntegrations(ctx, "o", "r", "e", opts) + if err != nil { + t.Errorf("Repositories.ListCustomDeploymentRuleIntegrations returned error: %v", err) + } + + want := &ListCustomDeploymentRuleIntegrationsResponse{ + TotalCount: Ptr(2), + AvailableIntegrations: []*CustomDeploymentProtectionRuleApp{ + {ID: Ptr(int64(1)), NodeID: Ptr("GHT58kRlcGxveW1lbnRTdTY!bbcy"), Slug: Ptr("a-custom-app"), IntegrationURL: Ptr("https://api.github.com/apps/a-custom-app")}, + {ID: Ptr(int64(2)), NodeID: Ptr("UHVE67RlcGxveW1lbnRTdTY!jfeuy"), Slug: Ptr("another-custom-app"), IntegrationURL: Ptr("https://api.github.com/apps/another-custom-app")}, + }, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ListCustomDeploymentRuleIntegrations = %+v, want %+v", got, want) + } + + const methodName = "ListCustomDeploymentRuleIntegrations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCustomDeploymentRuleIntegrations(ctx, "\n", "\n", "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCustomDeploymentRuleIntegrations(ctx, "o", "r", "e", opts) + if got != nil { + t.Errorf("got non-nil Repositories.ListCustomDeploymentRuleIntegrations response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetCustomDeploymentProtectionRule(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1, "node_id": "IEH37kRlcGxveW1lbnRTdGF0ddiv", "enabled": true, "app": {"id": 1, "node_id": "GHT58kRlcGxveW1lbnRTdTY!bbcy", "slug": "a-custom-app", "integration_url": "https://api.github.com/apps/a-custom-app"}}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.GetCustomDeploymentProtectionRule(ctx, "o", "r", "e", 1) + if err != nil { + t.Errorf("Repositories.GetCustomDeploymentProtectionRule returned error: %v", err) + } + + want := &CustomDeploymentProtectionRule{ + ID: Ptr(int64(1)), + NodeID: Ptr("IEH37kRlcGxveW1lbnRTdGF0ddiv"), + Enabled: Ptr(true), + App: &CustomDeploymentProtectionRuleApp{ + ID: Ptr(int64(1)), + NodeID: Ptr("GHT58kRlcGxveW1lbnRTdTY!bbcy"), + Slug: Ptr("a-custom-app"), + IntegrationURL: Ptr("https://api.github.com/apps/a-custom-app"), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Repositories.GetCustomDeploymentProtectionRule = %+v, want %+v", got, want) + } + + const methodName = "GetCustomDeploymentProtectionRule" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCustomDeploymentProtectionRule(ctx, "o", "r", "e", 1) + if got != nil { + t.Errorf("got non-nil Repositories.GetCustomDeploymentProtectionRule response: %+v", got) + } + return resp, err + }) +} + +func TestRepositoriesService_DisableCustomDeploymentProtectionRule(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e/deployment_protection_rules/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Repositories.DisableCustomDeploymentProtectionRule(ctx, "o", "r", "e", 1) + if err != nil { + t.Errorf("Repositories.DisableCustomDeploymentProtectionRule returned error: %v", err) + } + + if !cmp.Equal(resp.StatusCode, 204) { + t.Errorf("Repositories.DisableCustomDeploymentProtectionRule returned status code %+v, want %+v", resp.StatusCode, "204") + } + + const methodName = "DisableCustomDeploymentProtectionRule" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisableCustomDeploymentProtectionRule(ctx, "\n", "\n", "\n", 1) + return err + }) +} diff --git a/github/repos_deployments.go b/github/repos_deployments.go index 604632e91b6..a0e6c405a31 100644 --- a/github/repos_deployments.go +++ b/github/repos_deployments.go @@ -12,7 +12,7 @@ import ( "strings" ) -// Deployment represents a deployment in a repo +// Deployment represents a deployment in a repo. type Deployment struct { URL *string `json:"url,omitempty"` ID *int64 `json:"id,omitempty"` @@ -30,17 +30,17 @@ type Deployment struct { NodeID *string `json:"node_id,omitempty"` } -// DeploymentRequest represents a deployment request +// DeploymentRequest represents a deployment request. type DeploymentRequest struct { - Ref *string `json:"ref,omitempty"` - Task *string `json:"task,omitempty"` - AutoMerge *bool `json:"auto_merge,omitempty"` - RequiredContexts *[]string `json:"required_contexts,omitempty"` - Payload *string `json:"payload,omitempty"` - Environment *string `json:"environment,omitempty"` - Description *string `json:"description,omitempty"` - TransientEnvironment *bool `json:"transient_environment,omitempty"` - ProductionEnvironment *bool `json:"production_environment,omitempty"` + Ref string `json:"ref"` + Task *string `json:"task,omitempty"` + AutoMerge *bool `json:"auto_merge,omitempty"` + RequiredContexts []string `json:"required_contexts,omitzero"` + Payload any `json:"payload,omitempty"` + Environment *string `json:"environment,omitempty"` + Description *string `json:"description,omitempty"` + TransientEnvironment *bool `json:"transient_environment,omitempty"` + ProductionEnvironment *bool `json:"production_environment,omitempty"` } // DeploymentsListOptions specifies the optional parameters to the @@ -49,7 +49,7 @@ type DeploymentsListOptions struct { // SHA of the Deployment. SHA string `url:"sha,omitempty"` - // List deployments for a given ref. + // List deployments for a given ref. This can be a branch, tag, or SHA. Ref string `url:"ref,omitempty"` // List deployments for a given task. @@ -63,21 +63,23 @@ type DeploymentsListOptions struct { // ListDeployments lists the deployments of a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployments -func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo string, opt *DeploymentsListOptions) ([]*Deployment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/deployments/deployments?apiVersion=2022-11-28#list-deployments +// +//meta:operation GET /repos/{owner}/{repo}/deployments +func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo string, opts *DeploymentsListOptions) ([]*Deployment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var deployments []*Deployment - resp, err := s.client.Do(ctx, req, &deployments) + resp, err := s.client.Do(req, &deployments) if err != nil { return nil, resp, err } @@ -87,17 +89,19 @@ func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo s // GetDeployment returns a single deployment of a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment +// GitHub API docs: https://docs.github.com/rest/deployments/deployments?apiVersion=2022-11-28#get-a-deployment +// +//meta:operation GET /repos/{owner}/{repo}/deployments/{deployment_id} func (s *RepositoriesService) GetDeployment(ctx context.Context, owner, repo string, deploymentID int64) (*Deployment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/deployments/%v", owner, repo, deploymentID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - deployment := new(Deployment) - resp, err := s.client.Do(ctx, req, deployment) + var deployment *Deployment + resp, err := s.client.Do(req, &deployment) if err != nil { return nil, resp, err } @@ -107,21 +111,22 @@ func (s *RepositoriesService) GetDeployment(ctx context.Context, owner, repo str // CreateDeployment creates a new deployment for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment -func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo string, request *DeploymentRequest) (*Deployment, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/deployments/deployments?apiVersion=2022-11-28#create-a-deployment +// +//meta:operation POST /repos/{owner}/{repo}/deployments +func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo string, body DeploymentRequest) (*Deployment, *Response, error) { u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) - req, err := s.client.NewRequest("POST", u, request) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - d := new(Deployment) - resp, err := s.client.Do(ctx, req, d) + var d *Deployment + resp, err := s.client.Do(req, &d) if err != nil { return nil, resp, err } @@ -129,26 +134,48 @@ func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo return d, resp, nil } +// DeleteDeployment deletes an existing deployment for a repository. +// +// GitHub API docs: https://docs.github.com/rest/deployments/deployments?apiVersion=2022-11-28#delete-a-deployment +// +//meta:operation DELETE /repos/{owner}/{repo}/deployments/{deployment_id} +func (s *RepositoriesService) DeleteDeployment(ctx context.Context, owner, repo string, deploymentID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments/%v", owner, repo, deploymentID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(req, nil) +} + // DeploymentStatus represents the status of a // particular deployment. type DeploymentStatus struct { ID *int64 `json:"id,omitempty"` // State is the deployment state. - // Possible values are: "pending", "success", "failure", "error", "inactive". - State *string `json:"state,omitempty"` - Creator *User `json:"creator,omitempty"` - Description *string `json:"description,omitempty"` - TargetURL *string `json:"target_url,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - DeploymentURL *string `json:"deployment_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` - NodeID *string `json:"node_id,omitempty"` + // Possible values are: "pending", "success", "failure", "error", + // "inactive", "in_progress", "queued". + State *string `json:"state,omitempty"` + Creator *User `json:"creator,omitempty"` + Description *string `json:"description,omitempty"` + Environment *string `json:"environment,omitempty"` + NodeID *string `json:"node_id,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + TargetURL *string `json:"target_url,omitempty"` + DeploymentURL *string `json:"deployment_url,omitempty"` + RepositoryURL *string `json:"repository_url,omitempty"` + EnvironmentURL *string `json:"environment_url,omitempty"` + LogURL *string `json:"log_url,omitempty"` + URL *string `json:"url,omitempty"` } -// DeploymentStatusRequest represents a deployment request +// DeploymentStatusRequest represents a deployment status request. type DeploymentStatusRequest struct { - State *string `json:"state,omitempty"` + State string `json:"state"` + // TargetURL is the target URL to associate with this status. + // It's recommended to use LogURL instead, which replaces TargetURL. + TargetURL *string `json:"target_url,omitempty"` LogURL *string `json:"log_url,omitempty"` Description *string `json:"description,omitempty"` Environment *string `json:"environment,omitempty"` @@ -158,21 +185,26 @@ type DeploymentStatusRequest struct { // ListDeploymentStatuses lists the statuses of a given deployment of a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployment-statuses -func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, repo string, deployment int64, opt *ListOptions) ([]*DeploymentStatus, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/deployments/statuses?apiVersion=2022-11-28#list-deployment-statuses +// +//meta:operation GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses +func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, repo string, deployment int64, opts *ListOptions) ([]*DeploymentStatus, *Response, error) { u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } + acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + var statuses []*DeploymentStatus - resp, err := s.client.Do(ctx, req, &statuses) + resp, err := s.client.Do(req, &statuses) if err != nil { return nil, resp, err } @@ -182,21 +214,22 @@ func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, // GetDeploymentStatus returns a single deployment status of a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment-status +// GitHub API docs: https://docs.github.com/rest/deployments/statuses?apiVersion=2022-11-28#get-a-deployment-status +// +//meta:operation GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id} func (s *RepositoriesService) GetDeploymentStatus(ctx context.Context, owner, repo string, deploymentID, deploymentStatusID int64) (*DeploymentStatus, *Response, error) { u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses/%v", owner, repo, deploymentID, deploymentStatusID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - d := new(DeploymentStatus) - resp, err := s.client.Do(ctx, req, d) + var d *DeploymentStatus + resp, err := s.client.Do(req, &d) if err != nil { return nil, resp, err } @@ -206,21 +239,22 @@ func (s *RepositoriesService) GetDeploymentStatus(ctx context.Context, owner, re // CreateDeploymentStatus creates a new status for a deployment. // -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment-status -func (s *RepositoriesService) CreateDeploymentStatus(ctx context.Context, owner, repo string, deployment int64, request *DeploymentStatusRequest) (*DeploymentStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) +// GitHub API docs: https://docs.github.com/rest/deployments/statuses?apiVersion=2022-11-28#create-a-deployment-status +// +//meta:operation POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses +func (s *RepositoriesService) CreateDeploymentStatus(ctx context.Context, owner, repo string, deploymentID int64, body DeploymentStatusRequest) (*DeploymentStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deploymentID) - req, err := s.client.NewRequest("POST", u, request) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept headers when APIs fully launch. acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - d := new(DeploymentStatus) - resp, err := s.client.Do(ctx, req, d) + var d *DeploymentStatus + resp, err := s.client.Do(req, &d) if err != nil { return nil, resp, err } diff --git a/github/repos_deployments_test.go b/github/repos_deployments_test.go index 4e6ae6dcf54..f1fda8c9258 100644 --- a/github/repos_deployments_test.go +++ b/github/repos_deployments_test.go @@ -6,18 +6,17 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListDeployments(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/deployments", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -26,94 +25,187 @@ func TestRepositoriesService_ListDeployments(t *testing.T) { }) opt := &DeploymentsListOptions{Environment: "test"} - deployments, _, err := client.Repositories.ListDeployments(context.Background(), "o", "r", opt) + ctx := t.Context() + deployments, _, err := client.Repositories.ListDeployments(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListDeployments returned error: %v", err) } - want := []*Deployment{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(deployments, want) { + want := []*Deployment{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(deployments, want) { t.Errorf("Repositories.ListDeployments returned %+v, want %+v", deployments, want) } + + const methodName = "ListDeployments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListDeployments(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListDeployments(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetDeployment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/deployments/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":3}`) }) - deployment, _, err := client.Repositories.GetDeployment(context.Background(), "o", "r", 3) + ctx := t.Context() + deployment, _, err := client.Repositories.GetDeployment(ctx, "o", "r", 3) if err != nil { t.Errorf("Repositories.GetDeployment returned error: %v", err) } - want := &Deployment{ID: Int64(3)} + want := &Deployment{ID: Ptr(int64(3))} - if !reflect.DeepEqual(deployment, want) { + if !cmp.Equal(deployment, want) { t.Errorf("Repositories.GetDeployment returned %+v, want %+v", deployment, want) } + + const methodName = "GetDeployment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetDeployment(ctx, "\n", "\n", 3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetDeployment(ctx, "o", "r", 3) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_CreateDeployment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &DeploymentRequest{Ref: String("1111"), Task: String("deploy"), TransientEnvironment: Bool(true)} + input := DeploymentRequest{Ref: "1111", Task: Ptr("deploy"), TransientEnvironment: Ptr(true)} mux.HandleFunc("/repos/o/r/deployments", func(w http.ResponseWriter, r *http.Request) { - v := new(DeploymentRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") wantAcceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"ref": "1111", "task": "deploy"}`) }) - deployment, _, err := client.Repositories.CreateDeployment(context.Background(), "o", "r", input) + ctx := t.Context() + deployment, _, err := client.Repositories.CreateDeployment(ctx, "o", "r", input) if err != nil { t.Errorf("Repositories.CreateDeployment returned error: %v", err) } - want := &Deployment{Ref: String("1111"), Task: String("deploy")} - if !reflect.DeepEqual(deployment, want) { + want := &Deployment{Ref: Ptr("1111"), Task: Ptr("deploy")} + if !cmp.Equal(deployment, want) { t.Errorf("Repositories.CreateDeployment returned %+v, want %+v", deployment, want) } + + const methodName = "CreateDeployment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateDeployment(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateDeployment(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteDeployment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/deployments/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + resp, err := client.Repositories.DeleteDeployment(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteDeployment returned error: %v", err) + } + if resp.StatusCode != http.StatusNoContent { + t.Error("Repositories.DeleteDeployment should return a 204 status") + } + + resp, err = client.Repositories.DeleteDeployment(ctx, "o", "r", 2) + if err == nil { + t.Error("Repositories.DeleteDeployment should return an error") + } + if resp.StatusCode != http.StatusNotFound { + t.Error("Repositories.DeleteDeployment should return a 404 status") + } + + const methodName = "DeleteDeployment" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteDeployment(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteDeployment(ctx, "o", "r", 1) + }) } func TestRepositoriesService_ListDeploymentStatuses(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + wantAcceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} mux.HandleFunc("/repos/o/r/deployments/1/statuses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"id":1}, {"id":2}]`) }) opt := &ListOptions{Page: 2} - statutses, _, err := client.Repositories.ListDeploymentStatuses(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + statuses, _, err := client.Repositories.ListDeploymentStatuses(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Repositories.ListDeploymentStatuses returned error: %v", err) } - want := []*DeploymentStatus{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(statutses, want) { - t.Errorf("Repositories.ListDeploymentStatuses returned %+v, want %+v", statutses, want) + want := []*DeploymentStatus{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(statuses, want) { + t.Errorf("Repositories.ListDeploymentStatuses returned %+v, want %+v", statuses, want) } + + const methodName = "ListDeploymentStatuses" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListDeploymentStatuses(ctx, "\n", "\n", 1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListDeploymentStatuses(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetDeploymentStatus(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) wantAcceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} mux.HandleFunc("/repos/o/r/deployments/3/statuses/4", func(w http.ResponseWriter, r *http.Request) { @@ -122,44 +214,68 @@ func TestRepositoriesService_GetDeploymentStatus(t *testing.T) { fmt.Fprint(w, `{"id":4}`) }) - deploymentStatus, _, err := client.Repositories.GetDeploymentStatus(context.Background(), "o", "r", 3, 4) + ctx := t.Context() + deploymentStatus, _, err := client.Repositories.GetDeploymentStatus(ctx, "o", "r", 3, 4) if err != nil { t.Errorf("Repositories.GetDeploymentStatus returned error: %v", err) } - want := &DeploymentStatus{ID: Int64(4)} - if !reflect.DeepEqual(deploymentStatus, want) { + want := &DeploymentStatus{ID: Ptr(int64(4))} + if !cmp.Equal(deploymentStatus, want) { t.Errorf("Repositories.GetDeploymentStatus returned %+v, want %+v", deploymentStatus, want) } + + const methodName = "GetDeploymentStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetDeploymentStatus(ctx, "\n", "\n", 3, 4) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetDeploymentStatus(ctx, "o", "r", 3, 4) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_CreateDeploymentStatus(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &DeploymentStatusRequest{State: String("inactive"), Description: String("deploy"), AutoInactive: Bool(false)} + input := DeploymentStatusRequest{State: "inactive", Description: Ptr("deploy"), AutoInactive: Ptr(false)} mux.HandleFunc("/repos/o/r/deployments/1/statuses", func(w http.ResponseWriter, r *http.Request) { - v := new(DeploymentStatusRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") wantAcceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"state": "inactive", "description": "deploy"}`) }) - deploymentStatus, _, err := client.Repositories.CreateDeploymentStatus(context.Background(), "o", "r", 1, input) + ctx := t.Context() + deploymentStatus, _, err := client.Repositories.CreateDeploymentStatus(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Repositories.CreateDeploymentStatus returned error: %v", err) } - want := &DeploymentStatus{State: String("inactive"), Description: String("deploy")} - if !reflect.DeepEqual(deploymentStatus, want) { + want := &DeploymentStatus{State: Ptr("inactive"), Description: Ptr("deploy")} + if !cmp.Equal(deploymentStatus, want) { t.Errorf("Repositories.CreateDeploymentStatus returned %+v, want %+v", deploymentStatus, want) } + + const methodName = "CreateDeploymentStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateDeploymentStatus(ctx, "\n", "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateDeploymentStatus(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_environments.go b/github/repos_environments.go new file mode 100644 index 00000000000..5e892e5f59c --- /dev/null +++ b/github/repos_environments.go @@ -0,0 +1,257 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +// Environment represents a single environment in a repository. +type Environment struct { + Owner *string `json:"owner,omitempty"` + Repo *string `json:"repo,omitempty"` + EnvironmentName *string `json:"environment_name,omitempty"` + WaitTimer *int `json:"wait_timer,omitempty"` + Reviewers []*EnvReviewers `json:"reviewers,omitempty"` + DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy,omitempty"` + // Return/response only values + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CanAdminsBypass *bool `json:"can_admins_bypass,omitempty"` + ProtectionRules []*ProtectionRule `json:"protection_rules,omitempty"` +} + +// EnvReviewers represents a single environment reviewer entry. +type EnvReviewers struct { + Type *string `json:"type,omitempty"` + ID *int64 `json:"id,omitempty"` +} + +// BranchPolicy represents the options for whether a branch deployment policy is applied to this environment. +type BranchPolicy struct { + ProtectedBranches *bool `json:"protected_branches,omitempty"` + CustomBranchPolicies *bool `json:"custom_branch_policies,omitempty"` +} + +// EnvResponse represents the slightly different format of response that comes back when you list an environment. +type EnvResponse struct { + TotalCount *int `json:"total_count,omitempty"` + Environments []*Environment `json:"environments,omitempty"` +} + +// ProtectionRule represents a single protection rule applied to the environment. +type ProtectionRule struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + PreventSelfReview *bool `json:"prevent_self_review,omitempty"` + Type *string `json:"type,omitempty"` + WaitTimer *int `json:"wait_timer,omitempty"` + Reviewers []*RequiredReviewer `json:"reviewers,omitempty"` +} + +// RequiredReviewer represents a required reviewer. +type RequiredReviewer struct { + Type *string `json:"type,omitempty"` + Reviewer any `json:"reviewer,omitempty"` +} + +// EnvironmentListOptions specifies the optional parameters to the +// RepositoriesService.ListEnvironments method. +type EnvironmentListOptions struct { + ListOptions +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// This helps us handle the fact that RequiredReviewer can have either a User or Team type reviewer field. +func (r *RequiredReviewer) UnmarshalJSON(data []byte) error { + type aliasReviewer RequiredReviewer + var reviewer aliasReviewer + if err := json.Unmarshal(data, &reviewer); err != nil { + return err + } + + r.Type = reviewer.Type + if reviewer.Type == nil { + r.Reviewer = nil + return errors.New("reviewer.Type is nil, not a string of 'User' or 'Team', unable to unmarshal") + } + + switch *reviewer.Type { + case "User": + reviewer.Reviewer = &User{} + if err := json.Unmarshal(data, &reviewer); err != nil { + return err + } + r.Reviewer = reviewer.Reviewer + case "Team": + reviewer.Reviewer = &Team{} + if err := json.Unmarshal(data, &reviewer); err != nil { + return err + } + r.Reviewer = reviewer.Reviewer + default: + r.Type = nil + r.Reviewer = nil + return fmt.Errorf("reviewer.Type is %T, not a string of 'User' or 'Team', unable to unmarshal", reviewer.Type) + } + + return nil +} + +// ListEnvironments lists all environments for a repository. +// +// GitHub API docs: https://docs.github.com/rest/deployments/environments?apiVersion=2022-11-28#list-environments +// +//meta:operation GET /repos/{owner}/{repo}/environments +func (s *RepositoriesService) ListEnvironments(ctx context.Context, owner, repo string, opts *EnvironmentListOptions) (*EnvResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var list *EnvResponse + resp, err := s.client.Do(req, &list) + if err != nil { + return nil, resp, err + } + return list, resp, nil +} + +// GetEnvironment get a single environment for a repository. +// +// GitHub API docs: https://docs.github.com/rest/deployments/environments?apiVersion=2022-11-28#get-an-environment +// +//meta:operation GET /repos/{owner}/{repo}/environments/{environment_name} +func (s *RepositoriesService) GetEnvironment(ctx context.Context, owner, repo, name string) (*Environment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v", owner, repo, name) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var env *Environment + resp, err := s.client.Do(req, &env) + if err != nil { + return nil, resp, err + } + return env, resp, nil +} + +// MarshalJSON implements the json.Marshaler interface. +// As the only way to clear a WaitTimer is to set it to 0, a missing WaitTimer object should default to 0, not null. +// As the default value for CanAdminsBypass is true, a nil value here marshals to true. +func (c CreateUpdateEnvironment) MarshalJSON() ([]byte, error) { + type alias CreateUpdateEnvironment + if c.WaitTimer == nil { + c.WaitTimer = Ptr(0) + } + if c.CanAdminsBypass == nil { + c.CanAdminsBypass = Ptr(true) + } + return json.Marshal(&struct { + alias + }{ + alias: alias(c), + }) +} + +// CreateUpdateEnvironment represents the fields required for the create/update operation +// following the Create/Update release example. +// See https://github.com/google/go-github/issues/992 for more information. +// Removed omitempty here as the API expects null values for reviewers and deployment_branch_policy to clear them. +type CreateUpdateEnvironment struct { + WaitTimer *int `json:"wait_timer"` + Reviewers []*EnvReviewers `json:"reviewers"` + CanAdminsBypass *bool `json:"can_admins_bypass"` + DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"` + PreventSelfReview *bool `json:"prevent_self_review,omitempty"` +} + +// createUpdateEnvironmentNoEnterprise represents the fields accepted for Pro/Teams private repos. +// Ref: https://docs.github.com/actions/deployment/targeting-different-environments/using-environments-for-deployment +// See https://github.com/google/go-github/issues/2602 for more information. +type createUpdateEnvironmentNoEnterprise struct { + DeploymentBranchPolicy *BranchPolicy `json:"deployment_branch_policy"` +} + +// CreateUpdateEnvironment create or update a new environment for a repository. +// +// GitHub API docs: https://docs.github.com/rest/deployments/environments?apiVersion=2022-11-28#create-or-update-an-environment +// +//meta:operation PUT /repos/{owner}/{repo}/environments/{environment_name} +func (s *RepositoriesService) CreateUpdateEnvironment(ctx context.Context, owner, repo, name string, body *CreateUpdateEnvironment) (*Environment, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v", owner, repo, name) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var e *Environment + resp, err := s.client.Do(req, &e) + if err != nil { + // The API returns 422 when the pricing plan doesn't support all the fields sent. + // This path will be executed for Pro/Teams private repos. + // For public repos, regardless of the pricing plan, all fields supported. + // For Free plan private repos the returned error code is 404. + // We are checking that the user didn't try to send a value for unsupported fields, + // and return an error if they did. + if resp != nil && resp.StatusCode == http.StatusUnprocessableEntity && body != nil && len(body.Reviewers) == 0 && body.GetWaitTimer() == 0 { + return s.createNewEnvNoEnterprise(ctx, u, body) + } + return nil, resp, err + } + + return e, resp, nil +} + +// createNewEnvNoEnterprise is an internal function for cases where the original call returned 422. +// Currently only the `deployment_branch_policy` parameter is supported for Pro/Team private repos. +func (s *RepositoriesService) createNewEnvNoEnterprise(ctx context.Context, u string, environment *CreateUpdateEnvironment) (*Environment, *Response, error) { + req, err := s.client.NewRequest(ctx, "PUT", u, &createUpdateEnvironmentNoEnterprise{ + DeploymentBranchPolicy: environment.DeploymentBranchPolicy, + }) + if err != nil { + return nil, nil, err + } + + var e *Environment + resp, err := s.client.Do(req, &e) + if err != nil { + return nil, resp, err + } + + return e, resp, nil +} + +// DeleteEnvironment delete an environment from a repository. +// +// GitHub API docs: https://docs.github.com/rest/deployments/environments?apiVersion=2022-11-28#delete-an-environment +// +//meta:operation DELETE /repos/{owner}/{repo}/environments/{environment_name} +func (s *RepositoriesService) DeleteEnvironment(ctx context.Context, owner, repo, name string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/environments/%v", owner, repo, name) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_environments_test.go b/github/repos_environments_test.go new file mode 100644 index 00000000000..2dddb226bad --- /dev/null +++ b/github/repos_environments_test.go @@ -0,0 +1,335 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRequiredReviewer_UnmarshalJSON(t *testing.T) { + t.Parallel() + testCases := map[string]struct { + data []byte + wantRule []*RequiredReviewer + wantError bool + }{ + "User Reviewer": { + data: []byte(`[{"type": "User", "reviewer": {"id": 1,"login": "octocat"}}]`), + wantRule: []*RequiredReviewer{{Type: Ptr("User"), Reviewer: &User{ID: Ptr(int64(1)), Login: Ptr("octocat")}}}, + wantError: false, + }, + "Team Reviewer": { + data: []byte(`[{"type": "Team", "reviewer": {"id": 1, "name": "Justice League"}}]`), + wantRule: []*RequiredReviewer{{Type: Ptr("Team"), Reviewer: &Team{ID: Ptr(int64(1)), Name: Ptr("Justice League")}}}, + wantError: false, + }, + "Both Types Reviewer": { + data: []byte(`[{"type": "User", "reviewer": {"id": 1,"login": "octocat"}},{"type": "Team", "reviewer": {"id": 1, "name": "Justice League"}}]`), + wantRule: []*RequiredReviewer{{Type: Ptr("User"), Reviewer: &User{ID: Ptr(int64(1)), Login: Ptr("octocat")}}, {Type: Ptr("Team"), Reviewer: &Team{ID: Ptr(int64(1)), Name: Ptr("Justice League")}}}, + wantError: false, + }, + "Empty JSON Object": { + data: []byte(`[]`), + wantRule: []*RequiredReviewer{}, + wantError: false, + }, + "Bad JSON Object": { + data: []byte(`[badjson: 1]`), + wantRule: []*RequiredReviewer{}, + wantError: true, + }, + "Wrong Type in Reviewer Object": { + data: []byte(`[{"type": 1, "reviewer": {"id": 1}}]`), + wantRule: []*RequiredReviewer{{Type: nil, Reviewer: nil}}, + wantError: true, + }, + "Missing Type in Reviewer Object": { + data: []byte(`[{"reviewer": {"id": 1}}]`), + wantRule: []*RequiredReviewer{{Type: nil, Reviewer: nil}}, + wantError: true, + }, + "Null Type in Reviewer Object": { + data: []byte(`[{"type": null, "reviewer": {"id": 1}}]`), + wantRule: []*RequiredReviewer{{Type: nil, Reviewer: nil}}, + wantError: true, + }, + "Wrong ID Type in User Object": { + data: []byte(`[{"type": "User", "reviewer": {"id": "string"}}]`), + wantRule: []*RequiredReviewer{{Type: Ptr("User"), Reviewer: nil}}, + wantError: true, + }, + "Wrong ID Type in Team Object": { + data: []byte(`[{"type": "Team", "reviewer": {"id": "string"}}]`), + wantRule: []*RequiredReviewer{{Type: Ptr("Team"), Reviewer: nil}}, + wantError: true, + }, + "Wrong Type of Reviewer": { + data: []byte(`[{"type": "Cat", "reviewer": {"id": 1,"login": "octocat"}}]`), + wantRule: []*RequiredReviewer{{Type: nil, Reviewer: nil}}, + wantError: true, + }, + } + + for name, test := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + rule := []*RequiredReviewer{} + err := json.Unmarshal(test.data, &rule) + if err != nil && !test.wantError { + t.Error("RequiredReviewer.UnmarshalJSON returned an error when we expected nil") + } + if err == nil && test.wantError { + t.Error("RequiredReviewer.UnmarshalJSON returned no error when we expected one") + } + if !cmp.Equal(test.wantRule, rule) { + t.Errorf("RequiredReviewer.UnmarshalJSON expected rule %+v, got %+v", test.wantRule, rule) + } + }) + } +} + +func TestCreateUpdateEnvironment_MarshalJSON(t *testing.T) { + t.Parallel() + cu := CreateUpdateEnvironment{} + + want := `{"wait_timer":0,"reviewers":null,"can_admins_bypass":true,"deployment_branch_policy":null}` + testJSONMarshalOnly(t, cu, want) + testJSONMarshalOnly(t, &cu, want) +} + +func TestRepositoriesService_ListEnvironments(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"total_count":1, "environments":[{"id":1}, {"id": 2}]}`) + }) + + opt := &EnvironmentListOptions{ + ListOptions: ListOptions{ + Page: 2, + PerPage: 2, + }, + } + ctx := t.Context() + environments, _, err := client.Repositories.ListEnvironments(ctx, "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListEnvironments returned error: %v", err) + } + want := &EnvResponse{TotalCount: Ptr(1), Environments: []*Environment{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}}} + if !cmp.Equal(environments, want) { + t.Errorf("Repositories.ListEnvironments returned %+v, want %+v", environments, want) + } + + const methodName = "ListEnvironments" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListEnvironments(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListEnvironments(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetEnvironment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id": 1,"name": "staging", "deployment_branch_policy": {"protected_branches": true, "custom_branch_policies": false}, "can_admins_bypass": false}`) + }) + + ctx := t.Context() + release, resp, err := client.Repositories.GetEnvironment(ctx, "o", "r", "e") + if err != nil { + t.Errorf("Repositories.GetEnvironment returned error: %v\n%v", err, resp.Body) + } + + want := &Environment{ID: Ptr(int64(1)), Name: Ptr("staging"), DeploymentBranchPolicy: &BranchPolicy{ProtectedBranches: Ptr(true), CustomBranchPolicies: Ptr(false)}, CanAdminsBypass: Ptr(false)} + if !cmp.Equal(release, want) { + t.Errorf("Repositories.GetEnvironment returned %+v, want %+v", release, want) + } + + const methodName = "GetEnvironment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetEnvironment(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetEnvironment(ctx, "o", "r", "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateEnvironment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateUpdateEnvironment{ + WaitTimer: Ptr(30), + } + + mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + want := &CreateUpdateEnvironment{WaitTimer: Ptr(30), CanAdminsBypass: Ptr(true)} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": [{"id": 1, "type": "wait_timer", "wait_timer": 30}]}`) + }) + + ctx := t.Context() + release, _, err := client.Repositories.CreateUpdateEnvironment(ctx, "o", "r", "e", input) + if err != nil { + t.Errorf("Repositories.CreateUpdateEnvironment returned error: %v", err) + } + + want := &Environment{ID: Ptr(int64(1)), Name: Ptr("staging"), ProtectionRules: []*ProtectionRule{{ID: Ptr(int64(1)), Type: Ptr("wait_timer"), WaitTimer: Ptr(30)}}} + if !cmp.Equal(release, want) { + t.Errorf("Repositories.CreateUpdateEnvironment returned %+v, want %+v", release, want) + } + + const methodName = "CreateUpdateEnvironment" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateUpdateEnvironment(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateUpdateEnvironment(ctx, "o", "r", "e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateEnvironment_noEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateUpdateEnvironment{} + callCount := 0 + + mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + if callCount == 0 { + w.WriteHeader(http.StatusUnprocessableEntity) + callCount++ + } else { + want := &CreateUpdateEnvironment{} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": []}`) + } + }) + + ctx := t.Context() + release, _, err := client.Repositories.CreateUpdateEnvironment(ctx, "o", "r", "e", input) + if err != nil { + t.Errorf("Repositories.CreateUpdateEnvironment returned error: %v", err) + } + + want := &Environment{ID: Ptr(int64(1)), Name: Ptr("staging"), ProtectionRules: []*ProtectionRule{}} + if !cmp.Equal(release, want) { + t.Errorf("Repositories.CreateUpdateEnvironment returned %+v, want %+v", release, want) + } +} + +func TestRepositoriesService_createNewEnvNoEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &CreateUpdateEnvironment{ + DeploymentBranchPolicy: &BranchPolicy{ + ProtectedBranches: Ptr(true), + CustomBranchPolicies: Ptr(false), + }, + } + + mux.HandleFunc("/repos/o/r/environments/e", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id": 1, "name": "staging", "protection_rules": [{"id": 1, "node_id": "id", "type": "branch_policy"}], "deployment_branch_policy": {"protected_branches": true, "custom_branch_policies": false}}`) + }) + + ctx := t.Context() + release, _, err := client.Repositories.createNewEnvNoEnterprise(ctx, "repos/o/r/environments/e", input) + if err != nil { + t.Errorf("Repositories.createNewEnvNoEnterprise returned error: %v", err) + } + + want := &Environment{ + ID: Ptr(int64(1)), + Name: Ptr("staging"), + ProtectionRules: []*ProtectionRule{ + { + ID: Ptr(int64(1)), + NodeID: Ptr("id"), + Type: Ptr("branch_policy"), + }, + }, + DeploymentBranchPolicy: &BranchPolicy{ + ProtectedBranches: Ptr(true), + CustomBranchPolicies: Ptr(false), + }, + } + if !cmp.Equal(release, want) { + t.Errorf("Repositories.createNewEnvNoEnterprise returned %+v, want %+v", release, want) + } + + const methodName = "createNewEnvNoEnterprise" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.createNewEnvNoEnterprise(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.createNewEnvNoEnterprise(ctx, "repos/o/r/environments/e", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteEnvironment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/environments/e", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Repositories.DeleteEnvironment(ctx, "o", "r", "e") + if err != nil { + t.Errorf("Repositories.DeleteEnvironment returned error: %v", err) + } + + const methodName = "DeleteEnvironment" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteEnvironment(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteEnvironment(ctx, "o", "r", "e") + }) +} diff --git a/github/repos_forks.go b/github/repos_forks.go index bfff27bb951..b4f2e226417 100644 --- a/github/repos_forks.go +++ b/github/repos_forks.go @@ -7,9 +7,9 @@ package github import ( "context" - "fmt" - "encoding/json" + "errors" + "fmt" ) // RepositoryListForksOptions specifies the optional parameters to the @@ -24,24 +24,25 @@ type RepositoryListForksOptions struct { // ListForks lists the forks of the specified repository. // -// GitHub API docs: https://developer.github.com/v3/repos/forks/#list-forks -func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, opt *RepositoryListForksOptions) ([]*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/forks?apiVersion=2022-11-28#list-forks +// +//meta:operation GET /repos/{owner}/{repo}/forks +func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, opts *RepositoryListForksOptions) ([]*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when topics API fully launches. req.Header.Set("Accept", mediaTypeTopicsPreview) var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) + resp, err := s.client.Do(req, &repos) if err != nil { return nil, resp, err } @@ -53,7 +54,9 @@ func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, // RepositoriesService.CreateFork method. type RepositoryCreateForkOptions struct { // The organization to fork the repository into. - Organization string `url:"organization,omitempty"` + Organization string `json:"organization,omitempty"` + Name string `json:"name,omitempty"` + DefaultBranchOnly bool `json:"default_branch_only,omitempty"` } // CreateFork creates a fork of the specified repository. @@ -65,25 +68,24 @@ type RepositoryCreateForkOptions struct { // A follow up request, after a delay of a second or so, should result // in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/forks/#create-a-fork -func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opt *RepositoryCreateForkOptions) (*Repository, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/forks?apiVersion=2022-11-28#create-a-fork +// +//meta:operation POST /repos/{owner}/{repo}/forks +func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, body *RepositoryCreateForkOptions) (*Repository, *Response, error) { u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - req, err := s.client.NewRequest("POST", u, nil) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - fork := new(Repository) - resp, err := s.client.Do(ctx, req, fork) + var fork *Repository + resp, err := s.client.Do(req, &fork) if err != nil { // Persist AcceptedError's metadata to the Repository object. - if aerr, ok := err.(*AcceptedError); ok { - if err := json.Unmarshal(aerr.Raw, fork); err != nil { + var aerr *AcceptedError + if errors.As(err, &aerr) { + if err := json.Unmarshal(aerr.Raw, &fork); err != nil { return fork, resp, err } diff --git a/github/repos_forks_test.go b/github/repos_forks_test.go index 065f592735c..393e4d669b6 100644 --- a/github/repos_forks_test.go +++ b/github/repos_forks_test.go @@ -6,16 +6,17 @@ package github import ( - "context" + "errors" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListForks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -31,75 +32,133 @@ func TestRepositoriesService_ListForks(t *testing.T) { Sort: "newest", ListOptions: ListOptions{Page: 3}, } - repos, _, err := client.Repositories.ListForks(context.Background(), "o", "r", opt) + ctx := t.Context() + repos, _, err := client.Repositories.ListForks(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListForks returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(repos, want) { + want := []*Repository{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(repos, want) { t.Errorf("Repositories.ListForks returned %+v, want %+v", repos, want) } + + const methodName = "ListForks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListForks(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListForks(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListForks_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListForks(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListForks(ctx, "%", "r", nil) testURLParseError(t, err) } func TestRepositoriesService_CreateFork(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + opt := &RepositoryCreateForkOptions{Organization: "o", Name: "n", DefaultBranchOnly: true} mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testFormValues(t, r, values{"organization": "o"}) + testJSONBody(t, r, opt) fmt.Fprint(w, `{"id":1}`) }) - opt := &RepositoryCreateForkOptions{Organization: "o"} - repo, _, err := client.Repositories.CreateFork(context.Background(), "o", "r", opt) + ctx := t.Context() + repo, _, err := client.Repositories.CreateFork(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.CreateFork returned error: %v", err) } - want := &Repository{ID: Int64(1)} - if !reflect.DeepEqual(repo, want) { + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(repo, want) { t.Errorf("Repositories.CreateFork returned %+v, want %+v", repo, want) } + + const methodName = "CreateFork" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateFork(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateFork(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_CreateFork_deferred(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + opt := &RepositoryCreateForkOptions{Organization: "o", Name: "n", DefaultBranchOnly: true} mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testFormValues(t, r, values{"organization": "o"}) + testJSONBody(t, r, opt) // This response indicates the fork will happen asynchronously. w.WriteHeader(http.StatusAccepted) fmt.Fprint(w, `{"id":1}`) }) - opt := &RepositoryCreateForkOptions{Organization: "o"} - repo, _, err := client.Repositories.CreateFork(context.Background(), "o", "r", opt) - if _, ok := err.(*AcceptedError); !ok { + ctx := t.Context() + repo, _, err := client.Repositories.CreateFork(ctx, "o", "r", opt) + if !errors.As(err, new(*AcceptedError)) { t.Errorf("Repositories.CreateFork returned error: %v (want AcceptedError)", err) } - want := &Repository{ID: Int64(1)} - if !reflect.DeepEqual(repo, want) { + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(repo, want) { t.Errorf("Repositories.CreateFork returned %+v, want %+v", repo, want) } } +func TestRepositoriesService_CreateFork_deferred_badBody(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &RepositoryCreateForkOptions{Organization: "o", Name: "n", DefaultBranchOnly: true} + + mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, `{invalid json`) + }) + + ctx := t.Context() + repo, _, err := client.Repositories.CreateFork(ctx, "o", "r", opt) + if err == nil { + t.Fatal("Repositories.CreateFork returned nil error") + } + if repo != nil { + t.Errorf("Repositories.CreateFork returned non-nil repo: %+v", repo) + } +} + func TestRepositoriesService_CreateFork_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.CreateFork(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Repositories.CreateFork(ctx, "%", "r", nil) testURLParseError(t, err) } diff --git a/github/repos_hooks.go b/github/repos_hooks.go index 7674947df32..baf410eb8a8 100644 --- a/github/repos_hooks.go +++ b/github/repos_hooks.go @@ -7,8 +7,11 @@ package github import ( "context" + "errors" "fmt" - "time" + "net/http" + "net/url" + "strings" ) // WebHookPayload represents the data that is received from GitHub when a push @@ -18,67 +21,40 @@ import ( // here to account for these differences. // // GitHub API docs: https://help.github.com/articles/post-receive-hooks -type WebHookPayload struct { - After *string `json:"after,omitempty"` - Before *string `json:"before,omitempty"` - Commits []WebHookCommit `json:"commits,omitempty"` - Compare *string `json:"compare,omitempty"` - Created *bool `json:"created,omitempty"` - Deleted *bool `json:"deleted,omitempty"` - Forced *bool `json:"forced,omitempty"` - HeadCommit *WebHookCommit `json:"head_commit,omitempty"` - Pusher *User `json:"pusher,omitempty"` - Ref *string `json:"ref,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` -} - -func (w WebHookPayload) String() string { - return Stringify(w) -} +// +// Deprecated: Please use PushEvent instead. +type WebHookPayload = PushEvent // WebHookCommit represents the commit variant we receive from GitHub in a // WebHookPayload. -type WebHookCommit struct { - Added []string `json:"added,omitempty"` - Author *WebHookAuthor `json:"author,omitempty"` - Committer *WebHookAuthor `json:"committer,omitempty"` - Distinct *bool `json:"distinct,omitempty"` - ID *string `json:"id,omitempty"` - Message *string `json:"message,omitempty"` - Modified []string `json:"modified,omitempty"` - Removed []string `json:"removed,omitempty"` - Timestamp *time.Time `json:"timestamp,omitempty"` -} - -func (w WebHookCommit) String() string { - return Stringify(w) -} +// +// Deprecated: Please use HeadCommit instead. +type WebHookCommit = HeadCommit // WebHookAuthor represents the author or committer of a commit, as specified // in a WebHookCommit. The commit author may not correspond to a GitHub User. -type WebHookAuthor struct { - Email *string `json:"email,omitempty"` - Name *string `json:"name,omitempty"` - Username *string `json:"username,omitempty"` -} - -func (w WebHookAuthor) String() string { - return Stringify(w) -} +// +// Deprecated: Please use CommitAuthor instead. +// NOTE Breaking API change: the `Username` field is now called `Login`. +type WebHookAuthor = CommitAuthor // Hook represents a GitHub (web and service) hook for a repository. type Hook struct { - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - URL *string `json:"url,omitempty"` - ID *int64 `json:"id,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + URL *string `json:"url,omitempty"` + ID *int64 `json:"id,omitempty"` + Type *string `json:"type,omitempty"` + Name *string `json:"name,omitempty"` + TestURL *string `json:"test_url,omitempty"` + PingURL *string `json:"ping_url,omitempty"` + LastResponse map[string]any `json:"last_response,omitempty"` // Only the following fields are used when creating a hook. // Config is required. - Config map[string]interface{} `json:"config,omitempty"` - Events []string `json:"events,omitempty"` - Active *bool `json:"active,omitempty"` + Config *HookConfig `json:"config,omitempty"` + Events []string `json:"events,omitempty"` + Active *bool `json:"active,omitempty"` } func (h Hook) String() string { @@ -92,10 +68,10 @@ func (h Hook) String() string { // information. type createHookRequest struct { // Config is required. - Name string `json:"name"` - Config map[string]interface{} `json:"config,omitempty"` - Events []string `json:"events,omitempty"` - Active *bool `json:"active,omitempty"` + Name string `json:"name"` + Config *HookConfig `json:"config,omitempty"` + Events []string `json:"events,omitempty"` + Active *bool `json:"active,omitempty"` } // CreateHook creates a Hook for the specified repository. @@ -104,8 +80,14 @@ type createHookRequest struct { // Note that only a subset of the hook fields are used and hook must // not be nil. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#create-a-hook +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#create-a-repository-webhook +// +//meta:operation POST /repos/{owner}/{repo}/hooks func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string, hook *Hook) (*Hook, *Response, error) { + if hook == nil { + return nil, nil, errors.New("hook must be provided") + } + u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) hookReq := &createHookRequest{ @@ -115,13 +97,13 @@ func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string Config: hook.Config, } - req, err := s.client.NewRequest("POST", u, hookReq) + req, err := s.client.NewRequest(ctx, "POST", u, hookReq) if err != nil { return nil, nil, err } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) + var h *Hook + resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } @@ -131,21 +113,23 @@ func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string // ListHooks lists all Hooks for the specified repository. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#list -func (s *RepositoriesService) ListHooks(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Hook, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#list-repository-webhooks +// +//meta:operation GET /repos/{owner}/{repo}/hooks +func (s *RepositoriesService) ListHooks(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Hook, *Response, error) { u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var hooks []*Hook - resp, err := s.client.Do(ctx, req, &hooks) + resp, err := s.client.Do(req, &hooks) if err != nil { return nil, resp, err } @@ -155,15 +139,18 @@ func (s *RepositoriesService) ListHooks(ctx context.Context, owner, repo string, // GetHook returns a single specified Hook. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#get-single-hook +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#get-a-repository-webhook +// +//meta:operation GET /repos/{owner}/{repo}/hooks/{hook_id} func (s *RepositoriesService) GetHook(ctx context.Context, owner, repo string, id int64) (*Hook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/hooks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) + + var h *Hook + resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } @@ -173,15 +160,18 @@ func (s *RepositoriesService) GetHook(ctx context.Context, owner, repo string, i // EditHook updates a specified Hook. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#edit-a-hook -func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, id int64, hook *Hook) (*Hook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, hook) +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#update-a-repository-webhook +// +//meta:operation PATCH /repos/{owner}/{repo}/hooks/{hook_id} +func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, id int64, body *Hook) (*Hook, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) + + var h *Hook + resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } @@ -191,36 +181,99 @@ func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, // DeleteHook deletes a specified Hook. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#delete-a-hook +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#delete-a-repository-webhook +// +//meta:operation DELETE /repos/{owner}/{repo}/hooks/{hook_id} func (s *RepositoriesService) DeleteHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/hooks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + + return s.client.Do(req, nil) } // PingHook triggers a 'ping' event to be sent to the Hook. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#ping-a-hook +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#ping-a-repository-webhook +// +//meta:operation POST /repos/{owner}/{repo}/hooks/{hook_id}/pings func (s *RepositoriesService) PingHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d/pings", owner, repo, id) - req, err := s.client.NewRequest("POST", u, nil) + u := fmt.Sprintf("repos/%v/%v/hooks/%v/pings", owner, repo, id) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // TestHook triggers a test Hook by github. // -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#test-a-push-hook +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#test-the-push-repository-webhook +// +//meta:operation POST /repos/{owner}/{repo}/hooks/{hook_id}/tests func (s *RepositoriesService) TestHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d/tests", owner, repo, id) - req, err := s.client.NewRequest("POST", u, nil) + u := fmt.Sprintf("repos/%v/%v/hooks/%v/tests", owner, repo, id) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// Subscribe lets servers register to receive updates when a topic is updated. +// +// GitHub API docs: https://docs.github.com/webhooks/about-webhooks-for-repositories#pubsubhubbub +// +//meta:operation POST /hub +func (s *RepositoriesService) Subscribe(ctx context.Context, owner, repo, event, callback string, secret []byte) (*Response, error) { + req, err := s.createWebSubRequest(ctx, "subscribe", owner, repo, event, callback, secret) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// Unsubscribe lets servers unregister to no longer receive updates when a topic is updated. +// +// GitHub API docs: https://docs.github.com/webhooks/about-webhooks-for-repositories#pubsubhubbub +// +//meta:operation POST /hub +func (s *RepositoriesService) Unsubscribe(ctx context.Context, owner, repo, event, callback string, secret []byte) (*Response, error) { + req, err := s.createWebSubRequest(ctx, "unsubscribe", owner, repo, event, callback, secret) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// createWebSubRequest returns a subscribe/unsubscribe request that implements +// the WebSub (formerly PubSubHubbub) protocol. +// +// See: https://www.w3.org/TR/websub/#subscriber-sends-subscription-request +func (s *RepositoriesService) createWebSubRequest(ctx context.Context, hubMode, owner, repo, event, callback string, secret []byte) (*http.Request, error) { + topic := fmt.Sprintf( + "https://github.com/%v/%v/events/%v", + owner, + repo, + event, + ) + form := url.Values{} + form.Add("hub.mode", hubMode) + form.Add("hub.topic", topic) + form.Add("hub.callback", callback) + if secret != nil { + form.Add("hub.secret", string(secret)) + } + body := strings.NewReader(form.Encode()) + + req, err := s.client.NewFormRequest(ctx, "hub", body) + if err != nil { + return nil, err + } + + return req, nil } diff --git a/github/repos_hooks_configuration.go b/github/repos_hooks_configuration.go new file mode 100644 index 00000000000..ceb76fd986b --- /dev/null +++ b/github/repos_hooks_configuration.go @@ -0,0 +1,68 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// HookConfig describes metadata about a webhook configuration. +type HookConfig struct { + // The media type used to serialize the payloads + // Possible values are `json` and `form`, the field is not specified the default is `form` + ContentType *string `json:"content_type,omitempty"` + // The possible values are 0 and 1. + // Setting it to 1 will allow skipping certificate verification for the host, + // potentially exposing to MitM attacks: https://en.wikipedia.org/wiki/Man-in-the-middle_attack + InsecureSSL *string `json:"insecure_ssl,omitempty"` + URL *string `json:"url,omitempty"` + + // Secret is returned obfuscated by GitHub, but it can be set for outgoing requests. + Secret *string `json:"secret,omitempty"` +} + +// GetHookConfiguration returns the configuration for the specified repository webhook. +// +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#get-a-webhook-configuration-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/hooks/{hook_id}/config +func (s *RepositoriesService) GetHookConfiguration(ctx context.Context, owner, repo string, id int64) (*HookConfig, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%v/config", owner, repo, id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var config *HookConfig + resp, err := s.client.Do(req, &config) + if err != nil { + return nil, resp, err + } + + return config, resp, nil +} + +// UpdateHookConfiguration updates the configuration for the specified repository webhook. +// +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#update-a-webhook-configuration-for-a-repository +// +//meta:operation PATCH /repos/{owner}/{repo}/hooks/{hook_id}/config +func (s *RepositoriesService) UpdateHookConfiguration(ctx context.Context, owner, repo string, id int64, body HookConfig) (*HookConfig, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%v/config", owner, repo, id) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var c *HookConfig + resp, err := s.client.Do(req, &c) + if err != nil { + return nil, resp, err + } + + return c, resp, nil +} diff --git a/github/repos_hooks_configuration_test.go b/github/repos_hooks_configuration_test.go new file mode 100644 index 00000000000..81bae011c3b --- /dev/null +++ b/github/repos_hooks_configuration_test.go @@ -0,0 +1,115 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetHookConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/hooks/1/config", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"content_type": "json", "insecure_ssl": "0", "secret": "********", "url": "https://example.com/webhook"}`) + }) + + ctx := t.Context() + config, _, err := client.Repositories.GetHookConfiguration(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Repositories.GetHookConfiguration returned error: %v", err) + } + + want := &HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("0"), + Secret: Ptr("********"), + URL: Ptr("https://example.com/webhook"), + } + if !cmp.Equal(config, want) { + t.Errorf("Repositories.GetHookConfiguration returned %+v, want %+v", config, want) + } + + const methodName = "GetHookConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetHookConfiguration(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetHookConfiguration(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetHookConfiguration_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.GetHookConfiguration(ctx, "%", "%", 1) + testURLParseError(t, err) +} + +func TestRepositoriesService_UpdateHookConfiguration(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := HookConfig{} + + mux.HandleFunc("/repos/o/r/hooks/1/config", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"content_type": "json", "insecure_ssl": "0", "secret": "********", "url": "https://example.com/webhook"}`) + }) + + ctx := t.Context() + config, _, err := client.Repositories.UpdateHookConfiguration(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("Repositories.UpdateHookConfiguration returned error: %v", err) + } + + want := &HookConfig{ + ContentType: Ptr("json"), + InsecureSSL: Ptr("0"), + Secret: Ptr("********"), + URL: Ptr("https://example.com/webhook"), + } + if !cmp.Equal(config, want) { + t.Errorf("Repositories.UpdateHookConfiguration returned %+v, want %+v", config, want) + } + + const methodName = "UpdateHookConfiguration" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateHookConfiguration(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateHookConfiguration(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateHookConfiguration_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.UpdateHookConfiguration(ctx, "%", "%", 1, HookConfig{}) + testURLParseError(t, err) +} diff --git a/github/repos_hooks_deliveries.go b/github/repos_hooks_deliveries.go new file mode 100644 index 00000000000..85b83e04582 --- /dev/null +++ b/github/repos_hooks_deliveries.go @@ -0,0 +1,163 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "strings" +) + +// HookDelivery represents the data that is received from GitHub's Webhook Delivery API +// +// GitHub API docs: +// - https://docs.github.com/rest/webhooks/repo-deliveries?apiVersion=2022-11-28#list-deliveries-for-a-repository-webhook +// - https://docs.github.com/rest/webhooks/repo-deliveries?apiVersion=2022-11-28#get-a-delivery-for-a-repository-webhook +type HookDelivery struct { + ID *int64 `json:"id,omitempty"` + GUID *string `json:"guid,omitempty"` + DeliveredAt *Timestamp `json:"delivered_at,omitempty"` + Redelivery *bool `json:"redelivery,omitempty"` + Duration *float64 `json:"duration,omitempty"` + Status *string `json:"status,omitempty"` + StatusCode *int `json:"status_code,omitempty"` + Event *string `json:"event,omitempty"` + Action *string `json:"action,omitempty"` + InstallationID *int64 `json:"installation_id,omitempty"` + RepositoryID *int64 `json:"repository_id,omitempty"` + + // Request is populated by GetHookDelivery. + Request *HookRequest `json:"request,omitempty"` + // Response is populated by GetHookDelivery. + Response *HookResponse `json:"response,omitempty"` +} + +func (d HookDelivery) String() string { + return Stringify(d) +} + +// getHeader common function for GetHeader funcs of HookRequest & HookResponse. +func getHeader(headers map[string]string, key string) string { + for k, v := range headers { + if strings.EqualFold(k, key) { + return v + } + } + return "" +} + +// HookRequest is a part of HookDelivery that contains +// the HTTP headers and the JSON payload of the webhook request. +type HookRequest struct { + Headers map[string]string `json:"headers,omitempty"` + RawPayload *json.RawMessage `json:"payload,omitempty"` +} + +// GetHeader gets the value associated with the given key (ignoring key case). +func (r *HookRequest) GetHeader(key string) string { + return getHeader(r.Headers, key) +} + +func (r HookRequest) String() string { + return Stringify(r) +} + +// HookResponse is a part of HookDelivery that contains +// the HTTP headers and the response body served by the webhook endpoint. +type HookResponse struct { + Headers map[string]string `json:"headers,omitempty"` + RawPayload *json.RawMessage `json:"payload,omitempty"` +} + +// GetHeader gets the value associated with the given key (ignoring key case). +func (r *HookResponse) GetHeader(key string) string { + return getHeader(r.Headers, key) +} + +func (r HookResponse) String() string { + return Stringify(r) +} + +// ListHookDeliveries lists webhook deliveries for a webhook configured in a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#list-deliveries-for-a-repository-webhook +// +//meta:operation GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries +func (s *RepositoriesService) ListHookDeliveries(ctx context.Context, owner, repo string, id int64, opts *ListCursorOptions) ([]*HookDelivery, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%v/deliveries", owner, repo, id) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + deliveries := []*HookDelivery{} + resp, err := s.client.Do(req, &deliveries) + if err != nil { + return nil, resp, err + } + + return deliveries, resp, nil +} + +// GetHookDelivery returns a delivery for a webhook configured in a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#get-a-delivery-for-a-repository-webhook +// +//meta:operation GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id} +func (s *RepositoriesService) GetHookDelivery(ctx context.Context, owner, repo string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%v/deliveries/%v", owner, repo, hookID, deliveryID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var h *HookDelivery + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} + +// RedeliverHookDelivery redelivers a delivery for a webhook configured in a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/webhooks?apiVersion=2022-11-28#redeliver-a-delivery-for-a-repository-webhook +// +//meta:operation POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts +func (s *RepositoriesService) RedeliverHookDelivery(ctx context.Context, owner, repo string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/hooks/%v/deliveries/%v/attempts", owner, repo, hookID, deliveryID) + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, nil, err + } + + var h *HookDelivery + resp, err := s.client.Do(req, &h) + if err != nil { + return nil, resp, err + } + + return h, resp, nil +} + +// ParseRequestPayload parses the request payload. For recognized event types, +// a value of the corresponding struct type will be returned. +func (d *HookDelivery) ParseRequestPayload() (any, error) { + eType, ok := messageToTypeName[d.GetEvent()] + if !ok { + return nil, fmt.Errorf("unsupported event type %q", d.GetEvent()) + } + + e := &Event{Type: &eType, RawPayload: d.Request.RawPayload} + return e.ParsePayload() +} diff --git a/github/repos_hooks_deliveries_test.go b/github/repos_hooks_deliveries_test.go new file mode 100644 index 00000000000..df72de213f3 --- /dev/null +++ b/github/repos_hooks_deliveries_test.go @@ -0,0 +1,338 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListHookDeliveries(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/hooks/1/deliveries", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"cursor": "v1_12077215967"}) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListCursorOptions{Cursor: "v1_12077215967"} + + ctx := t.Context() + hooks, _, err := client.Repositories.ListHookDeliveries(ctx, "o", "r", 1, opt) + if err != nil { + t.Errorf("Repositories.ListHookDeliveries returned error: %v", err) + } + + want := []*HookDelivery{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if d := cmp.Diff(hooks, want); d != "" { + t.Errorf("Repositories.ListHooks want (-), got (+):\n%v", d) + } + + const methodName = "ListHookDeliveries" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListHookDeliveries(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListHookDeliveries(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListHookDeliveries_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.ListHookDeliveries(ctx, "%", "%", 1, nil) + testURLParseError(t, err) +} + +func TestRepositoriesService_GetHookDelivery(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/hooks/1/deliveries/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + hook, _, err := client.Repositories.GetHookDelivery(ctx, "o", "r", 1, 1) + if err != nil { + t.Errorf("Repositories.GetHookDelivery returned error: %v", err) + } + + want := &HookDelivery{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { + t.Errorf("Repositories.GetHookDelivery returned %+v, want %+v", hook, want) + } + + const methodName = "GetHookDelivery" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetHookDelivery(ctx, "\n", "\n", -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetHookDelivery(ctx, "o", "r", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetHookDelivery_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.GetHookDelivery(ctx, "%", "%", 1, 1) + testURLParseError(t, err) +} + +func TestRepositoriesService_RedeliverHookDelivery(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/hooks/1/deliveries/1/attempts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + hook, _, err := client.Repositories.RedeliverHookDelivery(ctx, "o", "r", 1, 1) + if err != nil { + t.Errorf("Repositories.RedeliverHookDelivery returned error: %v", err) + } + + want := &HookDelivery{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { + t.Errorf("Repositories.RedeliverHookDelivery returned %+v, want %+v", hook, want) + } + + const methodName = "RedeliverHookDelivery" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RedeliverHookDelivery(ctx, "\n", "\n", -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RedeliverHookDelivery(ctx, "o", "r", 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +var hookDeliveryPayloadTypeToStruct = map[string]any{ + "check_run": &CheckRunEvent{}, + "check_suite": &CheckSuiteEvent{}, + "code_scanning_alert": &CodeScanningAlertEvent{}, + "commit_comment": &CommitCommentEvent{}, + "content_reference": &ContentReferenceEvent{}, + "create": &CreateEvent{}, + "delete": &DeleteEvent{}, + "dependabot_alert": &DependabotAlertEvent{}, + "deploy_key": &DeployKeyEvent{}, + "deployment": &DeploymentEvent{}, + "deployment_status": &DeploymentStatusEvent{}, + "discussion_comment": &DiscussionCommentEvent{}, + "discussion": &DiscussionEvent{}, + "fork": &ForkEvent{}, + "github_app_authorization": &GitHubAppAuthorizationEvent{}, + "gollum": &GollumEvent{}, + "installation": &InstallationEvent{}, + "installation_repositories": &InstallationRepositoriesEvent{}, + "issue_comment": &IssueCommentEvent{}, + "issues": &IssuesEvent{}, + "label": &LabelEvent{}, + "marketplace_purchase": &MarketplacePurchaseEvent{}, + "member": &MemberEvent{}, + "membership": &MembershipEvent{}, + "meta": &MetaEvent{}, + "milestone": &MilestoneEvent{}, + "organization": &OrganizationEvent{}, + "org_block": &OrgBlockEvent{}, + "package": &PackageEvent{}, + "page_build": &PageBuildEvent{}, + "ping": &PingEvent{}, + "projects_v2": &ProjectV2Event{}, + "projects_v2_item": &ProjectV2ItemEvent{}, + "public": &PublicEvent{}, + "pull_request": &PullRequestEvent{}, + "pull_request_review": &PullRequestReviewEvent{}, + "pull_request_review_comment": &PullRequestReviewCommentEvent{}, + "pull_request_review_thread": &PullRequestReviewThreadEvent{}, + "pull_request_target": &PullRequestTargetEvent{}, + "push": &PushEvent{}, + "registry_package": &RegistryPackageEvent{}, + "release": &ReleaseEvent{}, + "repository": &RepositoryEvent{}, + "repository_dispatch": &RepositoryDispatchEvent{}, + "repository_import": &RepositoryImportEvent{}, + "repository_vulnerability_alert": &RepositoryVulnerabilityAlertEvent{}, + "secret_scanning_alert": &SecretScanningAlertEvent{}, + "security_advisory": &SecurityAdvisoryEvent{}, + "security_and_analysis": &SecurityAndAnalysisEvent{}, + "star": &StarEvent{}, + "status": &StatusEvent{}, + "team": &TeamEvent{}, + "team_add": &TeamAddEvent{}, + "user": &UserEvent{}, + "watch": &WatchEvent{}, + "workflow_dispatch": &WorkflowDispatchEvent{}, + "workflow_job": &WorkflowJobEvent{}, + "workflow_run": &WorkflowRunEvent{}, +} + +func TestHookDelivery_ParsePayload(t *testing.T) { + t.Parallel() + for evt, obj := range hookDeliveryPayloadTypeToStruct { + t.Run(evt, func(t *testing.T) { + t.Parallel() + bs, err := json.Marshal(obj) + if err != nil { + t.Fatal(err) + } + + p := json.RawMessage(bs) + + d := &HookDelivery{ + Event: &evt, + Request: &HookRequest{ + RawPayload: &p, + }, + } + + got, err := d.ParseRequestPayload() + if err != nil { + t.Error(err) + } + + if !cmp.Equal(obj, got) { + t.Errorf("want %T %v, got %T %v", obj, obj, got, got) + } + }) + } +} + +func TestHookDelivery_ParsePayload_invalidEvent(t *testing.T) { + t.Parallel() + p := json.RawMessage(nil) + + d := &HookDelivery{ + Event: Ptr("some_invalid_event"), + Request: &HookRequest{ + RawPayload: &p, + }, + } + + _, err := d.ParseRequestPayload() + if err == nil || err.Error() != `unsupported event type "some_invalid_event"` { + t.Errorf("unexpected error: %v", err) + } +} + +func TestHookDelivery_ParsePayload_invalidPayload(t *testing.T) { + t.Parallel() + p := json.RawMessage([]byte(`{"check_run":{"id":"invalid"}}`)) + + d := &HookDelivery{ + Event: Ptr("check_run"), + Request: &HookRequest{ + RawPayload: &p, + }, + } + + _, err := d.ParseRequestPayload() + if err == nil || !strings.Contains(err.Error(), "json: cannot unmarshal") || !strings.Contains(err.Error(), "check_run.id") { + t.Errorf("unexpected error: %v", err) + } +} + +func TestHookRequest_GetHeader(t *testing.T) { + t.Parallel() + + header := make(map[string]string) + header["key1"] = "value1" + header["Key+2"] = "value2" + header["kEy-3"] = "value3" + header["KEY_4"] = "value4" + + r := &HookRequest{ + Headers: header, + } + + // Checking positive cases + testPrefixes := []string{"key", "Key", "kEy", "KEY"} + for hdrKey, hdrValue := range header { + for _, prefix := range testPrefixes { + key := prefix + hdrKey[3:] + if val := r.GetHeader(key); val != hdrValue { + t.Errorf("GetHeader(%q) is not working: %q != %q", key, val, hdrValue) + } + } + } + + // Checking negative case + key := "asd" + if val := r.GetHeader(key); val != "" { + t.Errorf("GetHeader(%q) should return empty value: %q != %q", key, val, "") + } + key = "kay1" + if val := r.GetHeader(key); val != "" { + t.Errorf("GetHeader(%q) should return empty value: %q != %q", key, val, "") + } +} + +func TestHookResponse_GetHeader(t *testing.T) { + t.Parallel() + + header := make(map[string]string) + header["key1"] = "value1" + header["Key+2"] = "value2" + header["kEy-3"] = "value3" + header["KEY_4"] = "value4" + + r := &HookResponse{ + Headers: header, + } + + // Checking positive cases + testPrefixes := []string{"key", "Key", "kEy", "KEY"} + for hdrKey, hdrValue := range header { + for _, prefix := range testPrefixes { + key := prefix + hdrKey[3:] + if val := r.GetHeader(key); val != hdrValue { + t.Errorf("GetHeader(%q) is not working: %q != %q", key, val, hdrValue) + } + } + } + + // Checking negative case + key := "asd" + if val := r.GetHeader(key); val != "" { + t.Errorf("GetHeader(%q) should return empty value: %q != %q", key, val, "") + } + key = "kay1" + if val := r.GetHeader(key); val != "" { + t.Errorf("GetHeader(%q) should return empty value: %q != %q", key, val, "") + } +} diff --git a/github/repos_hooks_test.go b/github/repos_hooks_test.go index 9e4691c3b0e..b35294fb5dc 100644 --- a/github/repos_hooks_test.go +++ b/github/repos_hooks_test.go @@ -6,47 +6,59 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_CreateHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Hook{CreatedAt: &referenceTime} + input := &Hook{CreatedAt: &Timestamp{referenceTime}} mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { - v := new(createHookRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") want := &createHookRequest{Name: "web"} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } - + testJSONBody(t, r, want) fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Repositories.CreateHook(context.Background(), "o", "r", input) + ctx := t.Context() + hook, _, err := client.Repositories.CreateHook(ctx, "o", "r", input) if err != nil { t.Errorf("Repositories.CreateHook returned error: %v", err) } - want := &Hook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &Hook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Repositories.CreateHook returned %+v, want %+v", hook, want) } + + const methodName = "CreateHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateHook(ctx, "o", "r", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateHook(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateHook(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListHooks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -56,144 +68,298 @@ func TestRepositoriesService_ListHooks(t *testing.T) { opt := &ListOptions{Page: 2} - hooks, _, err := client.Repositories.ListHooks(context.Background(), "o", "r", opt) + ctx := t.Context() + hooks, _, err := client.Repositories.ListHooks(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListHooks returned error: %v", err) } - want := []*Hook{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(hooks, want) { + want := []*Hook{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(hooks, want) { t.Errorf("Repositories.ListHooks returned %+v, want %+v", hooks, want) } + + const methodName = "ListHooks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListHooks(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListHooks(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListHooks_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListHooks(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListHooks(ctx, "%", "%", nil) testURLParseError(t, err) } +func TestRepositoriesService_ListHooks_403_code_no_rate_limit(t *testing.T) { + t.Parallel() + testErrorResponseForStatusCode(t, http.StatusForbidden) +} + +func TestRepositoriesService_ListHooks_404_code(t *testing.T) { + t.Parallel() + testErrorResponseForStatusCode(t, http.StatusNotFound) +} + func TestRepositoriesService_GetHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Repositories.GetHook(context.Background(), "o", "r", 1) + ctx := t.Context() + hook, _, err := client.Repositories.GetHook(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetHook returned error: %v", err) } - want := &Hook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &Hook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Repositories.GetHook returned %+v, want %+v", hook, want) } + + const methodName = "GetHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetHook(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetHook(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.GetHook(context.Background(), "%", "%", 1) + ctx := t.Context() + _, _, err := client.Repositories.GetHook(ctx, "%", "%", 1) testURLParseError(t, err) } func TestRepositoriesService_EditHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := &Hook{} mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { - v := new(Hook) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Repositories.EditHook(context.Background(), "o", "r", 1, input) + ctx := t.Context() + hook, _, err := client.Repositories.EditHook(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Repositories.EditHook returned error: %v", err) } - want := &Hook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &Hook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Repositories.EditHook returned %+v, want %+v", hook, want) } + + const methodName = "EditHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.EditHook(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.EditHook(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_EditHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.EditHook(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Repositories.EditHook(ctx, "%", "%", 1, nil) testURLParseError(t, err) } func TestRepositoriesService_DeleteHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/hooks/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Repositories.DeleteHook(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteHook(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.DeleteHook returned error: %v", err) } + + const methodName = "DeleteHook" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteHook(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteHook(ctx, "o", "r", 1) + }) } func TestRepositoriesService_DeleteHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.DeleteHook(context.Background(), "%", "%", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteHook(ctx, "%", "%", 1) testURLParseError(t, err) } func TestRepositoriesService_PingHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/hooks/1/pings", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") }) - _, err := client.Repositories.PingHook(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.PingHook(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.PingHook returned error: %v", err) } + + const methodName = "PingHook" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.PingHook(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.PingHook(ctx, "o", "r", 1) + }) } func TestRepositoriesService_TestHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/hooks/1/tests", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/hooks/1/tests", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") }) - _, err := client.Repositories.TestHook(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.TestHook(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.TestHook returned error: %v", err) } + + const methodName = "TestHook" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.TestHook(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.TestHook(ctx, "o", "r", 1) + }) } func TestRepositoriesService_TestHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.TestHook(context.Background(), "%", "%", 1) + ctx := t.Context() + _, err := client.Repositories.TestHook(ctx, "%", "%", 1) testURLParseError(t, err) } + +func TestRepositoriesService_Subscribe(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/hub", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/x-www-form-urlencoded") + testFormValues(t, r, values{ + "hub.mode": "subscribe", + "hub.topic": "https://github.com/o/r/events/push", + "hub.callback": "http://localhost:8080/callback", + "hub.secret": "test secret", + }) + }) + + ctx := t.Context() + _, err := client.Repositories.Subscribe( + ctx, + "o", + "r", + "push", + "http://localhost:8080/callback", + []byte("test secret"), + ) + if err != nil { + t.Errorf("Repositories.Subscribe returned error: %v", err) + } + + testNewRequestAndDoFailure(t, "Subscribe", client, func() (*Response, error) { + return client.Repositories.Subscribe(ctx, "o", "r", "push", "http://localhost:8080", nil) + }) +} + +func TestRepositoriesService_Unsubscribe(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/hub", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/x-www-form-urlencoded") + testFormValues(t, r, values{ + "hub.mode": "unsubscribe", + "hub.topic": "https://github.com/o/r/events/push", + "hub.callback": "http://localhost:8080/callback", + "hub.secret": "test secret", + }) + }) + + ctx := t.Context() + _, err := client.Repositories.Unsubscribe( + ctx, + "o", + "r", + "push", + "http://localhost:8080/callback", + []byte("test secret"), + ) + if err != nil { + t.Errorf("Repositories.Unsubscribe returned error: %v", err) + } + + testNewRequestAndDoFailure(t, "Unsubscribe", client, func() (*Response, error) { + return client.Repositories.Unsubscribe(ctx, "o", "r", "push", "http://localhost:8080/callback", nil) + }) +} diff --git a/github/repos_immutable_releases.go b/github/repos_immutable_releases.go new file mode 100644 index 00000000000..db91b6d02bd --- /dev/null +++ b/github/repos_immutable_releases.go @@ -0,0 +1,82 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// RepoImmutableReleasesStatus represents the immutable releases status for a repository. +type RepoImmutableReleasesStatus struct { + Enabled *bool `json:"enabled,omitempty"` + EnforcedByOwner *bool `json:"enforced_by_owner,omitempty"` +} + +// EnableImmutableReleases enables immutable releases for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#enable-immutable-releases +// +//meta:operation PUT /repos/{owner}/{repo}/immutable-releases +func (s *RepositoriesService) EnableImmutableReleases(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/immutable-releases", owner, repo) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DisableImmutableReleases disables immutable releases for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#disable-immutable-releases +// +//meta:operation DELETE /repos/{owner}/{repo}/immutable-releases +func (s *RepositoriesService) DisableImmutableReleases(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/immutable-releases", owner, repo) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// AreImmutableReleasesEnabled checks if immutable releases are enabled for +// the repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#check-if-immutable-releases-are-enabled-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/immutable-releases +func (s *RepositoriesService) AreImmutableReleasesEnabled(ctx context.Context, owner, repo string) (*RepoImmutableReleasesStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/immutable-releases", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var status *RepoImmutableReleasesStatus + resp, err := s.client.Do(req, &status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} diff --git a/github/repos_immutable_releases_test.go b/github/repos_immutable_releases_test.go new file mode 100644 index 00000000000..9a5d352ba88 --- /dev/null +++ b/github/repos_immutable_releases_test.go @@ -0,0 +1,100 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_EnableImmutableReleases(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.EnableImmutableReleases(ctx, "owner", "repo") + if err != nil { + t.Errorf("Repositories.EnableImmutableReleases returned error: %v", err) + } + + const methodName = "EnableImmutableReleases" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EnableImmutableReleases(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.EnableImmutableReleases(ctx, "owner", "repo") + }) +} + +func TestRepositoriesService_DisableImmutableReleases(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.DisableImmutableReleases(ctx, "owner", "repo") + if err != nil { + t.Errorf("Repositories.DisableImmutableReleases returned error: %v", err) + } + + const methodName = "DisableImmutableReleases" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisableImmutableReleases(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DisableImmutableReleases(ctx, "owner", "repo") + }) +} + +func TestRepositoriesService_AreImmutableReleasesEnabled(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/owner/repo/immutable-releases", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled": true, "enforced_by_owner": false}`) + }) + + ctx := t.Context() + status, _, err := client.Repositories.AreImmutableReleasesEnabled(ctx, "owner", "repo") + if err != nil { + t.Errorf("Repositories.AreImmutableReleasesEnabled returned error: %v", err) + } + want := &RepoImmutableReleasesStatus{Enabled: Ptr(true), EnforcedByOwner: Ptr(false)} + if !cmp.Equal(status, want) { + t.Errorf("Repositories.AreImmutableReleasesEnabled returned %+v, want %+v", status, want) + } + + const methodName = "AreImmutableReleasesEnabled" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AreImmutableReleasesEnabled(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AreImmutableReleasesEnabled(ctx, "owner", "repo") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/repos_invitations.go b/github/repos_invitations.go index b88e9359f3e..9446101342a 100644 --- a/github/repos_invitations.go +++ b/github/repos_invitations.go @@ -23,25 +23,28 @@ type RepositoryInvitation struct { CreatedAt *Timestamp `json:"created_at,omitempty"` URL *string `json:"url,omitempty"` HTMLURL *string `json:"html_url,omitempty"` + Expired *bool `json:"expired,omitempty"` } // ListInvitations lists all currently-open repository invitations. // -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository -func (s *RepositoriesService) ListInvitations(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/collaborators/invitations?apiVersion=2022-11-28#list-repository-invitations +// +//meta:operation GET /repos/{owner}/{repo}/invitations +func (s *RepositoriesService) ListInvitations(ctx context.Context, owner, repo string, opts *ListOptions) ([]*RepositoryInvitation, *Response, error) { u := fmt.Sprintf("repos/%v/%v/invitations", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } invites := []*RepositoryInvitation{} - resp, err := s.client.Do(ctx, req, &invites) + resp, err := s.client.Do(req, &invites) if err != nil { return nil, resp, err } @@ -51,15 +54,17 @@ func (s *RepositoriesService) ListInvitations(ctx context.Context, owner, repo s // DeleteInvitation deletes a repository invitation. // -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#delete-a-repository-invitation +// GitHub API docs: https://docs.github.com/rest/collaborators/invitations?apiVersion=2022-11-28#delete-a-repository-invitation +// +//meta:operation DELETE /repos/{owner}/{repo}/invitations/{invitation_id} func (s *RepositoriesService) DeleteInvitation(ctx context.Context, owner, repo string, invitationID int64) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UpdateInvitation updates the permissions associated with a repository @@ -68,19 +73,21 @@ func (s *RepositoriesService) DeleteInvitation(ctx context.Context, owner, repo // permissions represents the permissions that the associated user will have // on the repository. Possible values are: "read", "write", "admin". // -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#update-a-repository-invitation +// GitHub API docs: https://docs.github.com/rest/collaborators/invitations?apiVersion=2022-11-28#update-a-repository-invitation +// +//meta:operation PATCH /repos/{owner}/{repo}/invitations/{invitation_id} func (s *RepositoriesService) UpdateInvitation(ctx context.Context, owner, repo string, invitationID int64, permissions string) (*RepositoryInvitation, *Response, error) { opts := &struct { Permissions string `json:"permissions"` }{Permissions: permissions} u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) - req, err := s.client.NewRequest("PATCH", u, opts) + req, err := s.client.NewRequest(ctx, "PATCH", u, opts) if err != nil { return nil, nil, err } - invite := &RepositoryInvitation{} - resp, err := s.client.Do(ctx, req, invite) + var invite *RepositoryInvitation + resp, err := s.client.Do(req, &invite) if err != nil { return nil, resp, err } diff --git a/github/repos_invitations_test.go b/github/repos_invitations_test.go index b3df03c3577..07d022a1f5c 100644 --- a/github/repos_invitations_test.go +++ b/github/repos_invitations_test.go @@ -6,66 +6,107 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListInvitations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/invitations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{"page": "2"}) - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) }) opt := &ListOptions{Page: 2} - got, _, err := client.Repositories.ListInvitations(context.Background(), "o", "r", opt) + ctx := t.Context() + got, _, err := client.Repositories.ListInvitations(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListInvitations returned error: %v", err) } - want := []*RepositoryInvitation{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(got, want) { + want := []*RepositoryInvitation{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListInvitations = %+v, want %+v", got, want) } + + const methodName = "ListInvitations" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListInvitations(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListInvitations(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_DeleteInvitation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/invitations/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Repositories.DeleteInvitation(context.Background(), "o", "r", 2) + ctx := t.Context() + _, err := client.Repositories.DeleteInvitation(ctx, "o", "r", 2) if err != nil { t.Errorf("Repositories.DeleteInvitation returned error: %v", err) } + + const methodName = "DeleteInvitation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteInvitation(ctx, "\n", "\n", 2) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteInvitation(ctx, "o", "r", 2) + }) } func TestRepositoriesService_UpdateInvitation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/invitations/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - fmt.Fprintf(w, `{"id":1}`) + fmt.Fprint(w, `{"id":1}`) }) - got, _, err := client.Repositories.UpdateInvitation(context.Background(), "o", "r", 2, "write") + ctx := t.Context() + got, _, err := client.Repositories.UpdateInvitation(ctx, "o", "r", 2, "write") if err != nil { t.Errorf("Repositories.UpdateInvitation returned error: %v", err) } - want := &RepositoryInvitation{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { + want := &RepositoryInvitation{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { t.Errorf("Repositories.UpdateInvitation = %+v, want %+v", got, want) } + + const methodName = "UpdateInvitation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateInvitation(ctx, "\n", "\n", 2, "write") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateInvitation(ctx, "o", "r", 2, "write") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_keys.go b/github/repos_keys.go index b484f844463..ec456c4e6b2 100644 --- a/github/repos_keys.go +++ b/github/repos_keys.go @@ -14,21 +14,23 @@ import ( // ListKeys lists the deploy keys for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/keys/#list -func (s *RepositoriesService) ListKeys(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Key, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/deploy-keys/deploy-keys?apiVersion=2022-11-28#list-deploy-keys +// +//meta:operation GET /repos/{owner}/{repo}/keys +func (s *RepositoriesService) ListKeys(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Key, *Response, error) { u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var keys []*Key - resp, err := s.client.Do(ctx, req, &keys) + resp, err := s.client.Do(req, &keys) if err != nil { return nil, resp, err } @@ -38,17 +40,19 @@ func (s *RepositoriesService) ListKeys(ctx context.Context, owner string, repo s // GetKey fetches a single deploy key. // -// GitHub API docs: https://developer.github.com/v3/repos/keys/#get -func (s *RepositoriesService) GetKey(ctx context.Context, owner string, repo string, id int64) (*Key, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/deploy-keys/deploy-keys?apiVersion=2022-11-28#get-a-deploy-key +// +//meta:operation GET /repos/{owner}/{repo}/keys/{key_id} +func (s *RepositoriesService) GetKey(ctx context.Context, owner, repo string, id int64) (*Key, *Response, error) { u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - key := new(Key) - resp, err := s.client.Do(ctx, req, key) + var key *Key + resp, err := s.client.Do(req, &key) if err != nil { return nil, resp, err } @@ -58,37 +62,19 @@ func (s *RepositoriesService) GetKey(ctx context.Context, owner string, repo str // CreateKey adds a deploy key for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/keys/#create -func (s *RepositoriesService) CreateKey(ctx context.Context, owner string, repo string, key *Key) (*Key, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) - - req, err := s.client.NewRequest("POST", u, key) - if err != nil { - return nil, nil, err - } - - k := new(Key) - resp, err := s.client.Do(ctx, req, k) - if err != nil { - return nil, resp, err - } - - return k, resp, nil -} - -// EditKey edits a deploy key. +// GitHub API docs: https://docs.github.com/rest/deploy-keys/deploy-keys?apiVersion=2022-11-28#create-a-deploy-key // -// GitHub API docs: https://developer.github.com/v3/repos/keys/#edit -func (s *RepositoriesService) EditKey(ctx context.Context, owner string, repo string, id int64, key *Key) (*Key, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) +//meta:operation POST /repos/{owner}/{repo}/keys +func (s *RepositoriesService) CreateKey(ctx context.Context, owner, repo string, body *Key) (*Key, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) - req, err := s.client.NewRequest("PATCH", u, key) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - k := new(Key) - resp, err := s.client.Do(ctx, req, k) + var k *Key + resp, err := s.client.Do(req, &k) if err != nil { return nil, resp, err } @@ -98,14 +84,16 @@ func (s *RepositoriesService) EditKey(ctx context.Context, owner string, repo st // DeleteKey deletes a deploy key. // -// GitHub API docs: https://developer.github.com/v3/repos/keys/#delete -func (s *RepositoriesService) DeleteKey(ctx context.Context, owner string, repo string, id int64) (*Response, error) { +// GitHub API docs: https://docs.github.com/rest/deploy-keys/deploy-keys?apiVersion=2022-11-28#delete-a-deploy-key +// +//meta:operation DELETE /repos/{owner}/{repo}/keys/{key_id} +func (s *RepositoriesService) DeleteKey(ctx context.Context, owner, repo string, id int64) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/repos_keys_test.go b/github/repos_keys_test.go index 75a17356edd..cee855a828e 100644 --- a/github/repos_keys_test.go +++ b/github/repos_keys_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListKeys(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/keys", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,145 +24,162 @@ func TestRepositoriesService_ListKeys(t *testing.T) { }) opt := &ListOptions{Page: 2} - keys, _, err := client.Repositories.ListKeys(context.Background(), "o", "r", opt) + ctx := t.Context() + keys, _, err := client.Repositories.ListKeys(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListKeys returned error: %v", err) } - want := []*Key{{ID: Int64(1)}} - if !reflect.DeepEqual(keys, want) { + want := []*Key{{ID: Ptr(int64(1))}} + if !cmp.Equal(keys, want) { t.Errorf("Repositories.ListKeys returned %+v, want %+v", keys, want) } + + const methodName = "ListKeys" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListKeys(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListKeys(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListKeys_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListKeys(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListKeys(ctx, "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_GetKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - key, _, err := client.Repositories.GetKey(context.Background(), "o", "r", 1) + ctx := t.Context() + key, _, err := client.Repositories.GetKey(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetKey returned error: %v", err) } - want := &Key{ID: Int64(1)} - if !reflect.DeepEqual(key, want) { + want := &Key{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { t.Errorf("Repositories.GetKey returned %+v, want %+v", key, want) } + + const methodName = "GetKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetKey(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetKey(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetKey_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.GetKey(context.Background(), "%", "%", 1) + ctx := t.Context() + _, _, err := client.Repositories.GetKey(ctx, "%", "%", 1) testURLParseError(t, err) } func TestRepositoriesService_CreateKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Key{Key: String("k"), Title: String("t")} + input := &Key{Key: Ptr("k"), Title: Ptr("t")} mux.HandleFunc("/repos/o/r/keys", func(w http.ResponseWriter, r *http.Request) { - v := new(Key) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - key, _, err := client.Repositories.CreateKey(context.Background(), "o", "r", input) + ctx := t.Context() + key, _, err := client.Repositories.CreateKey(ctx, "o", "r", input) if err != nil { - t.Errorf("Repositories.GetKey returned error: %v", err) + t.Errorf("Repositories.CreateKey returned error: %v", err) } - want := &Key{ID: Int64(1)} - if !reflect.DeepEqual(key, want) { - t.Errorf("Repositories.GetKey returned %+v, want %+v", key, want) + want := &Key{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { + t.Errorf("Repositories.CreateKey returned %+v, want %+v", key, want) } -} -func TestRepositoriesService_CreateKey_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() - - _, _, err := client.Repositories.CreateKey(context.Background(), "%", "%", nil) - testURLParseError(t, err) -} - -func TestRepositoriesService_EditKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &Key{Key: String("k"), Title: String("t")} - - mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { - v := new(Key) - json.NewDecoder(r.Body).Decode(v) + const methodName = "CreateKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateKey(ctx, "\n", "\n", input) + return err + }) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateKey(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } - - fmt.Fprint(w, `{"id":1}`) + return resp, err }) - - key, _, err := client.Repositories.EditKey(context.Background(), "o", "r", 1, input) - if err != nil { - t.Errorf("Repositories.EditKey returned error: %v", err) - } - - want := &Key{ID: Int64(1)} - if !reflect.DeepEqual(key, want) { - t.Errorf("Repositories.EditKey returned %+v, want %+v", key, want) - } } -func TestRepositoriesService_EditKey_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestRepositoriesService_CreateKey_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.EditKey(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Repositories.CreateKey(ctx, "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_DeleteKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/keys/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Repositories.DeleteKey(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteKey(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.DeleteKey returned error: %v", err) } + + const methodName = "DeleteKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteKey(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteKey(ctx, "o", "r", 1) + }) } func TestRepositoriesService_DeleteKey_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.DeleteKey(context.Background(), "%", "%", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteKey(ctx, "%", "%", 1) testURLParseError(t, err) } diff --git a/github/repos_lfs.go b/github/repos_lfs.go new file mode 100644 index 00000000000..685e17b277a --- /dev/null +++ b/github/repos_lfs.go @@ -0,0 +1,53 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// EnableLFS turns the LFS (Large File Storage) feature ON for the selected repo. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/repos/lfs?apiVersion=2022-11-28#enable-git-lfs-for-a-repository +// +//meta:operation PUT /repos/{owner}/{repo}/lfs +func (s *RepositoriesService) EnableLFS(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/lfs", owner, repo) + + req, err := s.client.NewRequest(ctx, "PUT", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DisableLFS turns the LFS (Large File Storage) feature OFF for the selected repo. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/repos/lfs?apiVersion=2022-11-28#disable-git-lfs-for-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/lfs +func (s *RepositoriesService) DisableLFS(ctx context.Context, owner, repo string) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/lfs", owner, repo) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/repos_lfs_test.go b/github/repos_lfs_test.go new file mode 100644 index 00000000000..4e4a5c1b95e --- /dev/null +++ b/github/repos_lfs_test.go @@ -0,0 +1,63 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "net/http" + "testing" +) + +func TestRepositoriesService_EnableLFS(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/lfs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Repositories.EnableLFS(ctx, "o", "r"); err != nil { + t.Errorf("Repositories.EnableLFS returned error: %v", err) + } + + const methodName = "EnableLFS" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EnableLFS(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.EnableLFS(ctx, "o", "r") + }) +} + +func TestRepositoriesService_DisableLFS(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/lfs", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + if _, err := client.Repositories.DisableLFS(ctx, "o", "r"); err != nil { + t.Errorf("Repositories.DisableLFS returned error: %v", err) + } + + const methodName = "DisableLFS" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisableLFS(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DisableLFS(ctx, "o", "r") + }) +} diff --git a/github/repos_merging.go b/github/repos_merging.go index 04383c1ae32..a1979542574 100644 --- a/github/repos_merging.go +++ b/github/repos_merging.go @@ -13,26 +13,64 @@ import ( // RepositoryMergeRequest represents a request to merge a branch in a // repository. type RepositoryMergeRequest struct { - Base *string `json:"base,omitempty"` - Head *string `json:"head,omitempty"` + Base string `json:"base"` + Head string `json:"head"` CommitMessage *string `json:"commit_message,omitempty"` } +// RepoMergeUpstreamRequest represents a request to sync a branch of +// a forked repository to keep it up-to-date with the upstream repository. +type RepoMergeUpstreamRequest struct { + Branch string `json:"branch"` +} + +// RepoMergeUpstreamResult represents the result of syncing a branch of +// a forked repository with the upstream repository. +type RepoMergeUpstreamResult struct { + Message *string `json:"message,omitempty"` + MergeType *string `json:"merge_type,omitempty"` + BaseBranch *string `json:"base_branch,omitempty"` +} + // Merge a branch in the specified repository. // -// GitHub API docs: https://developer.github.com/v3/repos/merging/#perform-a-merge -func (s *RepositoriesService) Merge(ctx context.Context, owner, repo string, request *RepositoryMergeRequest) (*RepositoryCommit, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/branches/branches?apiVersion=2022-11-28#merge-a-branch +// +//meta:operation POST /repos/{owner}/{repo}/merges +func (s *RepositoriesService) Merge(ctx context.Context, owner, repo string, body RepositoryMergeRequest) (*RepositoryCommit, *Response, error) { u := fmt.Sprintf("repos/%v/%v/merges", owner, repo) - req, err := s.client.NewRequest("POST", u, request) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - commit := new(RepositoryCommit) - resp, err := s.client.Do(ctx, req, commit) + var commit *RepositoryCommit + resp, err := s.client.Do(req, &commit) if err != nil { return nil, resp, err } return commit, resp, nil } + +// MergeUpstream syncs a branch of a forked repository to keep it up-to-date +// with the upstream repository. +// +// GitHub API docs: https://docs.github.com/rest/branches/branches?apiVersion=2022-11-28#sync-a-fork-branch-with-the-upstream-repository +// +//meta:operation POST /repos/{owner}/{repo}/merge-upstream +func (s *RepositoriesService) MergeUpstream(ctx context.Context, owner, repo string, body RepoMergeUpstreamRequest) (*RepoMergeUpstreamResult, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/merge-upstream", owner, repo) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var result *RepoMergeUpstreamResult + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} diff --git a/github/repos_merging_test.go b/github/repos_merging_test.go index 086f24bbc3e..f71a65741d6 100644 --- a/github/repos_merging_test.go +++ b/github/repos_merging_test.go @@ -6,43 +6,91 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_Merge(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &RepositoryMergeRequest{ - Base: String("b"), - Head: String("h"), - CommitMessage: String("c"), + input := RepositoryMergeRequest{ + Base: "b", + Head: "h", + CommitMessage: Ptr("c"), } mux.HandleFunc("/repos/o/r/merges", func(w http.ResponseWriter, r *http.Request) { - v := new(RepositoryMergeRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"sha":"s"}`) }) - commit, _, err := client.Repositories.Merge(context.Background(), "o", "r", input) + ctx := t.Context() + commit, _, err := client.Repositories.Merge(ctx, "o", "r", input) if err != nil { t.Errorf("Repositories.Merge returned error: %v", err) } - want := &RepositoryCommit{SHA: String("s")} - if !reflect.DeepEqual(commit, want) { + want := &RepositoryCommit{SHA: Ptr("s")} + if !cmp.Equal(commit, want) { t.Errorf("Repositories.Merge returned %+v, want %+v", commit, want) } + + const methodName = "Merge" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Merge(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.Merge(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_MergeUpstream(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepoMergeUpstreamRequest{ + Branch: "b", + } + + mux.HandleFunc("/repos/o/r/merge-upstream", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"merge_type":"m"}`) + }) + + ctx := t.Context() + result, _, err := client.Repositories.MergeUpstream(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.MergeUpstream returned error: %v", err) + } + + want := &RepoMergeUpstreamResult{MergeType: Ptr("m")} + if !cmp.Equal(result, want) { + t.Errorf("Repositories.MergeUpstream returned %+v, want %+v", result, want) + } + + const methodName = "MergeUpstream" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.MergeUpstream(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.MergeUpstream(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_pages.go b/github/repos_pages.go index 28674130d6a..49b01a6baf8 100644 --- a/github/repos_pages.go +++ b/github/repos_pages.go @@ -7,18 +7,22 @@ package github import ( "context" + "errors" "fmt" - "strings" ) // Pages represents a GitHub Pages site configuration. type Pages struct { - URL *string `json:"url,omitempty"` - Status *string `json:"status,omitempty"` - CNAME *string `json:"cname,omitempty"` - Custom404 *bool `json:"custom_404,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - Source *PagesSource `json:"source,omitempty"` + URL *string `json:"url,omitempty"` + Status *string `json:"status,omitempty"` + CNAME *string `json:"cname,omitempty"` + Custom404 *bool `json:"custom_404,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + BuildType *string `json:"build_type,omitempty"` + Source *PagesSource `json:"source,omitempty"` + Public *bool `json:"public,omitempty"` + HTTPSCertificate *PagesHTTPSCertificate `json:"https_certificate,omitempty"` + HTTPSEnforced *bool `json:"https_enforced,omitempty"` } // PagesSource represents a GitHub page's source. @@ -44,22 +48,86 @@ type PagesBuild struct { UpdatedAt *Timestamp `json:"updated_at,omitempty"` } +// PagesDomain represents a domain associated with a GitHub Pages site. +type PagesDomain struct { + Host *string `json:"host,omitempty"` + URI *string `json:"uri,omitempty"` + Nameservers *string `json:"nameservers,omitempty"` + DNSResolves *bool `json:"dns_resolves,omitempty"` + IsProxied *bool `json:"is_proxied,omitempty"` + IsCloudflareIP *bool `json:"is_cloudflare_ip,omitempty"` + IsFastlyIP *bool `json:"is_fastly_ip,omitempty"` + IsOldIPAddress *bool `json:"is_old_ip_address,omitempty"` + IsARecord *bool `json:"is_a_record,omitempty"` + HasCNAMERecord *bool `json:"has_cname_record,omitempty"` + HasMXRecordsPresent *bool `json:"has_mx_records_present,omitempty"` + IsValidDomain *bool `json:"is_valid_domain,omitempty"` + IsApexDomain *bool `json:"is_apex_domain,omitempty"` + ShouldBeARecord *bool `json:"should_be_a_record,omitempty"` + IsCNAMEToGithubUserDomain *bool `json:"is_cname_to_github_user_domain,omitempty"` + IsCNAMEToPagesDotGithubDotCom *bool `json:"is_cname_to_pages_dot_github_dot_com,omitempty"` + IsCNAMEToFastly *bool `json:"is_cname_to_fastly,omitempty"` + IsPointedToGithubPagesIP *bool `json:"is_pointed_to_github_pages_ip,omitempty"` + IsNonGithubPagesIPPresent *bool `json:"is_non_github_pages_ip_present,omitempty"` + IsPagesDomain *bool `json:"is_pages_domain,omitempty"` + IsServedByPages *bool `json:"is_served_by_pages,omitempty"` + IsValid *bool `json:"is_valid,omitempty"` + Reason *string `json:"reason,omitempty"` + RespondsToHTTPS *bool `json:"responds_to_https,omitempty"` + EnforcesHTTPS *bool `json:"enforces_https,omitempty"` + HTTPSError *string `json:"https_error,omitempty"` + IsHTTPSEligible *bool `json:"is_https_eligible,omitempty"` + CAAError *string `json:"caa_error,omitempty"` +} + +// PagesHealthCheckResponse represents the response given for the health check of a GitHub Pages site. +type PagesHealthCheckResponse struct { + Domain *PagesDomain `json:"domain,omitempty"` + AltDomain *PagesDomain `json:"alt_domain,omitempty"` +} + +// PagesHTTPSCertificate represents the HTTPS Certificate information for a GitHub Pages site. +type PagesHTTPSCertificate struct { + State *string `json:"state,omitempty"` + Description *string `json:"description,omitempty"` + Domains []string `json:"domains,omitempty"` + // GitHub's API doesn't return a standard Timestamp, rather it returns a YYYY-MM-DD string. + ExpiresAt *string `json:"expires_at,omitempty"` +} + +// createPagesRequest is a subset of Pages and is used internally +// by EnablePages to pass only the known fields for the endpoint. +type createPagesRequest struct { + BuildType *string `json:"build_type,omitempty"` + Source *PagesSource `json:"source,omitempty"` +} + // EnablePages enables GitHub Pages for the named repo. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#enable-a-pages-site -func (s *RepositoriesService) EnablePages(ctx context.Context, owner, repo string) (*Pages, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#create-a-github-pages-site +// +//meta:operation POST /repos/{owner}/{repo}/pages +func (s *RepositoriesService) EnablePages(ctx context.Context, owner, repo string, pages *Pages) (*Pages, *Response, error) { + if pages == nil { + return nil, nil, errors.New("pages must be provided") + } + u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) - req, err := s.client.NewRequest("POST", u, nil) + + pagesReq := &createPagesRequest{ + BuildType: pages.BuildType, + Source: pages.Source, + } + + req, err := s.client.NewRequest(ctx, "POST", u, pagesReq) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeEnablePagesAPIPreview, mediaTypePagesPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeEnablePagesAPIPreview) - enable := new(Pages) - resp, err := s.client.Do(ctx, req, enable) + var enable *Pages + resp, err := s.client.Do(req, &enable) if err != nil { return nil, resp, err } @@ -67,37 +135,109 @@ func (s *RepositoriesService) EnablePages(ctx context.Context, owner, repo strin return enable, resp, nil } +// PagesUpdate sets up parameters needed to update a GitHub Pages site. +type PagesUpdate struct { + // CNAME represents a custom domain for the repository. + // Leaving CNAME empty will remove the custom domain. + CNAME *string `json:"cname"` + // BuildType is optional and can either be "legacy" or "workflow". + // "workflow" - You are using a github workflow to build your pages. + // "legacy" - You are deploying from a branch. + BuildType *string `json:"build_type,omitempty"` + // Source must include the branch name, and may optionally specify the subdirectory "/docs". + // Possible values for Source.Branch are usually "gh-pages", "main", and "master", + // or any other existing branch name. + // Possible values for Source.Path are: "/", and "/docs". + Source *PagesSource `json:"source,omitempty"` + // Public configures access controls for the site. + // If "true", the site will be accessible to anyone on the internet. If "false", + // the site will be accessible to anyone with read access to the repository that + // published the site. + Public *bool `json:"public,omitempty"` + // HTTPSEnforced specifies whether HTTPS should be enforced for the repository. + HTTPSEnforced *bool `json:"https_enforced,omitempty"` +} + +// UpdatePages updates GitHub Pages for the named repo. +// +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#update-information-about-a-github-pages-site +// +//meta:operation PUT /repos/{owner}/{repo}/pages +func (s *RepositoriesService) UpdatePages(ctx context.Context, owner, repo string, body *PagesUpdate) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// PagesUpdateWithoutCNAME defines parameters for updating a GitHub Pages site on GitHub Enterprise Servers. +// Sending a request with a CNAME (any value, empty string, or null) results in a 400 error: "Custom domains are not available for GitHub Pages". +type PagesUpdateWithoutCNAME struct { + BuildType *string `json:"build_type,omitempty"` + Source *PagesSource `json:"source,omitempty"` + Public *bool `json:"public,omitempty"` + HTTPSEnforced *bool `json:"https_enforced,omitempty"` +} + +// UpdatePagesGHES updates GitHub Pages for the named repo in GitHub Enterprise Servers. +// +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#update-information-about-a-github-pages-site +// +//meta:operation PUT /repos/{owner}/{repo}/pages +func (s *RepositoriesService) UpdatePagesGHES(ctx context.Context, owner, repo string, body *PagesUpdateWithoutCNAME) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + // DisablePages disables GitHub Pages for the named repo. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#disable-a-pages-site +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#delete-a-github-pages-site +// +//meta:operation DELETE /repos/{owner}/{repo}/pages func (s *RepositoriesService) DisablePages(ctx context.Context, owner, repo string) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeEnablePagesAPIPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // GetPagesInfo fetches information about a GitHub Pages site. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#get-a-github-pages-site +// +//meta:operation GET /repos/{owner}/{repo}/pages func (s *RepositoriesService) GetPagesInfo(ctx context.Context, owner, repo string) (*Pages, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypePagesPreview) - - site := new(Pages) - resp, err := s.client.Do(ctx, req, site) + var site *Pages + resp, err := s.client.Do(req, &site) if err != nil { return nil, resp, err } @@ -107,21 +247,23 @@ func (s *RepositoriesService) GetPagesInfo(ctx context.Context, owner, repo stri // ListPagesBuilds lists the builds for a GitHub Pages site. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds -func (s *RepositoriesService) ListPagesBuilds(ctx context.Context, owner, repo string, opt *ListOptions) ([]*PagesBuild, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#list-github-pages-builds +// +//meta:operation GET /repos/{owner}/{repo}/pages/builds +func (s *RepositoriesService) ListPagesBuilds(ctx context.Context, owner, repo string, opts *ListOptions) ([]*PagesBuild, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var pages []*PagesBuild - resp, err := s.client.Do(ctx, req, &pages) + resp, err := s.client.Do(req, &pages) if err != nil { return nil, resp, err } @@ -131,16 +273,18 @@ func (s *RepositoriesService) ListPagesBuilds(ctx context.Context, owner, repo s // GetLatestPagesBuild fetches the latest build information for a GitHub pages site. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#get-latest-pages-build +// +//meta:operation GET /repos/{owner}/{repo}/pages/builds/latest func (s *RepositoriesService) GetLatestPagesBuild(ctx context.Context, owner, repo string) (*PagesBuild, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - build := new(PagesBuild) - resp, err := s.client.Do(ctx, req, build) + var build *PagesBuild + resp, err := s.client.Do(req, &build) if err != nil { return nil, resp, err } @@ -150,16 +294,18 @@ func (s *RepositoriesService) GetLatestPagesBuild(ctx context.Context, owner, re // GetPageBuild fetches the specific build information for a GitHub pages site. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#get-github-pages-build +// +//meta:operation GET /repos/{owner}/{repo}/pages/builds/{build_id} func (s *RepositoriesService) GetPageBuild(ctx context.Context, owner, repo string, id int64) (*PagesBuild, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - build := new(PagesBuild) - resp, err := s.client.Do(ctx, req, build) + var build *PagesBuild + resp, err := s.client.Do(req, &build) if err != nil { return nil, resp, err } @@ -169,22 +315,42 @@ func (s *RepositoriesService) GetPageBuild(ctx context.Context, owner, repo stri // RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit. // -// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#request-a-github-pages-build +// +//meta:operation POST /repos/{owner}/{repo}/pages/builds func (s *RepositoriesService) RequestPageBuild(ctx context.Context, owner, repo string) (*PagesBuild, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) - req, err := s.client.NewRequest("POST", u, nil) + req, err := s.client.NewRequest(ctx, "POST", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypePagesPreview) - - build := new(PagesBuild) - resp, err := s.client.Do(ctx, req, build) + var build *PagesBuild + resp, err := s.client.Do(req, &build) if err != nil { return nil, resp, err } return build, resp, nil } + +// GetPageHealthCheck gets a DNS health check for the CNAME record configured for a repository's GitHub Pages. +// +// GitHub API docs: https://docs.github.com/rest/pages/pages?apiVersion=2022-11-28#get-a-dns-health-check-for-github-pages +// +//meta:operation GET /repos/{owner}/{repo}/pages/health +func (s *RepositoriesService) GetPageHealthCheck(ctx context.Context, owner, repo string) (*PagesHealthCheckResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages/health", owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var healthCheckResponse *PagesHealthCheckResponse + resp, err := s.client.Do(req, &healthCheckResponse) + if err != nil { + return nil, resp, err + } + + return healthCheckResponse, resp, nil +} diff --git a/github/repos_pages_test.go b/github/repos_pages_test.go index 62d42470b0e..227cbcb688b 100644 --- a/github/repos_pages_test.go +++ b/github/repos_pages_test.go @@ -6,96 +6,326 @@ package github import ( - "context" "fmt" "net/http" - "reflect" - "strings" "testing" + + "github.com/google/go-cmp/cmp" ) -func TestRepositoriesService_EnablePages(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_EnablePagesLegacy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &Pages{ + BuildType: Ptr("legacy"), + Source: &PagesSource{ + Branch: Ptr("master"), + Path: Ptr("/"), + }, + CNAME: Ptr("www.example.com"), // not passed along. + } mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - wantAcceptHeaders := []string{mediaTypeEnablePagesAPIPreview, mediaTypePagesPreview} - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h", "source": {"branch":"master", "path":"/"}}`) + testHeader(t, r, "Accept", mediaTypeEnablePagesAPIPreview) + want := &createPagesRequest{BuildType: Ptr("legacy"), Source: &PagesSource{Branch: Ptr("master"), Path: Ptr("/")}} + testJSONBody(t, r, want) + + fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h","build_type": "legacy","source": {"branch":"master", "path":"/"}}`) }) - page, _, err := client.Repositories.EnablePages(context.Background(), "o", "r") + ctx := t.Context() + page, _, err := client.Repositories.EnablePages(ctx, "o", "r", input) if err != nil { t.Errorf("Repositories.EnablePages returned error: %v", err) } - want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h"), Source: &PagesSource{Branch: String("master"), Path: String("/")}} + want := &Pages{URL: Ptr("u"), Status: Ptr("s"), CNAME: Ptr("c"), Custom404: Ptr(false), HTMLURL: Ptr("h"), BuildType: Ptr("legacy"), Source: &PagesSource{Branch: Ptr("master"), Path: Ptr("/")}} - if !reflect.DeepEqual(page, want) { + if !cmp.Equal(page, want) { t.Errorf("Repositories.EnablePages returned %v, want %v", page, want) } + + const methodName = "EnablePages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.EnablePages(ctx, "o", "r", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.EnablePages(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.EnablePages(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_DisablePages(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_EnablePagesWorkflow(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &Pages{ + BuildType: Ptr("workflow"), + CNAME: Ptr("www.example.com"), // not passed along. + } + + mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeEnablePagesAPIPreview) + want := &createPagesRequest{BuildType: Ptr("workflow")} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h","build_type": "workflow"}`) + }) + + ctx := t.Context() + page, _, err := client.Repositories.EnablePages(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.EnablePages returned error: %v", err) + } + + want := &Pages{URL: Ptr("u"), Status: Ptr("s"), CNAME: Ptr("c"), Custom404: Ptr(false), HTMLURL: Ptr("h"), BuildType: Ptr("workflow")} + + if !cmp.Equal(page, want) { + t.Errorf("Repositories.EnablePages returned %v, want %v", page, want) + } + + const methodName = "EnablePages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.EnablePages(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.EnablePages(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdatePagesLegacy(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PagesUpdate{ + CNAME: Ptr("www.example.com"), + BuildType: Ptr("legacy"), + Source: &PagesSource{Branch: Ptr("gh-pages")}, + } + + mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"cname":"www.example.com","build_type":"legacy","source":{"branch":"gh-pages"}}`) + }) + + ctx := t.Context() + _, err := client.Repositories.UpdatePages(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.UpdatePages returned error: %v", err) + } + + const methodName = "UpdatePages" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.UpdatePages(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.UpdatePages(ctx, "o", "r", input) + }) +} + +func TestRepositoriesService_UpdatePagesWorkflow(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PagesUpdate{ + CNAME: Ptr("www.example.com"), + BuildType: Ptr("workflow"), + } + + mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"cname":"www.example.com","build_type":"workflow"}`) + }) + + ctx := t.Context() + _, err := client.Repositories.UpdatePages(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.UpdatePages returned error: %v", err) + } + + const methodName = "UpdatePages" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.UpdatePages(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.UpdatePages(ctx, "o", "r", input) + }) +} + +func TestRepositoriesService_UpdatePagesGHES(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PagesUpdateWithoutCNAME{ + BuildType: Ptr("workflow"), + } mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"build_type":"workflow"}`) + }) + + ctx := t.Context() + _, err := client.Repositories.UpdatePagesGHES(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.UpdatePagesGHES returned error: %v", err) + } + + const methodName = "UpdatePagesGHES" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.UpdatePagesGHES(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.UpdatePagesGHES(ctx, "o", "r", input) + }) +} + +func TestRepositoriesService_UpdatePages_NullCNAME(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PagesUpdate{ + Source: &PagesSource{Branch: Ptr("gh-pages")}, + } + + mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"cname":null,"source":{"branch":"gh-pages"}}`) + }) + + ctx := t.Context() + _, err := client.Repositories.UpdatePages(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.UpdatePages returned error: %v", err) + } +} + +func TestRepositoriesService_DisablePages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/pages", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") testHeader(t, r, "Accept", mediaTypeEnablePagesAPIPreview) }) - _, err := client.Repositories.DisablePages(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Repositories.DisablePages(ctx, "o", "r") if err != nil { t.Errorf("Repositories.DisablePages returned error: %v", err) } + + const methodName = "DisablePages" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisablePages(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DisablePages(ctx, "o", "r") + }) } func TestRepositoriesService_GetPagesInfo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypePagesPreview) - fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h"}`) + fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h","public":true, "https_certificate": {"state":"approved","description": "Certificate is approved","domains": ["developer.github.com"],"expires_at": "2021-05-22"},"https_enforced": true}`) }) - page, _, err := client.Repositories.GetPagesInfo(context.Background(), "o", "r") + ctx := t.Context() + page, _, err := client.Repositories.GetPagesInfo(ctx, "o", "r") if err != nil { t.Errorf("Repositories.GetPagesInfo returned error: %v", err) } - want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h")} - if !reflect.DeepEqual(page, want) { + want := &Pages{URL: Ptr("u"), Status: Ptr("s"), CNAME: Ptr("c"), Custom404: Ptr(false), HTMLURL: Ptr("h"), Public: Ptr(true), HTTPSCertificate: &PagesHTTPSCertificate{State: Ptr("approved"), Description: Ptr("Certificate is approved"), Domains: []string{"developer.github.com"}, ExpiresAt: Ptr("2021-05-22")}, HTTPSEnforced: Ptr(true)} + if !cmp.Equal(page, want) { t.Errorf("Repositories.GetPagesInfo returned %+v, want %+v", page, want) } + + const methodName = "GetPagesInfo" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPagesInfo(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPagesInfo(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListPagesBuilds(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"url":"u","status":"s","commit":"c"}]`) }) - pages, _, err := client.Repositories.ListPagesBuilds(context.Background(), "o", "r", nil) + ctx := t.Context() + pages, _, err := client.Repositories.ListPagesBuilds(ctx, "o", "r", nil) if err != nil { t.Errorf("Repositories.ListPagesBuilds returned error: %v", err) } - want := []*PagesBuild{{URL: String("u"), Status: String("s"), Commit: String("c")}} - if !reflect.DeepEqual(pages, want) { + want := []*PagesBuild{{URL: Ptr("u"), Status: Ptr("s"), Commit: Ptr("c")}} + if !cmp.Equal(pages, want) { t.Errorf("Repositories.ListPagesBuilds returned %+v, want %+v", pages, want) } + + const methodName = "ListPagesBuilds" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListPagesBuilds(ctx, "\n", "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListPagesBuilds(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListPagesBuilds_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -105,69 +335,162 @@ func TestRepositoriesService_ListPagesBuilds_withOptions(t *testing.T) { fmt.Fprint(w, `[]`) }) - _, _, err := client.Repositories.ListPagesBuilds(context.Background(), "o", "r", &ListOptions{Page: 2}) + ctx := t.Context() + _, _, err := client.Repositories.ListPagesBuilds(ctx, "o", "r", &ListOptions{Page: 2}) if err != nil { t.Errorf("Repositories.ListPagesBuilds returned error: %v", err) } } func TestRepositoriesService_GetLatestPagesBuild(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pages/builds/latest", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"url":"u","status":"s","commit":"c"}`) }) - build, _, err := client.Repositories.GetLatestPagesBuild(context.Background(), "o", "r") + ctx := t.Context() + build, _, err := client.Repositories.GetLatestPagesBuild(ctx, "o", "r") if err != nil { t.Errorf("Repositories.GetLatestPagesBuild returned error: %v", err) } - want := &PagesBuild{URL: String("u"), Status: String("s"), Commit: String("c")} - if !reflect.DeepEqual(build, want) { + want := &PagesBuild{URL: Ptr("u"), Status: Ptr("s"), Commit: Ptr("c")} + if !cmp.Equal(build, want) { t.Errorf("Repositories.GetLatestPagesBuild returned %+v, want %+v", build, want) } + + const methodName = "GetLatestPagesBuild" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetLatestPagesBuild(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetLatestPagesBuild(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetPageBuild(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pages/builds/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"url":"u","status":"s","commit":"c"}`) }) - build, _, err := client.Repositories.GetPageBuild(context.Background(), "o", "r", 1) + ctx := t.Context() + build, _, err := client.Repositories.GetPageBuild(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetPageBuild returned error: %v", err) } - want := &PagesBuild{URL: String("u"), Status: String("s"), Commit: String("c")} - if !reflect.DeepEqual(build, want) { + want := &PagesBuild{URL: Ptr("u"), Status: Ptr("s"), Commit: Ptr("c")} + if !cmp.Equal(build, want) { t.Errorf("Repositories.GetPageBuild returned %+v, want %+v", build, want) } + + const methodName = "GetPageBuild" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPageBuild(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPageBuild(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_RequestPageBuild(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypePagesPreview) fmt.Fprint(w, `{"url":"u","status":"s"}`) }) - build, _, err := client.Repositories.RequestPageBuild(context.Background(), "o", "r") + ctx := t.Context() + build, _, err := client.Repositories.RequestPageBuild(ctx, "o", "r") if err != nil { t.Errorf("Repositories.RequestPageBuild returned error: %v", err) } - want := &PagesBuild{URL: String("u"), Status: String("s")} - if !reflect.DeepEqual(build, want) { + want := &PagesBuild{URL: Ptr("u"), Status: Ptr("s")} + if !cmp.Equal(build, want) { t.Errorf("Repositories.RequestPageBuild returned %+v, want %+v", build, want) } + + const methodName = "RequestPageBuild" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RequestPageBuild(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RequestPageBuild(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetPageHealthCheck(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/pages/health", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"domain":{"host":"example.com","uri":"http://example.com/","nameservers":"default","dns_resolves":true},"alt_domain":{"host":"www.example.com","uri":"http://www.example.com/","nameservers":"default","dns_resolves":true}}`) + }) + + ctx := t.Context() + healthCheckResponse, _, err := client.Repositories.GetPageHealthCheck(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetPageHealthCheck returned error: %v", err) + } + + want := &PagesHealthCheckResponse{ + Domain: &PagesDomain{ + Host: Ptr("example.com"), + URI: Ptr("http://example.com/"), + Nameservers: Ptr("default"), + DNSResolves: Ptr(true), + }, + AltDomain: &PagesDomain{ + Host: Ptr("www.example.com"), + URI: Ptr("http://www.example.com/"), + Nameservers: Ptr("default"), + DNSResolves: Ptr(true), + }, + } + if !cmp.Equal(healthCheckResponse, want) { + t.Errorf("Repositories.GetPageHealthCheck returned %+v, want %+v", healthCheckResponse, want) + } + + const methodName = "GetPageHealthCheck" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPageHealthCheck(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPageHealthCheck(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_prereceive_hooks.go b/github/repos_prereceive_hooks.go index cab09f74791..7786a6c5f14 100644 --- a/github/repos_prereceive_hooks.go +++ b/github/repos_prereceive_hooks.go @@ -22,26 +22,33 @@ func (p PreReceiveHook) String() string { return Stringify(p) } +// UpdatePreReceiveHookRequest represents a request to update the enforcement +// of a pre-receive hook for a repository. +type UpdatePreReceiveHookRequest struct { + Enforcement *string `json:"enforcement,omitempty"` +} + // ListPreReceiveHooks lists all pre-receive hooks for the specified repository. // -// GitHub API docs: https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/#list-pre-receive-hooks -func (s *RepositoriesService) ListPreReceiveHooks(ctx context.Context, owner, repo string, opt *ListOptions) ([]*PreReceiveHook, *Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#list-pre-receive-hooks-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/pre-receive-hooks +func (s *RepositoriesService) ListPreReceiveHooks(ctx context.Context, owner, repo string, opts *ListOptions) ([]*PreReceiveHook, *Response, error) { u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypePreReceiveHooksPreview) var hooks []*PreReceiveHook - resp, err := s.client.Do(ctx, req, &hooks) + resp, err := s.client.Do(req, &hooks) if err != nil { return nil, resp, err } @@ -51,19 +58,20 @@ func (s *RepositoriesService) ListPreReceiveHooks(ctx context.Context, owner, re // GetPreReceiveHook returns a single specified pre-receive hook. // -// GitHub API docs: https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/#get-a-single-pre-receive-hook +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#get-a-pre-receive-hook-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/pre-receive-hooks/{pre_receive_hook_id} func (s *RepositoriesService) GetPreReceiveHook(ctx context.Context, owner, repo string, id int64) (*PreReceiveHook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypePreReceiveHooksPreview) - h := new(PreReceiveHook) - resp, err := s.client.Do(ctx, req, h) + var h *PreReceiveHook + resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } @@ -73,19 +81,20 @@ func (s *RepositoriesService) GetPreReceiveHook(ctx context.Context, owner, repo // UpdatePreReceiveHook updates a specified pre-receive hook. // -// GitHub API docs: https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/#update-pre-receive-hook-enforcement -func (s *RepositoriesService) UpdatePreReceiveHook(ctx context.Context, owner, repo string, id int64, hook *PreReceiveHook) (*PreReceiveHook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, hook) +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#update-pre-receive-hook-enforcement-for-a-repository +// +//meta:operation PATCH /repos/{owner}/{repo}/pre-receive-hooks/{pre_receive_hook_id} +func (s *RepositoriesService) UpdatePreReceiveHook(ctx context.Context, owner, repo string, id int64, body UpdatePreReceiveHookRequest) (*PreReceiveHook, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypePreReceiveHooksPreview) - h := new(PreReceiveHook) - resp, err := s.client.Do(ctx, req, h) + var h *PreReceiveHook + resp, err := s.client.Do(req, &h) if err != nil { return nil, resp, err } @@ -95,16 +104,17 @@ func (s *RepositoriesService) UpdatePreReceiveHook(ctx context.Context, owner, r // DeletePreReceiveHook deletes a specified pre-receive hook. // -// GitHub API docs: https://developer.github.com/enterprise/2.13/v3/repos/pre_receive_hooks/#remove-enforcement-overrides-for-a-pre-receive-hook +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#remove-pre-receive-hook-enforcement-for-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/pre-receive-hooks/{pre_receive_hook_id} func (s *RepositoriesService) DeletePreReceiveHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) + u := fmt.Sprintf("repos/%v/%v/pre-receive-hooks/%v", owner, repo, id) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypePreReceiveHooksPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/repos_prereceive_hooks_test.go b/github/repos_prereceive_hooks_test.go index 43ff6146d00..15ee94702aa 100644 --- a/github/repos_prereceive_hooks_test.go +++ b/github/repos_prereceive_hooks_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListPreReceiveHooks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pre-receive-hooks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,28 +26,44 @@ func TestRepositoriesService_ListPreReceiveHooks(t *testing.T) { opt := &ListOptions{Page: 2} - hooks, _, err := client.Repositories.ListPreReceiveHooks(context.Background(), "o", "r", opt) + ctx := t.Context() + hooks, _, err := client.Repositories.ListPreReceiveHooks(ctx, "o", "r", opt) if err != nil { - t.Errorf("Repositories.ListHooks returned error: %v", err) + t.Errorf("Repositories.ListPreReceiveHooks returned error: %v", err) } - want := []*PreReceiveHook{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(hooks, want) { + want := []*PreReceiveHook{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(hooks, want) { t.Errorf("Repositories.ListPreReceiveHooks returned %+v, want %+v", hooks, want) } + + const methodName = "ListPreReceiveHooks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListPreReceiveHooks(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListPreReceiveHooks(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListPreReceiveHooks_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListPreReceiveHooks(context.Background(), "%", "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListPreReceiveHooks(ctx, "%", "%", nil) testURLParseError(t, err) } func TestRepositoriesService_GetPreReceiveHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/pre-receive-hooks/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -56,80 +71,118 @@ func TestRepositoriesService_GetPreReceiveHook(t *testing.T) { fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Repositories.GetPreReceiveHook(context.Background(), "o", "r", 1) + ctx := t.Context() + hook, _, err := client.Repositories.GetPreReceiveHook(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetPreReceiveHook returned error: %v", err) } - want := &PreReceiveHook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &PreReceiveHook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Repositories.GetPreReceiveHook returned %+v, want %+v", hook, want) } + + const methodName = "GetPreReceiveHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPreReceiveHook(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPreReceiveHook(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetPreReceiveHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.GetPreReceiveHook(context.Background(), "%", "%", 1) + ctx := t.Context() + _, _, err := client.Repositories.GetPreReceiveHook(ctx, "%", "%", 1) testURLParseError(t, err) } func TestRepositoriesService_UpdatePreReceiveHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &PreReceiveHook{} + input := UpdatePreReceiveHookRequest{Enforcement: Ptr("enabled")} mux.HandleFunc("/repos/o/r/pre-receive-hooks/1", func(w http.ResponseWriter, r *http.Request) { - v := new(PreReceiveHook) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - hook, _, err := client.Repositories.UpdatePreReceiveHook(context.Background(), "o", "r", 1, input) + ctx := t.Context() + hook, _, err := client.Repositories.UpdatePreReceiveHook(ctx, "o", "r", 1, input) if err != nil { t.Errorf("Repositories.UpdatePreReceiveHook returned error: %v", err) } - want := &PreReceiveHook{ID: Int64(1)} - if !reflect.DeepEqual(hook, want) { + want := &PreReceiveHook{ID: Ptr(int64(1))} + if !cmp.Equal(hook, want) { t.Errorf("Repositories.UpdatePreReceiveHook returned %+v, want %+v", hook, want) } + + const methodName = "UpdatePreReceiveHook" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdatePreReceiveHook(ctx, "\n", "\n", -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdatePreReceiveHook(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_PreReceiveHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.UpdatePreReceiveHook(context.Background(), "%", "%", 1, nil) + ctx := t.Context() + _, _, err := client.Repositories.UpdatePreReceiveHook(ctx, "%", "%", 1, UpdatePreReceiveHookRequest{}) testURLParseError(t, err) } func TestRepositoriesService_DeletePreReceiveHook(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/pre-receive-hooks/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/pre-receive-hooks/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Repositories.DeletePreReceiveHook(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.DeletePreReceiveHook(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.DeletePreReceiveHook returned error: %v", err) } + + const methodName = "DeletePreReceiveHook" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeletePreReceiveHook(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeletePreReceiveHook(ctx, "o", "r", 1) + }) } func TestRepositoriesService_DeletePreReceiveHook_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Repositories.DeletePreReceiveHook(context.Background(), "%", "%", 1) + ctx := t.Context() + _, err := client.Repositories.DeletePreReceiveHook(ctx, "%", "%", 1) testURLParseError(t, err) } diff --git a/github/repos_projects.go b/github/repos_projects.go deleted file mode 100644 index d6486d293c8..00000000000 --- a/github/repos_projects.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "fmt" -) - -// ProjectListOptions specifies the optional parameters to the -// OrganizationsService.ListProjects and RepositoriesService.ListProjects methods. -type ProjectListOptions struct { - // Indicates the state of the projects to return. Can be either open, closed, or all. Default: open - State string `url:"state,omitempty"` - - ListOptions -} - -// ListProjects lists the projects for a repo. -// -// GitHub API docs: https://developer.github.com/v3/projects/#list-repository-projects -func (s *RepositoriesService) ListProjects(ctx context.Context, owner, repo string, opt *ProjectListOptions) ([]*Project, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - var projects []*Project - resp, err := s.client.Do(ctx, req, &projects) - if err != nil { - return nil, resp, err - } - - return projects, resp, nil -} - -// CreateProject creates a GitHub Project for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/projects/#create-a-repository-project -func (s *RepositoriesService) CreateProject(ctx context.Context, owner, repo string, opt *ProjectOptions) (*Project, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) - if err != nil { - return nil, resp, err - } - - return project, resp, nil -} diff --git a/github/repos_projects_test.go b/github/repos_projects_test.go deleted file mode 100644 index 2d54eaa1040..00000000000 --- a/github/repos_projects_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "reflect" - "testing" -) - -func TestRepositoriesService_ListProjects(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/projects", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - testFormValues(t, r, values{"page": "2"}) - fmt.Fprint(w, `[{"id":1}]`) - }) - - opt := &ProjectListOptions{ListOptions: ListOptions{Page: 2}} - projects, _, err := client.Repositories.ListProjects(context.Background(), "o", "r", opt) - if err != nil { - t.Errorf("Repositories.ListProjects returned error: %v", err) - } - - want := []*Project{{ID: Int64(1)}} - if !reflect.DeepEqual(projects, want) { - t.Errorf("Repositories.ListProjects returned %+v, want %+v", projects, want) - } -} - -func TestRepositoriesService_CreateProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &ProjectOptions{Name: String("Project Name"), Body: String("Project body.")} - - mux.HandleFunc("/repos/o/r/projects", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeProjectsPreview) - - v := &ProjectOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - - fmt.Fprint(w, `{"id":1}`) - }) - - project, _, err := client.Repositories.CreateProject(context.Background(), "o", "r", input) - if err != nil { - t.Errorf("Repositories.CreateProject returned error: %v", err) - } - - want := &Project{ID: Int64(1)} - if !reflect.DeepEqual(project, want) { - t.Errorf("Repositories.CreateProject returned %+v, want %+v", project, want) - } -} diff --git a/github/repos_properties.go b/github/repos_properties.go new file mode 100644 index 00000000000..11be33bc995 --- /dev/null +++ b/github/repos_properties.go @@ -0,0 +1,60 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetAllCustomPropertyValues gets all custom property values that are set for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/custom-properties?apiVersion=2022-11-28#get-all-custom-property-values-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/properties/values +func (s *RepositoriesService) GetAllCustomPropertyValues(ctx context.Context, org, repo string) ([]*CustomPropertyValue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/properties/values", org, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var customPropertyValues []*CustomPropertyValue + resp, err := s.client.Do(req, &customPropertyValues) + if err != nil { + return nil, resp, err + } + + return customPropertyValues, resp, nil +} + +// CreateOrUpdateCustomProperties creates new or updates existing custom property values for a repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/custom-properties?apiVersion=2022-11-28#create-or-update-custom-property-values-for-a-repository +// +//meta:operation PATCH /repos/{owner}/{repo}/properties/values +func (s *RepositoriesService) CreateOrUpdateCustomProperties(ctx context.Context, org, repo string, customPropertyValues []*CustomPropertyValue) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/properties/values", org, repo) + + params := struct { + Properties []*CustomPropertyValue `json:"properties"` + }{ + Properties: customPropertyValues, + } + + req, err := s.client.NewRequest(ctx, "PATCH", u, params) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/github/repos_properties_test.go b/github/repos_properties_test.go new file mode 100644 index 00000000000..d075f4947cc --- /dev/null +++ b/github/repos_properties_test.go @@ -0,0 +1,108 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_GetAllCustomPropertyValues(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "property_name": "environment", + "value": "production" + }, + { + "property_name": "service", + "value": "web" + }, + { + "property_name": "languages", + "value": ["Go", "JavaScript"] + }, + { + "property_name": "null_property", + "value": null + } + ]`) + }) + + ctx := t.Context() + customPropertyValues, _, err := client.Repositories.GetAllCustomPropertyValues(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetAllCustomPropertyValues returned error: %v", err) + } + + want := []*CustomPropertyValue{ + { + PropertyName: "environment", + Value: "production", + }, + { + PropertyName: "service", + Value: "web", + }, + { + PropertyName: "languages", + Value: []string{"Go", "JavaScript"}, + }, + { + PropertyName: "null_property", + Value: nil, + }, + } + + if !cmp.Equal(customPropertyValues, want) { + t.Errorf("Repositories.GetAllCustomPropertyValues returned %+v, want %+v", customPropertyValues, want) + } + + const methodName = "GetAllCustomPropertyValues" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAllCustomPropertyValues(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateOrUpdateCustomProperties(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/usr/r/properties/values", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + repoCustomProperty := []*CustomPropertyValue{ + { + PropertyName: "environment", + Value: "production", + }, + } + _, err := client.Repositories.CreateOrUpdateCustomProperties(ctx, "usr", "r", repoCustomProperty) + if err != nil { + t.Errorf("Repositories.CreateOrUpdateCustomProperties returned error: %v", err) + } + + const methodName = "CreateOrUpdateCustomProperties" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.CreateOrUpdateCustomProperties(ctx, "usr", "r", repoCustomProperty) + }) +} diff --git a/github/repos_releases.go b/github/repos_releases.go index 5c0a1cea84f..889afb6da86 100644 --- a/github/repos_releases.go +++ b/github/repos_releases.go @@ -19,32 +19,51 @@ import ( // RepositoryRelease represents a GitHub release in a repository. type RepositoryRelease struct { - TagName *string `json:"tag_name,omitempty"` - TargetCommitish *string `json:"target_commitish,omitempty"` - Name *string `json:"name,omitempty"` - Body *string `json:"body,omitempty"` - Draft *bool `json:"draft,omitempty"` - Prerelease *bool `json:"prerelease,omitempty"` - - // The following fields are not used in CreateRelease or EditRelease: - ID *int64 `json:"id,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PublishedAt *Timestamp `json:"published_at,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - AssetsURL *string `json:"assets_url,omitempty"` - Assets []ReleaseAsset `json:"assets,omitempty"` - UploadURL *string `json:"upload_url,omitempty"` - ZipballURL *string `json:"zipball_url,omitempty"` - TarballURL *string `json:"tarball_url,omitempty"` - Author *User `json:"author,omitempty"` - NodeID *string `json:"node_id,omitempty"` + TagName string `json:"tag_name"` + TargetCommitish string `json:"target_commitish"` + Name *string `json:"name"` + Body *string `json:"body,omitempty"` + Draft bool `json:"draft"` + Prerelease bool `json:"prerelease"` + Immutable *bool `json:"immutable,omitempty"` + ID int64 `json:"id"` + CreatedAt Timestamp `json:"created_at"` + PublishedAt *Timestamp `json:"published_at"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + URL string `json:"url"` + HTMLURL string `json:"html_url"` + AssetsURL string `json:"assets_url"` + Assets []*ReleaseAsset `json:"assets"` + UploadURL string `json:"upload_url"` + ZipballURL *string `json:"zipball_url"` + TarballURL *string `json:"tarball_url"` + Author *User `json:"author"` + NodeID string `json:"node_id"` + BodyHTML *string `json:"body_html,omitempty"` + BodyText *string `json:"body_text,omitempty"` + MentionsCount *int `json:"mentions_count,omitempty"` + DiscussionURL *string `json:"discussion_url,omitempty"` + Reactions *Reactions `json:"reactions,omitempty"` } func (r RepositoryRelease) String() string { return Stringify(r) } +// RepositoryReleaseNotes represents a GitHub-generated release notes. +type RepositoryReleaseNotes struct { + Name string `json:"name"` + Body string `json:"body"` +} + +// GenerateNotesRequest represents the request to generate release notes. +type GenerateNotesRequest struct { + TagName string `json:"tag_name"` + PreviousTagName *string `json:"previous_tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + ConfigurationFilePath *string `json:"configuration_file_path,omitempty"` +} + // ReleaseAsset represents a GitHub release asset in a repository. type ReleaseAsset struct { ID *int64 `json:"id,omitempty"` @@ -60,6 +79,14 @@ type ReleaseAsset struct { BrowserDownloadURL *string `json:"browser_download_url,omitempty"` Uploader *User `json:"uploader,omitempty"` NodeID *string `json:"node_id,omitempty"` + Digest *string `json:"digest,omitempty"` +} + +// UpdateReleaseAssetRequest represents the request to update a release asset. +type UpdateReleaseAssetRequest struct { + Name *string `json:"name,omitempty"` + Label *string `json:"label,omitempty"` + State *string `json:"state,omitempty"` } func (r ReleaseAsset) String() string { @@ -68,21 +95,23 @@ func (r ReleaseAsset) String() string { // ListReleases lists the releases for a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository -func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#list-releases +// +//meta:operation GET /repos/{owner}/{repo}/releases +func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo string, opts *ListOptions) ([]*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases", owner, repo) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var releases []*RepositoryRelease - resp, err := s.client.Do(ctx, req, &releases) + resp, err := s.client.Do(req, &releases) if err != nil { return nil, resp, err } @@ -91,149 +120,175 @@ func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo stri // GetRelease fetches a single release. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#get-a-release +// +//meta:operation GET /repos/{owner}/{repo}/releases/{release_id} func (s *RepositoriesService) GetRelease(ctx context.Context, owner, repo string, id int64) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) + u := fmt.Sprintf("repos/%v/%v/releases/%v", owner, repo, id) return s.getSingleRelease(ctx, u) } // GetLatestRelease fetches the latest published release for the repository. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-the-latest-release +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#get-the-latest-release +// +//meta:operation GET /repos/{owner}/{repo}/releases/latest func (s *RepositoriesService) GetLatestRelease(ctx context.Context, owner, repo string) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/latest", owner, repo) + u := fmt.Sprintf("repos/%v/%v/releases/latest", owner, repo) return s.getSingleRelease(ctx, u) } // GetReleaseByTag fetches a release with the specified tag. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#get-a-release-by-tag-name +// +//meta:operation GET /repos/{owner}/{repo}/releases/tags/{tag} func (s *RepositoriesService) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag) + u := fmt.Sprintf("repos/%v/%v/releases/tags/%v", owner, repo, tag) return s.getSingleRelease(ctx, u) } +// GenerateReleaseNotes generates the release notes for the given tag. +// +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#generate-release-notes-content-for-a-release +// +//meta:operation POST /repos/{owner}/{repo}/releases/generate-notes +func (s *RepositoriesService) GenerateReleaseNotes(ctx context.Context, owner, repo string, body GenerateNotesRequest) (*RepositoryReleaseNotes, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/generate-notes", owner, repo) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var r *RepositoryReleaseNotes + resp, err := s.client.Do(req, &r) + if err != nil { + return nil, resp, err + } + + return r, resp, nil +} + func (s *RepositoriesService) getSingleRelease(ctx context.Context, url string) (*RepositoryRelease, *Response, error) { - req, err := s.client.NewRequest("GET", url, nil) + req, err := s.client.NewRequest(ctx, "GET", url, nil) if err != nil { return nil, nil, err } - release := new(RepositoryRelease) - resp, err := s.client.Do(ctx, req, release) + var release *RepositoryRelease + resp, err := s.client.Do(req, &release) if err != nil { return nil, resp, err } + return release, resp, nil } -// repositoryReleaseRequest is a subset of RepositoryRelease and -// is used internally by CreateRelease and EditRelease to pass -// only the known fields for these endpoints. -// -// See https://github.com/google/go-github/issues/992 for more -// information. -type repositoryReleaseRequest struct { +// CreateReleaseRequest represents a request to create a release in a repository. +type CreateReleaseRequest struct { + TagName string `json:"tag_name"` + TargetCommitish *string `json:"target_commitish,omitempty"` + Name *string `json:"name,omitempty"` + Body *string `json:"body,omitempty"` + Draft *bool `json:"draft,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + // MakeLatest can be one of: "true", "false", or "legacy". + MakeLatest *string `json:"make_latest,omitempty"` + DiscussionCategoryName *string `json:"discussion_category_name,omitempty"` + GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"` +} + +// UpdateReleaseRequest represents a request to update a release in a repository. +type UpdateReleaseRequest struct { TagName *string `json:"tag_name,omitempty"` TargetCommitish *string `json:"target_commitish,omitempty"` Name *string `json:"name,omitempty"` Body *string `json:"body,omitempty"` Draft *bool `json:"draft,omitempty"` Prerelease *bool `json:"prerelease,omitempty"` + // MakeLatest can be one of: "true", "false", or "legacy". + MakeLatest *string `json:"make_latest,omitempty"` + DiscussionCategoryName *string `json:"discussion_category_name,omitempty"` } // CreateRelease adds a new release for a repository. // -// Note that only a subset of the release fields are used. -// See RepositoryRelease for more information. +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#create-a-release // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#create-a-release -func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) - - releaseReq := &repositoryReleaseRequest{ - TagName: release.TagName, - TargetCommitish: release.TargetCommitish, - Name: release.Name, - Body: release.Body, - Draft: release.Draft, - Prerelease: release.Prerelease, - } +//meta:operation POST /repos/{owner}/{repo}/releases +func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, body CreateReleaseRequest) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases", owner, repo) - req, err := s.client.NewRequest("POST", u, releaseReq) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - r := new(RepositoryRelease) - resp, err := s.client.Do(ctx, req, r) + var r *RepositoryRelease + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } + return r, resp, nil } -// EditRelease edits a repository release. +// UpdateRelease updates a repository release. // -// Note that only a subset of the release fields are used. -// See RepositoryRelease for more information. +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#update-a-release // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release -func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo string, id int64, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) - - releaseReq := &repositoryReleaseRequest{ - TagName: release.TagName, - TargetCommitish: release.TargetCommitish, - Name: release.Name, - Body: release.Body, - Draft: release.Draft, - Prerelease: release.Prerelease, - } +//meta:operation PATCH /repos/{owner}/{repo}/releases/{release_id} +func (s *RepositoriesService) UpdateRelease(ctx context.Context, owner, repo string, id int64, body UpdateReleaseRequest) (*RepositoryRelease, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, releaseReq) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - r := new(RepositoryRelease) - resp, err := s.client.Do(ctx, req, r) + var r *RepositoryRelease + resp, err := s.client.Do(req, &r) if err != nil { return nil, resp, err } + return r, resp, nil } // DeleteRelease delete a single release from a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release +// GitHub API docs: https://docs.github.com/rest/releases/releases?apiVersion=2022-11-28#delete-a-release +// +//meta:operation DELETE /repos/{owner}/{repo}/releases/{release_id} func (s *RepositoriesService) DeleteRelease(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) + u := fmt.Sprintf("repos/%v/%v/releases/%v", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListReleaseAssets lists the release's assets. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#list-assets-for-a-release -func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#list-release-assets +// +//meta:operation GET /repos/{owner}/{repo}/releases/{release_id}/assets +func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo string, id int64, opts *ListOptions) ([]*ReleaseAsset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/%v/assets", owner, repo, id) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var assets []*ReleaseAsset - resp, err := s.client.Do(ctx, req, &assets) + resp, err := s.client.Do(req, &assets) if err != nil { return nil, resp, err } @@ -242,20 +297,23 @@ func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo // GetReleaseAsset fetches a single release asset. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#get-a-release-asset +// +//meta:operation GET /repos/{owner}/{repo}/releases/assets/{asset_id} func (s *RepositoriesService) GetReleaseAsset(ctx context.Context, owner, repo string, id int64) (*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) + u := fmt.Sprintf("repos/%v/%v/releases/assets/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - asset := new(ReleaseAsset) - resp, err := s.client.Do(ctx, req, asset) + var asset *ReleaseAsset + resp, err := s.client.Do(req, &asset) if err != nil { return nil, resp, err } + return asset, resp, nil } @@ -266,83 +324,114 @@ func (s *RepositoriesService) GetReleaseAsset(ctx context.Context, owner, repo s // If a redirect is returned, the redirect URL will be returned as a string instead // of the io.ReadCloser. Exactly one of rc and redirectURL will be zero. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset -func (s *RepositoriesService) DownloadReleaseAsset(ctx context.Context, owner, repo string, id int64) (rc io.ReadCloser, redirectURL string, err error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) +// followRedirectsClient can be passed to download the asset from a redirected +// location. Specifying any http.Client is possible, but passing http.DefaultClient +// is recommended, except when the specified repository is private, in which case +// it's necessary to pass an http.Client that performs authenticated requests. +// If nil is passed the redirectURL will be returned instead. +// +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#get-a-release-asset +// +//meta:operation GET /repos/{owner}/{repo}/releases/assets/{asset_id} +func (s *RepositoriesService) DownloadReleaseAsset(ctx context.Context, owner, repo string, id int64, followRedirectsClient *http.Client) (rc io.ReadCloser, redirectURL string, err error) { + u := fmt.Sprintf("repos/%v/%v/releases/assets/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, "", err } req.Header.Set("Accept", defaultMediaType) - s.client.clientMu.Lock() - defer s.client.clientMu.Unlock() + loc, resp, err := s.client.bareDoUntilFound(req, 10) + if err != nil { + return nil, "", err + } - var loc string - saveRedirect := s.client.client.CheckRedirect - s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - loc = req.URL.String() - return errors.New("disable redirect") + // No redirect, stream the response body directly. + if loc == nil { + return resp.Body, "", nil } - defer func() { s.client.client.CheckRedirect = saveRedirect }() - req = withContext(ctx, req) - resp, err := s.client.client.Do(req) - if err != nil { - if !strings.Contains(err.Error(), "disable redirect") { - return nil, "", err - } - return nil, loc, nil // Intentionally return no error with valid redirect URL. + // Close body as it's not needed when following redirects or returning the redirect URL. + _ = resp.Body.Close() + + // Got a redirect URL. + redirectStr := loc.String() + if followRedirectsClient != nil { + rc, err := s.downloadReleaseAssetFromURL(ctx, followRedirectsClient, redirectStr) + return rc, "", err } + return nil, redirectStr, nil +} + +func (s *RepositoriesService) downloadReleaseAssetFromURL(ctx context.Context, followRedirectsClient *http.Client, url string) (rc io.ReadCloser, err error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, err + } + req.Header.Set("Accept", defaultMediaType) + resp, err := followRedirectsClient.Do(req) + if err != nil { + return nil, err + } if err := CheckResponse(resp); err != nil { - resp.Body.Close() - return nil, "", err + _ = resp.Body.Close() + return nil, err } - - return resp.Body, "", nil + return resp.Body, nil } -// EditReleaseAsset edits a repository release asset. +// UpdateReleaseAsset updates a repository release asset. +// +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#update-a-release-asset // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release-asset -func (s *RepositoriesService) EditReleaseAsset(ctx context.Context, owner, repo string, id int64, release *ReleaseAsset) (*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) +//meta:operation PATCH /repos/{owner}/{repo}/releases/assets/{asset_id} +func (s *RepositoriesService) UpdateReleaseAsset(ctx context.Context, owner, repo string, id int64, body UpdateReleaseAssetRequest) (*ReleaseAsset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/releases/assets/%v", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, release) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - asset := new(ReleaseAsset) - resp, err := s.client.Do(ctx, req, asset) + var asset *ReleaseAsset + resp, err := s.client.Do(req, &asset) if err != nil { return nil, resp, err } + return asset, resp, nil } // DeleteReleaseAsset delete a single release asset from a repository. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release-asset +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#delete-a-release-asset +// +//meta:operation DELETE /repos/{owner}/{repo}/releases/assets/{asset_id} func (s *RepositoriesService) DeleteReleaseAsset(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) + u := fmt.Sprintf("repos/%v/%v/releases/assets/%v", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UploadReleaseAsset creates an asset by uploading a file into a release repository. // To upload assets that cannot be represented by an os.File, call NewUploadRequest directly. // -// GitHub API docs: https://developer.github.com/v3/repos/releases/#upload-a-release-asset -func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, repo string, id int64, opt *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#upload-a-release-asset +// +//meta:operation POST /repos/{owner}/{repo}/releases/{release_id}/assets +func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, repo string, id int64, opts *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) { + if file == nil { + return nil, nil, errors.New("file must be provided") + } + + u := fmt.Sprintf("repos/%v/%v/releases/%v/assets", owner, repo, id) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } @@ -356,19 +445,94 @@ func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, rep } mediaType := mime.TypeByExtension(filepath.Ext(file.Name())) - if opt.MediaType != "" { - mediaType = opt.MediaType + if opts.MediaType != "" { + mediaType = opts.MediaType + } + + req, err := s.client.NewUploadRequest(ctx, u, file, stat.Size(), mediaType) + if err != nil { + return nil, nil, err } - req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType) + var asset *ReleaseAsset + resp, err := s.client.Do(req, &asset) + if err != nil { + return nil, resp, err + } + + return asset, resp, nil +} + +// UploadReleaseAssetFromRelease uploads an asset using the UploadURL that's embedded +// in a RepositoryRelease object. +// +// This is a convenience wrapper that extracts the release.UploadURL (which is usually +// templated like "https://uploads.github.com/.../assets{?name,label}") and uploads +// the provided data (reader + size) using the existing upload helpers. +// +// GitHub API docs: https://docs.github.com/rest/releases/assets?apiVersion=2022-11-28#upload-a-release-asset +// +//meta:operation POST /repos/{owner}/{repo}/releases/{release_id}/assets +func (s *RepositoriesService) UploadReleaseAssetFromRelease( + ctx context.Context, + release *RepositoryRelease, + opts *UploadOptions, + reader io.Reader, + size int64, +) (*ReleaseAsset, *Response, error) { + if release == nil || release.UploadURL == "" { + return nil, nil, errors.New("release UploadURL must be provided") + } + if reader == nil { + return nil, nil, errors.New("reader must be provided") + } + if size < 0 { + return nil, nil, errors.New("size must be >= 0") + } + + // Strip URI-template portion (e.g. "{?name,label}") if present. + uploadURL := release.UploadURL + if idx := strings.Index(uploadURL, "{"); idx != -1 { + uploadURL = uploadURL[:idx] + } + + // If this is a *relative* URL (no scheme), normalize it by trimming a leading "/" + // so it works with Client.BaseURL path prefixes (e.g. "/api-v3/"). + if !strings.HasPrefix(uploadURL, "http://") && !strings.HasPrefix(uploadURL, "https://") { + uploadURL = strings.TrimPrefix(uploadURL, "/") + } + + // addOptions will append name/label query params (same behavior as UploadReleaseAsset). + u, err := addOptions(uploadURL, opts) if err != nil { return nil, nil, err } - asset := new(ReleaseAsset) - resp, err := s.client.Do(ctx, req, asset) + // determine media type + mediaType := defaultMediaType + if opts != nil { + switch { + case opts.MediaType != "": + mediaType = opts.MediaType + case opts.Name != "": + if ext := filepath.Ext(opts.Name); ext != "" { + if mt := mime.TypeByExtension(ext); mt != "" { + mediaType = mt + } + } + } + } + + req, err := s.client.NewUploadRequest(ctx, u, reader, size, mediaType) + if err != nil { + return nil, nil, err + } + + var asset *ReleaseAsset + resp, err := s.client.Do(req, &asset) if err != nil { return nil, resp, err } + return asset, resp, nil } diff --git a/github/repos_releases_test.go b/github/repos_releases_test.go index 9e38d2d63e1..1a0b9d3aabd 100644 --- a/github/repos_releases_test.go +++ b/github/repos_releases_test.go @@ -7,20 +7,18 @@ package github import ( "bytes" - "context" - "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" - "os" - "reflect" "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListReleases(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -29,180 +27,289 @@ func TestRepositoriesService_ListReleases(t *testing.T) { }) opt := &ListOptions{Page: 2} - releases, _, err := client.Repositories.ListReleases(context.Background(), "o", "r", opt) + ctx := t.Context() + releases, _, err := client.Repositories.ListReleases(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListReleases returned error: %v", err) } - want := []*RepositoryRelease{{ID: Int64(1)}} - if !reflect.DeepEqual(releases, want) { + want := []*RepositoryRelease{{ID: 1}} + if !cmp.Equal(releases, want) { t.Errorf("Repositories.ListReleases returned %+v, want %+v", releases, want) } + + const methodName = "ListReleases" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListReleases(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListReleases(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GenerateReleaseNotes(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + body := GenerateNotesRequest{ + TagName: "v1.0.0", + } + + mux.HandleFunc("/repos/o/r/releases/generate-notes", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, body) + fmt.Fprint(w, `{"name":"v1.0.0","body":"**Full Changelog**: https://github.com/o/r/compare/v0.9.0...v1.0.0"}`) + }) + + ctx := t.Context() + releases, _, err := client.Repositories.GenerateReleaseNotes(ctx, "o", "r", body) + if err != nil { + t.Errorf("Repositories.GenerateReleaseNotes returned error: %v", err) + } + want := &RepositoryReleaseNotes{ + Name: "v1.0.0", + Body: "**Full Changelog**: https://github.com/o/r/compare/v0.9.0...v1.0.0", + } + if !cmp.Equal(releases, want) { + t.Errorf("Repositories.GenerateReleaseNotes returned %+v, want %+v", releases, want) + } + + const methodName = "GenerateReleaseNotes" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GenerateReleaseNotes(ctx, "\n", "\n", body) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GenerateReleaseNotes(ctx, "o", "r", body) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetRelease(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1,"author":{"login":"l"}}`) }) - release, resp, err := client.Repositories.GetRelease(context.Background(), "o", "r", 1) + ctx := t.Context() + release, resp, err := client.Repositories.GetRelease(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetRelease returned error: %v\n%v", err, resp.Body) } - want := &RepositoryRelease{ID: Int64(1), Author: &User{Login: String("l")}} - if !reflect.DeepEqual(release, want) { + want := &RepositoryRelease{ID: 1, Author: &User{Login: Ptr("l")}} + if !cmp.Equal(release, want) { t.Errorf("Repositories.GetRelease returned %+v, want %+v", release, want) } + + const methodName = "GetRelease" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetRelease(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetRelease(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetLatestRelease(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/latest", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":3}`) }) - release, resp, err := client.Repositories.GetLatestRelease(context.Background(), "o", "r") + ctx := t.Context() + release, resp, err := client.Repositories.GetLatestRelease(ctx, "o", "r") if err != nil { t.Errorf("Repositories.GetLatestRelease returned error: %v\n%v", err, resp.Body) } - want := &RepositoryRelease{ID: Int64(3)} - if !reflect.DeepEqual(release, want) { + want := &RepositoryRelease{ID: 3} + if !cmp.Equal(release, want) { t.Errorf("Repositories.GetLatestRelease returned %+v, want %+v", release, want) } + + const methodName = "GetLatestRelease" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetLatestRelease(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetLatestRelease(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetReleaseByTag(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/tags/foo", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":13}`) }) - release, resp, err := client.Repositories.GetReleaseByTag(context.Background(), "o", "r", "foo") + ctx := t.Context() + release, resp, err := client.Repositories.GetReleaseByTag(ctx, "o", "r", "foo") if err != nil { t.Errorf("Repositories.GetReleaseByTag returned error: %v\n%v", err, resp.Body) } - want := &RepositoryRelease{ID: Int64(13)} - if !reflect.DeepEqual(release, want) { + want := &RepositoryRelease{ID: 13} + if !cmp.Equal(release, want) { t.Errorf("Repositories.GetReleaseByTag returned %+v, want %+v", release, want) } + + const methodName = "GetReleaseByTag" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetReleaseByTag(ctx, "\n", "\n", "foo") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetReleaseByTag(ctx, "o", "r", "foo") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_CreateRelease(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &RepositoryRelease{ - Name: String("v1.0"), - // Fields to be removed: - ID: Int64(2), - CreatedAt: &Timestamp{referenceTime}, - PublishedAt: &Timestamp{referenceTime}, - URL: String("http://url/"), - HTMLURL: String("http://htmlurl/"), - AssetsURL: String("http://assetsurl/"), - Assets: []ReleaseAsset{{ID: Int64(5)}}, - UploadURL: String("http://uploadurl/"), - ZipballURL: String("http://zipballurl/"), - TarballURL: String("http://tarballurl/"), - Author: &User{Name: String("octocat")}, - NodeID: String("nodeid"), + t.Parallel() + client, mux, _ := setup(t) + + input := CreateReleaseRequest{ + TagName: "v1.0", + Name: Ptr("v1.0"), + DiscussionCategoryName: Ptr("General"), + GenerateReleaseNotes: Ptr(true), } mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) { - v := new(repositoryReleaseRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - want := &repositoryReleaseRequest{Name: String("v1.0")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - release, _, err := client.Repositories.CreateRelease(context.Background(), "o", "r", input) + ctx := t.Context() + release, _, err := client.Repositories.CreateRelease(ctx, "o", "r", input) if err != nil { t.Errorf("Repositories.CreateRelease returned error: %v", err) } - want := &RepositoryRelease{ID: Int64(1)} - if !reflect.DeepEqual(release, want) { + want := &RepositoryRelease{ID: 1} + if !cmp.Equal(release, want) { t.Errorf("Repositories.CreateRelease returned %+v, want %+v", release, want) } + + const methodName = "CreateRelease" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateRelease(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateRelease(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_EditRelease(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := &RepositoryRelease{ - Name: String("n"), - // Fields to be removed: - ID: Int64(2), - CreatedAt: &Timestamp{referenceTime}, - PublishedAt: &Timestamp{referenceTime}, - URL: String("http://url/"), - HTMLURL: String("http://htmlurl/"), - AssetsURL: String("http://assetsurl/"), - Assets: []ReleaseAsset{{ID: Int64(5)}}, - UploadURL: String("http://uploadurl/"), - ZipballURL: String("http://zipballurl/"), - TarballURL: String("http://tarballurl/"), - Author: &User{Name: String("octocat")}, - NodeID: String("nodeid"), +func TestRepositoriesService_UpdateRelease(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := UpdateReleaseRequest{ + Name: Ptr("n"), + DiscussionCategoryName: Ptr("General"), } mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { - v := new(repositoryReleaseRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - want := &repositoryReleaseRequest{Name: String("n")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - release, _, err := client.Repositories.EditRelease(context.Background(), "o", "r", 1, input) + ctx := t.Context() + release, _, err := client.Repositories.UpdateRelease(ctx, "o", "r", 1, input) if err != nil { - t.Errorf("Repositories.EditRelease returned error: %v", err) + t.Errorf("Repositories.UpdateRelease returned error: %v", err) } - want := &RepositoryRelease{ID: Int64(1)} - if !reflect.DeepEqual(release, want) { - t.Errorf("Repositories.EditRelease returned = %+v, want %+v", release, want) + want := &RepositoryRelease{ID: 1} + if !cmp.Equal(release, want) { + t.Errorf("Repositories.UpdateRelease returned = %+v, want %+v", release, want) } + + const methodName = "UpdateRelease" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateRelease(ctx, "\n", "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateRelease(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_DeleteRelease(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/releases/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Repositories.DeleteRelease(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteRelease(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.DeleteRelease returned error: %v", err) } + + const methodName = "DeleteRelease" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteRelease(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteRelease(ctx, "o", "r", 1) + }) } func TestRepositoriesService_ListReleaseAssets(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -211,38 +318,68 @@ func TestRepositoriesService_ListReleaseAssets(t *testing.T) { }) opt := &ListOptions{Page: 2} - assets, _, err := client.Repositories.ListReleaseAssets(context.Background(), "o", "r", 1, opt) + ctx := t.Context() + assets, _, err := client.Repositories.ListReleaseAssets(ctx, "o", "r", 1, opt) if err != nil { t.Errorf("Repositories.ListReleaseAssets returned error: %v", err) } - want := []*ReleaseAsset{{ID: Int64(1)}} - if !reflect.DeepEqual(assets, want) { + want := []*ReleaseAsset{{ID: Ptr(int64(1))}} + if !cmp.Equal(assets, want) { t.Errorf("Repositories.ListReleaseAssets returned %+v, want %+v", assets, want) } + + const methodName = "ListReleaseAssets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListReleaseAssets(ctx, "\n", "\n", 1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListReleaseAssets(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetReleaseAsset(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - asset, _, err := client.Repositories.GetReleaseAsset(context.Background(), "o", "r", 1) + ctx := t.Context() + asset, _, err := client.Repositories.GetReleaseAsset(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.GetReleaseAsset returned error: %v", err) } - want := &ReleaseAsset{ID: Int64(1)} - if !reflect.DeepEqual(asset, want) { + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { t.Errorf("Repositories.GetReleaseAsset returned %+v, want %+v", asset, want) } + + const methodName = "GetReleaseAsset" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetReleaseAsset(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetReleaseAsset(ctx, "o", "r", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_DownloadReleaseAsset_Stream(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -252,23 +389,30 @@ func TestRepositoriesService_DownloadReleaseAsset_Stream(t *testing.T) { fmt.Fprint(w, "Hello World") }) - reader, _, err := client.Repositories.DownloadReleaseAsset(context.Background(), "o", "r", 1) + ctx := t.Context() + reader, _, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, nil) if err != nil { t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) } want := []byte("Hello World") - content, err := ioutil.ReadAll(reader) + content, err := io.ReadAll(reader) if err != nil { t.Errorf("Repositories.DownloadReleaseAsset returned bad reader: %v", err) } if !bytes.Equal(want, content) { t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want) } + + const methodName = "DownloadReleaseAsset" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.DownloadReleaseAsset(ctx, "\n", "\n", -1, nil) + return err + }) } func TestRepositoriesService_DownloadReleaseAsset_Redirect(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -276,7 +420,8 @@ func TestRepositoriesService_DownloadReleaseAsset_Redirect(t *testing.T) { http.Redirect(w, r, "/yo", http.StatusFound) }) - _, got, err := client.Repositories.DownloadReleaseAsset(context.Background(), "o", "r", 1) + ctx := t.Context() + _, got, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, nil) if err != nil { t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) } @@ -286,9 +431,115 @@ func TestRepositoriesService_DownloadReleaseAsset_Redirect(t *testing.T) { } } +func TestRepositoriesService_DownloadReleaseAsset_FollowRedirect(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + // /yo, below will be served as baseURLPath/yo + http.Redirect(w, r, baseURLPath+"/yo", http.StatusFound) + }) + mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment; filename=hello-world.txt") + fmt.Fprint(w, "Hello World") + }) + + ctx := t.Context() + reader, _, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, http.DefaultClient) + if err != nil { + t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) + } + content, err := io.ReadAll(reader) + if err != nil { + t.Errorf("Reading Repositories.DownloadReleaseAsset returned error: %v", err) + } + reader.Close() + want := []byte("Hello World") + if !bytes.Equal(want, content) { + t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want) + } +} + +func TestRepositoriesService_DownloadReleaseAsset_FollowMultipleRedirects(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + // /yo, below will be served as baseURLPath/yo + http.Redirect(w, r, baseURLPath+"/yo", http.StatusMovedPermanently) + }) + mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html;charset=utf-8") + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + // /yo2, below will be served as baseURLPath/yo2 + http.Redirect(w, r, baseURLPath+"/yo2", http.StatusFound) + }) + mux.HandleFunc("/yo2", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment; filename=hello-world.txt") + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + fmt.Fprint(w, "Hello World") + }) + + ctx := t.Context() + reader, _, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, http.DefaultClient) + if err != nil { + t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) + } + content, err := io.ReadAll(reader) + if err != nil { + t.Errorf("Reading Repositories.DownloadReleaseAsset returned error: %v", err) + } + reader.Close() + want := []byte("Hello World") + if !bytes.Equal(want, content) { + t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want) + } +} + +func TestRepositoriesService_DownloadReleaseAsset_FollowRedirectToError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + // /yo, below will be served as baseURLPath/yo + http.Redirect(w, r, baseURLPath+"/yo", http.StatusFound) + }) + mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", defaultMediaType) + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, loc, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, http.DefaultClient) + if err == nil { + t.Error("Repositories.DownloadReleaseAsset did not return an error") + } + if resp != nil { + resp.Close() + t.Error("Repositories.DownloadReleaseAsset returned stream, want nil") + } + if loc != "" { + t.Errorf(`Repositories.DownloadReleaseAsset returned "%v", want empty ""`, loc) + } +} + func TestRepositoriesService_DownloadReleaseAsset_APIError(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -297,7 +548,8 @@ func TestRepositoriesService_DownloadReleaseAsset_APIError(t *testing.T) { fmt.Fprint(w, `{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}`) }) - resp, loc, err := client.Repositories.DownloadReleaseAsset(context.Background(), "o", "r", 1) + ctx := t.Context() + resp, loc, err := client.Repositories.DownloadReleaseAsset(ctx, "o", "r", 1, nil) if err == nil { t.Error("Repositories.DownloadReleaseAsset did not return an error") } @@ -308,52 +560,74 @@ func TestRepositoriesService_DownloadReleaseAsset_APIError(t *testing.T) { } if loc != "" { - t.Errorf(`Repositories.DownloadReleaseAsset returned "%s", want empty ""`, loc) + t.Errorf(`Repositories.DownloadReleaseAsset returned "%v", want empty ""`, loc) } } -func TestRepositoriesService_EditReleaseAsset(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_UpdateReleaseAsset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &ReleaseAsset{Name: String("n")} + input := UpdateReleaseAssetRequest{Name: Ptr("n")} mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { - v := new(ReleaseAsset) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - asset, _, err := client.Repositories.EditReleaseAsset(context.Background(), "o", "r", 1, input) + ctx := t.Context() + asset, _, err := client.Repositories.UpdateReleaseAsset(ctx, "o", "r", 1, input) if err != nil { - t.Errorf("Repositories.EditReleaseAsset returned error: %v", err) + t.Errorf("Repositories.UpdateReleaseAsset returned error: %v", err) } - want := &ReleaseAsset{ID: Int64(1)} - if !reflect.DeepEqual(asset, want) { - t.Errorf("Repositories.EditReleaseAsset returned = %+v, want %+v", asset, want) + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { + t.Errorf("Repositories.UpdateReleaseAsset returned = %+v, want %+v", asset, want) } + + const methodName = "UpdateReleaseAsset" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateReleaseAsset(ctx, "\n", "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateReleaseAsset(ctx, "o", "r", 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_DeleteReleaseAsset(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r/releases/assets/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Repositories.DeleteReleaseAsset(context.Background(), "o", "r", 1) + ctx := t.Context() + _, err := client.Repositories.DeleteReleaseAsset(ctx, "o", "r", 1) if err != nil { t.Errorf("Repositories.DeleteReleaseAsset returned error: %v", err) } + + const methodName = "DeleteReleaseAsset" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteReleaseAsset(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteReleaseAsset(ctx, "o", "r", 1) + }) } func TestRepositoriesService_UploadReleaseAsset(t *testing.T) { + t.Parallel() var ( defaultUploadOptions = &UploadOptions{Name: "n"} defaultExpectedFormValue = values{"name": "n"} @@ -409,34 +683,242 @@ func TestRepositoriesService_UploadReleaseAsset(t *testing.T) { }, } - client, mux, _, teardown := setup() - defer teardown() + client, mux, _ := setup(t) for key, test := range uploadTests { - releaseEndpoint := fmt.Sprintf("/repos/o/r/releases/%d/assets", key) + releaseEndpoint := fmt.Sprintf("/repos/o/r/releases/%v/assets", key) mux.HandleFunc(releaseEndpoint, func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") testHeader(t, r, "Content-Type", test.expectedMediaType) testHeader(t, r, "Content-Length", "12") testFormValues(t, r, test.expectedFormValues) - testBody(t, r, "Upload me !\n") + testPlainBody(t, r, "Upload me !\n") - fmt.Fprintf(w, `{"id":1}`) + fmt.Fprint(w, `{"id":1}`) }) - file, dir, err := openTestFile(test.fileName, "Upload me !\n") + file := openTestFile(t, test.fileName, "Upload me !\n") + + ctx := t.Context() + asset, _, err := client.Repositories.UploadReleaseAsset(ctx, "o", "r", int64(key), test.uploadOpts, file) if err != nil { - t.Fatalf("Unable to create temp file: %v", err) + t.Errorf("Repositories.UploadReleaseAsset returned error: %v", err) + } + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { + t.Errorf("Repositories.UploadReleaseAsset returned %+v, want %+v", asset, want) } - defer os.RemoveAll(dir) - asset, _, err := client.Repositories.UploadReleaseAsset(context.Background(), "o", "r", int64(key), test.uploadOpts, file) - if err != nil { - t.Errorf("Repositories.UploadReleaseAssert returned error: %v", err) + const methodName = "UploadReleaseAsset" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UploadReleaseAsset(ctx, "o", "r", int64(key), test.uploadOpts, nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UploadReleaseAsset(ctx, "\n", "\n", int64(key), test.uploadOpts, file) + return err + }) + } + testNewRequestAndDoFailure(t, "UploadReleaseAsset", client, func() (*Response, error) { + got, resp, err := client.Repositories.UploadReleaseAsset(t.Context(), "o", "r", 1, defaultUploadOptions, openTestFile(t, "upload.txt", "Upload me !\n")) + if got != nil { + t.Errorf("testNewRequestAndDoFailure UploadReleaseAsset = %#v, want nil", got) } - want := &ReleaseAsset{ID: Int64(1)} - if !reflect.DeepEqual(asset, want) { - t.Errorf("Repositories.UploadReleaseAssert returned %+v, want %+v", asset, want) + return resp, err + }) +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease(t *testing.T) { + t.Parallel() + + var ( + defaultUploadOptions = &UploadOptions{Name: "n.txt"} + defaultExpectedFormValue = values{"name": "n.txt"} + mediaTypeTextPlain = "text/plain; charset=utf-8" + ) + + client, mux, _ := setup(t) + + // Use the same endpoint path used in other release asset tests. + mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", mediaTypeTextPlain) + testHeader(t, r, "Content-Length", "12") + testFormValues(t, r, defaultExpectedFormValue) + testPlainBody(t, r, "Upload me !\n") + + fmt.Fprint(w, `{"id":1}`) + }) + + body := []byte("Upload me !\n") + reader := bytes.NewReader(body) + size := int64(len(body)) + + // Provide a templated upload URL like GitHub returns. + release := &RepositoryRelease{ + UploadURL: "/repos/o/r/releases/1/assets{?name,label}", + } + + ctx := t.Context() + asset, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, defaultUploadOptions, reader, size) + if err != nil { + t.Fatalf("Repositories.UploadReleaseAssetFromRelease returned error: %v", err) + } + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { + t.Fatalf("Repositories.UploadReleaseAssetFromRelease returned %+v, want %+v", asset, want) + } +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease_AbsoluteTemplate(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testFormValues(t, r, values{"name": "abs.txt"}) + fmt.Fprint(w, `{"id":1}`) + }) + + body := []byte("Upload me !\n") + reader := bytes.NewReader(body) + size := int64(len(body)) + + // Build an absolute URL using the test client's BaseURL. + absoluteUploadURL := client.baseURL.String() + "repos/o/r/releases/1/assets{?name,label}" + release := &RepositoryRelease{UploadURL: absoluteUploadURL} + + opts := &UploadOptions{Name: "abs.txt"} + ctx := t.Context() + asset, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, opts, reader, size) + if err != nil { + t.Fatalf("UploadReleaseAssetFromRelease returned error: %v", err) + } + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { + t.Fatalf("UploadReleaseAssetFromRelease returned %+v, want %+v", asset, want) + } +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease_NilRelease(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + body := []byte("Upload me !\n") + reader := bytes.NewReader(body) + size := int64(len(body)) + + ctx := t.Context() + _, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, nil, &UploadOptions{Name: "n.txt"}, reader, size) + if err == nil { + t.Fatal("expected error for nil release, got nil") + } + + const methodName = "UploadReleaseAssetFromRelease" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UploadReleaseAssetFromRelease(ctx, nil, &UploadOptions{Name: "n.txt"}, reader, size) + return err + }) +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease_NilReader(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + release := &RepositoryRelease{UploadURL: "/repos/o/r/releases/1/assets{?name,label}"} + + ctx := t.Context() + _, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, &UploadOptions{Name: "n.txt"}, nil, 12) + if err == nil { + t.Fatal("expected error when reader is nil") + } + + const methodName = "UploadReleaseAssetFromRelease" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UploadReleaseAssetFromRelease(ctx, release, &UploadOptions{Name: "n.txt"}, nil, 12) + return err + }) +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease_NegativeSize(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + release := &RepositoryRelease{UploadURL: "/repos/o/r/releases/1/assets{?name,label}"} + + body := []byte("Upload me !\n") + reader := bytes.NewReader(body) + + ctx := t.Context() + _, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, &UploadOptions{Name: "n..txt"}, reader, -1) + if err == nil { + t.Fatal("expected error when size is negative") + } +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease_NoOpts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // No opts: we just assert that the handler is hit and body is as expected. + mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testPlainBody(t, r, "Upload me !\n") + fmt.Fprint(w, `{"id":1}`) + }) + + body := []byte("Upload me !\n") + reader := bytes.NewReader(body) + size := int64(len(body)) + + release := &RepositoryRelease{UploadURL: "/repos/o/r/releases/1/assets{?name,label}"} + + ctx := t.Context() + asset, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, nil, reader, size) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { + t.Fatalf("Repositories.UploadReleaseAssetFromRelease returned %+v, want %+v", asset, want) + } + + const methodName = "UploadReleaseAssetFromRelease" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, nil, reader, size) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} + +func TestRepositoriesService_UploadReleaseAssetFromRelease_WithMediaType(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + // Expect explicit media type to be used. + mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "image/png") + fmt.Fprint(w, `{"id":1}`) + }) + + body := []byte("Binary!") + reader := bytes.NewReader(body) + size := int64(len(body)) + + release := &RepositoryRelease{UploadURL: "/repos/o/r/releases/1/assets{?name,label}"} + + opts := &UploadOptions{Name: "n.txt", MediaType: "image/png"} + + ctx := t.Context() + asset, _, err := client.Repositories.UploadReleaseAssetFromRelease(ctx, release, opts, reader, size) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + want := &ReleaseAsset{ID: Ptr(int64(1))} + if !cmp.Equal(asset, want) { + t.Fatalf("UploadReleaseAssetFromRelease returned %+v, want %+v", asset, want) } } diff --git a/github/repos_rules.go b/github/repos_rules.go new file mode 100644 index 00000000000..3baf0f64992 --- /dev/null +++ b/github/repos_rules.go @@ -0,0 +1,300 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "iter" +) + +// ListRulesForBranch gets all the repository rules that apply to the specified branch. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#get-rules-for-a-branch +// +//meta:operation GET /repos/{owner}/{repo}/rules/branches/{branch} +func (s *RepositoriesService) ListRulesForBranch(ctx context.Context, owner, repo, branch string, opts *ListOptions) (*BranchRules, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rules/branches/%v", owner, repo, branch) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rules *BranchRules + resp, err := s.client.Do(req, &rules) + if err != nil { + return nil, resp, err + } + + return rules, resp, nil +} + +// ListRulesForBranchIter returns an iterator that paginates through all results of ListRulesForBranch. +// +// Note that since [BranchRules] contains a large number of slices, this iterator +// returns type `any` and it is therefore the responsibility of the caller to perform a +// type switch to determine what item is being returned for each iteration. +func (s *RepositoriesService) ListRulesForBranchIter(ctx context.Context, owner, repo, branch string, opts *ListOptions) iter.Seq2[any, error] { + return func(yield func(any, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListOptions{} + } else { + opts = Ptr(*opts) + } + + for { + results, resp, err := s.ListRulesForBranch(ctx, owner, repo, branch, opts) + if err != nil { + yield(nil, err) + return + } + + // Now iterate through ALL possible results from [BranchRules]. + for _, item := range results.Creation { + if !yield(item, nil) { + return + } + } + for _, item := range results.Update { + if !yield(item, nil) { + return + } + } + for _, item := range results.Deletion { + if !yield(item, nil) { + return + } + } + for _, item := range results.RequiredLinearHistory { + if !yield(item, nil) { + return + } + } + for _, item := range results.MergeQueue { + if !yield(item, nil) { + return + } + } + for _, item := range results.RequiredDeployments { + if !yield(item, nil) { + return + } + } + for _, item := range results.RequiredSignatures { + if !yield(item, nil) { + return + } + } + for _, item := range results.PullRequest { + if !yield(item, nil) { + return + } + } + for _, item := range results.RequiredStatusChecks { + if !yield(item, nil) { + return + } + } + for _, item := range results.NonFastForward { + if !yield(item, nil) { + return + } + } + for _, item := range results.CommitMessagePattern { + if !yield(item, nil) { + return + } + } + for _, item := range results.CommitAuthorEmailPattern { + if !yield(item, nil) { + return + } + } + for _, item := range results.CommitterEmailPattern { + if !yield(item, nil) { + return + } + } + for _, item := range results.BranchNamePattern { + if !yield(item, nil) { + return + } + } + for _, item := range results.TagNamePattern { + if !yield(item, nil) { + return + } + } + for _, item := range results.Workflows { + if !yield(item, nil) { + return + } + } + for _, item := range results.CodeScanning { + if !yield(item, nil) { + return + } + } + for _, item := range results.CopilotCodeReview { + if !yield(item, nil) { + return + } + } + for _, item := range results.FileExtensionRestriction { + if !yield(item, nil) { + return + } + } + for _, item := range results.FilePathRestriction { + if !yield(item, nil) { + return + } + } + for _, item := range results.MaxFilePathLength { + if !yield(item, nil) { + return + } + } + for _, item := range results.MaxFileSize { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + } +} + +// RepositoryListRulesetsOptions specifies optional parameters to the +// RepositoriesService.GetAllRulesets method. +type RepositoryListRulesetsOptions struct { + // IncludesParents indicates whether to include rulesets configured at the organization or enterprise level that apply to the repository. + IncludesParents *bool `url:"includes_parents,omitempty"` + ListOptions +} + +// GetAllRulesets gets all the repository rulesets for the specified repository. +// By default, this endpoint will include rulesets configured at the organization or enterprise level that apply to the repository. +// To exclude those rulesets, set the `RepositoryListRulesetsOptions.IncludesParents` parameter to `false`. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#get-all-repository-rulesets +// +//meta:operation GET /repos/{owner}/{repo}/rulesets +func (s *RepositoriesService) GetAllRulesets(ctx context.Context, owner, repo string, opts *RepositoryListRulesetsOptions) ([]*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets", owner, repo) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset []*RepositoryRuleset + resp, err := s.client.Do(req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// CreateRuleset creates a repository ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#create-a-repository-ruleset +// +//meta:operation POST /repos/{owner}/{repo}/rulesets +func (s *RepositoriesService) CreateRuleset(ctx context.Context, owner, repo string, body RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// GetRuleset gets a repository ruleset for the specified repository. +// If includesParents is true, rulesets configured at the organization or enterprise level that apply to the repository will be returned. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#get-a-repository-ruleset +// +//meta:operation GET /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) GetRuleset(ctx context.Context, owner, repo string, rulesetID int64, includesParents bool) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v?includes_parents=%v", owner, repo, rulesetID, includesParents) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *RepositoryRuleset + resp, err := s.client.Do(req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRuleset updates a repository ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#update-a-repository-ruleset +// +//meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) UpdateRuleset(ctx context.Context, owner, repo string, rulesetID int64, body RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// DeleteRuleset deletes a repository ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#delete-a-repository-ruleset +// +//meta:operation DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) DeleteRuleset(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_rules_test.go b/github/repos_rules_test.go new file mode 100644 index 00000000000..33df6ab4857 --- /dev/null +++ b/github/repos_rules_test.go @@ -0,0 +1,607 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListRulesForBranch(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "ruleset_id": 42069, + "ruleset_source_type": "Repository", + "ruleset_source": "google/a", + "type": "creation" + }, + { + "ruleset_id": 42069, + "ruleset_source_type": "Organization", + "ruleset_source": "google", + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + } + ]`) + }) + + ctx := t.Context() + rules, _, err := client.Repositories.ListRulesForBranch(ctx, "o", "repo", "branch", nil) + if err != nil { + t.Errorf("Repositories.ListRulesForBranch returned error: %v", err) + } + + want := &BranchRules{ + Creation: []*BranchRuleMetadata{{RulesetSourceType: RulesetSourceTypeRepository, RulesetSource: "google/a", RulesetID: 42069}}, + Update: []*UpdateBranchRule{{BranchRuleMetadata: BranchRuleMetadata{RulesetSourceType: RulesetSourceTypeOrganization, RulesetSource: "google", RulesetID: 42069}, Parameters: UpdateRuleParameters{UpdateAllowsFetchAndMerge: true}}}, + } + + if !cmp.Equal(rules, want) { + t.Errorf("Repositories.ListRulesForBranch returned %+v, want %+v", rules, want) + } + + const methodName = "ListRulesForBranch" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListRulesForBranch(ctx, "o", "repo", "branch", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListRulesForBranchIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{"type":"creation"},{"type":"deletion"},{"type":"update"}]`) + case 2: + fmt.Fprint(w, `[{"type":"creation"},{"type":"deletion"},{"type":"update"},{"type":"workflows"}]`) + case 3, 5: + fmt.Fprint(w, `[{"type":"creation"},{"type":"deletion"}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + } + }) + + iter := client.Repositories.ListRulesForBranchIter(t.Context(), "o", "r", "b", nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Repositories.ListRulesForBranchIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListOptions{} + iter = client.Repositories.ListRulesForBranchIter(t.Context(), "o", "r", "b", opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Repositories.ListRulesForBranchIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Repositories.ListRulesForBranchIter(t.Context(), "o", "r", "b", nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Repositories.ListRulesForBranchIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Repositories.ListRulesForBranchIter(t.Context(), "o", "r", "b", nil) + gotItems = 0 + iter(func(_ any, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Repositories.ListRulesForBranchIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestRepositoriesService_UpdateRuleset_OmitZero_Nil(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepositoryRuleset{ + Name: "ruleset", + Enforcement: RulesetEnforcementActive, + BypassActors: nil, + } + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source": "o/repo", + "enforcement": "active" + }`) + }) + + ctx := t.Context() + + _, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, input) + if err != nil { + t.Errorf("Repositories.UpdateRuleset returned error: %v", err) + } +} + +func TestRepositoriesService_UpdateRuleset_OmitZero_EmptySlice(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := RepositoryRuleset{ + Name: "ruleset", + Enforcement: RulesetEnforcementActive, + BypassActors: []*BypassActor{}, + } + + // Scenario 2: User passes empty slice (non-zero value). + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source": "o/repo", + "enforcement": "active", + "bypass_actors": [] + }`) + }) + + ctx := t.Context() + _, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, input) + if err != nil { + t.Errorf("Repositories.UpdateRuleset returned error: %v", err) + } +} + +func TestRepositoriesService_ListRulesForBranch_ListOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + "per_page": "35", + }) + fmt.Fprint(w, `[ + { + "ruleset_id": 42069, + "type": "creation" + } + ]`) + }) + + opts := &ListOptions{Page: 2, PerPage: 35} + ctx := t.Context() + rules, _, err := client.Repositories.ListRulesForBranch(ctx, "o", "repo", "branch", opts) + if err != nil { + t.Errorf("Repositories.ListRulesForBranch returned error: %v", err) + } + + want := &BranchRules{ + Creation: []*BranchRuleMetadata{{RulesetID: 42069}}, + } + + if !cmp.Equal(rules, want) { + t.Errorf("Repositories.ListRulesForBranch returned %+v, want %+v", rules, want) + } + + const methodName = "ListRulesForBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListRulesForBranch(ctx, "\n", "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListRulesForBranch(ctx, "o", "repo", "branch", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetAllRulesets(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `[ + { + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "active", + "created_at": %[1]s, + "updated_at": %[1]s + }, + { + "id": 314, + "name": "Another ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "active", + "created_at": %[1]s, + "updated_at": %[1]s + } + ]`, referenceTimeStr) + }) + + ctx := t.Context() + ruleSet, _, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", nil) + if err != nil { + t.Errorf("Repositories.GetAllRulesets returned error: %v", err) + } + + want := []*RepositoryRuleset{ + { + ID: Ptr(int64(42)), + Name: "ruleset", + SourceType: Ptr(RulesetSourceTypeRepository), + Source: "o/repo", + Enforcement: RulesetEnforcementActive, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + }, + { + ID: Ptr(int64(314)), + Name: "Another ruleset", + SourceType: Ptr(RulesetSourceTypeRepository), + Source: "o/repo", + Enforcement: RulesetEnforcementActive, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + }, + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.GetAllRulesets returned %+v, want %+v", ruleSet, want) + } + + const methodName = "GetAllRulesets" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetAllRulesets_ListOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "includes_parents": "false", + "page": "2", + "per_page": "35", + }) + fmt.Fprint(w, `[ + { + "id": 42 + } + ]`) + }) + + opts := &RepositoryListRulesetsOptions{ + IncludesParents: Ptr(false), + ListOptions: ListOptions{ + Page: 2, + PerPage: 35, + }, + } + ctx := t.Context() + ruleSet, _, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", opts) + if err != nil { + t.Errorf("Repositories.GetAllRulesets returned error: %v", err) + } + + want := []*RepositoryRuleset{ + { + ID: Ptr(int64(42)), + }, + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.GetAllRulesets returned %+v, want %+v", ruleSet, want) + } + + const methodName = "GetAllRulesets" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetAllRulesets(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "active" + }`) + }) + + ctx := t.Context() + ruleSet, _, err := client.Repositories.CreateRuleset(ctx, "o", "repo", RepositoryRuleset{ + Name: "ruleset", + Enforcement: RulesetEnforcementActive, + }) + if err != nil { + t.Errorf("Repositories.CreateRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(42)), + Name: "ruleset", + SourceType: Ptr(RulesetSourceTypeRepository), + Source: "o/repo", + Enforcement: RulesetEnforcementActive, + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.CreateRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "CreateRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateRuleset(ctx, "o", "repo", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateRulesetWithPushRules(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "active", + "target": "push", + "rules": [ + { + "type": "file_path_restriction", + "parameters": { + "restricted_file_paths": ["/a/file"] + } + }, + { + "type": "max_file_path_length", + "parameters": { + "max_file_path_length": 255 + } + }, + { + "type": "file_extension_restriction", + "parameters": { + "restricted_file_extensions": [".exe"] + } + }, + { + "type": "max_file_size", + "parameters": { + "max_file_size": 1024 + } + } + ] + }`) + }) + + ctx := t.Context() + ruleSet, _, err := client.Repositories.CreateRuleset(ctx, "o", "repo", RepositoryRuleset{ + Name: "ruleset", + Enforcement: RulesetEnforcementActive, + }) + if err != nil { + t.Errorf("Repositories.CreateRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(42)), + Name: "ruleset", + SourceType: Ptr(RulesetSourceTypeRepository), + Source: "o/repo", + Target: Ptr(RulesetTargetPush), + Enforcement: RulesetEnforcementActive, + Rules: &RepositoryRulesetRules{ + FilePathRestriction: &FilePathRestrictionRuleParameters{RestrictedFilePaths: []string{"/a/file"}}, + MaxFilePathLength: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 255}, + FileExtensionRestriction: &FileExtensionRestrictionRuleParameters{RestrictedFileExtensions: []string{".exe"}}, + MaxFileSize: &MaxFileSizeRuleParameters{MaxFileSize: 1024}, + }, + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.CreateRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "CreateRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateRuleset(ctx, "o", "repo", RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+` + }`) + }) + + ctx := t.Context() + ruleSet, _, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) + if err != nil { + t.Errorf("Repositories.GetRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(42)), + Name: "ruleset", + SourceType: Ptr(RulesetSourceTypeOrganization), + Source: "o", + Enforcement: RulesetEnforcementActive, + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.GetRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "GetRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "active" + }`) + }) + + ctx := t.Context() + ruleSet, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, RepositoryRuleset{ + Name: "ruleset", + Enforcement: RulesetEnforcementActive, + }) + if err != nil { + t.Errorf("Repositories.UpdateRuleset returned error: %v", err) + } + + want := &RepositoryRuleset{ + ID: Ptr(int64(42)), + Name: "ruleset", + SourceType: Ptr(RulesetSourceTypeRepository), + Source: "o/repo", + Enforcement: "active", + } + + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.UpdateRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "UpdateRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, RepositoryRuleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteRuleset(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) + if err != nil { + t.Errorf("Repositories.DeleteRuleset returned error: %v", err) + } + + const methodName = "DeleteRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) + }) +} diff --git a/github/repos_stats.go b/github/repos_stats.go index bb355aeadd3..8234886f0ad 100644 --- a/github/repos_stats.go +++ b/github/repos_stats.go @@ -14,9 +14,9 @@ import ( // ContributorStats represents a contributor to a repository and their // weekly contributions to a given repo. type ContributorStats struct { - Author *Contributor `json:"author,omitempty"` - Total *int `json:"total,omitempty"` - Weeks []WeeklyStats `json:"weeks,omitempty"` + Author *Contributor `json:"author,omitempty"` + Total *int `json:"total,omitempty"` + Weeks []*WeeklyStats `json:"weeks,omitempty"` } func (c ContributorStats) String() string { @@ -45,16 +45,18 @@ func (w WeeklyStats) String() string { // it is now computing the requested statistics. A follow up request, after a // delay of a second or so, should result in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#contributors +// GitHub API docs: https://docs.github.com/rest/metrics/statistics?apiVersion=2022-11-28#get-all-contributor-commit-activity +// +//meta:operation GET /repos/{owner}/{repo}/stats/contributors func (s *RepositoriesService) ListContributorsStats(ctx context.Context, owner, repo string) ([]*ContributorStats, *Response, error) { u := fmt.Sprintf("repos/%v/%v/stats/contributors", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var contributorStats []*ContributorStats - resp, err := s.client.Do(ctx, req, &contributorStats) + resp, err := s.client.Do(req, &contributorStats) if err != nil { return nil, resp, err } @@ -84,16 +86,18 @@ func (w WeeklyCommitActivity) String() string { // it is now computing the requested statistics. A follow up request, after a // delay of a second or so, should result in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#commit-activity +// GitHub API docs: https://docs.github.com/rest/metrics/statistics?apiVersion=2022-11-28#get-the-last-year-of-commit-activity +// +//meta:operation GET /repos/{owner}/{repo}/stats/commit_activity func (s *RepositoriesService) ListCommitActivity(ctx context.Context, owner, repo string) ([]*WeeklyCommitActivity, *Response, error) { u := fmt.Sprintf("repos/%v/%v/stats/commit_activity", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var weeklyCommitActivity []*WeeklyCommitActivity - resp, err := s.client.Do(ctx, req, &weeklyCommitActivity) + resp, err := s.client.Do(req, &weeklyCommitActivity) if err != nil { return nil, resp, err } @@ -111,16 +115,21 @@ func (s *RepositoriesService) ListCommitActivity(ctx context.Context, owner, rep // it is now computing the requested statistics. A follow up request, after a // delay of a second or so, should result in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#code-frequency +// GitHub API docs: https://docs.github.com/rest/metrics/statistics?apiVersion=2022-11-28#get-the-weekly-commit-activity +// +//meta:operation GET /repos/{owner}/{repo}/stats/code_frequency func (s *RepositoriesService) ListCodeFrequency(ctx context.Context, owner, repo string) ([]*WeeklyStats, *Response, error) { u := fmt.Sprintf("repos/%v/%v/stats/code_frequency", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var weeks [][]int - resp, err := s.client.Do(ctx, req, &weeks) + resp, err := s.client.Do(req, &weeks) + if err != nil { + return nil, resp, err + } // convert int slices into WeeklyStats var stats []*WeeklyStats @@ -130,18 +139,18 @@ func (s *RepositoriesService) ListCodeFrequency(ctx context.Context, owner, repo } stat := &WeeklyStats{ Week: &Timestamp{time.Unix(int64(week[0]), 0)}, - Additions: Int(week[1]), - Deletions: Int(week[2]), + Additions: Ptr(week[1]), + Deletions: Ptr(week[2]), } stats = append(stats, stat) } - return stats, resp, err + return stats, resp, nil } // RepositoryParticipation is the number of commits by everyone // who has contributed to the repository (including the owner) -// as well as the number of commits by the owner themself. +// as well as the number of commits by the owner themselves. type RepositoryParticipation struct { All []int `json:"all,omitempty"` Owner []int `json:"owner,omitempty"` @@ -164,16 +173,18 @@ func (r RepositoryParticipation) String() string { // it is now computing the requested statistics. A follow up request, after a // delay of a second or so, should result in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#participation +// GitHub API docs: https://docs.github.com/rest/metrics/statistics?apiVersion=2022-11-28#get-the-weekly-commit-count +// +//meta:operation GET /repos/{owner}/{repo}/stats/participation func (s *RepositoriesService) ListParticipation(ctx context.Context, owner, repo string) (*RepositoryParticipation, *Response, error) { u := fmt.Sprintf("repos/%v/%v/stats/participation", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - participation := new(RepositoryParticipation) - resp, err := s.client.Do(ctx, req, participation) + var participation *RepositoryParticipation + resp, err := s.client.Do(req, &participation) if err != nil { return nil, resp, err } @@ -197,16 +208,21 @@ type PunchCard struct { // it is now computing the requested statistics. A follow up request, after a // delay of a second or so, should result in a successful request. // -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#punch-card +// GitHub API docs: https://docs.github.com/rest/metrics/statistics?apiVersion=2022-11-28#get-the-hourly-commit-count-for-each-day +// +//meta:operation GET /repos/{owner}/{repo}/stats/punch_card func (s *RepositoriesService) ListPunchCard(ctx context.Context, owner, repo string) ([]*PunchCard, *Response, error) { u := fmt.Sprintf("repos/%v/%v/stats/punch_card", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var results [][]int - resp, err := s.client.Do(ctx, req, &results) + resp, err := s.client.Do(req, &results) + if err != nil { + return nil, resp, err + } // convert int slices into Punchcards var cards []*PunchCard @@ -215,12 +231,12 @@ func (s *RepositoriesService) ListPunchCard(ctx context.Context, owner, repo str continue } card := &PunchCard{ - Day: Int(result[0]), - Hour: Int(result[1]), - Commits: Int(result[2]), + Day: Ptr(result[0]), + Hour: Ptr(result[1]), + Commits: Ptr(result[2]), } cards = append(cards, card) } - return cards, resp, err + return cards, resp, nil } diff --git a/github/repos_stats_test.go b/github/repos_stats_test.go index 055504d7027..77eb5e23d2b 100644 --- a/github/repos_stats_test.go +++ b/github/repos_stats_test.go @@ -6,17 +6,17 @@ package github import ( - "context" + "errors" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListContributorsStats(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,12 +25,13 @@ func TestRepositoriesService_ListContributorsStats(t *testing.T) { [ { "author": { - "id": 1 + "id": 1, + "node_id": "nodeid-1" }, "total": 135, "weeks": [ { - "w": 1367712000, + "w": `+referenceUnixTimeStr+`, "a": 6898, "d": 77, "c": 10 @@ -41,7 +42,8 @@ func TestRepositoriesService_ListContributorsStats(t *testing.T) { `) }) - stats, _, err := client.Repositories.ListContributorsStats(context.Background(), "o", "r") + ctx := t.Context() + stats, _, err := client.Repositories.ListContributorsStats(ctx, "o", "r") if err != nil { t.Errorf("RepositoriesService.ListContributorsStats returned error: %v", err) } @@ -49,28 +51,43 @@ func TestRepositoriesService_ListContributorsStats(t *testing.T) { want := []*ContributorStats{ { Author: &Contributor{ - ID: Int64(1), + ID: Ptr(int64(1)), + NodeID: Ptr("nodeid-1"), }, - Total: Int(135), - Weeks: []WeeklyStats{ + Total: Ptr(135), + Weeks: []*WeeklyStats{ { - Week: &Timestamp{time.Date(2013, time.May, 05, 00, 00, 00, 0, time.UTC).Local()}, - Additions: Int(6898), - Deletions: Int(77), - Commits: Int(10), + Week: &referenceTimestamp, + Additions: Ptr(6898), + Deletions: Ptr(77), + Commits: Ptr(10), }, }, }, } - if !reflect.DeepEqual(stats, want) { + if !cmp.Equal(stats, want) { t.Errorf("RepositoriesService.ListContributorsStats returned %+v, want %+v", stats, want) } + + const methodName = "ListContributorsStats" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListContributorsStats(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListContributorsStats(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListCommitActivity(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stats/commit_activity", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -80,13 +97,14 @@ func TestRepositoriesService_ListCommitActivity(t *testing.T) { { "days": [0, 3, 26, 20, 39, 1, 0], "total": 89, - "week": 1336280400 + "week": `+referenceUnixTimeStr+` } ] `) }) - activity, _, err := client.Repositories.ListCommitActivity(context.Background(), "o", "r") + ctx := t.Context() + activity, _, err := client.Repositories.ListCommitActivity(ctx, "o", "r") if err != nil { t.Errorf("RepositoriesService.ListCommitActivity returned error: %v", err) } @@ -94,45 +112,74 @@ func TestRepositoriesService_ListCommitActivity(t *testing.T) { want := []*WeeklyCommitActivity{ { Days: []int{0, 3, 26, 20, 39, 1, 0}, - Total: Int(89), - Week: &Timestamp{time.Date(2012, time.May, 06, 05, 00, 00, 0, time.UTC).Local()}, + Total: Ptr(89), + Week: &referenceTimestamp, }, } - if !reflect.DeepEqual(activity, want) { + if !cmp.Equal(activity, want) { t.Errorf("RepositoriesService.ListCommitActivity returned %+v, want %+v", activity, want) } + + const methodName = "ListCommitActivity" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCommitActivity(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCommitActivity(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListCodeFrequency(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stats/code_frequency", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `[[1302998400, 1124, -435]]`) + fmt.Fprint(w, `[[`+referenceUnixTimeStr+`, 1124, -435]]`) }) - code, _, err := client.Repositories.ListCodeFrequency(context.Background(), "o", "r") + ctx := t.Context() + code, _, err := client.Repositories.ListCodeFrequency(ctx, "o", "r") if err != nil { t.Errorf("RepositoriesService.ListCodeFrequency returned error: %v", err) } want := []*WeeklyStats{{ - Week: &Timestamp{time.Date(2011, time.April, 17, 00, 00, 00, 0, time.UTC).Local()}, - Additions: Int(1124), - Deletions: Int(-435), + Week: &referenceTimestamp, + Additions: Ptr(1124), + Deletions: Ptr(-435), }} - if !reflect.DeepEqual(code, want) { + if !cmp.Equal(code, want) { t.Errorf("RepositoriesService.ListCodeFrequency returned %+v, want %+v", code, want) } + + const methodName = "ListCodeFrequency" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCodeFrequency(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCodeFrequency(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_Participation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stats/participation", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -155,7 +202,8 @@ func TestRepositoriesService_Participation(t *testing.T) { `) }) - participation, _, err := client.Repositories.ListParticipation(context.Background(), "o", "r") + ctx := t.Context() + participation, _, err := client.Repositories.ListParticipation(ctx, "o", "r") if err != nil { t.Errorf("RepositoriesService.ListParticipation returned error: %v", err) } @@ -175,14 +223,28 @@ func TestRepositoriesService_Participation(t *testing.T) { }, } - if !reflect.DeepEqual(participation, want) { + if !cmp.Equal(participation, want) { t.Errorf("RepositoriesService.ListParticipation returned %+v, want %+v", participation, want) } + + const methodName = "ListParticipation" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListParticipation(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListParticipation(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListPunchCard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stats/punch_card", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -194,25 +256,40 @@ func TestRepositoriesService_ListPunchCard(t *testing.T) { ]`) }) - card, _, err := client.Repositories.ListPunchCard(context.Background(), "o", "r") + ctx := t.Context() + card, _, err := client.Repositories.ListPunchCard(ctx, "o", "r") if err != nil { t.Errorf("RepositoriesService.ListPunchCard returned error: %v", err) } want := []*PunchCard{ - {Day: Int(0), Hour: Int(0), Commits: Int(5)}, - {Day: Int(0), Hour: Int(1), Commits: Int(43)}, - {Day: Int(0), Hour: Int(2), Commits: Int(21)}, + {Day: Ptr(0), Hour: Ptr(0), Commits: Ptr(5)}, + {Day: Ptr(0), Hour: Ptr(1), Commits: Ptr(43)}, + {Day: Ptr(0), Hour: Ptr(2), Commits: Ptr(21)}, } - if !reflect.DeepEqual(card, want) { + if !cmp.Equal(card, want) { t.Errorf("RepositoriesService.ListPunchCard returned %+v, want %+v", card, want) } + + const methodName = "ListPunchCard" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListPunchCard(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListPunchCard(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_AcceptedError(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListContributorsStats_AcceptedError(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -221,16 +298,31 @@ func TestRepositoriesService_AcceptedError(t *testing.T) { fmt.Fprint(w, `{"id":1}`) }) - stats, _, err := client.Repositories.ListContributorsStats(context.Background(), "o", "r") + ctx := t.Context() + stats, _, err := client.Repositories.ListContributorsStats(ctx, "o", "r") if err == nil { - t.Errorf("RepositoriesService.AcceptedError should have returned an error") + t.Error("Repositories.ListContributorsStats should have returned an error") } - if _, ok := err.(*AcceptedError); !ok { - t.Errorf("RepositoriesService.AcceptedError returned an AcceptedError: %v", err) + if !errors.As(err, new(*AcceptedError)) { + t.Errorf("Repositories.ListContributorsStats returned an AcceptedError: %v", err) } if stats != nil { - t.Errorf("RepositoriesService.AcceptedError expected stats to be nil: %v", stats) + t.Errorf("Repositories.ListContributorsStats expected stats to be nil: %v", stats) } + + const methodName = "ListContributorsStats" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListContributorsStats(ctx, "o", "r") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListContributorsStats(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_statuses.go b/github/repos_statuses.go index 6d6a0d2fe4e..2b845b1a489 100644 --- a/github/repos_statuses.go +++ b/github/repos_statuses.go @@ -8,8 +8,6 @@ package github import ( "context" "fmt" - "net/url" - "time" ) // RepoStatus represents the status of a repository at a particular reference. @@ -32,9 +30,12 @@ type RepoStatus struct { // A string label to differentiate this status from the statuses of other systems. Context *string `json:"context,omitempty"` + // AvatarURL is the URL of the avatar of this status. + AvatarURL *string `json:"avatar_url,omitempty"` + Creator *User `json:"creator,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` } func (r RepoStatus) String() string { @@ -42,23 +43,25 @@ func (r RepoStatus) String() string { } // ListStatuses lists the statuses of a repository at the specified -// reference. ref can be a SHA, a branch name, or a tag name. +// reference. The ref can be a SHA, a branch name, or a tag name. +// +// GitHub API docs: https://docs.github.com/rest/commits/statuses?apiVersion=2022-11-28#list-commit-statuses-for-a-reference // -// GitHub API docs: https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref -func (s *RepositoriesService) ListStatuses(ctx context.Context, owner, repo, ref string, opt *ListOptions) ([]*RepoStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/statuses", owner, repo, url.QueryEscape(ref)) - u, err := addOptions(u, opt) +//meta:operation GET /repos/{owner}/{repo}/commits/{ref}/statuses +func (s *RepositoriesService) ListStatuses(ctx context.Context, owner, repo, ref string, opts *ListOptions) ([]*RepoStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/statuses", owner, repo, refURLEscape(ref)) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var statuses []*RepoStatus - resp, err := s.client.Do(ctx, req, &statuses) + resp, err := s.client.Do(req, &statuses) if err != nil { return nil, resp, err } @@ -67,18 +70,20 @@ func (s *RepositoriesService) ListStatuses(ctx context.Context, owner, repo, ref } // CreateStatus creates a new status for a repository at the specified -// reference. Ref can be a SHA, a branch name, or a tag name. +// reference. The ref can be a SHA, a branch name, or a tag name. // -// GitHub API docs: https://developer.github.com/v3/repos/statuses/#create-a-status -func (s *RepositoriesService) CreateStatus(ctx context.Context, owner, repo, ref string, status *RepoStatus) (*RepoStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/statuses/%v", owner, repo, url.QueryEscape(ref)) - req, err := s.client.NewRequest("POST", u, status) +// GitHub API docs: https://docs.github.com/rest/commits/statuses?apiVersion=2022-11-28#create-a-commit-status +// +//meta:operation POST /repos/{owner}/{repo}/statuses/{sha} +func (s *RepositoriesService) CreateStatus(ctx context.Context, owner, repo, ref string, status RepoStatus) (*RepoStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/statuses/%v", owner, repo, refURLEscape(ref)) + req, err := s.client.NewRequest(ctx, "POST", u, &status) if err != nil { return nil, nil, err } - repoStatus := new(RepoStatus) - resp, err := s.client.Do(ctx, req, repoStatus) + var repoStatus *RepoStatus + resp, err := s.client.Do(req, &repoStatus) if err != nil { return nil, resp, err } @@ -92,10 +97,10 @@ type CombinedStatus struct { // failure, pending, or success. State *string `json:"state,omitempty"` - Name *string `json:"name,omitempty"` - SHA *string `json:"sha,omitempty"` - TotalCount *int `json:"total_count,omitempty"` - Statuses []RepoStatus `json:"statuses,omitempty"` + Name *string `json:"name,omitempty"` + SHA *string `json:"sha,omitempty"` + TotalCount *int `json:"total_count,omitempty"` + Statuses []*RepoStatus `json:"statuses,omitempty"` CommitURL *string `json:"commit_url,omitempty"` RepositoryURL *string `json:"repository_url,omitempty"` @@ -106,23 +111,25 @@ func (s CombinedStatus) String() string { } // GetCombinedStatus returns the combined status of a repository at the specified -// reference. ref can be a SHA, a branch name, or a tag name. +// reference. The ref can be a SHA, a branch name, or a tag name. +// +// GitHub API docs: https://docs.github.com/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference // -// GitHub API docs: https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref -func (s *RepositoriesService) GetCombinedStatus(ctx context.Context, owner, repo, ref string, opt *ListOptions) (*CombinedStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/status", owner, repo, url.QueryEscape(ref)) - u, err := addOptions(u, opt) +//meta:operation GET /repos/{owner}/{repo}/commits/{ref}/status +func (s *RepositoriesService) GetCombinedStatus(ctx context.Context, owner, repo, ref string, opts *ListOptions) (*CombinedStatus, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/commits/%v/status", owner, repo, refURLEscape(ref)) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - status := new(CombinedStatus) - resp, err := s.client.Do(ctx, req, status) + var status *CombinedStatus + resp, err := s.client.Do(req, &status) if err != nil { return nil, resp, err } diff --git a/github/repos_statuses_test.go b/github/repos_statuses_test.go index b556b716695..a48e5229d2d 100644 --- a/github/repos_statuses_test.go +++ b/github/repos_statuses_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListStatuses(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/r/statuses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,64 +24,91 @@ func TestRepositoriesService_ListStatuses(t *testing.T) { }) opt := &ListOptions{Page: 2} - statuses, _, err := client.Repositories.ListStatuses(context.Background(), "o", "r", "r", opt) + ctx := t.Context() + statuses, _, err := client.Repositories.ListStatuses(ctx, "o", "r", "r", opt) if err != nil { t.Errorf("Repositories.ListStatuses returned error: %v", err) } - want := []*RepoStatus{{ID: Int64(1)}} - if !reflect.DeepEqual(statuses, want) { + want := []*RepoStatus{{ID: Ptr(int64(1))}} + if !cmp.Equal(statuses, want) { t.Errorf("Repositories.ListStatuses returned %+v, want %+v", statuses, want) } + + const methodName = "ListStatuses" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListStatuses(ctx, "\n", "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListStatuses(ctx, "o", "r", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListStatuses_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListStatuses(context.Background(), "%", "r", "r", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListStatuses(ctx, "%", "r", "r", nil) testURLParseError(t, err) } func TestRepositoriesService_CreateStatus(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &RepoStatus{State: String("s"), TargetURL: String("t"), Description: String("d")} + input := RepoStatus{State: Ptr("s"), TargetURL: Ptr("t"), Description: Ptr("d")} mux.HandleFunc("/repos/o/r/statuses/r", func(w http.ResponseWriter, r *http.Request) { - v := new(RepoStatus) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - status, _, err := client.Repositories.CreateStatus(context.Background(), "o", "r", "r", input) + ctx := t.Context() + status, _, err := client.Repositories.CreateStatus(ctx, "o", "r", "r", input) if err != nil { t.Errorf("Repositories.CreateStatus returned error: %v", err) } - want := &RepoStatus{ID: Int64(1)} - if !reflect.DeepEqual(status, want) { + want := &RepoStatus{ID: Ptr(int64(1))} + if !cmp.Equal(status, want) { t.Errorf("Repositories.CreateStatus returned %+v, want %+v", status, want) } + + const methodName = "CreateStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateStatus(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateStatus(ctx, "o", "r", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_CreateStatus_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.CreateStatus(context.Background(), "%", "r", "r", nil) + ctx := t.Context() + _, _, err := client.Repositories.CreateStatus(ctx, "%", "r", "r", RepoStatus{}) testURLParseError(t, err) } func TestRepositoriesService_GetCombinedStatus(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/commits/r/status", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -91,13 +117,28 @@ func TestRepositoriesService_GetCombinedStatus(t *testing.T) { }) opt := &ListOptions{Page: 2} - status, _, err := client.Repositories.GetCombinedStatus(context.Background(), "o", "r", "r", opt) + ctx := t.Context() + status, _, err := client.Repositories.GetCombinedStatus(ctx, "o", "r", "r", opt) if err != nil { t.Errorf("Repositories.GetCombinedStatus returned error: %v", err) } - want := &CombinedStatus{State: String("success"), Statuses: []RepoStatus{{ID: Int64(1)}}} - if !reflect.DeepEqual(status, want) { + want := &CombinedStatus{State: Ptr("success"), Statuses: []*RepoStatus{{ID: Ptr(int64(1))}}} + if !cmp.Equal(status, want) { t.Errorf("Repositories.GetCombinedStatus returned %+v, want %+v", status, want) } + + const methodName = "GetCombinedStatus" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCombinedStatus(ctx, "\n", "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCombinedStatus(ctx, "o", "r", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/repos_tags.go b/github/repos_tags.go new file mode 100644 index 00000000000..a45e76cc577 --- /dev/null +++ b/github/repos_tags.go @@ -0,0 +1,88 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// TagProtection represents a repository tag protection. +type TagProtection struct { + ID *int64 `json:"id"` + Pattern *string `json:"pattern"` +} + +// tagProtectionRequest represents a request to create tag protection. +type tagProtectionRequest struct { + // An optional glob pattern to match against when enforcing tag protection. + Pattern string `json:"pattern"` +} + +// ListTagProtection lists tag protection of the specified repository. +// +// Deprecated: This operation is deprecated and will be removed after August 30, 2024. Use the "Repository Rulesets" endpoint instead: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#get-all-repository-rulesets +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.20/rest/repos/tags#closing-down---list-tag-protection-states-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/tags/protection +func (s *RepositoriesService) ListTagProtection(ctx context.Context, owner, repo string) ([]*TagProtection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var tagProtections []*TagProtection + resp, err := s.client.Do(req, &tagProtections) + if err != nil { + return nil, resp, err + } + + return tagProtections, resp, nil +} + +// CreateTagProtection creates the tag protection of the specified repository. +// +// Deprecated: This operation is deprecated and will be removed after August 30, 2024. Use the "Repository Rulesets" endpoint instead: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#create-a-repository-ruleset +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.20/rest/repos/tags#closing-down---create-a-tag-protection-state-for-a-repository +// +//meta:operation POST /repos/{owner}/{repo}/tags/protection +func (s *RepositoriesService) CreateTagProtection(ctx context.Context, owner, repo, pattern string) (*TagProtection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo) + r := &tagProtectionRequest{Pattern: pattern} + req, err := s.client.NewRequest(ctx, "POST", u, r) + if err != nil { + return nil, nil, err + } + + var tagProtection *TagProtection + resp, err := s.client.Do(req, &tagProtection) + if err != nil { + return nil, resp, err + } + + return tagProtection, resp, nil +} + +// DeleteTagProtection deletes a tag protection from the specified repository. +// +// Deprecated: This operation is deprecated and will be removed after August 30, 2024. Use the "Repository Rulesets" endpoint instead: https://docs.github.com/rest/repos/rules?apiVersion=2022-11-28#delete-a-repository-ruleset +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.20/rest/repos/tags#closing-down---delete-a-tag-protection-state-for-a-repository +// +//meta:operation DELETE /repos/{owner}/{repo}/tags/protection/{tag_protection_id} +func (s *RepositoriesService) DeleteTagProtection(ctx context.Context, owner, repo string, tagProtectionID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection/%v", owner, repo, tagProtectionID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/repos_tags_test.go b/github/repos_tags_test.go new file mode 100644 index 00000000000..c92f3ff3c9d --- /dev/null +++ b/github/repos_tags_test.go @@ -0,0 +1,124 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListTagProtection(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `[{"id":1, "pattern":"tag1"},{"id":2, "pattern":"tag2"}]`) + }) + + ctx := t.Context() + tagProtections, _, err := client.Repositories.ListTagProtection(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.ListTagProtection returned error: %v", err) + } + + want := []*TagProtection{{ID: Ptr(int64(1)), Pattern: Ptr("tag1")}, {ID: Ptr(int64(2)), Pattern: Ptr("tag2")}} + if !cmp.Equal(tagProtections, want) { + t.Errorf("Repositories.ListTagProtection returned %+v, want %+v", tagProtections, want) + } + + const methodName = "ListTagProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTagProtection(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTagProtection(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListTagProtection_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.ListTagProtection(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestRepositoriesService_CreateTagProtection(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + pattern := "tag*" + + mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + want := &tagProtectionRequest{Pattern: "tag*"} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"id":1,"pattern":"tag*"}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.CreateTagProtection(ctx, "o", "r", pattern) + if err != nil { + t.Errorf("Repositories.CreateTagProtection returned error: %v", err) + } + + want := &TagProtection{ID: Ptr(int64(1)), Pattern: Ptr("tag*")} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.CreateTagProtection returned %+v, want %+v", got, want) + } + + const methodName = "CreateTagProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateTagProtection(ctx, "\n", "\n", pattern) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateTagProtection(ctx, "o", "r", pattern) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteTagProtection(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/tags/protection/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.DeleteTagProtection(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteTagProtection returned error: %v", err) + } + + const methodName = "DeleteTagProtection" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteTagProtection(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteTagProtection(ctx, "o", "r", 1) + }) +} diff --git a/github/repos_test.go b/github/repos_test.go index 75ccbb0da68..8b4c558a0a5 100644 --- a/github/repos_test.go +++ b/github/repos_test.go @@ -6,136 +6,134 @@ package github import ( - "context" "encoding/json" + "errors" "fmt" "net/http" - "reflect" + "net/url" "strings" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) -func TestRepositoriesService_List_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListByAuthenticatedUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeTopicsPreview} mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) fmt.Fprint(w, `[{"id":1},{"id":2}]`) }) - ctx := context.Background() - got, _, err := client.Repositories.List(ctx, "", nil) + ctx := t.Context() + got, _, err := client.Repositories.ListByAuthenticatedUser(ctx, nil) if err != nil { - t.Errorf("Repositories.List returned error: %v", err) + t.Errorf("Repositories.ListByAuthenticatedUser returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.List returned %+v, want %+v", got, want) + want := []*Repository{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ListByAuthenticatedUser returned %+v, want %+v", got, want) } - // Test addOptions failure - _, _, err = client.Repositories.List(ctx, "\n", &RepositoryListOptions{}) - if err == nil { - t.Error("bad options List err = nil, want error") - } + const methodName = "ListByAuthenticatedUser" - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err := client.Repositories.List(ctx, "", nil) - if got != nil { - t.Errorf("rate.Reset.Time > now List = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now List resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now List err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListByAuthenticatedUser(ctx, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_List_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListByUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeTopicsPreview} mux.HandleFunc("/users/u/repos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) testFormValues(t, r, values{ - "visibility": "public", - "affiliation": "owner,collaborator", - "sort": "created", - "direction": "asc", - "page": "2", + "sort": "created", + "direction": "asc", + "page": "2", }) fmt.Fprint(w, `[{"id":1}]`) }) - opt := &RepositoryListOptions{ - Visibility: "public", - Affiliation: "owner,collaborator", + opt := &RepositoryListByUserOptions{ Sort: "created", Direction: "asc", ListOptions: ListOptions{Page: 2}, } - repos, _, err := client.Repositories.List(context.Background(), "u", opt) + ctx := t.Context() + repos, _, err := client.Repositories.ListByUser(ctx, "u", opt) if err != nil { - t.Errorf("Repositories.List returned error: %v", err) + t.Errorf("Repositories.ListByUser returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(repos, want) { - t.Errorf("Repositories.List returned %+v, want %+v", repos, want) + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(repos, want) { + t.Errorf("Repositories.ListByUser returned %+v, want %+v", repos, want) } + + const methodName = "ListByUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListByUser(ctx, "\n", &RepositoryListByUserOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListByUser(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_List_specifiedUser_type(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListByUser_type(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeTopicsPreview} mux.HandleFunc("/users/u/repos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) testFormValues(t, r, values{ "type": "owner", }) fmt.Fprint(w, `[{"id":1}]`) }) - opt := &RepositoryListOptions{ + opt := &RepositoryListByUserOptions{ Type: "owner", } - repos, _, err := client.Repositories.List(context.Background(), "u", opt) + ctx := t.Context() + repos, _, err := client.Repositories.ListByUser(ctx, "u", opt) if err != nil { - t.Errorf("Repositories.List returned error: %v", err) + t.Errorf("Repositories.ListByUser returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(repos, want) { - t.Errorf("Repositories.List returned %+v, want %+v", repos, want) + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(repos, want) { + t.Errorf("Repositories.ListByUser returned %+v, want %+v", repos, want) } } -func TestRepositoriesService_List_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListByUser_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.List(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListByUser(ctx, "%", nil) testURLParseError(t, err) } func TestRepositoriesService_ListByOrg(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeTopicsPreview} + wantAcceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeRepositoryVisibilityPreview} mux.HandleFunc("/orgs/o/repos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) @@ -146,50 +144,48 @@ func TestRepositoriesService_ListByOrg(t *testing.T) { fmt.Fprint(w, `[{"id":1}]`) }) - ctx := context.Background() - opt := &RepositoryListByOrgOptions{"forks", ListOptions{Page: 2}} + ctx := t.Context() + opt := &RepositoryListByOrgOptions{ + Type: "forks", + ListOptions: ListOptions{Page: 2}, + } got, _, err := client.Repositories.ListByOrg(ctx, "o", opt) if err != nil { t.Errorf("Repositories.ListByOrg returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(got, want) { + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListByOrg returned %+v, want %+v", got, want) } - // Test addOptions failure - _, _, err = client.Repositories.ListByOrg(ctx, "\n", opt) - if err == nil { - t.Error("bad options ListByOrg err = nil, want error") - } + const methodName = "ListByOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListByOrg(ctx, "\n", opt) + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err := client.Repositories.ListByOrg(ctx, "o", opt) - if got != nil { - t.Errorf("rate.Reset.Time > now ListByOrg = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListByOrg resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListByOrg err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListByOrg(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListByOrg_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.ListByOrg(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Repositories.ListByOrg(ctx, "%", nil) testURLParseError(t, err) } func TestRepositoriesService_ListAll(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repositories", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -199,437 +195,427 @@ func TestRepositoriesService_ListAll(t *testing.T) { fmt.Fprint(w, `[{"id":1}]`) }) - ctx := context.Background() + ctx := t.Context() opt := &RepositoryListAllOptions{1} got, _, err := client.Repositories.ListAll(ctx, opt) if err != nil { t.Errorf("Repositories.ListAll returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(got, want) { + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListAll returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.ListAll(ctx, &RepositoryListAllOptions{1}) - if got != nil { - t.Errorf("client.BaseURL.Path='' ListAll = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListAll resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListAll err = nil, want error") - } - - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.ListAll(ctx, &RepositoryListAllOptions{1}) - if got != nil { - t.Errorf("rate.Reset.Time > now ListAll = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListAll resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListAll err = nil, want error") - } + const methodName = "ListAll" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListAll(ctx, &RepositoryListAllOptions{1}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_Create_user(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := &Repository{ - Name: String("n"), - Archived: Bool(true), // not passed along. + Name: Ptr("n"), + Archived: Ptr(true), // not passed along. } + wantAcceptHeaders := []string{mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} mux.HandleFunc("/user/repos", func(w http.ResponseWriter, r *http.Request) { - v := new(createRepoRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeRepositoryTemplatePreview) - want := &createRepoRequest{Name: String("n")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + want := &createRepoRequest{Name: Ptr("n")} + testJSONBody(t, r, want) fmt.Fprint(w, `{"id":1}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.Create(ctx, "", input) if err != nil { t.Errorf("Repositories.Create returned error: %v", err) } - want := &Repository{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { t.Errorf("Repositories.Create returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.Create(ctx, "", input) - if got != nil { - t.Errorf("client.BaseURL.Path='' Create = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' Create resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' Create err = nil, want error") - } + const methodName = "Create" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Create(ctx, "", nil) + return err + }) + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Create(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.Create(ctx, "", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_Create_org(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.Create(ctx, "", input) - if got != nil { - t.Errorf("rate.Reset.Time > now Create = %#v, want nil", got) + input := &Repository{ + Name: Ptr("n"), + Archived: Ptr(true), // not passed along. } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now Create resp = %#v, want StatusCode=%v", resp.Response, want) + + wantAcceptHeaders := []string{mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} + mux.HandleFunc("/orgs/o/repos", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + want := &createRepoRequest{Name: Ptr("n")} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + repo, _, err := client.Repositories.Create(ctx, "o", input) + if err != nil { + t.Errorf("Repositories.Create returned error: %v", err) } - if err == nil { - t.Error("rate.Reset.Time > now Create err = nil, want error") + + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(repo, want) { + t.Errorf("Repositories.Create returned %+v, want %+v", repo, want) } } -func TestRepositoriesService_Create_org(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_Create_withCustomProperties(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) input := &Repository{ - Name: String("n"), - Archived: Bool(true), // not passed along. + Name: Ptr("n"), + CustomProperties: map[string]any{ + "environment": "production", + "team": "backend", + "priority": 1, + }, } + wantAcceptHeaders := []string{mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} mux.HandleFunc("/orgs/o/repos", func(w http.ResponseWriter, r *http.Request) { - v := new(createRepoRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeRepositoryTemplatePreview) - want := &createRepoRequest{Name: String("n")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + want := &createRepoRequest{ + Name: Ptr("n"), + CustomProperties: map[string]any{ + "environment": "production", + "team": "backend", + "priority": float64(1), // JSON unmarshals numbers as float64 + }, } - + testJSONBody(t, r, want) fmt.Fprint(w, `{"id":1}`) }) - repo, _, err := client.Repositories.Create(context.Background(), "o", input) + ctx := t.Context() + repo, _, err := client.Repositories.Create(ctx, "o", input) if err != nil { t.Errorf("Repositories.Create returned error: %v", err) } - want := &Repository{ID: Int64(1)} - if !reflect.DeepEqual(repo, want) { + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(repo, want) { t.Errorf("Repositories.Create returned %+v, want %+v", repo, want) } } func TestRepositoriesService_CreateFromTemplate(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - templateRepoReq := &TemplateRepoRequest{ - Name: String("n"), + templateRepoReq := TemplateRepoRequest{ + Name: "n", } mux.HandleFunc("/repos/to/tr/generate", func(w http.ResponseWriter, r *http.Request) { - v := new(TemplateRepoRequest) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") testHeader(t, r, "Accept", mediaTypeRepositoryTemplatePreview) - want := &TemplateRepoRequest{Name: String("n")} - if !reflect.DeepEqual(v, want) { - t.Errorf("Request body = %+v, want %+v", v, want) - } - + testJSONBody(t, r, templateRepoReq) fmt.Fprint(w, `{"id":1,"name":"n"}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.CreateFromTemplate(ctx, "to", "tr", templateRepoReq) if err != nil { t.Errorf("Repositories.CreateFromTemplate returned error: %v", err) } - want := &Repository{ID: Int64(1), Name: String("n")} - if !reflect.DeepEqual(got, want) { + want := &Repository{ID: Ptr(int64(1)), Name: Ptr("n")} + if !cmp.Equal(got, want) { t.Errorf("Repositories.CreateFromTemplate returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.CreateFromTemplate(ctx, "to", "tr", templateRepoReq) - if got != nil { - t.Errorf("client.BaseURL.Path='' CreateFromTemplate = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' CreateFromTemplate resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' CreateFromTemplate err = nil, want error") - } + const methodName = "CreateFromTemplate" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateFromTemplate(ctx, "\n", "\n", templateRepoReq) + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.CreateFromTemplate(ctx, "to", "tr", templateRepoReq) - if got != nil { - t.Errorf("rate.Reset.Time > now CreateFromTemplate = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now CreateFromTemplate resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now CreateFromTemplate err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateFromTemplate(ctx, "to", "tr", templateRepoReq) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_Get(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview, mediaTypeRepositoryTemplatePreview} + wantAcceptHeaders := []string{mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview, mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) + fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"},"security_and_analysis":{"advanced_security":{"status":"enabled"},"secret_scanning":{"status":"enabled"},"secret_scanning_push_protection":{"status":"enabled"},"dependabot_security_updates":{"status": "enabled"}, "secret_scanning_validity_checks":{"status":"enabled"}, "code_security":{"status":"enabled"}}}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.Get(ctx, "o", "r") if err != nil { t.Errorf("Repositories.Get returned error: %v", err) } - want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} - if !reflect.DeepEqual(got, want) { + want := &Repository{ID: Ptr(int64(1)), Name: Ptr("n"), Description: Ptr("d"), Owner: &User{Login: Ptr("l")}, License: &License{Key: Ptr("mit")}, SecurityAndAnalysis: &SecurityAndAnalysis{AdvancedSecurity: &AdvancedSecurity{Status: Ptr("enabled")}, SecretScanning: &SecretScanning{Ptr("enabled")}, SecretScanningPushProtection: &SecretScanningPushProtection{Ptr("enabled")}, DependabotSecurityUpdates: &DependabotSecurityUpdates{Ptr("enabled")}, SecretScanningValidityChecks: &SecretScanningValidityChecks{Ptr("enabled")}, CodeSecurity: &CodeSecurity{Ptr("enabled")}}} + if !cmp.Equal(got, want) { t.Errorf("Repositories.Get returned %+v, want %+v", got, want) } - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err := client.Repositories.Get(ctx, "o", "r") - if got != nil { - t.Errorf("rate.Reset.Time > now Get = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now Get resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now Get err = nil, want error") - } + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Get(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.Get(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetCodeOfConduct(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/community/code_of_conduct", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", mediaTypeCodesOfConductPreview) fmt.Fprint(w, `{ - "key": "key", - "name": "name", - "url": "url", - "body": "body"}`, + "code_of_conduct": { + "key": "key", + "name": "name", + "url": "url", + "body": "body" + }}`, ) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.GetCodeOfConduct(ctx, "o", "r") if err != nil { t.Errorf("Repositories.GetCodeOfConduct returned error: %v", err) } want := &CodeOfConduct{ - Key: String("key"), - Name: String("name"), - URL: String("url"), - Body: String("body"), + Key: Ptr("key"), + Name: Ptr("name"), + URL: Ptr("url"), + Body: Ptr("body"), } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Repositories.GetCodeOfConduct returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.GetCodeOfConduct(ctx, "o", "r") - if got != nil { - t.Errorf("client.BaseURL.Path='' GetCodeOfConduct = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' GetCodeOfConduct resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' GetCodeOfConduct err = nil, want error") - } + const methodName = "GetCodeOfConduct" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetCodeOfConduct(ctx, "\n", "\n") + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.GetCodeOfConduct(ctx, "o", "r") - if got != nil { - t.Errorf("rate.Reset.Time > now GetCodeOfConduct = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now GetCodeOfConduct resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now GetCodeOfConduct err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetCodeOfConduct(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetByID(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repositories/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1,"name":"n","description":"d","owner":{"login":"l"},"license":{"key":"mit"}}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.GetByID(ctx, 1) if err != nil { t.Fatalf("Repositories.GetByID returned error: %v", err) } - want := &Repository{ID: Int64(1), Name: String("n"), Description: String("d"), Owner: &User{Login: String("l")}, License: &License{Key: String("mit")}} - if !reflect.DeepEqual(got, want) { + want := &Repository{ID: Ptr(int64(1)), Name: Ptr("n"), Description: Ptr("d"), Owner: &User{Login: Ptr("l")}, License: &License{Key: Ptr("mit")}} + if !cmp.Equal(got, want) { t.Errorf("Repositories.GetByID returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.GetByID(ctx, 1) - if got != nil { - t.Errorf("client.BaseURL.Path='' GetByID = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' GetByID resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' GetByID err = nil, want error") - } - - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.GetByID(ctx, 1) - if got != nil { - t.Errorf("rate.Reset.Time > now GetByID = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now GetByID resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now GetByID err = nil, want error") - } + const methodName = "GetByID" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetByID(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - i := true - input := &Repository{HasIssues: &i} + input := &Repository{HasIssues: Ptr(true)} + wantAcceptHeaders := []string{mediaTypeRepositoryTemplatePreview, mediaTypeRepositoryVisibilityPreview} mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - v := new(Repository) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeRepositoryTemplatePreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.Edit(ctx, "o", "r", input) if err != nil { t.Errorf("Repositories.Edit returned error: %v", err) } - want := &Repository{ID: Int64(1)} - if !reflect.DeepEqual(got, want) { + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { t.Errorf("Repositories.Edit returned %+v, want %+v", got, want) } - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err := client.Repositories.Edit(ctx, "o", "r", input) - if got != nil { - t.Errorf("rate.Reset.Time > now Edit = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now Edit resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now Edit err = nil, want error") - } + const methodName = "Edit" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Edit(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.Edit(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_Delete(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/o/r", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - ctx := context.Background() + ctx := t.Context() _, err := client.Repositories.Delete(ctx, "o", "r") if err != nil { t.Errorf("Repositories.Delete returned error: %v", err) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - resp, err := client.Repositories.Delete(ctx, "o", "r") - if resp != nil { - t.Errorf("client.BaseURL.Path='' Delete resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' Delete err = nil, want error") - } + const methodName = "Delete" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.Delete(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.Delete(ctx, "o", "r") + }) } func TestRepositoriesService_Get_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.Get(context.Background(), "%", "r") + ctx := t.Context() + _, _, err := client.Repositories.Get(ctx, "%", "r") testURLParseError(t, err) } func TestRepositoriesService_Edit_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Repositories.Edit(context.Background(), "%", "r", nil) + ctx := t.Context() + _, _, err := client.Repositories.Edit(ctx, "%", "r", nil) testURLParseError(t, err) } +func TestRepositoriesService_GetVulnerabilityAlerts(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/vulnerability-alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRequiredVulnerabilityAlertsPreview) + + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + vulnerabilityAlertsEnabled, _, err := client.Repositories.GetVulnerabilityAlerts(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetVulnerabilityAlerts returned error: %v", err) + } + + if want := true; vulnerabilityAlertsEnabled != want { + t.Errorf("Repositories.GetVulnerabilityAlerts returned %+v, want %+v", vulnerabilityAlertsEnabled, want) + } + + const methodName = "GetVulnerabilityAlerts" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetVulnerabilityAlerts(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetVulnerabilityAlerts(ctx, "o", "r") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) +} + func TestRepositoriesService_EnableVulnerabilityAlerts(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/vulnerability-alerts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -638,14 +624,25 @@ func TestRepositoriesService_EnableVulnerabilityAlerts(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Repositories.EnableVulnerabilityAlerts(context.Background(), "o", "r"); err != nil { + ctx := t.Context() + if _, err := client.Repositories.EnableVulnerabilityAlerts(ctx, "o", "r"); err != nil { t.Errorf("Repositories.EnableVulnerabilityAlerts returned error: %v", err) } + + const methodName = "EnableVulnerabilityAlerts" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EnableVulnerabilityAlerts(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.EnableVulnerabilityAlerts(ctx, "o", "r") + }) } func TestRepositoriesService_DisableVulnerabilityAlerts(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/vulnerability-alerts", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -654,46 +651,94 @@ func TestRepositoriesService_DisableVulnerabilityAlerts(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Repositories.DisableVulnerabilityAlerts(context.Background(), "o", "r"); err != nil { + ctx := t.Context() + if _, err := client.Repositories.DisableVulnerabilityAlerts(ctx, "o", "r"); err != nil { t.Errorf("Repositories.DisableVulnerabilityAlerts returned error: %v", err) } + + const methodName = "DisableVulnerabilityAlerts" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisableVulnerabilityAlerts(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DisableVulnerabilityAlerts(ctx, "o", "r") + }) } func TestRepositoriesService_EnableAutomatedSecurityFixes(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/automated-security-fixes", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeRequiredAutomatedSecurityFixesPreview) w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Repositories.EnableAutomatedSecurityFixes(context.Background(), "o", "r"); err != nil { + ctx := t.Context() + if _, err := client.Repositories.EnableAutomatedSecurityFixes(ctx, "o", "r"); err != nil { t.Errorf("Repositories.EnableAutomatedSecurityFixes returned error: %v", err) } } +func TestRepositoriesService_GetAutomatedSecurityFixes(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/automated-security-fixes", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"enabled": true, "paused": false}`) + }) + + ctx := t.Context() + fixes, _, err := client.Repositories.GetAutomatedSecurityFixes(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.GetAutomatedSecurityFixes returned error: %v", err) + } + + want := &AutomatedSecurityFixes{ + Enabled: Ptr(true), + Paused: Ptr(false), + } + if !cmp.Equal(fixes, want) { + t.Errorf("Repositories.GetAutomatedSecurityFixes returned %#v, want %#v", fixes, want) + } + + const methodName = "GetAutomatedSecurityFixes" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetAutomatedSecurityFixes(ctx, "\n", "\n") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAutomatedSecurityFixes(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestRepositoriesService_DisableAutomatedSecurityFixes(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/automated-security-fixes", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeRequiredAutomatedSecurityFixesPreview) w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Repositories.DisableAutomatedSecurityFixes(context.Background(), "o", "r"); err != nil { + ctx := t.Context() + if _, err := client.Repositories.DisableAutomatedSecurityFixes(ctx, "o", "r"); err != nil { t.Errorf("Repositories.DisableAutomatedSecurityFixes returned error: %v", err) } } func TestRepositoriesService_ListContributors(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/contributors", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -705,63 +750,107 @@ func TestRepositoriesService_ListContributors(t *testing.T) { }) opts := &ListContributorsOptions{Anon: "true", ListOptions: ListOptions{Page: 2}} - contributors, _, err := client.Repositories.ListContributors(context.Background(), "o", "r", opts) + ctx := t.Context() + contributors, _, err := client.Repositories.ListContributors(ctx, "o", "r", opts) if err != nil { t.Errorf("Repositories.ListContributors returned error: %v", err) } - want := []*Contributor{{Contributions: Int(42)}} - if !reflect.DeepEqual(contributors, want) { + want := []*Contributor{{Contributions: Ptr(42)}} + if !cmp.Equal(contributors, want) { t.Errorf("Repositories.ListContributors returned %+v, want %+v", contributors, want) } + + const methodName = "ListContributors" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListContributors(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListContributors(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListLanguages(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/languages", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"go":1}`) }) - languages, _, err := client.Repositories.ListLanguages(context.Background(), "o", "r") + ctx := t.Context() + languages, _, err := client.Repositories.ListLanguages(ctx, "o", "r") if err != nil { t.Errorf("Repositories.ListLanguages returned error: %v", err) } want := map[string]int{"go": 1} - if !reflect.DeepEqual(languages, want) { + if !cmp.Equal(languages, want) { t.Errorf("Repositories.ListLanguages returned %+v, want %+v", languages, want) } + + const methodName = "ListLanguages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListLanguages(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListLanguages(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListTeams(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/teams", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"id":1}]`) }) opt := &ListOptions{Page: 2} - teams, _, err := client.Repositories.ListTeams(context.Background(), "o", "r", opt) + ctx := t.Context() + teams, _, err := client.Repositories.ListTeams(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListTeams returned error: %v", err) } - want := []*Team{{ID: Int64(1)}} - if !reflect.DeepEqual(teams, want) { + want := []*Team{{ID: Ptr(int64(1))}} + if !cmp.Equal(teams, want) { t.Errorf("Repositories.ListTeams returned %+v, want %+v", teams, want) } + + const methodName = "ListTeams" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTeams(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTeams(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListTags(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/tags", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -770,859 +859,3714 @@ func TestRepositoriesService_ListTags(t *testing.T) { }) opt := &ListOptions{Page: 2} - tags, _, err := client.Repositories.ListTags(context.Background(), "o", "r", opt) + ctx := t.Context() + tags, _, err := client.Repositories.ListTags(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListTags returned error: %v", err) } want := []*RepositoryTag{ { - Name: String("n"), + Name: Ptr("n"), Commit: &Commit{ - SHA: String("s"), - URL: String("u"), + SHA: Ptr("s"), + URL: Ptr("u"), }, - ZipballURL: String("z"), - TarballURL: String("t"), + ZipballURL: Ptr("z"), + TarballURL: Ptr("t"), }, } - if !reflect.DeepEqual(tags, want) { + if !cmp.Equal(tags, want) { t.Errorf("Repositories.ListTags returned %+v, want %+v", tags, want) } + + const methodName = "ListTags" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTags(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTags(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListBranches(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/branches", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"name":"master", "commit" : {"sha" : "a57781", "url" : "https://api.github.com/repos/o/r/commits/a57781"}}]`) }) - opt := &ListOptions{Page: 2} - branches, _, err := client.Repositories.ListBranches(context.Background(), "o", "r", opt) + opt := &BranchListOptions{ + Protected: nil, + ListOptions: ListOptions{Page: 2}, + } + ctx := t.Context() + branches, _, err := client.Repositories.ListBranches(ctx, "o", "r", opt) if err != nil { t.Errorf("Repositories.ListBranches returned error: %v", err) } - want := []*Branch{{Name: String("master"), Commit: &RepositoryCommit{SHA: String("a57781"), URL: String("https://api.github.com/repos/o/r/commits/a57781")}}} - if !reflect.DeepEqual(branches, want) { + want := []*Branch{{Name: Ptr("master"), Commit: &RepositoryCommit{SHA: Ptr("a57781"), URL: Ptr("https://api.github.com/repos/o/r/commits/a57781")}}} + if !cmp.Equal(branches, want) { t.Errorf("Repositories.ListBranches returned %+v, want %+v", branches, want) } + + const methodName = "ListBranches" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListBranches(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListBranches(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_GetBranch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) + + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25"}, + } + + for _, test := range tests { + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"n", "commit":{"sha":"s","commit":{"message":"m"}}, "protected":true, "protection":{"required_status_checks":{"contexts":["c"]}}}`) + }) + + ctx := t.Context() + branch, _, err := client.Repositories.GetBranch(ctx, "o", "r", test.branch, 0) + if err != nil { + t.Errorf("Repositories.GetBranch returned error: %v", err) + } + + want := &Branch{ + Name: Ptr("n"), + Commit: &RepositoryCommit{ + SHA: Ptr("s"), + Commit: &Commit{ + Message: Ptr("m"), + }, + }, + Protected: Ptr(true), + Protection: &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Contexts: &[]string{"c"}, + }, + }, + } + + if !cmp.Equal(branch, want) { + t.Errorf("Repositories.GetBranch returned %+v, want %+v", branch, want) + } + + const methodName = "GetBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetBranch(ctx, "\n", "\n", "\n", 0) + return err + }) + } +} + +func TestRepositoriesService_GetBranch_BadJSONResponse(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"n", "commit":{"sha":...truncated`) + }) + + ctx := t.Context() + if _, _, err := client.Repositories.GetBranch(ctx, "o", "r", test.branch, 0); err == nil { + t.Error("Repositories.GetBranch returned no error; wanted JSON error") + } + }) + } +} + +func TestRepositoriesService_GetBranch_StatusMovedPermanently_followRedirects(t *testing.T) { + t.Parallel() + client, mux, serverURL := setup(t) mux.HandleFunc("/repos/o/r/branches/b", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprint(w, `{"name":"n", "commit":{"sha":"s","commit":{"message":"m"}}, "protected":true}`) + redirectURL, _ := url.Parse(serverURL + baseURLPath + "/repos/o/r/branches/br") + http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) }) - - branch, _, err := client.Repositories.GetBranch(context.Background(), "o", "r", "b") + mux.HandleFunc("/repos/o/r/branches/br", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"n", "commit":{"sha":"s","commit":{"message":"m"}}, "protected":true, "protection":{"required_status_checks":{"contexts":["c"]}}}`) + }) + ctx := t.Context() + branch, resp, err := client.Repositories.GetBranch(ctx, "o", "r", "b", 1) if err != nil { t.Errorf("Repositories.GetBranch returned error: %v", err) } + if resp.StatusCode != http.StatusOK { + t.Errorf("Repositories.GetBranch returned status: %v, want %v", resp.StatusCode, http.StatusOK) + } want := &Branch{ - Name: String("n"), + Name: Ptr("n"), Commit: &RepositoryCommit{ - SHA: String("s"), + SHA: Ptr("s"), Commit: &Commit{ - Message: String("m"), + Message: Ptr("m"), + }, + }, + Protected: Ptr(true), + Protection: &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Contexts: &[]string{"c"}, }, }, - Protected: Bool(true), } - - if !reflect.DeepEqual(branch, want) { + if !cmp.Equal(branch, want) { t.Errorf("Repositories.GetBranch returned %+v, want %+v", branch, want) } } -func TestRepositoriesService_GetBranchProtection(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_GetBranch_notFound(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat-branch-50%"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + http.Error(w, "branch not found", http.StatusNotFound) + }) + ctx := t.Context() + _, resp, err := client.Repositories.GetBranch(ctx, "o", "r", test.branch, 1) + if err == nil { + t.Error("Repositories.GetBranch returned error: nil") + } + if resp.StatusCode != http.StatusNotFound { + t.Errorf("Repositories.GetBranch returned status: %v, want %v", resp.StatusCode, http.StatusNotFound) + } - mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { - v := new(ProtectionRequest) - json.NewDecoder(r.Body).Decode(v) + // Add custom round tripper + client.client.Transport = roundTripperFunc(func(*http.Request) (*http.Response, error) { + return nil, errors.New("failed to get branch") + }) - testMethod(t, r, "GET") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprintf(w, `{ - "required_status_checks":{ - "strict":true, - "contexts":["continuous-integration"] - }, - "required_pull_request_reviews":{ - "dismissal_restrictions":{ - "users":[{ - "id":3, - "login":"u" - }], - "teams":[{ - "id":4, - "slug":"t" - }] - }, - "dismiss_stale_reviews":true, - "require_code_owner_reviews":true, - "required_approving_review_count":1 - }, - "enforce_admins":{ - "url":"/repos/o/r/branches/b/protection/enforce_admins", - "enabled":true - }, - "restrictions":{ - "users":[{"id":1,"login":"u"}], - "teams":[{"id":2,"slug":"t"}] + const methodName = "GetBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetBranch(ctx, "\n", "\n", "\n", 1) + return err + }) + }) + } +} + +func TestRepositoriesService_RenameBranch(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/rename"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/rename"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + renameBranchReq := "nn" + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + want := &renameBranchRequest{NewName: renameBranchReq} + testJSONBody(t, r, want) + fmt.Fprint(w, `{"protected":true,"name":"nn"}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.RenameBranch(ctx, "o", "r", test.branch, renameBranchReq) + if err != nil { + t.Errorf("Repositories.RenameBranch returned error: %v", err) + } + + want := &Branch{Name: Ptr("nn"), Protected: Ptr(true)} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.RenameBranch returned %+v, want %+v", got, want) + } + + const methodName = "RenameBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RenameBranch(ctx, "\n", "\n", "\n", renameBranchReq) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RenameBranch(ctx, "o", "r", test.branch, renameBranchReq) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} + +func TestRepositoriesService_GetBranchProtection(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + enforceAdminsURLPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection", enforceAdminsURLPath: "/repos/o/r/branches/b/protection/enforce_admins"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection", enforceAdminsURLPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/enforce_admins"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprintf(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":["continuous-integration"], + "checks": [ + { + "context": "continuous-integration", + "app_id": null + } + ] + }, + "required_pull_request_reviews":{ + "dismissal_restrictions":{ + "users":[{ + "id":3, + "login":"u" + }], + "teams":[{ + "id":4, + "slug":"t" + }], + "apps":[{ + "id":5, + "slug":"a" + }] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "require_last_push_approval":false, + "required_approving_review_count":1 + }, + "enforce_admins":{ + "url":"%v", + "enabled":true + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + }, + "required_conversation_resolution": { + "enabled": true + }, + "block_creations": { + "enabled": false + }, + "lock_branch": { + "enabled": false + }, + "allow_fork_syncing": { + "enabled": false + } + }`, test.enforceAdminsURLPath) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.GetBranchProtection(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.GetBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + }, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(3))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(4))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(5))}, + }, + }, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: 1, + RequireLastPushApproval: false, + }, + EnforceAdmins: &AdminEnforcement{ + URL: &test.enforceAdminsURLPath, + Enabled: true, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, + }, + RequiredConversationResolution: &RequiredConversationResolution{ + Enabled: true, + }, + BlockCreations: &BlockCreations{ + Enabled: Ptr(false), + }, + LockBranch: &LockBranch{ + Enabled: Ptr(false), + }, + AllowForkSyncing: &AllowForkSyncing{ + Enabled: Ptr(false), + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.GetBranchProtection returned %+v, want %+v", protection, want) + } + + const methodName = "GetBranchProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetBranchProtection(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetBranchProtection(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} + +func TestRepositoriesService_GetBranchProtection_noDismissalRestrictions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + tests := []struct { + branch string + urlPath string + enforceAdminsURLPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection", enforceAdminsURLPath: "/repos/o/r/branches/b/protection/enforce_admins"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection", enforceAdminsURLPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/enforce_admins"}, + } + + for _, test := range tests { + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprintf(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":["continuous-integration"], + "checks": [ + { + "context": "continuous-integration", + "app_id": null + } + ] + }, + "required_pull_request_reviews":{ + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "required_approving_review_count":1 + }, + "enforce_admins":{ + "url":"%v", + "enabled":true + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}] + } + }`, test.enforceAdminsURLPath) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.GetBranchProtection(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.GetBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + }, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: nil, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: 1, + }, + EnforceAdmins: &AdminEnforcement{ + URL: &test.enforceAdminsURLPath, + Enabled: true, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.GetBranchProtection returned %+v, want %+v", protection, want) + } + } +} + +func TestRepositoriesService_GetBranchProtection_branchNotProtected(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, `{ + "message": %q, + "documentation_url": "https://docs.github.com/rest/repos#get-branch-protection" + }`, githubBranchNotProtected) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.GetBranchProtection(ctx, "o", "r", test.branch) + + if protection != nil { + t.Error("Repositories.GetBranchProtection returned non-nil protection data") + } + + if !errors.Is(err, ErrBranchNotProtected) { + t.Errorf("Repositories.GetBranchProtection returned an invalid error: %v", err) + } + }) + } +} + +func TestRepositoriesService_UpdateBranchProtection_Contexts(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ProtectionRequest{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + DismissStaleReviews: true, + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: &[]string{"uu"}, + Teams: &[]string{"tt"}, + Apps: &[]string{"aa"}, + }, + BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{ + Users: []string{"uuu"}, + Teams: []string{"ttt"}, + Apps: []string{"aaa"}, + }, + }, + Restrictions: &BranchRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + Apps: []string{"a"}, + }, + BlockCreations: Ptr(true), + LockBranch: Ptr(true), + AllowForkSyncing: Ptr(true), + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":["continuous-integration"], + "checks": [ + { + "context": "continuous-integration", + "app_id": null + } + ] + }, + "required_pull_request_reviews":{ + "dismissal_restrictions":{ + "users":[{ + "id":3, + "login":"uu" + }], + "teams":[{ + "id":4, + "slug":"tt" + }], + "apps":[{ + "id":5, + "slug":"aa" + }] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "bypass_pull_request_allowances": { + "users":[{"id":10,"login":"uuu"}], + "teams":[{"id":20,"slug":"ttt"}], + "apps":[{"id":30,"slug":"aaa"}] + } + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + }, + "block_creations": { + "enabled": true + }, + "lock_branch": { + "enabled": true + }, + "allow_fork_syncing": { + "enabled": true } }`) - }) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + }, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("uu"), ID: Ptr(int64(3))}, + }, + Teams: []*Team{ + {Slug: Ptr("tt"), ID: Ptr(int64(4))}, + }, + Apps: []*App{ + {Slug: Ptr("aa"), ID: Ptr(int64(5))}, + }, + }, + RequireCodeOwnerReviews: true, + BypassPullRequestAllowances: &BypassPullRequestAllowances{ + Users: []*User{ + {Login: Ptr("uuu"), ID: Ptr(int64(10))}, + }, + Teams: []*Team{ + {Slug: Ptr("ttt"), ID: Ptr(int64(20))}, + }, + Apps: []*App{ + {Slug: Ptr("aaa"), ID: Ptr(int64(30))}, + }, + }, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, + }, + BlockCreations: &BlockCreations{ + Enabled: Ptr(true), + }, + LockBranch: &LockBranch{ + Enabled: Ptr(true), + }, + AllowForkSyncing: &AllowForkSyncing{ + Enabled: Ptr(true), + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } + + const methodName = "UpdateBranchProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateBranchProtection(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} + +func TestRepositoriesService_UpdateBranchProtection_EmptyContexts(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ProtectionRequest{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + DismissStaleReviews: true, + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: &[]string{"uu"}, + Teams: &[]string{"tt"}, + Apps: &[]string{"aa"}, + }, + BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{ + Users: []string{"uuu"}, + Teams: []string{"ttt"}, + Apps: []string{"aaa"}, + }, + }, + Restrictions: &BranchRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + Apps: []string{"a"}, + }, + BlockCreations: Ptr(true), + LockBranch: Ptr(true), + AllowForkSyncing: Ptr(true), + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":[], + "checks": null + }, + "required_pull_request_reviews":{ + "dismissal_restrictions":{ + "users":[{ + "id":3, + "login":"uu" + }], + "teams":[{ + "id":4, + "slug":"tt" + }], + "apps":[{ + "id":5, + "slug":"aa" + }] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "bypass_pull_request_allowances": { + "users":[{"id":10,"login":"uuu"}], + "teams":[{"id":20,"slug":"ttt"}], + "apps":[{"id":30,"slug":"aaa"}] + } + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + }, + "block_creations": { + "enabled": true + }, + "lock_branch": { + "enabled": true + }, + "allow_fork_syncing": { + "enabled": true + } + }`) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("uu"), ID: Ptr(int64(3))}, + }, + Teams: []*Team{ + {Slug: Ptr("tt"), ID: Ptr(int64(4))}, + }, + Apps: []*App{ + {Slug: Ptr("aa"), ID: Ptr(int64(5))}, + }, + }, + RequireCodeOwnerReviews: true, + BypassPullRequestAllowances: &BypassPullRequestAllowances{ + Users: []*User{ + {Login: Ptr("uuu"), ID: Ptr(int64(10))}, + }, + Teams: []*Team{ + {Slug: Ptr("ttt"), ID: Ptr(int64(20))}, + }, + Apps: []*App{ + {Slug: Ptr("aaa"), ID: Ptr(int64(30))}, + }, + }, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, + }, + BlockCreations: &BlockCreations{ + Enabled: Ptr(true), + }, + LockBranch: &LockBranch{ + Enabled: Ptr(true), + }, + AllowForkSyncing: &AllowForkSyncing{ + Enabled: Ptr(true), + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } + + const methodName = "UpdateBranchProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateBranchProtection(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} + +func TestRepositoriesService_UpdateBranchProtection_Checks(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ProtectionRequest{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + }, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + DismissStaleReviews: true, + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: &[]string{"uu"}, + Teams: &[]string{"tt"}, + Apps: &[]string{"aa"}, + }, + BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{ + Users: []string{"uuu"}, + Teams: []string{"ttt"}, + Apps: []string{"aaa"}, + }, + }, + Restrictions: &BranchRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + Apps: []string{"a"}, + }, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":["continuous-integration"], + "checks": [ + { + "context": "continuous-integration", + "app_id": null + } + ] + }, + "required_pull_request_reviews":{ + "dismissal_restrictions":{ + "users":[{ + "id":3, + "login":"uu" + }], + "teams":[{ + "id":4, + "slug":"tt" + }], + "apps":[{ + "id":5, + "slug":"aa" + }] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "bypass_pull_request_allowances": { + "users":[{"id":10,"login":"uuu"}], + "teams":[{"id":20,"slug":"ttt"}], + "apps":[{"id":30,"slug":"aaa"}] + } + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + } + }`) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + }, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("uu"), ID: Ptr(int64(3))}, + }, + Teams: []*Team{ + {Slug: Ptr("tt"), ID: Ptr(int64(4))}, + }, + Apps: []*App{ + {Slug: Ptr("aa"), ID: Ptr(int64(5))}, + }, + }, + RequireCodeOwnerReviews: true, + BypassPullRequestAllowances: &BypassPullRequestAllowances{ + Users: []*User{ + {Login: Ptr("uuu"), ID: Ptr(int64(10))}, + }, + Teams: []*Team{ + {Slug: Ptr("ttt"), ID: Ptr(int64(20))}, + }, + Apps: []*App{ + {Slug: Ptr("aaa"), ID: Ptr(int64(30))}, + }, + }, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } + }) + } +} + +func TestRepositoriesService_UpdateBranchProtection_EmptyChecks(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ProtectionRequest{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Checks: &[]*RequiredStatusCheck{}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + DismissStaleReviews: true, + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: &[]string{"uu"}, + Teams: &[]string{"tt"}, + Apps: &[]string{"aa"}, + }, + BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{ + Users: []string{"uuu"}, + Teams: []string{"ttt"}, + Apps: []string{"aaa"}, + }, + }, + Restrictions: &BranchRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + Apps: []string{"a"}, + }, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":null, + "checks": [] + }, + "required_pull_request_reviews":{ + "dismissal_restrictions":{ + "users":[{ + "id":3, + "login":"uu" + }], + "teams":[{ + "id":4, + "slug":"tt" + }], + "apps":[{ + "id":5, + "slug":"aa" + }] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "bypass_pull_request_allowances": { + "users":[{"id":10,"login":"uuu"}], + "teams":[{"id":20,"slug":"ttt"}], + "apps":[{"id":30,"slug":"aaa"}] + } + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + } + }`) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Checks: &[]*RequiredStatusCheck{}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("uu"), ID: Ptr(int64(3))}, + }, + Teams: []*Team{ + {Slug: Ptr("tt"), ID: Ptr(int64(4))}, + }, + Apps: []*App{ + {Slug: Ptr("aa"), ID: Ptr(int64(5))}, + }, + }, + RequireCodeOwnerReviews: true, + BypassPullRequestAllowances: &BypassPullRequestAllowances{ + Users: []*User{ + {Login: Ptr("uuu"), ID: Ptr(int64(10))}, + }, + Teams: []*Team{ + {Slug: Ptr("ttt"), ID: Ptr(int64(20))}, + }, + Apps: []*App{ + {Slug: Ptr("aaa"), ID: Ptr(int64(30))}, + }, + }, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } + }) + } +} + +func TestRepositoriesService_UpdateBranchProtection_StrictNoChecks(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ProtectionRequest{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + DismissStaleReviews: true, + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: &[]string{"uu"}, + Teams: &[]string{"tt"}, + Apps: &[]string{"aa"}, + }, + BypassPullRequestAllowancesRequest: &BypassPullRequestAllowancesRequest{ + Users: []string{"uuu"}, + Teams: []string{"ttt"}, + Apps: []string{"aaa"}, + }, + }, + Restrictions: &BranchRestrictionsRequest{ + Users: []string{"u"}, + Teams: []string{"t"}, + Apps: []string{"a"}, + }, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "required_status_checks":{ + "strict":true, + "contexts":[] + }, + "required_pull_request_reviews":{ + "dismissal_restrictions":{ + "users":[{ + "id":3, + "login":"uu" + }], + "teams":[{ + "id":4, + "slug":"tt" + }], + "apps":[{ + "id":5, + "slug":"aa" + }] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "require_last_push_approval":false, + "bypass_pull_request_allowances": { + "users":[{"id":10,"login":"uuu"}], + "teams":[{"id":20,"slug":"ttt"}], + "apps":[{"id":30,"slug":"aaa"}] + } + }, + "restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + } + }`) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredStatusChecks: &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{}, + }, + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("uu"), ID: Ptr(int64(3))}, + }, + Teams: []*Team{ + {Slug: Ptr("tt"), ID: Ptr(int64(4))}, + }, + Apps: []*App{ + {Slug: Ptr("aa"), ID: Ptr(int64(5))}, + }, + }, + RequireCodeOwnerReviews: true, + BypassPullRequestAllowances: &BypassPullRequestAllowances{ + Users: []*User{ + {Login: Ptr("uuu"), ID: Ptr(int64(10))}, + }, + Teams: []*Team{ + {Slug: Ptr("ttt"), ID: Ptr(int64(20))}, + }, + Apps: []*App{ + {Slug: Ptr("aaa"), ID: Ptr(int64(30))}, + }, + }, + }, + Restrictions: &BranchRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } + }) + } +} + +func TestRepositoriesService_UpdateBranchProtection_RequireLastPushApproval(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &ProtectionRequest{ + RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ + RequireLastPushApproval: Ptr(true), + }, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "required_pull_request_reviews":{ + "require_last_push_approval":true + } + }`) + }) + + ctx := t.Context() + protection, _, err := client.Repositories.UpdateBranchProtection(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + } + + want := &Protection{ + RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ + RequireLastPushApproval: true, + }, + } + if !cmp.Equal(protection, want) { + t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + } + }) + } +} + +func TestRepositoriesService_RemoveBranchProtection(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.RemoveBranchProtection(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.RemoveBranchProtection returned error: %v", err) + } + + const methodName = "RemoveBranchProtection" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.RemoveBranchProtection(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.RemoveBranchProtection(ctx, "o", "r", test.branch) + }) + }) + } +} + +func TestRepositoriesService_ListLanguages_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.ListLanguages(ctx, "%", "%") + testURLParseError(t, err) +} + +func TestRepositoriesService_License(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/license", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name": "LICENSE", "path": "LICENSE", "license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}}`) + }) + + ctx := t.Context() + got, _, err := client.Repositories.License(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.License returned error: %v", err) + } + + want := &RepositoryLicense{ + Name: Ptr("LICENSE"), + Path: Ptr("LICENSE"), + License: &License{ + Name: Ptr("MIT License"), + Key: Ptr("mit"), + SPDXID: Ptr("MIT"), + URL: Ptr("https://api.github.com/licenses/mit"), + Featured: Ptr(true), + }, + } + + if !cmp.Equal(got, want) { + t.Errorf("Repositories.License returned %+v, want %+v", got, want) + } + + const methodName = "License" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.License(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.License(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetRequiredStatusChecks(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "strict": true, + "contexts": ["x","y","z"], + "checks": [ + { + "context": "x", + "app_id": null + }, + { + "context": "y", + "app_id": null + }, + { + "context": "z", + "app_id": null + } + ] + }`) + }) + + ctx := t.Context() + checks, _, err := client.Repositories.GetRequiredStatusChecks(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.GetRequiredStatusChecks returned error: %v", err) + } + + want := &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"x", "y", "z"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "x", + }, + { + Context: "y", + }, + { + Context: "z", + }, + }, + } + if !cmp.Equal(checks, want) { + t.Errorf("Repositories.GetRequiredStatusChecks returned %+v, want %+v", checks, want) + } + + const methodName = "GetRequiredStatusChecks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetRequiredStatusChecks(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetRequiredStatusChecks(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} + +func TestRepositoriesService_GetRequiredStatusChecks_branchNotProtected(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, `{ + "message": %q, + "documentation_url": "https://docs.github.com/rest/repos#get-branch-protection" + }`, githubBranchNotProtected) + }) + + ctx := t.Context() + checks, _, err := client.Repositories.GetRequiredStatusChecks(ctx, "o", "r", test.branch) + + if checks != nil { + t.Error("Repositories.GetRequiredStatusChecks returned non-nil status-checks data") + } + + if !errors.Is(err, ErrBranchNotProtected) { + t.Errorf("Repositories.GetRequiredStatusChecks returned an invalid error: %v", err) + } + }) + } +} + +func TestRepositoriesService_UpdateRequiredStatusChecks_Contexts(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &RequiredStatusChecksRequest{ + Strict: Ptr(true), + Contexts: []string{"continuous-integration"}, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + testHeader(t, r, "Accept", mediaTypeV3) + fmt.Fprint(w, `{ + "strict":true, + "contexts":["continuous-integration"], + "checks": [ + { + "context": "continuous-integration", + "app_id": null + } + ] + }`) + }) - protection, _, err := client.Repositories.GetBranchProtection(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.GetBranchProtection returned error: %v", err) + ctx := t.Context() + statusChecks, _, err := client.Repositories.UpdateRequiredStatusChecks(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateRequiredStatusChecks returned error: %v", err) + } + + want := &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + }, + } + if !cmp.Equal(statusChecks, want) { + t.Errorf("Repositories.UpdateRequiredStatusChecks returned %+v, want %+v", statusChecks, want) + } + + const methodName = "UpdateRequiredStatusChecks" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdateRequiredStatusChecks(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateRequiredStatusChecks(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - want := &Protection{ - RequiredStatusChecks: &RequiredStatusChecks{ - Strict: true, - Contexts: []string{"continuous-integration"}, - }, - RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ - DismissStaleReviews: true, - DismissalRestrictions: DismissalRestrictions{ - Users: []*User{ - {Login: String("u"), ID: Int64(3)}, +func TestRepositoriesService_UpdateRequiredStatusChecks_Checks(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + appID := int64(123) + noAppID := int64(-1) + input := &RequiredStatusChecksRequest{ + Strict: Ptr(true), + Checks: []*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + { + Context: "continuous-integration2", + AppID: &appID, + }, + { + Context: "continuous-integration3", + AppID: &noAppID, + }, }, - Teams: []*Team{ - {Slug: String("t"), ID: Int64(4)}, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + testHeader(t, r, "Accept", mediaTypeV3) + fmt.Fprint(w, `{ + "strict":true, + "contexts":["continuous-integration"], + "checks": [ + { + "context": "continuous-integration", + "app_id": null + }, + { + "context": "continuous-integration2", + "app_id": 123 + }, + { + "context": "continuous-integration3", + "app_id": null + } + ] + }`) + }) + + ctx := t.Context() + statusChecks, _, err := client.Repositories.UpdateRequiredStatusChecks(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdateRequiredStatusChecks returned error: %v", err) + } + + want := &RequiredStatusChecks{ + Strict: true, + Contexts: &[]string{"continuous-integration"}, + Checks: &[]*RequiredStatusCheck{ + { + Context: "continuous-integration", + }, + { + Context: "continuous-integration2", + AppID: &appID, + }, + { + Context: "continuous-integration3", + }, }, - }, - RequireCodeOwnerReviews: true, - RequiredApprovingReviewCount: 1, - }, - EnforceAdmins: &AdminEnforcement{ - URL: String("/repos/o/r/branches/b/protection/enforce_admins"), - Enabled: true, - }, - Restrictions: &BranchRestrictions{ - Users: []*User{ - {Login: String("u"), ID: Int64(1)}, - }, - Teams: []*Team{ - {Slug: String("t"), ID: Int64(2)}, - }, - }, + } + if !cmp.Equal(statusChecks, want) { + t.Errorf("Repositories.UpdateRequiredStatusChecks returned %+v, want %+v", statusChecks, want) + } + }) } - if !reflect.DeepEqual(protection, want) { - t.Errorf("Repositories.GetBranchProtection returned %+v, want %+v", protection, want) +} + +func TestRepositoriesService_RemoveRequiredStatusChecks(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeV3) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.RemoveRequiredStatusChecks(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.RemoveRequiredStatusChecks returned error: %v", err) + } + + const methodName = "RemoveRequiredStatusChecks" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.RemoveRequiredStatusChecks(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.RemoveRequiredStatusChecks(ctx, "o", "r", test.branch) + }) + }) } } -func TestRepositoriesService_UpdateBranchProtection(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListRequiredStatusChecksContexts(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks/contexts"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks/contexts"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `["x", "y", "z"]`) + }) + + ctx := t.Context() + contexts, _, err := client.Repositories.ListRequiredStatusChecksContexts(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.ListRequiredStatusChecksContexts returned error: %v", err) + } - input := &ProtectionRequest{ - RequiredStatusChecks: &RequiredStatusChecks{ - Strict: true, - Contexts: []string{"continuous-integration"}, - }, - RequiredPullRequestReviews: &PullRequestReviewsEnforcementRequest{ - DismissStaleReviews: true, - DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ - Users: &[]string{"uu"}, - Teams: &[]string{"tt"}, - }, - }, - Restrictions: &BranchRestrictionsRequest{ - Users: []string{"u"}, - Teams: []string{"t"}, - }, + want := []string{"x", "y", "z"} + if !cmp.Equal(contexts, want) { + t.Errorf("Repositories.ListRequiredStatusChecksContexts returned %+v, want %+v", contexts, want) + } + + const methodName = "ListRequiredStatusChecksContexts" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListRequiredStatusChecksContexts(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListRequiredStatusChecksContexts(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { - v := new(ProtectionRequest) - json.NewDecoder(r.Body).Decode(v) +func TestRepositoriesService_ListRequiredStatusChecksContexts_branchNotProtected(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_status_checks/contexts"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_status_checks/contexts"}, + } - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, `{ + "message": %q, + "documentation_url": "https://docs.github.com/rest/repos#get-branch-protection" + }`, githubBranchNotProtected) + }) + + ctx := t.Context() + contexts, _, err := client.Repositories.ListRequiredStatusChecksContexts(ctx, "o", "r", test.branch) + + if contexts != nil { + t.Error("Repositories.ListRequiredStatusChecksContexts returned non-nil contexts data") + } + + if !errors.Is(err, ErrBranchNotProtected) { + t.Errorf("Repositories.ListRequiredStatusChecksContexts returned an invalid error: %v", err) + } + }) + } +} - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprintf(w, `{ - "required_status_checks":{ - "strict":true, - "contexts":["continuous-integration"] +func TestRepositoriesService_GetPullRequestReviewEnforcement(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_pull_request_reviews"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_pull_request_reviews"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "dismissal_restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] }, - "required_pull_request_reviews":{ - "dismissal_restrictions":{ - "users":[{ - "id":3, - "login":"uu" - }], - "teams":[{ - "id":4, - "slug":"tt" - }] + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "required_approving_review_count":1 + }`) + }) + + ctx := t.Context() + enforcement, _, err := client.Repositories.GetPullRequestReviewEnforcement(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.GetPullRequestReviewEnforcement returned error: %v", err) + } + + want := &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, }, - "dismiss_stale_reviews":true, - "require_code_owner_reviews":true - }, - "restrictions":{ - "users":[{"id":1,"login":"u"}], - "teams":[{"id":2,"slug":"t"}] + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: 1, } - }`) - }) - protection, _, err := client.Repositories.UpdateBranchProtection(context.Background(), "o", "r", "b", input) - if err != nil { - t.Errorf("Repositories.UpdateBranchProtection returned error: %v", err) + if !cmp.Equal(enforcement, want) { + t.Errorf("Repositories.GetPullRequestReviewEnforcement returned %+v, want %+v", enforcement, want) + } + + const methodName = "GetPullRequestReviewEnforcement" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetPullRequestReviewEnforcement(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetPullRequestReviewEnforcement(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - want := &Protection{ - RequiredStatusChecks: &RequiredStatusChecks{ - Strict: true, - Contexts: []string{"continuous-integration"}, - }, - RequiredPullRequestReviews: &PullRequestReviewsEnforcement{ - DismissStaleReviews: true, - DismissalRestrictions: DismissalRestrictions{ - Users: []*User{ - {Login: String("uu"), ID: Int64(3)}, +func TestRepositoriesService_UpdatePullRequestReviewEnforcement(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_pull_request_reviews"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_pull_request_reviews"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &PullRequestReviewsEnforcementUpdate{ + DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ + Users: &[]string{"u"}, + Teams: &[]string{"t"}, + Apps: &[]string{"a"}, }, - Teams: []*Team{ - {Slug: String("tt"), ID: Int64(4)}, + } + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + fmt.Fprint(w, `{ + "dismissal_restrictions":{ + "users":[{"id":1,"login":"u"}], + "teams":[{"id":2,"slug":"t"}], + "apps":[{"id":3,"slug":"a"}] + }, + "dismiss_stale_reviews":true, + "require_code_owner_reviews":true, + "required_approving_review_count":3 + }`) + }) + + ctx := t.Context() + enforcement, _, err := client.Repositories.UpdatePullRequestReviewEnforcement(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned error: %v", err) + } + + want := &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: &DismissalRestrictions{ + Users: []*User{ + {Login: Ptr("u"), ID: Ptr(int64(1))}, + }, + Teams: []*Team{ + {Slug: Ptr("t"), ID: Ptr(int64(2))}, + }, + Apps: []*App{ + {Slug: Ptr("a"), ID: Ptr(int64(3))}, + }, }, - }, - RequireCodeOwnerReviews: true, - }, - Restrictions: &BranchRestrictions{ - Users: []*User{ - {Login: String("u"), ID: Int64(1)}, - }, - Teams: []*Team{ - {Slug: String("t"), ID: Int64(2)}, - }, - }, - } - if !reflect.DeepEqual(protection, want) { - t.Errorf("Repositories.UpdateBranchProtection returned %+v, want %+v", protection, want) + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: 3, + } + if !cmp.Equal(enforcement, want) { + t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned %+v, want %+v", enforcement, want) + } + + const methodName = "UpdatePullRequestReviewEnforcement" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.UpdatePullRequestReviewEnforcement(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdatePullRequestReviewEnforcement(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_RemoveBranchProtection(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_DisableDismissalRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_pull_request_reviews"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_pull_request_reviews"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) + testJSONBody(t, r, struct { + DismissalRestrictionsRequest DismissalRestrictionsRequest `json:"dismissal_restrictions"` + }{ + DismissalRestrictionsRequest: DismissalRestrictionsRequest{}, + }) + fmt.Fprint(w, `{"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count":1}`) + }) + + ctx := t.Context() + enforcement, _, err := client.Repositories.DisableDismissalRestrictions(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.DisableDismissalRestrictions returned error: %v", err) + } - mux.HandleFunc("/repos/o/r/branches/b/protection", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - w.WriteHeader(http.StatusNoContent) - }) + want := &PullRequestReviewsEnforcement{ + DismissStaleReviews: true, + DismissalRestrictions: nil, + RequireCodeOwnerReviews: true, + RequiredApprovingReviewCount: 1, + } + if !cmp.Equal(enforcement, want) { + t.Errorf("Repositories.DisableDismissalRestrictions returned %+v, want %+v", enforcement, want) + } - _, err := client.Repositories.RemoveBranchProtection(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.RemoveBranchProtection returned error: %v", err) + const methodName = "DisableDismissalRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.DisableDismissalRestrictions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.DisableDismissalRestrictions(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_ListLanguages_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestRepositoriesService_RemovePullRequestReviewEnforcement(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_pull_request_reviews"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_pull_request_reviews"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.RemovePullRequestReviewEnforcement(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.RemovePullRequestReviewEnforcement returned error: %v", err) + } - _, _, err := client.Repositories.ListLanguages(context.Background(), "%", "%") - testURLParseError(t, err) + const methodName = "RemovePullRequestReviewEnforcement" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.RemovePullRequestReviewEnforcement(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.RemovePullRequestReviewEnforcement(ctx, "o", "r", test.branch) + }) + }) + } } -func TestRepositoriesService_License(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_GetAdminEnforcement(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/enforce_admins"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/enforce_admins"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true}`) + }) + + ctx := t.Context() + enforcement, _, err := client.Repositories.GetAdminEnforcement(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.GetAdminEnforcement returned error: %v", err) + } - mux.HandleFunc("/repos/o/r/license", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - fmt.Fprint(w, `{"name": "LICENSE", "path": "LICENSE", "license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}}`) - }) + want := &AdminEnforcement{ + URL: Ptr("/repos/o/r/branches/b/protection/enforce_admins"), + Enabled: true, + } - got, _, err := client.Repositories.License(context.Background(), "o", "r") - if err != nil { - t.Errorf("Repositories.License returned error: %v", err) + if !cmp.Equal(enforcement, want) { + t.Errorf("Repositories.GetAdminEnforcement returned %+v, want %+v", enforcement, want) + } + + const methodName = "GetAdminEnforcement" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetAdminEnforcement(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAdminEnforcement(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - want := &RepositoryLicense{ - Name: String("LICENSE"), - Path: String("LICENSE"), - License: &License{ - Name: String("MIT License"), - Key: String("mit"), - SPDXID: String("MIT"), - URL: String("https://api.github.com/licenses/mit"), - Featured: Bool(true), - }, +func TestRepositoriesService_AddAdminEnforcement(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/enforce_admins"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/enforce_admins"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true}`) + }) + + ctx := t.Context() + enforcement, _, err := client.Repositories.AddAdminEnforcement(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.AddAdminEnforcement returned error: %v", err) + } + + want := &AdminEnforcement{ + URL: Ptr("/repos/o/r/branches/b/protection/enforce_admins"), + Enabled: true, + } + if !cmp.Equal(enforcement, want) { + t.Errorf("Repositories.AddAdminEnforcement returned %+v, want %+v", enforcement, want) + } + + const methodName = "AddAdminEnforcement" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddAdminEnforcement(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddAdminEnforcement(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.License returned %+v, want %+v", got, want) +func TestRepositoriesService_RemoveAdminEnforcement(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/enforce_admins"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/enforce_admins"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.RemoveAdminEnforcement(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.RemoveAdminEnforcement returned error: %v", err) + } + + const methodName = "RemoveAdminEnforcement" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.RemoveAdminEnforcement(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.RemoveAdminEnforcement(ctx, "o", "r", test.branch) + }) + }) } } -func TestRepositoriesService_GetRequiredStatusChecks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_GetSignaturesProtectedBranch(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_signatures"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_signatures"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeSignaturePreview) + fmt.Fprint(w, `{"url":"/repos/o/r/branches/b/protection/required_signatures","enabled":false}`) + }) + + ctx := t.Context() + signature, _, err := client.Repositories.GetSignaturesProtectedBranch(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.GetSignaturesProtectedBranch returned error: %v", err) + } + + want := &SignaturesProtectedBranch{ + URL: Ptr("/repos/o/r/branches/b/protection/required_signatures"), + Enabled: Ptr(false), + } - mux.HandleFunc("/repos/o/r/branches/b/protection/required_status_checks", func(w http.ResponseWriter, r *http.Request) { - v := new(ProtectionRequest) - json.NewDecoder(r.Body).Decode(v) + if !cmp.Equal(signature, want) { + t.Errorf("Repositories.GetSignaturesProtectedBranch returned %+v, want %+v", signature, want) + } - testMethod(t, r, "GET") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprint(w, `{"strict": true,"contexts": ["x","y","z"]}`) - }) + const methodName = "GetSignaturesProtectedBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.GetSignaturesProtectedBranch(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetSignaturesProtectedBranch(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} - checks, _, err := client.Repositories.GetRequiredStatusChecks(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.GetRequiredStatusChecks returned error: %v", err) +func TestRepositoriesService_GetSignaturesProtectedBranch_branchNotProtected(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_signatures"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_signatures"}, } - want := &RequiredStatusChecks{ - Strict: true, - Contexts: []string{"x", "y", "z"}, + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, `{ + "message": %q, + "documentation_url": "https://docs.github.com/rest/repos#get-branch-protection" + }`, githubBranchNotProtected) + }) + + ctx := t.Context() + checks, _, err := client.Repositories.GetSignaturesProtectedBranch(ctx, "o", "r", test.branch) + + if checks != nil { + t.Error("Repositories.GetSignaturesProtectedBranch returned non-nil status-checks data") + } + + if !errors.Is(err, ErrBranchNotProtected) { + t.Errorf("Repositories.GetSignaturesProtectedBranch returned an invalid error: %v", err) + } + }) } - if !reflect.DeepEqual(checks, want) { - t.Errorf("Repositories.GetRequiredStatusChecks returned %+v, want %+v", checks, want) +} + +func TestRepositoriesService_RequireSignaturesOnProtectedBranch(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_signatures"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_signatures"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Accept", mediaTypeSignaturePreview) + fmt.Fprint(w, `{"url":"/repos/o/r/branches/b/protection/required_signatures","enabled":true}`) + }) + + ctx := t.Context() + signature, _, err := client.Repositories.RequireSignaturesOnProtectedBranch(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.RequireSignaturesOnProtectedBranch returned error: %v", err) + } + + want := &SignaturesProtectedBranch{ + URL: Ptr("/repos/o/r/branches/b/protection/required_signatures"), + Enabled: Ptr(true), + } + + if !cmp.Equal(signature, want) { + t.Errorf("Repositories.RequireSignaturesOnProtectedBranch returned %+v, want %+v", signature, want) + } + + const methodName = "RequireSignaturesOnProtectedBranch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RequireSignaturesOnProtectedBranch(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RequireSignaturesOnProtectedBranch(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_UpdateRequiredStatusChecks(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_OptionalSignaturesOnProtectedBranch(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/required_signatures"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/required_signatures"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeSignaturePreview) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Repositories.OptionalSignaturesOnProtectedBranch(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.OptionalSignaturesOnProtectedBranch returned error: %v", err) + } + + const methodName = "OptionalSignaturesOnProtectedBranch" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.OptionalSignaturesOnProtectedBranch(ctx, "\n", "\n", "\n") + return err + }) - input := &RequiredStatusChecksRequest{ - Strict: Bool(true), - Contexts: []string{"continuous-integration"}, + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.OptionalSignaturesOnProtectedBranch(ctx, "o", "r", test.branch) + }) + }) } +} - mux.HandleFunc("/repos/o/r/branches/b/protection/required_status_checks", func(w http.ResponseWriter, r *http.Request) { - v := new(RequiredStatusChecksRequest) - json.NewDecoder(r.Body).Decode(v) +func TestRepositoriesService_ListAllTopics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - testHeader(t, r, "Accept", mediaTypeV3) - fmt.Fprintf(w, `{"strict":true,"contexts":["continuous-integration"]}`) + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + testFormValues(t, r, values{ + "page": "1", + "per_page": "30", + }) + fmt.Fprint(w, `{"names":["go", "go-github", "github"]}`) }) - statusChecks, _, err := client.Repositories.UpdateRequiredStatusChecks(context.Background(), "o", "r", "b", input) + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 30} + got, _, err := client.Repositories.ListAllTopics(ctx, "o", "r", opts) if err != nil { - t.Errorf("Repositories.UpdateRequiredStatusChecks returned error: %v", err) + t.Fatalf("Repositories.ListAllTopics returned error: %v", err) } - want := &RequiredStatusChecks{ - Strict: true, - Contexts: []string{"continuous-integration"}, - } - if !reflect.DeepEqual(statusChecks, want) { - t.Errorf("Repositories.UpdateRequiredStatusChecks returned %+v, want %+v", statusChecks, want) + want := []string{"go", "go-github", "github"} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ListAllTopics returned %+v, want %+v", got, want) } -} -func TestRepositoriesService_ListRequiredStatusChecksContexts(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + const methodName = "ListAllTopics" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListAllTopics(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListAllTopics(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - mux.HandleFunc("/repos/o/r/branches/b/protection/required_status_checks/contexts", func(w http.ResponseWriter, r *http.Request) { - v := new(ProtectionRequest) - json.NewDecoder(r.Body).Decode(v) +func TestRepositoriesService_ListAllTopics_emptyTopics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprint(w, `["x", "y", "z"]`) + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + fmt.Fprint(w, `{"names":[]}`) }) - contexts, _, err := client.Repositories.ListRequiredStatusChecksContexts(context.Background(), "o", "r", "b") + ctx := t.Context() + got, _, err := client.Repositories.ListAllTopics(ctx, "o", "r", nil) if err != nil { - t.Errorf("Repositories.ListRequiredStatusChecksContexts returned error: %v", err) + t.Fatalf("Repositories.ListAllTopics returned error: %v", err) } - want := []string{"x", "y", "z"} - if !reflect.DeepEqual(contexts, want) { - t.Errorf("Repositories.ListRequiredStatusChecksContexts returned %+v, want %+v", contexts, want) + want := []string{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ListAllTopics returned %+v, want %+v", got, want) } } -func TestRepositoriesService_GetPullRequestReviewEnforcement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ReplaceAllTopics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprintf(w, `{ - "dismissal_restrictions":{ - "users":[{"id":1,"login":"u"}], - "teams":[{"id":2,"slug":"t"}] - }, - "dismiss_stale_reviews":true, - "require_code_owner_reviews":true, - "required_approving_review_count":1 - }`) + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + fmt.Fprint(w, `{"names":["go", "go-github", "github"]}`) }) - enforcement, _, err := client.Repositories.GetPullRequestReviewEnforcement(context.Background(), "o", "r", "b") + ctx := t.Context() + got, _, err := client.Repositories.ReplaceAllTopics(ctx, "o", "r", []string{"go", "go-github", "github"}) if err != nil { - t.Errorf("Repositories.GetPullRequestReviewEnforcement returned error: %v", err) - } - - want := &PullRequestReviewsEnforcement{ - DismissStaleReviews: true, - DismissalRestrictions: DismissalRestrictions{ - Users: []*User{ - {Login: String("u"), ID: Int64(1)}, - }, - Teams: []*Team{ - {Slug: String("t"), ID: Int64(2)}, - }, - }, - RequireCodeOwnerReviews: true, - RequiredApprovingReviewCount: 1, + t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) } - if !reflect.DeepEqual(enforcement, want) { - t.Errorf("Repositories.GetPullRequestReviewEnforcement returned %+v, want %+v", enforcement, want) + want := []string{"go", "go-github", "github"} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) } -} -func TestRepositoriesService_UpdatePullRequestReviewEnforcement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + const methodName = "ReplaceAllTopics" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ReplaceAllTopics(ctx, "\n", "\n", []string{"\n", "\n", "\n"}) + return err + }) - input := &PullRequestReviewsEnforcementUpdate{ - DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ - Users: &[]string{"u"}, - Teams: &[]string{"t"}, - }, - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ReplaceAllTopics(ctx, "o", "r", []string{"go", "go-github", "github"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { - v := new(PullRequestReviewsEnforcementUpdate) - json.NewDecoder(r.Body).Decode(v) +func TestRepositoriesService_ReplaceAllTopics_nilSlice(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + want := repositoryTopics{ + Names: []string{}, } - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprintf(w, `{ - "dismissal_restrictions":{ - "users":[{"id":1,"login":"u"}], - "teams":[{"id":2,"slug":"t"}] - }, - "dismiss_stale_reviews":true, - "require_code_owner_reviews":true, - "required_approving_review_count":3 - }`) + testJSONBody(t, r, want) + fmt.Fprint(w, `{"names":[]}`) }) - enforcement, _, err := client.Repositories.UpdatePullRequestReviewEnforcement(context.Background(), "o", "r", "b", input) + ctx := t.Context() + got, _, err := client.Repositories.ReplaceAllTopics(ctx, "o", "r", nil) if err != nil { - t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned error: %v", err) + t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) } - want := &PullRequestReviewsEnforcement{ - DismissStaleReviews: true, - DismissalRestrictions: DismissalRestrictions{ - Users: []*User{ - {Login: String("u"), ID: Int64(1)}, - }, - Teams: []*Team{ - {Slug: String("t"), ID: Int64(2)}, - }, - }, - RequireCodeOwnerReviews: true, - RequiredApprovingReviewCount: 3, - } - if !reflect.DeepEqual(enforcement, want) { - t.Errorf("Repositories.UpdatePullRequestReviewEnforcement returned %+v, want %+v", enforcement, want) + want := []string{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) } } -func TestRepositoriesService_DisableDismissalRestrictions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ReplaceAllTopics_emptySlice(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PATCH") - // TODO: remove custom Accept header when this API fully launches - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - testBody(t, r, `{"dismissal_restrictions":[]}`+"\n") - fmt.Fprintf(w, `{"dismissal_restrictions":{"users":[],"teams":[]},"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_approving_review_count":1}`) + input := []string{} + + mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + want := repositoryTopics{ + Names: input, + } + testJSONBody(t, r, want) + fmt.Fprint(w, `{"names":[]}`) }) - enforcement, _, err := client.Repositories.DisableDismissalRestrictions(context.Background(), "o", "r", "b") + ctx := t.Context() + got, _, err := client.Repositories.ReplaceAllTopics(ctx, "o", "r", input) if err != nil { - t.Errorf("Repositories.DisableDismissalRestrictions returned error: %v", err) + t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) } - want := &PullRequestReviewsEnforcement{ - DismissStaleReviews: true, - DismissalRestrictions: DismissalRestrictions{ - Users: []*User{}, - Teams: []*Team{}, - }, - RequireCodeOwnerReviews: true, - RequiredApprovingReviewCount: 1, - } - if !reflect.DeepEqual(enforcement, want) { - t.Errorf("Repositories.DisableDismissalRestrictions returned %+v, want %+v", enforcement, want) + want := []string{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) } } -func TestRepositoriesService_RemovePullRequestReviewEnforcement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/branches/b/protection/required_pull_request_reviews", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - w.WriteHeader(http.StatusNoContent) - }) +func TestRepositoriesService_ListAppRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/apps"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/apps"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + ctx := t.Context() + _, _, err := client.Repositories.ListAppRestrictions(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.ListAppRestrictions returned error: %v", err) + } - _, err := client.Repositories.RemovePullRequestReviewEnforcement(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.RemovePullRequestReviewEnforcement returned error: %v", err) + const methodName = "ListAppRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListAppRestrictions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListAppRestrictions(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_GetAdminEnforcement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/branches/b/protection/enforce_admins", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true}`) - }) +func TestRepositoriesService_ReplaceAppRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/apps"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/apps"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.ReplaceAppRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.ReplaceAppRestrictions returned error: %v", err) + } + want := []*App{ + {Name: Ptr("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceAppRestrictions returned %+v, want %+v", got, want) + } - enforcement, _, err := client.Repositories.GetAdminEnforcement(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.GetAdminEnforcement returned error: %v", err) + const methodName = "ReplaceAppRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ReplaceAppRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ReplaceAppRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - want := &AdminEnforcement{ - URL: String("/repos/o/r/branches/b/protection/enforce_admins"), - Enabled: true, - } +func TestRepositoriesService_AddAppRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/apps"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/apps"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.AddAppRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.AddAppRestrictions returned error: %v", err) + } + want := []*App{ + {Name: Ptr("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.AddAppRestrictions returned %+v, want %+v", got, want) + } - if !reflect.DeepEqual(enforcement, want) { - t.Errorf("Repositories.GetAdminEnforcement returned %+v, want %+v", enforcement, want) + const methodName = "AddAppRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddAppRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddAppRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_AddAdminEnforcement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/branches/b/protection/enforce_admins", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/enforce_admins","enabled":true}`) - }) +func TestRepositoriesService_RemoveAppRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/apps"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/apps"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `[]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.RemoveAppRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.RemoveAppRestrictions returned error: %v", err) + } + want := []*App{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.RemoveAppRestrictions returned %+v, want %+v", got, want) + } - enforcement, _, err := client.Repositories.AddAdminEnforcement(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.AddAdminEnforcement returned error: %v", err) + const methodName = "RemoveAppRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RemoveAppRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RemoveAppRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} + +func TestRepositoriesService_ListTeamRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/teams"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/teams"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + ctx := t.Context() + _, _, err := client.Repositories.ListTeamRestrictions(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.ListTeamRestrictions returned error: %v", err) + } - want := &AdminEnforcement{ - URL: String("/repos/o/r/branches/b/protection/enforce_admins"), - Enabled: true, + const methodName = "ListTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTeamRestrictions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTeamRestrictions(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } - if !reflect.DeepEqual(enforcement, want) { - t.Errorf("Repositories.AddAdminEnforcement returned %+v, want %+v", enforcement, want) +} + +func TestRepositoriesService_ReplaceTeamRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/teams"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/teams"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.ReplaceTeamRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.ReplaceTeamRestrictions returned error: %v", err) + } + want := []*Team{ + {Name: Ptr("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceTeamRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "ReplaceTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ReplaceTeamRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ReplaceTeamRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_RemoveAdminEnforcement(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_AddTeamRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/teams"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/teams"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.AddTeamRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.AddTeamRestrictions returned error: %v", err) + } + want := []*Team{ + {Name: Ptr("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.AddTeamRestrictions returned %+v, want %+v", got, want) + } - mux.HandleFunc("/repos/o/r/branches/b/protection/enforce_admins", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeRequiredApprovingReviewsPreview) - w.WriteHeader(http.StatusNoContent) - }) + const methodName = "AddTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddTeamRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddTeamRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} - _, err := client.Repositories.RemoveAdminEnforcement(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.RemoveAdminEnforcement returned error: %v", err) +func TestRepositoriesService_RemoveTeamRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/teams"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/teams"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `[]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.RemoveTeamRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.RemoveTeamRestrictions returned error: %v", err) + } + want := []*Team{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.RemoveTeamRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "RemoveTeamRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RemoveTeamRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RemoveTeamRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_GetSignaturesProtectedBranch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListUserRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/users"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/users"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + }) + + ctx := t.Context() + _, _, err := client.Repositories.ListUserRestrictions(ctx, "o", "r", test.branch) + if err != nil { + t.Errorf("Repositories.ListUserRestrictions returned error: %v", err) + } - mux.HandleFunc("/repos/o/r/branches/b/protection/required_signatures", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeSignaturePreview) - fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/required_signatures","enabled":false}`) - }) + const methodName = "ListUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListUserRestrictions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListUserRestrictions(ctx, "o", "r", test.branch) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) + } +} - signature, _, err := client.Repositories.GetSignaturesProtectedBranch(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.GetSignaturesProtectedBranch returned error: %v", err) +func TestRepositoriesService_ReplaceUserRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/users"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/users"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.ReplaceUserRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.ReplaceUserRestrictions returned error: %v", err) + } + want := []*User{ + {Name: Ptr("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.ReplaceUserRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "ReplaceUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ReplaceUserRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ReplaceUserRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} - want := &SignaturesProtectedBranch{ - URL: String("/repos/o/r/branches/b/protection/required_signatures"), - Enabled: Bool(false), +func TestRepositoriesService_AddUserRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/users"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/users"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `[{ + "name": "octocat" + }]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.AddUserRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.AddUserRestrictions returned error: %v", err) + } + want := []*User{ + {Name: Ptr("octocat")}, + } + if !cmp.Equal(got, want) { + t.Errorf("Repositories.AddUserRestrictions returned %+v, want %+v", got, want) + } + + const methodName = "AddUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.AddUserRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.AddUserRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } +} + +func TestRepositoriesService_RemoveUserRestrictions(t *testing.T) { + t.Parallel() + tests := []struct { + branch string + urlPath string + }{ + {branch: "b", urlPath: "/repos/o/r/branches/b/protection/restrictions/users"}, + {branch: "feat/branch-50%", urlPath: "/repos/o/r/branches/feat%2fbranch-50%25/protection/restrictions/users"}, + } + + for _, test := range tests { + t.Run(test.branch, func(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc(test.urlPath, func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + fmt.Fprint(w, `[]`) + }) + input := []string{"octocat"} + ctx := t.Context() + got, _, err := client.Repositories.RemoveUserRestrictions(ctx, "o", "r", test.branch, input) + if err != nil { + t.Errorf("Repositories.RemoveUserRestrictions returned error: %v", err) + } + want := []*User{} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.RemoveUserRestrictions returned %+v, want %+v", got, want) + } - if !reflect.DeepEqual(signature, want) { - t.Errorf("Repositories.GetSignaturesProtectedBranch returned %+v, want %+v", signature, want) + const methodName = "RemoveUserRestrictions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.RemoveUserRestrictions(ctx, "\n", "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.RemoveUserRestrictions(ctx, "o", "r", test.branch, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + }) } } -func TestRepositoriesService_RequireSignaturesOnProtectedBranch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_Transfer(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/branches/b/protection/required_signatures", func(w http.ResponseWriter, r *http.Request) { + input := TransferRequest{NewOwner: "a", NewName: Ptr("b"), TeamID: []int64{123}} + + mux.HandleFunc("/repos/o/r/transfer", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeSignaturePreview) - fmt.Fprintf(w, `{"url":"/repos/o/r/branches/b/protection/required_signatures","enabled":true}`) + testJSONBody(t, r, input) + fmt.Fprint(w, `{"owner":{"login":"a"}}`) }) - signature, _, err := client.Repositories.RequireSignaturesOnProtectedBranch(context.Background(), "o", "r", "b") + ctx := t.Context() + got, _, err := client.Repositories.Transfer(ctx, "o", "r", input) if err != nil { - t.Errorf("Repositories.RequireSignaturesOnProtectedBranch returned error: %v", err) + t.Errorf("Repositories.Transfer returned error: %v", err) } - want := &SignaturesProtectedBranch{ - URL: String("/repos/o/r/branches/b/protection/required_signatures"), - Enabled: Bool(true), + want := &Repository{Owner: &User{Login: Ptr("a")}} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.Transfer returned %+v, want %+v", got, want) } - if !reflect.DeepEqual(signature, want) { - t.Errorf("Repositories.RequireSignaturesOnProtectedBranch returned %+v, want %+v", signature, want) - } + const methodName = "Transfer" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Transfer(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.Transfer(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_OptionalSignaturesOnProtectedBranch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_Dispatch(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/branches/b/protection/required_signatures", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeSignaturePreview) - w.WriteHeader(http.StatusNoContent) + var input DispatchRequestOptions + + mux.HandleFunc("/repos/o/r/dispatches", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"owner":{"login":"a"}}`) }) - _, err := client.Repositories.OptionalSignaturesOnProtectedBranch(context.Background(), "o", "r", "b") - if err != nil { - t.Errorf("Repositories.OptionalSignaturesOnProtectedBranch returned error: %v", err) + ctx := t.Context() + + testCases := []any{ + nil, + struct { + Foo string + }{ + Foo: "test", + }, + struct { + Bar int + }{ + Bar: 42, + }, + struct { + Foo string + Bar int + Baz bool + }{ + Foo: "test", + Bar: 42, + Baz: false, + }, } -} -func TestPullRequestReviewsEnforcementRequest_MarshalJSON_nilDismissalRestirctions(t *testing.T) { - req := PullRequestReviewsEnforcementRequest{} + for _, tc := range testCases { + if tc == nil { + input = DispatchRequestOptions{EventType: "go"} + } else { + bytes, _ := json.Marshal(tc) + payload := json.RawMessage(bytes) + input = DispatchRequestOptions{EventType: "go", ClientPayload: &payload} + } - got, err := json.Marshal(req) - if err != nil { - t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned error: %v", err) - } + got, _, err := client.Repositories.Dispatch(ctx, "o", "r", input) + if err != nil { + t.Errorf("Repositories.Dispatch returned error: %v", err) + } - want := `{"dismiss_stale_reviews":false,"require_code_owner_reviews":false,"required_approving_review_count":0}` - if want != string(got) { - t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned %+v, want %+v", string(got), want) + want := &Repository{Owner: &User{Login: Ptr("a")}} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.Dispatch returned %+v, want %+v", got, want) + } } - req = PullRequestReviewsEnforcementRequest{ - DismissalRestrictionsRequest: &DismissalRestrictionsRequest{}, - } + const methodName = "Dispatch" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.Dispatch(ctx, "\n", "\n", input) + return err + }) - got, err = json.Marshal(req) - if err != nil { - t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned error: %v", err) - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.Dispatch(ctx, "o", "r", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - want = `{"dismissal_restrictions":{},"dismiss_stale_reviews":false,"require_code_owner_reviews":false,"required_approving_review_count":0}` - if want != string(got) { - t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned %+v, want %+v", string(got), want) - } +func TestRepositoriesService_EnablePrivateReporting(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - req = PullRequestReviewsEnforcementRequest{ - DismissalRestrictionsRequest: &DismissalRestrictionsRequest{ - Users: &[]string{}, - Teams: &[]string{}, - }, - } + mux.HandleFunc("/repos/owner/repo/private-vulnerability-reporting", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusNoContent) + }) - got, err = json.Marshal(req) + ctx := t.Context() + _, err := client.Repositories.EnablePrivateReporting(ctx, "owner", "repo") if err != nil { - t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned error: %v", err) + t.Errorf("Repositories.EnablePrivateReporting returned error: %v", err) } - want = `{"dismissal_restrictions":{"users":[],"teams":[]},"dismiss_stale_reviews":false,"require_code_owner_reviews":false,"required_approving_review_count":0}` - if want != string(got) { - t.Errorf("PullRequestReviewsEnforcementRequest.MarshalJSON returned %+v, want %+v", string(got), want) - } + const methodName = "EnablePrivateReporting" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.EnablePrivateReporting(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.EnablePrivateReporting(ctx, "owner", "repo") + }) } -func TestRepositoriesService_ListAllTopics(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_DisablePrivateReporting(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTopicsPreview) - fmt.Fprint(w, `{"names":["go", "go-github", "github"]}`) + mux.HandleFunc("/repos/owner/repo/private-vulnerability-reporting", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) }) - got, _, err := client.Repositories.ListAllTopics(context.Background(), "o", "r") + ctx := t.Context() + _, err := client.Repositories.DisablePrivateReporting(ctx, "owner", "repo") if err != nil { - t.Fatalf("Repositories.ListAllTopics returned error: %v", err) + t.Errorf("Repositories.DisablePrivateReporting returned error: %v", err) } - want := []string{"go", "go-github", "github"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.ListAllTopics returned %+v, want %+v", got, want) - } + const methodName = "DisablePrivateReporting" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DisablePrivateReporting(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DisablePrivateReporting(ctx, "owner", "repo") + }) } -func TestRepositoriesService_ListAllTopics_emptyTopics(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_IsPrivateReportingEnabled(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/repos/owner/repo/private-vulnerability-reporting", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTopicsPreview) - fmt.Fprint(w, `{"names":[]}`) + fmt.Fprint(w, `{"enabled": true}`) }) - got, _, err := client.Repositories.ListAllTopics(context.Background(), "o", "r") + ctx := t.Context() + enabled, _, err := client.Repositories.IsPrivateReportingEnabled(ctx, "owner", "repo") if err != nil { - t.Fatalf("Repositories.ListAllTopics returned error: %v", err) + t.Errorf("Repositories.IsPrivateReportingEnabled returned error: %v", err) } - - want := []string{} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.ListAllTopics returned %+v, want %+v", got, want) + if want := true; enabled != want { + t.Errorf("Repositories.IsPrivateReportingEnabled returned %+v, want %+v", enabled, want) } + + const methodName = "IsPrivateReportingEnabled" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.IsPrivateReportingEnabled(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.IsPrivateReportingEnabled(ctx, "owner", "repo") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_ReplaceAllTopics(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListRepositoryActivities(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeTopicsPreview) - fmt.Fprint(w, `{"names":["go", "go-github", "github"]}`) + mux.HandleFunc("/repos/o/r/activity", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "per_page": "100", + }) + fmt.Fprint(w, `[ + { + "id": 123456789, + "node_id": "PSH_test123", + "before": "abc123def456", + "after": "def456ghi789", + "ref": "refs/heads/main", + "timestamp": `+referenceTimeStr+`, + "activity_type": "push", + "actor": { + "login": "testuser1", + "id": 111111, + "node_id": "MDQ6VXNlcjExMTExMQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/111111?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/testuser1", + "html_url": "https://github.com/testuser1", + "followers_url": "https://api.github.com/users/testuser1/followers", + "following_url": "https://api.github.com/users/testuser1/following{/other_user}", + "gists_url": "https://api.github.com/users/testuser1/gists{/gist_id}", + "starred_url": "https://api.github.com/users/testuser1/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/testuser1/subscriptions", + "organizations_url": "https://api.github.com/users/testuser1/orgs", + "repos_url": "https://api.github.com/users/testuser1/repos", + "events_url": "https://api.github.com/users/testuser1/events{/privacy}", + "received_events_url": "https://api.github.com/users/testuser1/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + { + "id": 123456788, + "node_id": "PSH_test124", + "before": "def456ghi789", + "after": "ghi789jkl012", + "ref": "refs/heads/feature", + "timestamp": `+referenceTimeStr+`, + "activity_type": "branch_deletion", + "actor": { + "login": "testuser2", + "id": 222222, + "node_id": "MDQ6VXNlcjIyMjIyMg==", + "avatar_url": "https://avatars.githubusercontent.com/u/222222?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/testuser2", + "html_url": "https://github.com/testuser2", + "followers_url": "https://api.github.com/users/testuser2/followers", + "following_url": "https://api.github.com/users/testuser2/following{/other_user}", + "gists_url": "https://api.github.com/users/testuser2/gists{/gist_id}", + "starred_url": "https://api.github.com/users/testuser2/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/testuser2/subscriptions", + "organizations_url": "https://api.github.com/users/testuser2/orgs", + "repos_url": "https://api.github.com/users/testuser2/repos", + "events_url": "https://api.github.com/users/testuser2/events{/privacy}", + "received_events_url": "https://api.github.com/users/testuser2/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + } + ]`) }) - got, _, err := client.Repositories.ReplaceAllTopics(context.Background(), "o", "r", []string{"go", "go-github", "github"}) + opts := &ListRepositoryActivityOptions{PerPage: 100} + ctx := t.Context() + activities, _, err := client.Repositories.ListRepositoryActivities(ctx, "o", "r", opts) if err != nil { - t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) + t.Errorf("Repositories.ListRepositoryActivities returned error: %v", err) } - want := []string{"go", "go-github", "github"} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) + want := []*RepositoryActivity{ + { + ID: 123456789, + NodeID: "PSH_test123", + Before: "abc123def456", + After: "def456ghi789", + Ref: "refs/heads/main", + Timestamp: &referenceTimestamp, + ActivityType: "push", + Actor: &RepositoryActor{ + Login: Ptr("testuser1"), + ID: Ptr(int64(111111)), + NodeID: Ptr("MDQ6VXNlcjExMTExMQ=="), + AvatarURL: Ptr("https://avatars.githubusercontent.com/u/111111?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/testuser1"), + HTMLURL: Ptr("https://github.com/testuser1"), + FollowersURL: Ptr("https://api.github.com/users/testuser1/followers"), + FollowingURL: Ptr("https://api.github.com/users/testuser1/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/testuser1/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/testuser1/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/testuser1/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/testuser1/orgs"), + ReposURL: Ptr("https://api.github.com/users/testuser1/repos"), + EventsURL: Ptr("https://api.github.com/users/testuser1/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/testuser1/received_events"), + Type: Ptr("User"), + UserViewType: Ptr("public"), + SiteAdmin: Ptr(false), + }, + }, + { + ID: 123456788, + NodeID: "PSH_test124", + Before: "def456ghi789", + After: "ghi789jkl012", + Ref: "refs/heads/feature", + Timestamp: &referenceTimestamp, + ActivityType: "branch_deletion", + Actor: &RepositoryActor{ + Login: Ptr("testuser2"), + ID: Ptr(int64(222222)), + NodeID: Ptr("MDQ6VXNlcjIyMjIyMg=="), + AvatarURL: Ptr("https://avatars.githubusercontent.com/u/222222?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/testuser2"), + HTMLURL: Ptr("https://github.com/testuser2"), + FollowersURL: Ptr("https://api.github.com/users/testuser2/followers"), + FollowingURL: Ptr("https://api.github.com/users/testuser2/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/testuser2/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/testuser2/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/testuser2/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/testuser2/orgs"), + ReposURL: Ptr("https://api.github.com/users/testuser2/repos"), + EventsURL: Ptr("https://api.github.com/users/testuser2/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/testuser2/received_events"), + Type: Ptr("User"), + UserViewType: Ptr("public"), + SiteAdmin: Ptr(false), + }, + }, + } + + if !cmp.Equal(activities, want) { + t.Errorf("Repositories.ListRepositoryActivities returned %+v, want %+v", activities, want) } + + const methodName = "ListRepositoryActivities" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListRepositoryActivities(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListRepositoryActivities(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestRepositoriesService_ReplaceAllTopics_nilSlice(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListRepositoryActivities_withOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeTopicsPreview) - testBody(t, r, `{"names":[]}`+"\n") - fmt.Fprint(w, `{"names":[]}`) + mux.HandleFunc("/repos/o/r/activity", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "direction": "desc", + "before": refTimeStr(1136178000), + "after": refTimeStr(1136178001), + "ref": "refs/heads/main", + "actor": "testuser1", + "time_period": "day", + "activity_type": "push", + "per_page": "50", + }) + fmt.Fprint(w, `[ + { + "id": 123456789, + "node_id": "PSH_test123", + "before": "abc123def456", + "after": "def456ghi789", + "ref": "refs/heads/main", + "timestamp": `+referenceTimeStr+`, + "activity_type": "push", + "actor": { + "login": "testuser1", + "id": 111111, + "node_id": "MDQ6VXNlcjExMTExMQ==", + "avatar_url": "https://avatars.githubusercontent.com/u/111111?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/testuser1", + "html_url": "https://github.com/testuser1", + "followers_url": "https://api.github.com/users/testuser1/followers", + "following_url": "https://api.github.com/users/testuser1/following{/other_user}", + "gists_url": "https://api.github.com/users/testuser1/gists{/gist_id}", + "starred_url": "https://api.github.com/users/testuser1/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/testuser1/subscriptions", + "organizations_url": "https://api.github.com/users/testuser1/orgs", + "repos_url": "https://api.github.com/users/testuser1/repos", + "events_url": "https://api.github.com/users/testuser1/events{/privacy}", + "received_events_url": "https://api.github.com/users/testuser1/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + } + ]`) }) - got, _, err := client.Repositories.ReplaceAllTopics(context.Background(), "o", "r", nil) + opts := &ListRepositoryActivityOptions{ + Direction: "desc", + Before: refTimeStr(1136178000), + After: refTimeStr(1136178001), + Ref: "refs/heads/main", + Actor: "testuser1", + TimePeriod: "day", + ActivityType: "push", + PerPage: 50, + } + ctx := t.Context() + activities, _, err := client.Repositories.ListRepositoryActivities(ctx, "o", "r", opts) if err != nil { - t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) + t.Errorf("Repositories.ListRepositoryActivities returned error: %v", err) } - want := []string{} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) + want := []*RepositoryActivity{ + { + ID: 123456789, + NodeID: "PSH_test123", + Before: "abc123def456", + After: "def456ghi789", + Ref: "refs/heads/main", + Timestamp: &referenceTimestamp, + ActivityType: "push", + Actor: &RepositoryActor{ + Login: Ptr("testuser1"), + ID: Ptr(int64(111111)), + NodeID: Ptr("MDQ6VXNlcjExMTExMQ=="), + AvatarURL: Ptr("https://avatars.githubusercontent.com/u/111111?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/testuser1"), + HTMLURL: Ptr("https://github.com/testuser1"), + FollowersURL: Ptr("https://api.github.com/users/testuser1/followers"), + FollowingURL: Ptr("https://api.github.com/users/testuser1/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/testuser1/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/testuser1/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/testuser1/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/testuser1/orgs"), + ReposURL: Ptr("https://api.github.com/users/testuser1/repos"), + EventsURL: Ptr("https://api.github.com/users/testuser1/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/testuser1/received_events"), + Type: Ptr("User"), + UserViewType: Ptr("public"), + SiteAdmin: Ptr(false), + }, + }, + } + + if !cmp.Equal(activities, want) { + t.Errorf("Repositories.ListRepositoryActivities returned %+v, want %+v", activities, want) } } -func TestRepositoriesService_ReplaceAllTopics_emptySlice(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestRepositoriesService_ListRepositoryActivities_emptyResponse(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/repos/o/r/topics", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeTopicsPreview) - testBody(t, r, `{"names":[]}`+"\n") - fmt.Fprint(w, `{"names":[]}`) + mux.HandleFunc("/repos/o/r/activity", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[]`) }) - got, _, err := client.Repositories.ReplaceAllTopics(context.Background(), "o", "r", []string{}) + ctx := t.Context() + activities, _, err := client.Repositories.ListRepositoryActivities(ctx, "o", "r", nil) if err != nil { - t.Fatalf("Repositories.ReplaceAllTopics returned error: %v", err) + t.Errorf("Repositories.ListRepositoryActivities returned error: %v", err) } - want := []string{} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.ReplaceAllTopics returned %+v, want %+v", got, want) + want := []*RepositoryActivity{} + if !cmp.Equal(activities, want) { + t.Errorf("Repositories.ListRepositoryActivities returned %+v, want %+v", activities, want) } } -func TestRepositoriesService_Transfer(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - input := TransferRequest{NewOwner: "a", TeamID: []int64{123}} - - mux.HandleFunc("/repos/o/r/transfer", func(w http.ResponseWriter, r *http.Request) { - var v TransferRequest - json.NewDecoder(r.Body).Decode(&v) - - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeRepositoryTransferPreview) - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - - fmt.Fprint(w, `{"owner":{"login":"a"}}`) - }) +func TestRepositoriesService_ListRepositoryActivities_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - got, _, err := client.Repositories.Transfer(context.Background(), "o", "r", input) - if err != nil { - t.Errorf("Repositories.Transfer returned error: %v", err) + ctx := t.Context() + _, _, err := client.Repositories.ListRepositoryActivities(ctx, "%", "r", nil) + if err == nil { + t.Error("Expected error to be returned") } +} - want := &Repository{Owner: &User{Login: String("a")}} - if !reflect.DeepEqual(got, want) { - t.Errorf("Repositories.Transfer returned %+v, want %+v", got, want) +func TestRepositoriesService_ListRepositoryActivities_invalidRepo(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Repositories.ListRepositoryActivities(ctx, "o", "%", nil) + if err == nil { + t.Error("Expected error to be returned") } } diff --git a/github/repos_traffic.go b/github/repos_traffic.go index fb1c97648a7..b67af094b75 100644 --- a/github/repos_traffic.go +++ b/github/repos_traffic.go @@ -54,17 +54,19 @@ type TrafficBreakdownOptions struct { // ListTrafficReferrers list the top 10 referrers over the last 14 days. // -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#list-referrers +// GitHub API docs: https://docs.github.com/rest/metrics/traffic?apiVersion=2022-11-28#get-top-referral-sources +// +//meta:operation GET /repos/{owner}/{repo}/traffic/popular/referrers func (s *RepositoriesService) ListTrafficReferrers(ctx context.Context, owner, repo string) ([]*TrafficReferrer, *Response, error) { u := fmt.Sprintf("repos/%v/%v/traffic/popular/referrers", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var trafficReferrers []*TrafficReferrer - resp, err := s.client.Do(ctx, req, &trafficReferrers) + resp, err := s.client.Do(req, &trafficReferrers) if err != nil { return nil, resp, err } @@ -74,17 +76,19 @@ func (s *RepositoriesService) ListTrafficReferrers(ctx context.Context, owner, r // ListTrafficPaths list the top 10 popular content over the last 14 days. // -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#list-paths +// GitHub API docs: https://docs.github.com/rest/metrics/traffic?apiVersion=2022-11-28#get-top-referral-paths +// +//meta:operation GET /repos/{owner}/{repo}/traffic/popular/paths func (s *RepositoriesService) ListTrafficPaths(ctx context.Context, owner, repo string) ([]*TrafficPath, *Response, error) { u := fmt.Sprintf("repos/%v/%v/traffic/popular/paths", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var paths []*TrafficPath - resp, err := s.client.Do(ctx, req, &paths) + resp, err := s.client.Do(req, &paths) if err != nil { return nil, resp, err } @@ -94,21 +98,23 @@ func (s *RepositoriesService) ListTrafficPaths(ctx context.Context, owner, repo // ListTrafficViews get total number of views for the last 14 days and breaks it down either per day or week. // -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#views -func (s *RepositoriesService) ListTrafficViews(ctx context.Context, owner, repo string, opt *TrafficBreakdownOptions) (*TrafficViews, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/metrics/traffic?apiVersion=2022-11-28#get-page-views +// +//meta:operation GET /repos/{owner}/{repo}/traffic/views +func (s *RepositoriesService) ListTrafficViews(ctx context.Context, owner, repo string, opts *TrafficBreakdownOptions) (*TrafficViews, *Response, error) { u := fmt.Sprintf("repos/%v/%v/traffic/views", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - trafficViews := new(TrafficViews) - resp, err := s.client.Do(ctx, req, &trafficViews) + var trafficViews *TrafficViews + resp, err := s.client.Do(req, &trafficViews) if err != nil { return nil, resp, err } @@ -118,21 +124,23 @@ func (s *RepositoriesService) ListTrafficViews(ctx context.Context, owner, repo // ListTrafficClones get total number of clones for the last 14 days and breaks it down either per day or week for the last 14 days. // -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#views -func (s *RepositoriesService) ListTrafficClones(ctx context.Context, owner, repo string, opt *TrafficBreakdownOptions) (*TrafficClones, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/metrics/traffic?apiVersion=2022-11-28#get-repository-clones +// +//meta:operation GET /repos/{owner}/{repo}/traffic/clones +func (s *RepositoriesService) ListTrafficClones(ctx context.Context, owner, repo string, opts *TrafficBreakdownOptions) (*TrafficClones, *Response, error) { u := fmt.Sprintf("repos/%v/%v/traffic/clones", owner, repo) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - trafficClones := new(TrafficClones) - resp, err := s.client.Do(ctx, req, &trafficClones) + var trafficClones *TrafficClones + resp, err := s.client.Do(req, &trafficClones) if err != nil { return nil, resp, err } diff --git a/github/repos_traffic_test.go b/github/repos_traffic_test.go index 1479caaaf56..3578739fbe3 100644 --- a/github/repos_traffic_test.go +++ b/github/repos_traffic_test.go @@ -6,142 +6,115 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) func TestRepositoriesService_ListTrafficReferrers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/traffic/popular/referrers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `[{ + fmt.Fprint(w, `[{ "referrer": "Google", "count": 4, "uniques": 3 }]`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.ListTrafficReferrers(ctx, "o", "r") if err != nil { t.Errorf("Repositories.ListTrafficReferrers returned error: %+v", err) } want := []*TrafficReferrer{{ - Referrer: String("Google"), - Count: Int(4), - Uniques: Int(3), + Referrer: Ptr("Google"), + Count: Ptr(4), + Uniques: Ptr(3), }} - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListTrafficReferrers returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.ListTrafficReferrers(ctx, "o", "r") - if got != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficReferrers = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficReferrers resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListTrafficReferrers err = nil, want error") - } + const methodName = "ListTrafficReferrers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTrafficReferrers(ctx, "\n", "\n") + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.ListTrafficReferrers(ctx, "o", "r") - if got != nil { - t.Errorf("rate.Reset.Time > now ListTrafficReferrers = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListTrafficReferrers resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListTrafficReferrers err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTrafficReferrers(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListTrafficPaths(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/traffic/popular/paths", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `[{ + fmt.Fprint(w, `[{ "path": "/github/hubot", "title": "github/hubot: A customizable life embetterment robot.", "count": 3542, "uniques": 2225 }]`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.ListTrafficPaths(ctx, "o", "r") if err != nil { t.Errorf("Repositories.ListTrafficPaths returned error: %+v", err) } want := []*TrafficPath{{ - Path: String("/github/hubot"), - Title: String("github/hubot: A customizable life embetterment robot."), - Count: Int(3542), - Uniques: Int(2225), + Path: Ptr("/github/hubot"), + Title: Ptr("github/hubot: A customizable life embetterment robot."), + Count: Ptr(3542), + Uniques: Ptr(2225), }} - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListTrafficPaths returned %+v, want %+v", got, want) } - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.ListTrafficPaths(ctx, "o", "r") - if got != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficPaths = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficPaths resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListTrafficPaths err = nil, want error") - } + const methodName = "ListTrafficPaths" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTrafficPaths(ctx, "\n", "\n") + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.ListTrafficPaths(ctx, "o", "r") - if got != nil { - t.Errorf("rate.Reset.Time > now ListTrafficPaths = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListTrafficPaths resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListTrafficPaths err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTrafficPaths(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListTrafficViews(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/traffic/views", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `{"count": 7, + fmt.Fprint(w, `{"count": 7, "uniques": 6, "views": [{ - "timestamp": "2016-05-31T16:00:00.000Z", + "timestamp": `+referenceTimeStr+`, "count": 7, "uniques": 6 }]}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.ListTrafficViews(ctx, "o", "r", nil) if err != nil { t.Errorf("Repositories.ListTrafficViews returned error: %+v", err) @@ -149,68 +122,49 @@ func TestRepositoriesService_ListTrafficViews(t *testing.T) { want := &TrafficViews{ Views: []*TrafficData{{ - Timestamp: &Timestamp{time.Date(2016, time.May, 31, 16, 0, 0, 0, time.UTC)}, - Count: Int(7), - Uniques: Int(6), + Timestamp: &referenceTimestamp, + Count: Ptr(7), + Uniques: Ptr(6), }}, - Count: Int(7), - Uniques: Int(6), + Count: Ptr(7), + Uniques: Ptr(6), } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListTrafficViews returned %+v, want %+v", got, want) } - // Test addOptions failure - _, _, err = client.Repositories.ListTrafficViews(ctx, "\n", "\n", &TrafficBreakdownOptions{}) - if err == nil { - t.Error("bad options ListTrafficViews err = nil, want error") - } - - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.ListTrafficViews(ctx, "o", "r", nil) - if got != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficViews = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficViews resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListTrafficViews err = nil, want error") - } + const methodName = "ListTrafficViews" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTrafficViews(ctx, "\n", "\n", &TrafficBreakdownOptions{}) + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.ListTrafficViews(ctx, "o", "r", nil) - if got != nil { - t.Errorf("rate.Reset.Time > now ListTrafficViews = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListTrafficViews resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListTrafficViews err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTrafficViews(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestRepositoriesService_ListTrafficClones(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/repos/o/r/traffic/clones", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `{"count": 7, + fmt.Fprint(w, `{"count": 7, "uniques": 6, "clones": [{ - "timestamp": "2016-05-31T16:00:00.00Z", + "timestamp": `+referenceTimeStr+`, "count": 7, "uniques": 6 }]}`) }) - ctx := context.Background() + ctx := t.Context() got, _, err := client.Repositories.ListTrafficClones(ctx, "o", "r", nil) if err != nil { t.Errorf("Repositories.ListTrafficClones returned error: %+v", err) @@ -218,48 +172,29 @@ func TestRepositoriesService_ListTrafficClones(t *testing.T) { want := &TrafficClones{ Clones: []*TrafficData{{ - Timestamp: &Timestamp{time.Date(2016, time.May, 31, 16, 0, 0, 0, time.UTC)}, - Count: Int(7), - Uniques: Int(6), + Timestamp: &referenceTimestamp, + Count: Ptr(7), + Uniques: Ptr(6), }}, - Count: Int(7), - Uniques: Int(6), + Count: Ptr(7), + Uniques: Ptr(6), } - if !reflect.DeepEqual(got, want) { + if !cmp.Equal(got, want) { t.Errorf("Repositories.ListTrafficClones returned %+v, want %+v", got, want) } - // Test addOptions failure - _, _, err = client.Repositories.ListTrafficClones(ctx, "\n", "\n", &TrafficBreakdownOptions{}) - if err == nil { - t.Error("bad options ListTrafficViews err = nil, want error") - } - - // Test s.client.NewRequest failure - client.BaseURL.Path = "" - got, resp, err := client.Repositories.ListTrafficClones(ctx, "o", "r", nil) - if got != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficClones = %#v, want nil", got) - } - if resp != nil { - t.Errorf("client.BaseURL.Path='' ListTrafficClones resp = %#v, want nil", resp) - } - if err == nil { - t.Error("client.BaseURL.Path='' ListTrafficClones err = nil, want error") - } + const methodName = "ListTrafficClones" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTrafficClones(ctx, "\n", "\n", &TrafficBreakdownOptions{}) + return err + }) - // Test s.client.Do failure - client.BaseURL.Path = "/api-v3/" - client.rateLimits[0].Reset.Time = time.Now().Add(10 * time.Minute) - got, resp, err = client.Repositories.ListTrafficClones(ctx, "o", "r", nil) - if got != nil { - t.Errorf("rate.Reset.Time > now ListTrafficClones = %#v, want nil", got) - } - if want := http.StatusForbidden; resp == nil || resp.Response.StatusCode != want { - t.Errorf("rate.Reset.Time > now ListTrafficClones resp = %#v, want StatusCode=%v", resp.Response, want) - } - if err == nil { - t.Error("rate.Reset.Time > now ListTrafficClones err = nil, want error") - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTrafficClones(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/rules.go b/github/rules.go new file mode 100644 index 00000000000..93ccf95ad99 --- /dev/null +++ b/github/rules.go @@ -0,0 +1,1439 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// RulesetTarget represents a GitHub ruleset target. +type RulesetTarget string + +// This is the set of GitHub ruleset targets. +const ( + RulesetTargetBranch RulesetTarget = "branch" + RulesetTargetTag RulesetTarget = "tag" + RulesetTargetPush RulesetTarget = "push" + RulesetTargetRepository RulesetTarget = "repository" +) + +// RulesetSourceType represents a GitHub ruleset source type. +type RulesetSourceType string + +// This is the set of GitHub ruleset source types. +const ( + RulesetSourceTypeRepository RulesetSourceType = "Repository" + RulesetSourceTypeOrganization RulesetSourceType = "Organization" + RulesetSourceTypeEnterprise RulesetSourceType = "Enterprise" +) + +// RulesetEnforcement represents a GitHub ruleset enforcement. +type RulesetEnforcement string + +// This is the set of GitHub ruleset enforcements. +const ( + RulesetEnforcementDisabled RulesetEnforcement = "disabled" + RulesetEnforcementActive RulesetEnforcement = "active" + RulesetEnforcementEvaluate RulesetEnforcement = "evaluate" +) + +// BypassActorType represents a GitHub ruleset bypass actor type. +type BypassActorType string + +// This is the set of GitHub ruleset bypass actor types. +const ( + BypassActorTypeIntegration BypassActorType = "Integration" + BypassActorTypeOrganizationAdmin BypassActorType = "OrganizationAdmin" + BypassActorTypeRepositoryRole BypassActorType = "RepositoryRole" + BypassActorTypeTeam BypassActorType = "Team" + BypassActorTypeDeployKey BypassActorType = "DeployKey" +) + +// BypassMode represents a GitHub ruleset bypass mode. +type BypassMode string + +// This is the set of GitHub ruleset bypass modes. +const ( + BypassModeAlways BypassMode = "always" + BypassModeExempt BypassMode = "exempt" + BypassModeNever BypassMode = "never" + BypassModePullRequest BypassMode = "pull_request" +) + +// RepositoryRuleType represents a GitHub ruleset rule type. +type RepositoryRuleType string + +// This is the set of GitHub ruleset rule types. +const ( + // Branch or tag target rules. + RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern" + RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning" + RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern" + RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern" + RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern" + RulesetRuleTypeCopilotCodeReview RepositoryRuleType = "copilot_code_review" + RulesetRuleTypeCreation RepositoryRuleType = "creation" + RulesetRuleTypeDeletion RepositoryRuleType = "deletion" + RulesetRuleTypeMergeQueue RepositoryRuleType = "merge_queue" + RulesetRuleTypeNonFastForward RepositoryRuleType = "non_fast_forward" + RulesetRuleTypePullRequest RepositoryRuleType = "pull_request" + RulesetRuleTypeRequiredDeployments RepositoryRuleType = "required_deployments" + RulesetRuleTypeRequiredLinearHistory RepositoryRuleType = "required_linear_history" + RulesetRuleTypeRequiredSignatures RepositoryRuleType = "required_signatures" + RulesetRuleTypeRequiredStatusChecks RepositoryRuleType = "required_status_checks" + RulesetRuleTypeTagNamePattern RepositoryRuleType = "tag_name_pattern" + RulesetRuleTypeUpdate RepositoryRuleType = "update" + RulesetRuleTypeWorkflows RepositoryRuleType = "workflows" + + // Push target rules. + RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction" + RulesetRuleTypeFilePathRestriction RepositoryRuleType = "file_path_restriction" + RulesetRuleTypeMaxFilePathLength RepositoryRuleType = "max_file_path_length" + RulesetRuleTypeMaxFileSize RepositoryRuleType = "max_file_size" + + // Repository target rules. + RulesetRuleTypeRepositoryCreate RepositoryRuleType = "repository_create" + RulesetRuleTypeRepositoryDelete RepositoryRuleType = "repository_delete" + RulesetRuleTypeRepositoryName RepositoryRuleType = "repository_name" + RulesetRuleTypeRepositoryTransfer RepositoryRuleType = "repository_transfer" + RulesetRuleTypeRepositoryVisibility RepositoryRuleType = "repository_visibility" +) + +// MergeGroupingStrategy models a GitHub merge grouping strategy. +type MergeGroupingStrategy string + +// This is the set of GitHub merge grouping strategies. +const ( + MergeGroupingStrategyAllGreen MergeGroupingStrategy = "ALLGREEN" + MergeGroupingStrategyHeadGreen MergeGroupingStrategy = "HEADGREEN" +) + +// PullRequestMergeMethod is used in PullRequestRuleParameters, +// where the GitHub API expects lowercase merge method values: "merge", "rebase", "squash". +// +// NOTE: GitHub's API inconsistently uses different casing for the same logical values +// across different rules. +// +// TODO: Unify with MergeQueueMergeMethod once the GitHub API uses consistent casing. +type PullRequestMergeMethod string + +const ( + PullRequestMergeMethodMerge PullRequestMergeMethod = "merge" + PullRequestMergeMethodRebase PullRequestMergeMethod = "rebase" + PullRequestMergeMethodSquash PullRequestMergeMethod = "squash" +) + +// MergeQueueMergeMethod is used in MergeQueueRuleParameters, +// where the GitHub API expects uppercase merge method values: "MERGE", "REBASE", "SQUASH". +// +// NOTE: This type exists alongside PullRequestMergeMethod solely due to API casing inconsistencies. +// It enforces the correct usage by API context. +// +// TODO: Unify with PullRequestMergeMethod once the GitHub API uses consistent casing. +type MergeQueueMergeMethod string + +const ( + MergeQueueMergeMethodMerge MergeQueueMergeMethod = "MERGE" + MergeQueueMergeMethodRebase MergeQueueMergeMethod = "REBASE" + MergeQueueMergeMethodSquash MergeQueueMergeMethod = "SQUASH" +) + +// RulesetReviewerType represents the type of reviewer in a ruleset required reviewer. +type RulesetReviewerType string + +// This is the set of GitHub ruleset reviewer types. +const ( + RulesetReviewerTypeTeam RulesetReviewerType = "Team" +) + +// PatternRuleOperator models a GitHub pattern rule operator. +type PatternRuleOperator string + +// This is the set of GitHub pattern rule operators. +const ( + PatternRuleOperatorStartsWith PatternRuleOperator = "starts_with" + PatternRuleOperatorEndsWith PatternRuleOperator = "ends_with" + PatternRuleOperatorContains PatternRuleOperator = "contains" + PatternRuleOperatorRegex PatternRuleOperator = "regex" +) + +// CodeScanningAlertsThreshold models a GitHub code scanning alerts threshold. +type CodeScanningAlertsThreshold string + +// This is the set of GitHub code scanning alerts thresholds. +const ( + CodeScanningAlertsThresholdNone CodeScanningAlertsThreshold = "none" + CodeScanningAlertsThresholdErrors CodeScanningAlertsThreshold = "errors" + CodeScanningAlertsThresholdErrorsAndWarnings CodeScanningAlertsThreshold = "errors_and_warnings" + CodeScanningAlertsThresholdAll CodeScanningAlertsThreshold = "all" +) + +// CodeScanningSecurityAlertsThreshold models a GitHub code scanning security alerts threshold. +type CodeScanningSecurityAlertsThreshold string + +// This is the set of GitHub code scanning security alerts thresholds. +const ( + CodeScanningSecurityAlertsThresholdNone CodeScanningSecurityAlertsThreshold = "none" + CodeScanningSecurityAlertsThresholdCritical CodeScanningSecurityAlertsThreshold = "critical" + CodeScanningSecurityAlertsThresholdHighOrHigher CodeScanningSecurityAlertsThreshold = "high_or_higher" + CodeScanningSecurityAlertsThresholdMediumOrHigher CodeScanningSecurityAlertsThreshold = "medium_or_higher" + CodeScanningSecurityAlertsThresholdAll CodeScanningSecurityAlertsThreshold = "all" +) + +// RepositoryRuleset represents a GitHub ruleset object. +type RepositoryRuleset struct { + ID *int64 `json:"id,omitempty"` + Name string `json:"name"` + Target *RulesetTarget `json:"target,omitempty"` + SourceType *RulesetSourceType `json:"source_type,omitempty"` + Source string `json:"source"` + Enforcement RulesetEnforcement `json:"enforcement"` + BypassActors []*BypassActor `json:"bypass_actors,omitzero"` + CurrentUserCanBypass *BypassMode `json:"current_user_can_bypass,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Links *RepositoryRulesetLinks `json:"_links,omitempty"` + Conditions *RepositoryRulesetConditions `json:"conditions,omitempty"` + Rules *RepositoryRulesetRules `json:"rules,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + +// BypassActor represents the bypass actors from a ruleset. +type BypassActor struct { + ActorID *int64 `json:"actor_id,omitempty"` + ActorType *BypassActorType `json:"actor_type,omitempty"` + BypassMode *BypassMode `json:"bypass_mode,omitempty"` +} + +// RepositoryRulesetLinks represents the "_links" object in a Ruleset. +type RepositoryRulesetLinks struct { + Self *RepositoryRulesetLink `json:"self,omitempty"` + HTML *RepositoryRulesetLink `json:"html,omitempty"` +} + +// RepositoryRulesetLink represents a single link object from GitHub ruleset request _links. +type RepositoryRulesetLink struct { + HRef *string `json:"href,omitempty"` +} + +// RepositoryRulesetConditions represents the conditions object in a ruleset. +// Set either RepositoryName or RepositoryID or RepositoryProperty, not more than one. +type RepositoryRulesetConditions struct { + RefName *RepositoryRulesetRefConditionParameters `json:"ref_name,omitempty"` + RepositoryID *RepositoryRulesetRepositoryIDsConditionParameters `json:"repository_id,omitempty"` + RepositoryName *RepositoryRulesetRepositoryNamesConditionParameters `json:"repository_name,omitempty"` + RepositoryProperty *RepositoryRulesetRepositoryPropertyConditionParameters `json:"repository_property,omitempty"` + OrganizationID *RepositoryRulesetOrganizationIDsConditionParameters `json:"organization_id,omitempty"` + OrganizationName *RepositoryRulesetOrganizationNamesConditionParameters `json:"organization_name,omitempty"` + OrganizationProperty *RepositoryRulesetOrganizationPropertyConditionParameters `json:"organization_property,omitempty"` +} + +// RepositoryRulesetRefConditionParameters represents the conditions object for ref_names. +type RepositoryRulesetRefConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` +} + +// RepositoryRulesetOrganizationPropertyConditionParameters represents the conditions object for an organization property selector. +type RepositoryRulesetOrganizationPropertyConditionParameters struct { + Include []*RepositoryRulesetRepositoryPropertyTargetParameters `json:"include"` + Exclude []*RepositoryRulesetRepositoryPropertyTargetParameters `json:"exclude"` +} + +// RepositoryRulesetRepositoryIDsConditionParameters represents the conditions object for repository_id. +type RepositoryRulesetRepositoryIDsConditionParameters struct { + RepositoryIDs []int64 `json:"repository_ids,omitempty"` +} + +// RepositoryRulesetRepositoryNamesConditionParameters represents the conditions object for repository_name. +type RepositoryRulesetRepositoryNamesConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` + Protected *bool `json:"protected,omitempty"` +} + +// RepositoryRulesetRepositoryPropertyConditionParameters represents the conditions object for repository_property. +type RepositoryRulesetRepositoryPropertyConditionParameters struct { + Include []*RepositoryRulesetRepositoryPropertyTargetParameters `json:"include"` + Exclude []*RepositoryRulesetRepositoryPropertyTargetParameters `json:"exclude"` +} + +// RepositoryRulesetRepositoryPropertyTargetParameters represents a repository_property name and values to be used for targeting. +type RepositoryRulesetRepositoryPropertyTargetParameters struct { + Name string `json:"name"` + PropertyValues []string `json:"property_values"` + Source *string `json:"source,omitempty"` +} + +// RepositoryRulesetOrganizationIDsConditionParameters represents the conditions object for organization_id. +type RepositoryRulesetOrganizationIDsConditionParameters struct { + OrganizationIDs []int64 `json:"organization_ids,omitempty"` +} + +// RepositoryRulesetOrganizationNamesConditionParameters represents the conditions object for organization_name. +type RepositoryRulesetOrganizationNamesConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` +} + +// RepositoryRule represents a GitHub ruleset rule object. +type RepositoryRule struct { + Type RepositoryRuleType `json:"type"` + Parameters any `json:"parameters,omitempty"` +} + +// RepositoryRulesetRules represents a GitHub ruleset rules object. +// This type doesn't have JSON annotations as it uses custom marshaling. +type RepositoryRulesetRules struct { + // Branch or tag target rules. + Creation *EmptyRuleParameters + Update *UpdateRuleParameters + Deletion *EmptyRuleParameters + RequiredLinearHistory *EmptyRuleParameters + MergeQueue *MergeQueueRuleParameters + RequiredDeployments *RequiredDeploymentsRuleParameters + RequiredSignatures *EmptyRuleParameters + PullRequest *PullRequestRuleParameters + RequiredStatusChecks *RequiredStatusChecksRuleParameters + NonFastForward *EmptyRuleParameters + CommitMessagePattern *PatternRuleParameters + CommitAuthorEmailPattern *PatternRuleParameters + CommitterEmailPattern *PatternRuleParameters + BranchNamePattern *PatternRuleParameters + TagNamePattern *PatternRuleParameters + Workflows *WorkflowsRuleParameters + CodeScanning *CodeScanningRuleParameters + CopilotCodeReview *CopilotCodeReviewRuleParameters + + // Push target rules. + FileExtensionRestriction *FileExtensionRestrictionRuleParameters + FilePathRestriction *FilePathRestrictionRuleParameters + MaxFilePathLength *MaxFilePathLengthRuleParameters + MaxFileSize *MaxFileSizeRuleParameters + + // Repository target rules. + RepositoryCreate *EmptyRuleParameters + RepositoryDelete *EmptyRuleParameters + RepositoryName *SimplePatternRuleParameters + RepositoryTransfer *EmptyRuleParameters + RepositoryVisibility *RepositoryVisibilityRuleParameters +} + +// BranchRules represents the rules active for a GitHub repository branch. +// This type doesn't have JSON annotations as it uses custom marshaling. +type BranchRules struct { + // Branch or tag target rules. + Creation []*BranchRuleMetadata + Update []*UpdateBranchRule + Deletion []*BranchRuleMetadata + RequiredLinearHistory []*BranchRuleMetadata + MergeQueue []*MergeQueueBranchRule + RequiredDeployments []*RequiredDeploymentsBranchRule + RequiredSignatures []*BranchRuleMetadata + PullRequest []*PullRequestBranchRule + RequiredStatusChecks []*RequiredStatusChecksBranchRule + NonFastForward []*BranchRuleMetadata + CommitMessagePattern []*PatternBranchRule + CommitAuthorEmailPattern []*PatternBranchRule + CommitterEmailPattern []*PatternBranchRule + BranchNamePattern []*PatternBranchRule + TagNamePattern []*PatternBranchRule + Workflows []*WorkflowsBranchRule + CodeScanning []*CodeScanningBranchRule + CopilotCodeReview []*CopilotCodeReviewBranchRule + + // Push target rules. + FileExtensionRestriction []*FileExtensionRestrictionBranchRule + FilePathRestriction []*FilePathRestrictionBranchRule + MaxFilePathLength []*MaxFilePathLengthBranchRule + MaxFileSize []*MaxFileSizeBranchRule +} + +// BranchRuleMetadata represents the metadata for a branch rule. +type BranchRuleMetadata struct { + RulesetSourceType RulesetSourceType `json:"ruleset_source_type"` + RulesetSource string `json:"ruleset_source"` + RulesetID int64 `json:"ruleset_id"` +} + +// UpdateBranchRule represents an update branch rule. +type UpdateBranchRule struct { + BranchRuleMetadata + Parameters UpdateRuleParameters `json:"parameters"` +} + +// MergeQueueBranchRule represents a merge queue branch rule. +type MergeQueueBranchRule struct { + BranchRuleMetadata + Parameters MergeQueueRuleParameters `json:"parameters"` +} + +// RequiredDeploymentsBranchRule represents a required deployments branch rule. +type RequiredDeploymentsBranchRule struct { + BranchRuleMetadata + Parameters RequiredDeploymentsRuleParameters `json:"parameters"` +} + +// PullRequestBranchRule represents a pull request branch rule. +type PullRequestBranchRule struct { + BranchRuleMetadata + Parameters PullRequestRuleParameters `json:"parameters"` +} + +// RequiredStatusChecksBranchRule represents a required status checks branch rule. +type RequiredStatusChecksBranchRule struct { + BranchRuleMetadata + Parameters RequiredStatusChecksRuleParameters `json:"parameters"` +} + +// PatternBranchRule represents a pattern branch rule. +type PatternBranchRule struct { + BranchRuleMetadata + Parameters PatternRuleParameters `json:"parameters"` +} + +// FilePathRestrictionBranchRule represents a file path restriction branch rule. +type FilePathRestrictionBranchRule struct { + BranchRuleMetadata + Parameters FilePathRestrictionRuleParameters `json:"parameters"` +} + +// MaxFilePathLengthBranchRule represents a max file path length branch rule. +type MaxFilePathLengthBranchRule struct { + BranchRuleMetadata + Parameters MaxFilePathLengthRuleParameters `json:"parameters"` +} + +// FileExtensionRestrictionBranchRule represents a file extension restriction branch rule. +type FileExtensionRestrictionBranchRule struct { + BranchRuleMetadata + Parameters FileExtensionRestrictionRuleParameters `json:"parameters"` +} + +// MaxFileSizeBranchRule represents a max file size branch rule. +type MaxFileSizeBranchRule struct { + BranchRuleMetadata + Parameters MaxFileSizeRuleParameters `json:"parameters"` +} + +// WorkflowsBranchRule represents a workflows branch rule. +type WorkflowsBranchRule struct { + BranchRuleMetadata + Parameters WorkflowsRuleParameters `json:"parameters"` +} + +// CodeScanningBranchRule represents a code scanning branch rule. +type CodeScanningBranchRule struct { + BranchRuleMetadata + Parameters CodeScanningRuleParameters `json:"parameters"` +} + +// CopilotCodeReviewBranchRule represents a copilot code review branch rule. +type CopilotCodeReviewBranchRule struct { + BranchRuleMetadata + Parameters CopilotCodeReviewRuleParameters `json:"parameters"` +} + +// EmptyRuleParameters represents the parameters for a rule with no options. +type EmptyRuleParameters struct{} + +// UpdateRuleParameters represents the update rule parameters. +type UpdateRuleParameters struct { + UpdateAllowsFetchAndMerge bool `json:"update_allows_fetch_and_merge,omitempty"` +} + +// MergeQueueRuleParameters represents the merge_queue rule parameters. +type MergeQueueRuleParameters struct { + CheckResponseTimeoutMinutes int `json:"check_response_timeout_minutes"` + GroupingStrategy MergeGroupingStrategy `json:"grouping_strategy"` + MaxEntriesToBuild int `json:"max_entries_to_build"` + MaxEntriesToMerge int `json:"max_entries_to_merge"` + MergeMethod MergeQueueMergeMethod `json:"merge_method"` + MinEntriesToMerge int `json:"min_entries_to_merge"` + MinEntriesToMergeWaitMinutes int `json:"min_entries_to_merge_wait_minutes"` +} + +// RequiredDeploymentsRuleParameters represents the required deployments rule parameters. +type RequiredDeploymentsRuleParameters struct { + RequiredDeploymentEnvironments []string `json:"required_deployment_environments"` +} + +// PullRequestRuleParameters represents the pull_request rule parameters. +type PullRequestRuleParameters struct { + AllowedMergeMethods []PullRequestMergeMethod `json:"allowed_merge_methods,omitempty"` + DismissStaleReviewsOnPush bool `json:"dismiss_stale_reviews_on_push"` + RequireCodeOwnerReview bool `json:"require_code_owner_review"` + RequireLastPushApproval bool `json:"require_last_push_approval"` + RequiredApprovingReviewCount int `json:"required_approving_review_count"` + RequiredReviewers []*RulesetRequiredReviewer `json:"required_reviewers,omitempty"` + RequiredReviewThreadResolution bool `json:"required_review_thread_resolution"` +} + +// RulesetRequiredReviewer represents required reviewer parameters for pull requests in rulesets. +type RulesetRequiredReviewer struct { + MinimumApprovals *int `json:"minimum_approvals,omitempty"` + FilePatterns []string `json:"file_patterns,omitempty"` + Reviewer *RulesetReviewer `json:"reviewer,omitempty"` +} + +// RulesetReviewer represents a reviewer in a ruleset required reviewer rule. +type RulesetReviewer struct { + ID *int64 `json:"id,omitempty"` + Type *RulesetReviewerType `json:"type,omitempty"` +} + +// UnmarshalJSON is a custom JSON unmarshaler for RulesetReviewer. +func (r *RulesetReviewer) UnmarshalJSON(data []byte) error { + var aux struct { + ID any `json:"id,omitempty"` + Type *RulesetReviewerType `json:"type,omitempty"` + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + r.Type = aux.Type + + if aux.ID != nil { + switch id := aux.ID.(type) { + case float64: + r.ID = Ptr(int64(id)) + case string: + i, err := strconv.ParseInt(id, 10, 64) + if err != nil { + return err + } + r.ID = &i + default: + return fmt.Errorf("unexpected type for reviewer.ID: %T", id) + } + } + + return nil +} + +// RequiredStatusChecksRuleParameters represents the required status checks rule parameters. +type RequiredStatusChecksRuleParameters struct { + DoNotEnforceOnCreate *bool `json:"do_not_enforce_on_create,omitempty"` + RequiredStatusChecks []*RuleStatusCheck `json:"required_status_checks"` + StrictRequiredStatusChecksPolicy bool `json:"strict_required_status_checks_policy"` +} + +// RuleStatusCheck represents a status checks for the required status checks rule parameters. +type RuleStatusCheck struct { + Context string `json:"context"` + IntegrationID *int64 `json:"integration_id,omitempty"` +} + +// PatternRuleParameters represents the parameters for a pattern rule. +type PatternRuleParameters struct { + Name *string `json:"name,omitempty"` + // If Negate is true, the rule will fail if the pattern matches. + Negate *bool `json:"negate,omitempty"` + Operator PatternRuleOperator `json:"operator"` + Pattern string `json:"pattern"` +} + +// FilePathRestrictionRuleParameters represents the file path restriction rule parameters. +type FilePathRestrictionRuleParameters struct { + RestrictedFilePaths []string `json:"restricted_file_paths"` +} + +// MaxFilePathLengthRuleParameters represents the max file path length rule parameters. +type MaxFilePathLengthRuleParameters struct { + MaxFilePathLength int `json:"max_file_path_length"` +} + +// FileExtensionRestrictionRuleParameters represents the file extension restriction rule parameters. +type FileExtensionRestrictionRuleParameters struct { + RestrictedFileExtensions []string `json:"restricted_file_extensions"` +} + +// MaxFileSizeRuleParameters represents the max file size rule parameters. +type MaxFileSizeRuleParameters struct { + MaxFileSize int64 `json:"max_file_size"` +} + +// WorkflowsRuleParameters represents the workflows rule parameters. +type WorkflowsRuleParameters struct { + DoNotEnforceOnCreate *bool `json:"do_not_enforce_on_create,omitempty"` + Workflows []*RuleWorkflow `json:"workflows"` +} + +// RuleWorkflow represents a Workflow for the workflows rule parameters. +type RuleWorkflow struct { + Path string `json:"path"` + Ref *string `json:"ref,omitempty"` + RepositoryID *int64 `json:"repository_id,omitempty"` + SHA *string `json:"sha,omitempty"` +} + +// CodeScanningRuleParameters represents the code scanning rule parameters. +type CodeScanningRuleParameters struct { + CodeScanningTools []*RuleCodeScanningTool `json:"code_scanning_tools"` +} + +// CopilotCodeReviewRuleParameters represents the copilot_code_review rule parameters. +type CopilotCodeReviewRuleParameters struct { + ReviewOnPush bool `json:"review_on_push"` + ReviewDraftPullRequests bool `json:"review_draft_pull_requests"` +} + +// RuleCodeScanningTool represents a single code scanning tool for the code scanning parameters. +type RuleCodeScanningTool struct { + AlertsThreshold CodeScanningAlertsThreshold `json:"alerts_threshold"` + SecurityAlertsThreshold CodeScanningSecurityAlertsThreshold `json:"security_alerts_threshold"` + Tool string `json:"tool"` +} + +// SimplePatternRuleParameters represents the parameters for a simple pattern rule. +type SimplePatternRuleParameters struct { + Negate bool `json:"negate"` + Pattern string `json:"pattern"` +} + +// RepositoryVisibilityRuleParameters represents the repository visibility rule parameters. +type RepositoryVisibilityRuleParameters struct { + Internal bool `json:"internal"` + Private bool `json:"private"` +} + +// repositoryRulesetRuleWrapper is a helper type to marshal & unmarshal a ruleset rule. +type repositoryRulesetRuleWrapper struct { + Type RepositoryRuleType `json:"type"` + Parameters json.RawMessage `json:"parameters,omitempty"` +} + +// MarshalJSON is a custom JSON marshaler for RulesetRules. +func (r RepositoryRulesetRules) MarshalJSON() ([]byte, error) { + var rawRules []json.RawMessage + + if r.Creation != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCreation, r.Creation) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.Update != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeUpdate, r.Update) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.Deletion != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeDeletion, r.Deletion) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredLinearHistory != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredLinearHistory, r.RequiredLinearHistory) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.MergeQueue != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeMergeQueue, r.MergeQueue) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredDeployments != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredDeployments, r.RequiredDeployments) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredSignatures != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredSignatures, r.RequiredSignatures) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.PullRequest != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypePullRequest, r.PullRequest) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredStatusChecks != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredStatusChecks, r.RequiredStatusChecks) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.NonFastForward != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeNonFastForward, r.NonFastForward) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CommitMessagePattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCommitMessagePattern, r.CommitMessagePattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CommitAuthorEmailPattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCommitAuthorEmailPattern, r.CommitAuthorEmailPattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CommitterEmailPattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCommitterEmailPattern, r.CommitterEmailPattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.BranchNamePattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeBranchNamePattern, r.BranchNamePattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.TagNamePattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeTagNamePattern, r.TagNamePattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.FilePathRestriction != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeFilePathRestriction, r.FilePathRestriction) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.MaxFilePathLength != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeMaxFilePathLength, r.MaxFilePathLength) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.FileExtensionRestriction != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeFileExtensionRestriction, r.FileExtensionRestriction) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.MaxFileSize != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeMaxFileSize, r.MaxFileSize) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.Workflows != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeWorkflows, r.Workflows) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CodeScanning != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCodeScanning, r.CodeScanning) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CopilotCodeReview != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCopilotCodeReview, r.CopilotCodeReview) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RepositoryCreate != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryCreate, r.RepositoryCreate) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RepositoryDelete != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryDelete, r.RepositoryDelete) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RepositoryName != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryName, r.RepositoryName) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RepositoryTransfer != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryTransfer, r.RepositoryTransfer) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RepositoryVisibility != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRepositoryVisibility, r.RepositoryVisibility) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if len(rawRules) == 0 { + return []byte("[]"), nil + } + + return json.Marshal(rawRules) +} + +// marshalRepositoryRulesetRule is a helper function to marshal a ruleset rule. +func marshalRepositoryRulesetRule[T any](t RepositoryRuleType, params T) ([]byte, error) { + hasParams := true + + switch t { + case RulesetRuleTypeCreation, + RulesetRuleTypeDeletion, + RulesetRuleTypeRequiredLinearHistory, + RulesetRuleTypeRequiredSignatures, + RulesetRuleTypeNonFastForward, + RulesetRuleTypeRepositoryCreate, + RulesetRuleTypeRepositoryDelete, + RulesetRuleTypeRepositoryTransfer: + hasParams = false + case RulesetRuleTypeUpdate: + paramsTyped, ok := any(params).(*UpdateRuleParameters) + if !ok { + return nil, fmt.Errorf("expected UpdateRuleParameters for rule type %v", t) + } + if paramsTyped == nil || *paramsTyped == (UpdateRuleParameters{}) { + hasParams = false + } + } + + if !hasParams { + return json.Marshal(repositoryRulesetRuleWrapper{Type: t}) + } + + bytes, err := json.Marshal(params) + if err != nil { + return nil, err + } + + return json.Marshal(repositoryRulesetRuleWrapper{Type: t, Parameters: json.RawMessage(bytes)}) +} + +// UnmarshalJSON is a custom JSON unmarshaler for RulesetRules. +func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error { + var wrappers []*repositoryRulesetRuleWrapper + + if err := json.Unmarshal(data, &wrappers); err != nil { + return err + } + + for _, w := range wrappers { + switch w.Type { + case RulesetRuleTypeCreation: + r.Creation = &EmptyRuleParameters{} + case RulesetRuleTypeUpdate: + r.Update = &UpdateRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.Update); err != nil { + return err + } + } + case RulesetRuleTypeDeletion: + r.Deletion = &EmptyRuleParameters{} + case RulesetRuleTypeRequiredLinearHistory: + r.RequiredLinearHistory = &EmptyRuleParameters{} + case RulesetRuleTypeMergeQueue: + r.MergeQueue = &MergeQueueRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.MergeQueue); err != nil { + return err + } + } + case RulesetRuleTypeRequiredDeployments: + r.RequiredDeployments = &RequiredDeploymentsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.RequiredDeployments); err != nil { + return err + } + } + case RulesetRuleTypeRequiredSignatures: + r.RequiredSignatures = &EmptyRuleParameters{} + case RulesetRuleTypePullRequest: + r.PullRequest = &PullRequestRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.PullRequest); err != nil { + return err + } + } + case RulesetRuleTypeRequiredStatusChecks: + r.RequiredStatusChecks = &RequiredStatusChecksRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.RequiredStatusChecks); err != nil { + return err + } + } + case RulesetRuleTypeNonFastForward: + r.NonFastForward = &EmptyRuleParameters{} + case RulesetRuleTypeCommitMessagePattern: + r.CommitMessagePattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CommitMessagePattern); err != nil { + return err + } + } + case RulesetRuleTypeCommitAuthorEmailPattern: + r.CommitAuthorEmailPattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CommitAuthorEmailPattern); err != nil { + return err + } + } + case RulesetRuleTypeCommitterEmailPattern: + r.CommitterEmailPattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CommitterEmailPattern); err != nil { + return err + } + } + case RulesetRuleTypeBranchNamePattern: + r.BranchNamePattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.BranchNamePattern); err != nil { + return err + } + } + case RulesetRuleTypeTagNamePattern: + r.TagNamePattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.TagNamePattern); err != nil { + return err + } + } + case RulesetRuleTypeFilePathRestriction: + r.FilePathRestriction = &FilePathRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.FilePathRestriction); err != nil { + return err + } + } + case RulesetRuleTypeMaxFilePathLength: + r.MaxFilePathLength = &MaxFilePathLengthRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.MaxFilePathLength); err != nil { + return err + } + } + case RulesetRuleTypeFileExtensionRestriction: + r.FileExtensionRestriction = &FileExtensionRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.FileExtensionRestriction); err != nil { + return err + } + } + case RulesetRuleTypeMaxFileSize: + r.MaxFileSize = &MaxFileSizeRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.MaxFileSize); err != nil { + return err + } + } + case RulesetRuleTypeWorkflows: + r.Workflows = &WorkflowsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.Workflows); err != nil { + return err + } + } + case RulesetRuleTypeCodeScanning: + r.CodeScanning = &CodeScanningRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CodeScanning); err != nil { + return err + } + } + case RulesetRuleTypeCopilotCodeReview: + r.CopilotCodeReview = &CopilotCodeReviewRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CopilotCodeReview); err != nil { + return err + } + } + case RulesetRuleTypeRepositoryCreate: + r.RepositoryCreate = &EmptyRuleParameters{} + case RulesetRuleTypeRepositoryDelete: + r.RepositoryDelete = &EmptyRuleParameters{} + case RulesetRuleTypeRepositoryName: + r.RepositoryName = &SimplePatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.RepositoryName); err != nil { + return err + } + } + case RulesetRuleTypeRepositoryTransfer: + r.RepositoryTransfer = &EmptyRuleParameters{} + case RulesetRuleTypeRepositoryVisibility: + r.RepositoryVisibility = &RepositoryVisibilityRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.RepositoryVisibility); err != nil { + return err + } + } + } + } + + return nil +} + +// branchRuleWrapper is a helper type to unmarshal a branch rule. +type branchRuleWrapper struct { + Type RepositoryRuleType `json:"type"` + BranchRuleMetadata + Parameters json.RawMessage `json:"parameters,omitempty"` +} + +// UnmarshalJSON is a custom JSON unmarshaler for BranchRules. +func (r *BranchRules) UnmarshalJSON(data []byte) error { + var wrappers []*branchRuleWrapper + + if err := json.Unmarshal(data, &wrappers); err != nil { + return err + } + + for _, w := range wrappers { + switch w.Type { + case RulesetRuleTypeCreation: + r.Creation = append(r.Creation, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeUpdate: + params := &UpdateRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.Update = append(r.Update, &UpdateBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeDeletion: + r.Deletion = append(r.Deletion, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeRequiredLinearHistory: + r.RequiredLinearHistory = append(r.RequiredLinearHistory, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeMergeQueue: + params := &MergeQueueRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.MergeQueue = append(r.MergeQueue, &MergeQueueBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeRequiredDeployments: + params := &RequiredDeploymentsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.RequiredDeployments = append(r.RequiredDeployments, &RequiredDeploymentsBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeRequiredSignatures: + r.RequiredSignatures = append(r.RequiredSignatures, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypePullRequest: + params := &PullRequestRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.PullRequest = append(r.PullRequest, &PullRequestBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeRequiredStatusChecks: + params := &RequiredStatusChecksRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.RequiredStatusChecks = append(r.RequiredStatusChecks, &RequiredStatusChecksBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeNonFastForward: + r.NonFastForward = append(r.NonFastForward, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeCommitMessagePattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CommitMessagePattern = append(r.CommitMessagePattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCommitAuthorEmailPattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CommitAuthorEmailPattern = append(r.CommitAuthorEmailPattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCommitterEmailPattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CommitterEmailPattern = append(r.CommitterEmailPattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeBranchNamePattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.BranchNamePattern = append(r.BranchNamePattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeTagNamePattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.TagNamePattern = append(r.TagNamePattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeFilePathRestriction: + params := &FilePathRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.FilePathRestriction = append(r.FilePathRestriction, &FilePathRestrictionBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeMaxFilePathLength: + params := &MaxFilePathLengthRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.MaxFilePathLength = append(r.MaxFilePathLength, &MaxFilePathLengthBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeFileExtensionRestriction: + params := &FileExtensionRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.FileExtensionRestriction = append(r.FileExtensionRestriction, &FileExtensionRestrictionBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeMaxFileSize: + params := &MaxFileSizeRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.MaxFileSize = append(r.MaxFileSize, &MaxFileSizeBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeWorkflows: + params := &WorkflowsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.Workflows = append(r.Workflows, &WorkflowsBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCodeScanning: + params := &CodeScanningRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CodeScanning = append(r.CodeScanning, &CodeScanningBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCopilotCodeReview: + params := &CopilotCodeReviewRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CopilotCodeReview = append(r.CopilotCodeReview, &CopilotCodeReviewBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + } + } + + return nil +} + +// UnmarshalJSON is a custom JSON unmarshaler for RulesetRule. +func (r *RepositoryRule) UnmarshalJSON(data []byte) error { + w := repositoryRulesetRuleWrapper{} + + if err := json.Unmarshal(data, &w); err != nil { + return err + } + + r.Type = w.Type + + switch r.Type { + case RulesetRuleTypeCreation, + RulesetRuleTypeDeletion, + RulesetRuleTypeRequiredLinearHistory, + RulesetRuleTypeRequiredSignatures, + RulesetRuleTypeNonFastForward, + RulesetRuleTypeRepositoryCreate, + RulesetRuleTypeRepositoryDelete, + RulesetRuleTypeRepositoryTransfer: + r.Parameters = nil + case RulesetRuleTypeUpdate: + p := &UpdateRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeMergeQueue: + p := &MergeQueueRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRequiredDeployments: + p := &RequiredDeploymentsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypePullRequest: + p := &PullRequestRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRequiredStatusChecks: + p := &RequiredStatusChecksRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeCommitMessagePattern, + RulesetRuleTypeCommitAuthorEmailPattern, + RulesetRuleTypeCommitterEmailPattern, + RulesetRuleTypeBranchNamePattern, + RulesetRuleTypeTagNamePattern: + p := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeFilePathRestriction: + p := &FilePathRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeMaxFilePathLength: + p := &MaxFilePathLengthRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeFileExtensionRestriction: + p := &FileExtensionRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeMaxFileSize: + p := &MaxFileSizeRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeWorkflows: + p := &WorkflowsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeCodeScanning: + p := &CodeScanningRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeCopilotCodeReview: + p := &CopilotCodeReviewRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRepositoryName: + p := &SimplePatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRepositoryVisibility: + p := &RepositoryVisibilityRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + r.Parameters = p + } + + return nil +} diff --git a/github/rules_test.go b/github/rules_test.go new file mode 100644 index 00000000000..cf46f60dd62 --- /dev/null +++ b/github/rules_test.go @@ -0,0 +1,1253 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoryRulesetRules(t *testing.T) { + t.Parallel() + tests := []struct { + name string + rules *RepositoryRulesetRules + json string + }{ + {"empty", &RepositoryRulesetRules{}, `[]`}, + { + "single_rule_with_empty_params", + &RepositoryRulesetRules{Creation: &EmptyRuleParameters{}}, + `[{"type":"creation"}]`, + }, + { + "single_rule_with_required_params", + &RepositoryRulesetRules{ + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + }, + `[{"type":"required_deployments","parameters":{"required_deployment_environments":["test"]}}]`, + }, + { + "all_rules_with_required_params", + &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{}, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + MergeQueue: &MergeQueueRuleParameters{ + CheckResponseTimeoutMinutes: 5, + GroupingStrategy: MergeGroupingStrategyAllGreen, + MaxEntriesToBuild: 10, + MaxEntriesToMerge: 20, + MergeMethod: MergeQueueMergeMethodSquash, + MinEntriesToMerge: 1, + MinEntriesToMergeWaitMinutes: 15, + }, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test1", "test2"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 2, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []*RuleStatusCheck{ + {Context: "test1"}, + {Context: "test2"}, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + BranchNamePattern: &PatternRuleParameters{ + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + TagNamePattern: &PatternRuleParameters{ + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + FilePathRestriction: &FilePathRestrictionRuleParameters{ + RestrictedFilePaths: []string{"test1", "test2"}, + }, + MaxFilePathLength: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 512}, + FileExtensionRestriction: &FileExtensionRestrictionRuleParameters{ + RestrictedFileExtensions: []string{".exe", ".pkg"}, + }, + MaxFileSize: &MaxFileSizeRuleParameters{MaxFileSize: 1024}, + Workflows: &WorkflowsRuleParameters{ + Workflows: []*RuleWorkflow{ + {Path: ".github/workflows/test1.yaml"}, + {Path: ".github/workflows/test2.yaml"}, + }, + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdAll, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll, + Tool: "test", + }, + { + AlertsThreshold: CodeScanningAlertsThresholdNone, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone, + Tool: "test", + }, + }, + }, + CopilotCodeReview: &CopilotCodeReviewRuleParameters{ + ReviewOnPush: true, + ReviewDraftPullRequests: false, + }, + RepositoryCreate: &EmptyRuleParameters{}, + RepositoryDelete: &EmptyRuleParameters{}, + RepositoryName: &SimplePatternRuleParameters{Pattern: "^test-.+", Negate: false}, + RepositoryTransfer: &EmptyRuleParameters{}, + RepositoryVisibility: &RepositoryVisibilityRuleParameters{Internal: false, Private: false}, + }, + `[{"type":"creation"},{"type":"update"},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}},{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test1"},{"context":"test2"}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"workflows":[{"path":".github/workflows/test1.yaml"},{"path":".github/workflows/test2.yaml"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false}}]`, + }, + { + "all_rules_with_all_params", + &RepositoryRulesetRules{ + Creation: &EmptyRuleParameters{}, + Update: &UpdateRuleParameters{UpdateAllowsFetchAndMerge: true}, + Deletion: &EmptyRuleParameters{}, + RequiredLinearHistory: &EmptyRuleParameters{}, + MergeQueue: &MergeQueueRuleParameters{ + CheckResponseTimeoutMinutes: 5, + GroupingStrategy: MergeGroupingStrategyAllGreen, + MaxEntriesToBuild: 10, + MaxEntriesToMerge: 20, + MergeMethod: MergeQueueMergeMethodSquash, + MinEntriesToMerge: 1, + MinEntriesToMergeWaitMinutes: 15, + }, + RequiredDeployments: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test1", "test2"}, + }, + RequiredSignatures: &EmptyRuleParameters{}, + PullRequest: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{ + PullRequestMergeMethodSquash, + PullRequestMergeMethodRebase, + }, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 2, + RequiredReviewThreadResolution: true, + }, + RequiredStatusChecks: &RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + {Context: "test1", IntegrationID: Ptr(int64(1))}, + {Context: "test2", IntegrationID: Ptr(int64(2))}, + }, + StrictRequiredStatusChecksPolicy: true, + }, + NonFastForward: &EmptyRuleParameters{}, + CommitMessagePattern: &PatternRuleParameters{ + Name: Ptr("cmp"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + CommitAuthorEmailPattern: &PatternRuleParameters{ + Name: Ptr("caep"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + CommitterEmailPattern: &PatternRuleParameters{ + Name: Ptr("cep"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + BranchNamePattern: &PatternRuleParameters{ + Name: Ptr("bp"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + TagNamePattern: &PatternRuleParameters{ + Name: Ptr("tp"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + FilePathRestriction: &FilePathRestrictionRuleParameters{ + RestrictedFilePaths: []string{"test1", "test2"}, + }, + MaxFilePathLength: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 512}, + FileExtensionRestriction: &FileExtensionRestrictionRuleParameters{ + RestrictedFileExtensions: []string{".exe", ".pkg"}, + }, + MaxFileSize: &MaxFileSizeRuleParameters{MaxFileSize: 1024}, + Workflows: &WorkflowsRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + Workflows: []*RuleWorkflow{ + { + Path: ".github/workflows/test1.yaml", + Ref: Ptr("main"), + RepositoryID: Ptr(int64(1)), + SHA: Ptr("aaaa"), + }, + { + Path: ".github/workflows/test2.yaml", + Ref: Ptr("main"), + RepositoryID: Ptr(int64(2)), + SHA: Ptr("bbbb"), + }, + }, + }, + CodeScanning: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdAll, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll, + Tool: "test", + }, + { + AlertsThreshold: CodeScanningAlertsThresholdNone, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone, + Tool: "test", + }, + }, + }, + CopilotCodeReview: &CopilotCodeReviewRuleParameters{ + ReviewOnPush: true, + ReviewDraftPullRequests: false, + }, + RepositoryCreate: &EmptyRuleParameters{}, + RepositoryDelete: &EmptyRuleParameters{}, + RepositoryName: &SimplePatternRuleParameters{Pattern: "^test-.+", Negate: false}, + RepositoryTransfer: &EmptyRuleParameters{}, + RepositoryVisibility: &RepositoryVisibilityRuleParameters{Internal: false, Private: false}, + }, + `[{"type":"creation"},{"type":"update","parameters":{"update_allows_fetch_and_merge":true}},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}},{"type":"required_status_checks","parameters":{"do_not_enforce_on_create":true,"required_status_checks":[{"context":"test1","integration_id":1},{"context":"test2","integration_id":2}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"name":"cmp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"name":"caep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"name":"cep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"name":"bp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"name":"tp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"do_not_enforce_on_create":true,"workflows":[{"path":".github/workflows/test1.yaml","ref":"main","repository_id":1,"sha":"aaaa"},{"path":".github/workflows/test2.yaml","ref":"main","repository_id":2,"sha":"bbbb"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false}}]`, + }, + } + + t.Run("MarshalJSON", func(t *testing.T) { + t.Parallel() + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + testJSONMarshalOnly(t, test.rules, test.json) + testJSONMarshalOnly(t, *test.rules, test.json) + }) + } + }) + + t.Run("UnmarshalJSON", func(t *testing.T) { + t.Parallel() + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + got := &RepositoryRulesetRules{} + err := json.Unmarshal([]byte(test.json), got) + if err != nil { + t.Errorf("Unable to unmarshal JSON %v: %v", test.json, err) + } + + if diff := cmp.Diff(test.rules, got); diff != "" { + t.Errorf( + "json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v", + got, + test.rules, + diff, + ) + } + }) + } + }) + + t.Run("UnmarshalJSON_Error", func(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + json string + }{ + { + "invalid_copilot_code_review_bool", + `[{"type":"copilot_code_review","parameters":{"review_on_push":"invalid_bool"}}]`, + }, + { + "invalid_copilot_code_review_draft_pr", + `[{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":"not_a_bool"}}]`, + }, + { + "invalid_copilot_code_review_parameters", + `[{"type":"copilot_code_review","parameters":"not_an_object"}]`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := &RepositoryRulesetRules{} + err := json.Unmarshal([]byte(tt.json), got) + if err == nil { + t.Errorf("Expected error unmarshaling %q, got nil", tt.json) + } + }) + } + }) +} + +func TestBranchRules(t *testing.T) { + t.Parallel() + tests := []struct { + name string + rules *BranchRules + json string + }{ + {"empty", &BranchRules{}, `[]`}, + { + "single_rule_type_single_rule_empty_params", + &BranchRules{ + Creation: []*BranchRuleMetadata{ + { + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + }, + }, + `[{"type":"creation","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1}]`, + }, + { + "single_rule_type_single_rule_with_params", + &BranchRules{ + Update: []*UpdateBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: UpdateRuleParameters{UpdateAllowsFetchAndMerge: true}, + }, + }, + }, + `[{"type":"update","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"update_allows_fetch_and_merge":true}}]`, + }, + { + "all_rule_types_with_all_parameters", + &BranchRules{ + Creation: []*BranchRuleMetadata{ + { + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + }, + Update: []*UpdateBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: UpdateRuleParameters{UpdateAllowsFetchAndMerge: true}, + }, + }, + Deletion: []*BranchRuleMetadata{ + { + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + }, + RequiredLinearHistory: []*BranchRuleMetadata{ + { + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + }, + MergeQueue: []*MergeQueueBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: MergeQueueRuleParameters{ + CheckResponseTimeoutMinutes: 5, + GroupingStrategy: MergeGroupingStrategyAllGreen, + MaxEntriesToBuild: 10, + MaxEntriesToMerge: 20, + MergeMethod: MergeQueueMergeMethodSquash, + MinEntriesToMerge: 1, + MinEntriesToMergeWaitMinutes: 15, + }, + }, + }, + RequiredDeployments: []*RequiredDeploymentsBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test1", "test2"}, + }, + }, + }, + RequiredSignatures: []*BranchRuleMetadata{ + { + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + }, + PullRequest: []*PullRequestBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{ + PullRequestMergeMethodSquash, + PullRequestMergeMethodRebase, + }, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 2, + RequiredReviewThreadResolution: true, + }, + }, + }, + RequiredStatusChecks: []*RequiredStatusChecksBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: RequiredStatusChecksRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + RequiredStatusChecks: []*RuleStatusCheck{ + {Context: "test1", IntegrationID: Ptr(int64(1))}, + {Context: "test2", IntegrationID: Ptr(int64(2))}, + }, + StrictRequiredStatusChecksPolicy: true, + }, + }, + }, + NonFastForward: []*BranchRuleMetadata{ + { + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + }, + CommitMessagePattern: []*PatternBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: PatternRuleParameters{ + Name: Ptr("cmp"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + }, + CommitAuthorEmailPattern: []*PatternBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: PatternRuleParameters{ + Name: Ptr("caep"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + }, + CommitterEmailPattern: []*PatternBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: PatternRuleParameters{ + Name: Ptr("cep"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + }, + BranchNamePattern: []*PatternBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: PatternRuleParameters{ + Name: Ptr("bp"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + }, + TagNamePattern: []*PatternBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: PatternRuleParameters{ + Name: Ptr("tp"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + }, + FilePathRestriction: []*FilePathRestrictionBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: FilePathRestrictionRuleParameters{ + RestrictedFilePaths: []string{"test1", "test2"}, + }, + }, + }, + MaxFilePathLength: []*MaxFilePathLengthBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: MaxFilePathLengthRuleParameters{MaxFilePathLength: 512}, + }, + }, + FileExtensionRestriction: []*FileExtensionRestrictionBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: FileExtensionRestrictionRuleParameters{ + RestrictedFileExtensions: []string{".exe", ".pkg"}, + }, + }, + }, + MaxFileSize: []*MaxFileSizeBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: MaxFileSizeRuleParameters{MaxFileSize: 1024}, + }, + }, + Workflows: []*WorkflowsBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: WorkflowsRuleParameters{ + DoNotEnforceOnCreate: Ptr(true), + Workflows: []*RuleWorkflow{ + { + Path: ".github/workflows/test1.yaml", + Ref: Ptr("main"), + RepositoryID: Ptr(int64(1)), + SHA: Ptr("aaaa"), + }, + { + Path: ".github/workflows/test2.yaml", + Ref: Ptr("main"), + RepositoryID: Ptr(int64(2)), + SHA: Ptr("bbbb"), + }, + }, + }, + }, + }, + CodeScanning: []*CodeScanningBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdAll, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll, + Tool: "test", + }, + { + AlertsThreshold: CodeScanningAlertsThresholdNone, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone, + Tool: "test", + }, + }, + }, + }, + }, + CopilotCodeReview: []*CopilotCodeReviewBranchRule{ + { + BranchRuleMetadata: BranchRuleMetadata{ + RulesetSourceType: RulesetSourceTypeRepository, + RulesetSource: "test/test", + RulesetID: 1, + }, + Parameters: CopilotCodeReviewRuleParameters{ + ReviewOnPush: true, + ReviewDraftPullRequests: false, + }, + }, + }, + }, + `[{"type":"creation","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"update","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"update_allows_fetch_and_merge":true}},{"type":"deletion","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"required_linear_history","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"merge_queue","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"pull_request","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"allowed_merge_methods":["squash","rebase"],"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}},{"type":"required_status_checks","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"do_not_enforce_on_create":true,"required_status_checks":[{"context":"test1","integration_id":1},{"context":"test2","integration_id":2}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"commit_message_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"cmp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"caep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"cep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"bp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"tp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"max_file_size":1024}},{"type":"workflows","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"do_not_enforce_on_create":true,"workflows":[{"path":".github/workflows/test1.yaml","ref":"main","repository_id":1,"sha":"aaaa"},{"path":".github/workflows/test2.yaml","ref":"main","repository_id":2,"sha":"bbbb"}]}},{"type":"code_scanning","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"review_on_push":true,"review_draft_pull_requests":false}}]`, + }, + } + + t.Run("UnmarshalJSON", func(t *testing.T) { + t.Parallel() + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + got := &BranchRules{} + err := json.Unmarshal([]byte(test.json), got) + if err != nil { + t.Errorf("Unable to unmarshal JSON %v: %v", test.json, err) + } + + if diff := cmp.Diff(test.rules, got); diff != "" { + t.Errorf( + "json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v", + got, + test.rules, + diff, + ) + } + }) + } + }) + + t.Run("UnmarshalJSON_Error", func(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + json string + }{ + { + "invalid_copilot_code_review_parameters", + `[{"type":"copilot_code_review","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":"not_an_object"}]`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := &BranchRules{} + err := json.Unmarshal([]byte(tt.json), got) + if err == nil { + t.Errorf("Expected error unmarshaling %q, got nil", tt.json) + } + }) + } + }) +} + +func TestRepositoryRule(t *testing.T) { + t.Parallel() + tests := []struct { + name string + rule *RepositoryRule + json string + }{ + { + "empty", + &RepositoryRule{}, + `{}`, + }, + { + "creation", + &RepositoryRule{Type: RulesetRuleTypeCreation, Parameters: nil}, + `{"type":"creation"}`, + }, + { + "update", + &RepositoryRule{Type: RulesetRuleTypeUpdate, Parameters: &UpdateRuleParameters{}}, + `{"type":"update"}`, + }, + { + "update_params_empty", + &RepositoryRule{Type: RulesetRuleTypeUpdate, Parameters: &UpdateRuleParameters{}}, + `{"type":"update","parameters":{}}`, + }, + { + "update_params_set", + &RepositoryRule{ + Type: RulesetRuleTypeUpdate, + Parameters: &UpdateRuleParameters{UpdateAllowsFetchAndMerge: true}, + }, + `{"type":"update","parameters":{"update_allows_fetch_and_merge":true}}`, + }, + { + "deletion", + &RepositoryRule{Type: RulesetRuleTypeDeletion, Parameters: nil}, + `{"type":"deletion"}`, + }, + { + "required_linear_history", + &RepositoryRule{Type: RulesetRuleTypeRequiredLinearHistory, Parameters: nil}, + `{"type":"required_linear_history"}`, + }, + { + "merge_queue", + &RepositoryRule{ + Type: RulesetRuleTypeMergeQueue, + Parameters: &MergeQueueRuleParameters{ + CheckResponseTimeoutMinutes: 5, + GroupingStrategy: MergeGroupingStrategyAllGreen, + MaxEntriesToBuild: 10, + MaxEntriesToMerge: 20, + MergeMethod: MergeQueueMergeMethodSquash, + MinEntriesToMerge: 1, + MinEntriesToMergeWaitMinutes: 15, + }, + }, + `{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}}`, + }, + { + "required_deployments", + &RepositoryRule{ + Type: RulesetRuleTypeRequiredDeployments, + Parameters: &RequiredDeploymentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test1", "test2"}, + }, + }, + `{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}}`, + }, + { + "required_signatures", + &RepositoryRule{Type: RulesetRuleTypeRequiredSignatures, Parameters: nil}, + `{"type":"required_signatures"}`, + }, + { + "pull_request", + &RepositoryRule{ + Type: RulesetRuleTypePullRequest, + Parameters: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{ + PullRequestMergeMethodSquash, + PullRequestMergeMethodRebase, + }, + DismissStaleReviewsOnPush: true, + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 2, + RequiredReviewThreadResolution: true, + }, + }, + `{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}}`, + }, + { + "pull_request_with_required_reviewers", + &RepositoryRule{ + Type: RulesetRuleTypePullRequest, + Parameters: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{ + PullRequestMergeMethodMerge, + PullRequestMergeMethodSquash, + PullRequestMergeMethodRebase, + }, + DismissStaleReviewsOnPush: false, + RequireCodeOwnerReview: false, + RequireLastPushApproval: false, + RequiredApprovingReviewCount: 0, + RequiredReviewThreadResolution: false, + RequiredReviewers: []*RulesetRequiredReviewer{ + { + MinimumApprovals: Ptr(1), + FilePatterns: []string{"*"}, + Reviewer: &RulesetReviewer{ + ID: Ptr(int64(123456)), + Type: Ptr(RulesetReviewerTypeTeam), + }, + }, + }, + }, + }, + `{"type":"pull_request","parameters":{"allowed_merge_methods":["merge","squash","rebase"],"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_last_push_approval":false,"required_approving_review_count":0,"required_reviewers":[{"minimum_approvals":1,"file_patterns":["*"],"reviewer":{"id":123456,"type":"Team"}}],"required_review_thread_resolution":false}}`, + }, + { + "pull_request_string_id", + &RepositoryRule{ + Type: RulesetRuleTypePullRequest, + Parameters: &PullRequestRuleParameters{ + AllowedMergeMethods: []PullRequestMergeMethod{ + PullRequestMergeMethodMerge, + PullRequestMergeMethodSquash, + PullRequestMergeMethodRebase, + }, + DismissStaleReviewsOnPush: false, + RequireCodeOwnerReview: false, + RequireLastPushApproval: false, + RequiredApprovingReviewCount: 0, + RequiredReviewThreadResolution: false, + RequiredReviewers: []*RulesetRequiredReviewer{ + { + MinimumApprovals: Ptr(1), + FilePatterns: []string{"*"}, + Reviewer: &RulesetReviewer{ + ID: Ptr(int64(123456)), + Type: Ptr(RulesetReviewerTypeTeam), + }, + }, + }, + }, + }, + `{"type":"pull_request","parameters":{"allowed_merge_methods":["merge","squash","rebase"],"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_last_push_approval":false,"required_approving_review_count":0,"required_reviewers":[{"minimum_approvals":1,"file_patterns":["*"],"reviewer":{"id":"123456","type":"Team"}}],"required_review_thread_resolution":false}}`, + }, + { + "required_status_checks", + &RepositoryRule{ + Type: RulesetRuleTypeRequiredStatusChecks, + Parameters: &RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []*RuleStatusCheck{ + {Context: "test1"}, + {Context: "test2"}, + }, + StrictRequiredStatusChecksPolicy: true, + }, + }, + `{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test1"},{"context":"test2"}],"strict_required_status_checks_policy":true}}`, + }, + { + "non_fast_forward", + &RepositoryRule{Type: RulesetRuleTypeNonFastForward, Parameters: nil}, + `{"type":"non_fast_forward"}`, + }, + { + "commit_message_pattern", + &RepositoryRule{ + Type: RulesetRuleTypeCommitMessagePattern, + Parameters: &PatternRuleParameters{ + Name: Ptr("test"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + `{"type":"commit_message_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`, + }, + { + "commit_author_email_pattern", + &RepositoryRule{ + Type: RulesetRuleTypeCommitAuthorEmailPattern, + Parameters: &PatternRuleParameters{ + Name: Ptr("test"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + `{"type":"commit_author_email_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`, + }, + { + "committer_email_pattern", + &RepositoryRule{ + Type: RulesetRuleTypeCommitterEmailPattern, + Parameters: &PatternRuleParameters{ + Name: Ptr("test"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + `{"type":"committer_email_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`, + }, + { + "branch_name_pattern", + &RepositoryRule{ + Type: RulesetRuleTypeBranchNamePattern, + Parameters: &PatternRuleParameters{ + Name: Ptr("test"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + `{"type":"branch_name_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`, + }, + { + "tag_name_pattern", + &RepositoryRule{ + Type: RulesetRuleTypeTagNamePattern, + Parameters: &PatternRuleParameters{ + Name: Ptr("test"), + Negate: Ptr(false), + Operator: PatternRuleOperatorStartsWith, + Pattern: "test", + }, + }, + `{"type":"tag_name_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`, + }, + { + "file_path_restriction", + &RepositoryRule{ + Type: RulesetRuleTypeFilePathRestriction, + Parameters: &FilePathRestrictionRuleParameters{ + RestrictedFilePaths: []string{"test1", "test2"}, + }, + }, + `{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}}`, + }, + { + "max_file_path_length", + &RepositoryRule{ + Type: RulesetRuleTypeMaxFilePathLength, + Parameters: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 512}, + }, + `{"type":"max_file_path_length","parameters":{"max_file_path_length":512}}`, + }, + { + "file_extension_restriction", + &RepositoryRule{ + Type: RulesetRuleTypeFileExtensionRestriction, + Parameters: &FileExtensionRestrictionRuleParameters{ + RestrictedFileExtensions: []string{".exe", ".pkg"}, + }, + }, + `{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}}`, + }, + { + "max_file_size", + &RepositoryRule{ + Type: RulesetRuleTypeMaxFileSize, + Parameters: &MaxFileSizeRuleParameters{MaxFileSize: 1024}, + }, + `{"type":"max_file_size","parameters":{"max_file_size":1024}}`, + }, + { + "workflows", + &RepositoryRule{ + Type: RulesetRuleTypeWorkflows, + Parameters: &WorkflowsRuleParameters{ + Workflows: []*RuleWorkflow{ + {Path: ".github/workflows/test1.yaml"}, + {Path: ".github/workflows/test2.yaml"}, + }, + }, + }, + `{"type":"workflows","parameters":{"workflows":[{"path":".github/workflows/test1.yaml"},{"path":".github/workflows/test2.yaml"}]}}`, + }, + { + "code_scanning", + &RepositoryRule{ + Type: RulesetRuleTypeCodeScanning, + Parameters: &CodeScanningRuleParameters{ + CodeScanningTools: []*RuleCodeScanningTool{ + { + AlertsThreshold: CodeScanningAlertsThresholdAll, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll, + Tool: "test", + }, + { + AlertsThreshold: CodeScanningAlertsThresholdNone, + SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone, + Tool: "test", + }, + }, + }, + }, + `{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}}`, + }, + { + "copilot_code_review", + &RepositoryRule{ + Type: RulesetRuleTypeCopilotCodeReview, + Parameters: &CopilotCodeReviewRuleParameters{ + ReviewOnPush: true, + ReviewDraftPullRequests: false, + }, + }, + `{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}}`, + }, + { + "copilot_code_review_empty_params", + &RepositoryRule{ + Type: RulesetRuleTypeCopilotCodeReview, + Parameters: &CopilotCodeReviewRuleParameters{}, + }, + `{"type":"copilot_code_review","parameters":{"review_on_push":false,"review_draft_pull_requests":false}}`, + }, + { + "repository_create", + &RepositoryRule{Type: RulesetRuleTypeRepositoryCreate, Parameters: nil}, + `{"type":"repository_create"}`, + }, + { + "repository_delete", + &RepositoryRule{Type: RulesetRuleTypeRepositoryDelete, Parameters: nil}, + `{"type":"repository_delete"}`, + }, + { + "repository_name", + &RepositoryRule{ + Type: RulesetRuleTypeRepositoryName, + Parameters: &SimplePatternRuleParameters{ + Negate: false, + Pattern: "^test-.+", + }, + }, + `{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}}`, + }, + { + "repository_transfer", + &RepositoryRule{Type: RulesetRuleTypeRepositoryTransfer, Parameters: nil}, + `{"type":"repository_transfer"}`, + }, + { + "repository_visibility", + &RepositoryRule{ + Type: RulesetRuleTypeRepositoryVisibility, + Parameters: &RepositoryVisibilityRuleParameters{ + Internal: false, + Private: false, + }, + }, + `{"type":"repository_visibility","parameters":{"internal":false,"private":false}}`, + }, + } + + marshalTests := []struct { + name string + rule *RepositoryRule + json string + }{ + { + "creation", + &RepositoryRule{Type: RulesetRuleTypeCreation, Parameters: nil}, + `{"type":"creation"}`, + }, + { + "copilot_code_review", + &RepositoryRule{ + Type: RulesetRuleTypeCopilotCodeReview, + Parameters: &CopilotCodeReviewRuleParameters{ + ReviewOnPush: true, + ReviewDraftPullRequests: false, + }, + }, + `{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}}`, + }, + { + "copilot_code_review_empty_params", + &RepositoryRule{ + Type: RulesetRuleTypeCopilotCodeReview, + Parameters: &CopilotCodeReviewRuleParameters{}, + }, + `{"type":"copilot_code_review","parameters":{"review_on_push":false,"review_draft_pull_requests":false}}`, + }, + } + + t.Run("MarshalJSON", func(t *testing.T) { + t.Parallel() + + for _, test := range marshalTests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + got, err := json.Marshal(test.rule) + if err != nil { + t.Errorf("Unable to marshal JSON for %#v", test.rule) + } + + if diff := cmp.Diff(test.json, string(got)); diff != "" { + t.Errorf( + "json.Marshal returned:\n%v\nwant:\n%v\ndiff:\n%v", + string(got), + test.json, + diff, + ) + } + }) + } + }) + + t.Run("UnmarshalJSON", func(t *testing.T) { + t.Parallel() + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + got := &RepositoryRule{} + err := json.Unmarshal([]byte(test.json), got) + if err != nil { + t.Errorf("Unable to unmarshal JSON %v: %v", test.json, err) + } + + if diff := cmp.Diff(test.rule, got); diff != "" { + t.Errorf( + "json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v", + got, + test.rule, + diff, + ) + } + }) + } + }) + + t.Run("UnmarshalJSON_Error", func(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + json string + }{ + { + "invalid_copilot_code_review_bool", + `{"type":"copilot_code_review","parameters":{"review_on_push":"invalid_bool"}}`, + }, + { + "invalid_copilot_code_review_draft_pr", + `{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":"not_a_bool"}}`, + }, + { + "invalid_copilot_code_review_parameters", + `{"type":"copilot_code_review","parameters":"not_an_object"}`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := &RepositoryRule{} + err := json.Unmarshal([]byte(tt.json), got) + if err == nil { + t.Errorf("Expected error unmarshaling %q, got nil", tt.json) + } + }) + } + }) +} + +func TestRulesetReviewer_UnmarshalJSON(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + json string + wantID int64 + wantErr bool + }{ + { + name: "integer_id", + json: `{"id": 123456, "type": "Team"}`, + wantID: 123456, + wantErr: false, + }, + { + name: "string_id", + json: `{"id": "123456", "type": "Team"}`, + wantID: 123456, + wantErr: false, + }, + { + name: "invalid_string_id", + json: `{"id": "not-a-number", "type": "Team"}`, + wantErr: true, + }, + { + name: "invalid_type_id", + json: `{"id": {}, "type": "Team"}`, + wantErr: true, + }, + { + name: "malformed_json", + json: `{"id":`, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + var got RulesetReviewer + err := json.Unmarshal([]byte(tt.json), &got) + if (err != nil) != tt.wantErr { + t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !tt.wantErr { + if got.GetID() != tt.wantID { + t.Errorf("UnmarshalJSON() got ID = %v, want %v", got.GetID(), tt.wantID) + } + if got.GetType() == nil || *got.GetType() != RulesetReviewerTypeTeam { + t.Errorf("UnmarshalJSON() got Type = %v, want %v", got.GetType(), RulesetReviewerTypeTeam) + } + } + }) + } +} diff --git a/github/scim.go b/github/scim.go new file mode 100644 index 00000000000..6537ab4ff4c --- /dev/null +++ b/github/scim.go @@ -0,0 +1,249 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" +) + +// SCIMService provides access to SCIM related functions in the +// GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/scim?apiVersion=2022-11-28 +type SCIMService service + +// SCIMUserAttributes represents supported SCIM User attributes. +// +// GitHub Enterprise Cloud API docs: https://docs.github.com/rest/scim?apiVersion=2022-11-28#supported-scim-user-attributes +// GitHub Enterprise Server API docs: https://docs.github.com/en/enterprise-server@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#supported-scim-user-attributes +type SCIMUserAttributes struct { + UserName string `json:"userName"` // Configured by the admin. Could be an email, login, or username. (Required.) + Name SCIMUserName `json:"name"` // (Required.) + DisplayName *string `json:"displayName,omitempty"` // The name of the user, suitable for display to end-users. (Optional.) + Emails []*SCIMUserEmail `json:"emails"` // User emails. (Required.) + Schemas []string `json:"schemas,omitempty"` // (Optional.) + ExternalID *string `json:"externalId,omitempty"` // (Optional.) + Groups []string `json:"groups,omitempty"` // (Optional.) + Roles []*SCIMUserRole `json:"roles,omitempty"` // (Optional, GHES only.) + Active *bool `json:"active,omitempty"` // (Optional.) + // Only populated as a result of calling ListSCIMProvisionedIdentitiesOptions or GetSCIMProvisioningInfoForUser: + ID *string `json:"id,omitempty"` + Meta *SCIMMeta `json:"meta,omitempty"` +} + +// SCIMUserName represents SCIM user information. +type SCIMUserName struct { + GivenName string `json:"givenName"` // The first name of the user. (Required.) + FamilyName string `json:"familyName"` // The family name of the user. (Required.) + Formatted *string `json:"formatted,omitempty"` // (Optional.) +} + +// SCIMUserEmail represents SCIM user email. +type SCIMUserEmail struct { + Value string `json:"value"` // (Required.) + Primary *bool `json:"primary,omitempty"` // (Optional.) + Type *string `json:"type,omitempty"` // (Optional.) +} + +// SCIMUserRole is an enterprise-wide role granted to the user. This is only +// supported in GitHub Enterprise Server, and not GitHub Enterprise Cloud. +// See the docs for allowed role names. +// +// https://docs.github.com/en/enterprise-server@latest/rest/enterprise-admin/scim?apiVersion=2022-11-28#provision-a-scim-enterprise-user +type SCIMUserRole struct { + Value string `json:"value"` // (Required.) + Display *string `json:"display,omitempty"` // (Optional.) + Type *string `json:"type,omitempty"` // (Optional.) + Primary *bool `json:"primary,omitempty"` // (Optional.) +} + +// SCIMMeta represents metadata about the SCIM resource. +type SCIMMeta struct { + ResourceType *string `json:"resourceType,omitempty"` + Created *Timestamp `json:"created,omitempty"` + LastModified *Timestamp `json:"lastModified,omitempty"` + Location *string `json:"location,omitempty"` +} + +// SCIMProvisionedIdentities represents the result of calling ListSCIMProvisionedIdentities. +type SCIMProvisionedIdentities struct { + Schemas []string `json:"schemas,omitempty"` + TotalResults *int `json:"totalResults,omitempty"` + ItemsPerPage *int `json:"itemsPerPage,omitempty"` + StartIndex *int `json:"startIndex,omitempty"` + Resources []*SCIMUserAttributes `json:"Resources,omitempty"` +} + +// ListSCIMProvisionedIdentitiesOptions represents options for ListSCIMProvisionedIdentities. +// +// GitHub API docs: https://docs.github.com/rest/scim#list-scim-provisioned-identities--parameters +type ListSCIMProvisionedIdentitiesOptions struct { + StartIndex *int `url:"startIndex,omitempty"` // Used for pagination: the index of the first result to return. (Optional.) + Count *int `url:"count,omitempty"` // Used for pagination: the number of results to return. (Optional.) + // Filter results using the equals query parameter operator (eq). + // You can filter results that are equal to id, userName, emails, and external_id. + // For example, to search for an identity with the userName Octocat, you would use this query: ?filter=userName%20eq%20\"Octocat\". + // To filter results for the identity with the email octocat@github.com, you would use this query: ?filter=emails%20eq%20\"octocat@github.com\". + // (Optional.) + Filter *string `url:"filter,omitempty"` +} + +// ListSCIMProvisionedIdentities lists SCIM provisioned identities. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim?apiVersion=2022-11-28#list-scim-provisioned-identities +// +//meta:operation GET /scim/v2/organizations/{org}/Users +func (s *SCIMService) ListSCIMProvisionedIdentities(ctx context.Context, org string, opts *ListSCIMProvisionedIdentitiesOptions) (*SCIMProvisionedIdentities, *Response, error) { + u := fmt.Sprintf("scim/v2/organizations/%v/Users", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var identities *SCIMProvisionedIdentities + resp, err := s.client.Do(req, &identities) + if err != nil { + return nil, resp, err + } + + return identities, resp, nil +} + +// ProvisionAndInviteSCIMUser provisions organization membership for a user, and sends an activation email to the email address. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim?apiVersion=2022-11-28#provision-and-invite-a-scim-user +// +//meta:operation POST /scim/v2/organizations/{org}/Users +func (s *SCIMService) ProvisionAndInviteSCIMUser(ctx context.Context, org string, body *SCIMUserAttributes) (*SCIMUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/organizations/%v/Users", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var user *SCIMUserAttributes + resp, err := s.client.Do(req, &user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// GetSCIMProvisioningInfoForUser returns SCIM provisioning information for a user. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim?apiVersion=2022-11-28#get-scim-provisioning-information-for-a-user +// +//meta:operation GET /scim/v2/organizations/{org}/Users/{scim_user_id} +func (s *SCIMService) GetSCIMProvisioningInfoForUser(ctx context.Context, org, scimUserID string) (*SCIMUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/organizations/%v/Users/%v", org, scimUserID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var user *SCIMUserAttributes + resp, err := s.client.Do(req, &user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// UpdateProvisionedOrgMembershipRequest represents the body of a request to +// update a provisioned organization membership. +type UpdateProvisionedOrgMembershipRequest struct { + UserName string `json:"userName"` // Configured by the admin. Could be an email, login, or username. (Required.) + Name SCIMUserName `json:"name"` // (Required.) + DisplayName *string `json:"displayName,omitempty"` // The name of the user, suitable for display to end-users. (Optional.) + Emails []*SCIMUserEmail `json:"emails"` // User emails. (Required.) + Schemas []string `json:"schemas,omitempty"` // (Optional.) + ExternalID *string `json:"externalId,omitempty"` // (Optional.) + Groups []string `json:"groups,omitempty"` // (Optional.) + Active *bool `json:"active,omitempty"` // (Optional.) +} + +// UpdateProvisionedOrgMembership updates a provisioned organization membership. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim?apiVersion=2022-11-28#update-a-provisioned-organization-membership +// +//meta:operation PUT /scim/v2/organizations/{org}/Users/{scim_user_id} +func (s *SCIMService) UpdateProvisionedOrgMembership(ctx context.Context, org, scimUserID string, body UpdateProvisionedOrgMembershipRequest) (*SCIMUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/organizations/%v/Users/%v", org, scimUserID) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var user *SCIMUserAttributes + resp, err := s.client.Do(req, &user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// UpdateAttributeForSCIMUserRequest represents the body of a request to update an +// attribute for a SCIM user. +// +// GitHub API docs: https://docs.github.com/rest/scim#update-an-attribute-for-a-scim-user--parameters +type UpdateAttributeForSCIMUserRequest struct { + Schemas []string `json:"schemas,omitempty"` // (Optional.) + Operations []*UpdateAttributeForSCIMUserOperations `json:"Operations"` // Set of operations to be performed. (Required.) +} + +// UpdateAttributeForSCIMUserOperations represents operations for UpdateAttributeForSCIMUser. +type UpdateAttributeForSCIMUserOperations struct { + Op string `json:"op"` // (Required.) + Path *string `json:"path,omitempty"` // (Optional.) + Value json.RawMessage `json:"value,omitempty"` // (Optional.) +} + +// UpdateAttributeForSCIMUser updates an attribute for an SCIM user. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim?apiVersion=2022-11-28#update-an-attribute-for-a-scim-user +// +//meta:operation PATCH /scim/v2/organizations/{org}/Users/{scim_user_id} +func (s *SCIMService) UpdateAttributeForSCIMUser(ctx context.Context, org, scimUserID string, body UpdateAttributeForSCIMUserRequest) (*SCIMUserAttributes, *Response, error) { + u := fmt.Sprintf("scim/v2/organizations/%v/Users/%v", org, scimUserID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var user *SCIMUserAttributes + resp, err := s.client.Do(req, &user) + if err != nil { + return nil, resp, err + } + + return user, resp, nil +} + +// DeleteSCIMUserFromOrg deletes SCIM user from an organization. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim?apiVersion=2022-11-28#delete-a-scim-user-from-an-organization +// +//meta:operation DELETE /scim/v2/organizations/{org}/Users/{scim_user_id} +func (s *SCIMService) DeleteSCIMUserFromOrg(ctx context.Context, org, scimUserID string) (*Response, error) { + u := fmt.Sprintf("scim/v2/organizations/%v/Users/%v", org, scimUserID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/scim_test.go b/github/scim_test.go new file mode 100644 index 00000000000..a26a3cb17c1 --- /dev/null +++ b/github/scim_test.go @@ -0,0 +1,412 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSCIMService_ListSCIMProvisionedIdentities(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/organizations/o/Users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"startIndex": "1", "count": "10", "filter": `userName="Octocat"`}) + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{ + "schemas": [ + "urn:ietf:params:scim:api:messages:2.0:ListResponse" + ], + "totalResults": 1, + "itemsPerPage": 1, + "startIndex": 1, + "Resources": [ + { + "schemas": [ + "urn:ietf:params:scim:schemas:core:2.0:User" + ], + "id": "5fc0c238-1112-11e8-8e45-920c87bdbd75", + "externalId": "00u1dhhb1fkIGP7RL1d8", + "userName": "octocat@github.com", + "displayName": "Mona Octocat", + "name": { + "givenName": "Mona", + "familyName": "Octocat", + "formatted": "Mona Octocat" + }, + "emails": [ + { + "value": "octocat@github.com", + "primary": true + } + ], + "active": true, + "meta": { + "resourceType": "User", + "created": ` + refTimeStr(1136178000) + `, + "lastModified": ` + refTimeStr(1136178001) + `, + "location": "https://api.github.com/scim/v2/organizations/octo-org/Users/5fc0c238-1112-11e8-8e45-920c87bdbd75" + } + } + ] + }`)) + }) + + ctx := t.Context() + opts := &ListSCIMProvisionedIdentitiesOptions{ + StartIndex: Ptr(1), + Count: Ptr(10), + Filter: Ptr(`userName="Octocat"`), + } + identities, _, err := client.SCIM.ListSCIMProvisionedIdentities(ctx, "o", opts) + if err != nil { + t.Errorf("SCIM.ListSCIMProvisionedIdentities returned error: %v", err) + } + + want := SCIMProvisionedIdentities{ + Schemas: []string{"urn:ietf:params:scim:api:messages:2.0:ListResponse"}, + TotalResults: Ptr(1), + ItemsPerPage: Ptr(1), + StartIndex: Ptr(1), + Resources: []*SCIMUserAttributes{ + { + ID: Ptr("5fc0c238-1112-11e8-8e45-920c87bdbd75"), + Meta: &SCIMMeta{ + ResourceType: Ptr("User"), + Created: refTimestamp(1136178000), + LastModified: refTimestamp(1136178001), + Location: Ptr("https://api.github.com/scim/v2/organizations/octo-org/Users/5fc0c238-1112-11e8-8e45-920c87bdbd75"), + }, + UserName: "octocat@github.com", + Name: SCIMUserName{ + GivenName: "Mona", + FamilyName: "Octocat", + Formatted: Ptr("Mona Octocat"), + }, + DisplayName: Ptr("Mona Octocat"), + Emails: []*SCIMUserEmail{ + { + Value: "octocat@github.com", + Primary: Ptr(true), + }, + }, + Schemas: []string{"urn:ietf:params:scim:schemas:core:2.0:User"}, + ExternalID: Ptr("00u1dhhb1fkIGP7RL1d8"), + Groups: nil, + Active: Ptr(true), + }, + }, + } + + if !cmp.Equal(identities, &want) { + diff := cmp.Diff(identities, want) + t.Errorf("SCIM.ListSCIMProvisionedIdentities returned %+v, want %+v: diff %+v", identities, want, diff) + } + + const methodName = "ListSCIMProvisionedIdentities" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SCIM.ListSCIMProvisionedIdentities(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, r, err := client.SCIM.ListSCIMProvisionedIdentities(ctx, "o", nil) + return r, err + }) +} + +func TestSCIMService_ProvisionAndInviteSCIMUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/organizations/o/Users", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusCreated) + fmt.Fprint(w, `{"id":"1234567890","userName":"userName"}`) + }) + + ctx := t.Context() + opts := &SCIMUserAttributes{ + UserName: "userName", + Name: SCIMUserName{ + GivenName: "givenName", + FamilyName: "familyName", + }, + Emails: []*SCIMUserEmail{ + { + Value: "octocat@github.com", + }, + }, + } + user, _, err := client.SCIM.ProvisionAndInviteSCIMUser(ctx, "o", opts) + if err != nil { + t.Errorf("SCIM.ProvisionAndInviteSCIMUser returned error: %v", err) + } + + want := &SCIMUserAttributes{ + ID: Ptr("1234567890"), + UserName: "userName", + } + if !cmp.Equal(user, want) { + t.Errorf("SCIM.ProvisionAndInviteSCIMUser returned %+v, want %+v", user, want) + } + + const methodName = "ProvisionAndInviteSCIMUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SCIM.ProvisionAndInviteSCIMUser(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SCIM.ProvisionAndInviteSCIMUser(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSCIMService_GetSCIMProvisioningInfoForUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{ + "schemas": [ + "urn:ietf:params:scim:schemas:core:2.0:User" + ], + "id": "edefdfedf-050c-11e7-8d32", + "externalId": "a7d0f98382", + "userName": "mona.octocat@okta.example.com", + "displayName": "Mona Octocat", + "name": { + "givenName": "Mona", + "familyName": "Octocat", + "formatted": "Mona Octocat" + }, + "emails": [ + { + "value": "mona.octocat@okta.example.com", + "primary": true + }, + { + "value": "mona@octocat.github.com" + } + ], + "active": true, + "meta": { + "resourceType": "User", + "created": ` + refTimeStr(1136178000) + `, + "lastModified": ` + refTimeStr(1136178001) + `, + "location": "https://api.github.com/scim/v2/organizations/octo-org/Users/edefdfedf-050c-11e7-8d32" + } + }`)) + }) + + ctx := t.Context() + user, _, err := client.SCIM.GetSCIMProvisioningInfoForUser(ctx, "o", "123") + if err != nil { + t.Errorf("SCIM.GetSCIMProvisioningInfoForUser returned error: %v", err) + } + + want := SCIMUserAttributes{ + ID: Ptr("edefdfedf-050c-11e7-8d32"), + Meta: &SCIMMeta{ + ResourceType: Ptr("User"), + Created: refTimestamp(1136178000), + LastModified: refTimestamp(1136178001), + Location: Ptr("https://api.github.com/scim/v2/organizations/octo-org/Users/edefdfedf-050c-11e7-8d32"), + }, + UserName: "mona.octocat@okta.example.com", + Name: SCIMUserName{ + GivenName: "Mona", + FamilyName: "Octocat", + Formatted: Ptr("Mona Octocat"), + }, + DisplayName: Ptr("Mona Octocat"), + Emails: []*SCIMUserEmail{ + { + Value: "mona.octocat@okta.example.com", + Primary: Ptr(true), + }, + { + Value: "mona@octocat.github.com", + }, + }, + Schemas: []string{"urn:ietf:params:scim:schemas:core:2.0:User"}, + ExternalID: Ptr("a7d0f98382"), + Groups: nil, + Active: Ptr(true), + } + + if !cmp.Equal(user, &want) { + diff := cmp.Diff(user, want) + t.Errorf("SCIM.ListSCIMProvisionedIdentities returned %+v, want %+v: diff %+v", user, want, diff) + } + + const methodName = "GetSCIMProvisioningInfoForUser" + testBadOptions(t, methodName, func() error { + _, _, err := client.SCIM.GetSCIMProvisioningInfoForUser(ctx, "\n", "123") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, r, err := client.SCIM.GetSCIMProvisioningInfoForUser(ctx, "o", "123") + return r, err + }) +} + +func TestSCIMService_UpdateProvisionedOrgMembership(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + body := UpdateProvisionedOrgMembershipRequest{ + UserName: "userName", + Name: SCIMUserName{ + GivenName: "givenName", + FamilyName: "familyName", + }, + Emails: []*SCIMUserEmail{ + { + Value: "octocat@github.com", + }, + }, + } + + mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, body) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{"id":"123","userName":"userName"}`) + }) + + ctx := t.Context() + user, _, err := client.SCIM.UpdateProvisionedOrgMembership(ctx, "o", "123", body) + if err != nil { + t.Errorf("SCIM.UpdateProvisionedOrgMembership returned error: %v", err) + } + + want := &SCIMUserAttributes{ + ID: Ptr("123"), + UserName: "userName", + } + if !cmp.Equal(user, want) { + t.Errorf("SCIM.UpdateProvisionedOrgMembership returned %+v, want %+v", user, want) + } + + const methodName = "UpdateProvisionedOrgMembership" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SCIM.UpdateProvisionedOrgMembership(ctx, "\n", "123", body) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SCIM.UpdateProvisionedOrgMembership(ctx, "o", "123", body) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSCIMService_UpdateAttributeForSCIMUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + body := UpdateAttributeForSCIMUserRequest{ + Schemas: []string{SCIMSchemasURINamespacesPatchOp}, + Operations: []*UpdateAttributeForSCIMUserOperations{ + { + Op: "replace", + Path: Ptr("displayName"), + Value: json.RawMessage(`"NewDisplayName"`), + }, + }, + } + + // Verify the wire format against an expected map (not the same struct) so + // that JSON key names are pinned. Per the SCIM PatchOp schema the request + // uses a capitalized "Operations" key (and lowercase "schemas"). + wantBody := map[string]any{ + "schemas": []any{SCIMSchemasURINamespacesPatchOp}, + "Operations": []any{ + map[string]any{ + "op": "replace", + "path": "displayName", + "value": "NewDisplayName", + }, + }, + } + + mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, wantBody) + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{"id":"123","userName":"userName"}`) + }) + + ctx := t.Context() + user, _, err := client.SCIM.UpdateAttributeForSCIMUser(ctx, "o", "123", body) + if err != nil { + t.Errorf("SCIM.UpdateAttributeForSCIMUser returned error: %v", err) + } + + want := &SCIMUserAttributes{ + ID: Ptr("123"), + UserName: "userName", + } + if !cmp.Equal(user, want) { + t.Errorf("SCIM.UpdateAttributeForSCIMUser returned %+v, want %+v", user, want) + } + + const methodName = "UpdateAttributeForSCIMUser" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SCIM.UpdateAttributeForSCIMUser(ctx, "\n", "123", body) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SCIM.UpdateAttributeForSCIMUser(ctx, "o", "123", body) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSCIMService_DeleteSCIMUserFromOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/scim/v2/organizations/o/Users/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.SCIM.DeleteSCIMUserFromOrg(ctx, "o", "123") + if err != nil { + t.Errorf("SCIM.DeleteSCIMUserFromOrg returned error: %v", err) + } + + const methodName = "DeleteSCIMUserFromOrg" + testBadOptions(t, methodName, func() error { + _, err := client.SCIM.DeleteSCIMUserFromOrg(ctx, "\n", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.SCIM.DeleteSCIMUserFromOrg(ctx, "o", "123") + }) +} diff --git a/github/search.go b/github/search.go index 24156f31831..a25e4010c60 100644 --- a/github/search.go +++ b/github/search.go @@ -9,6 +9,7 @@ import ( "context" "fmt" "strconv" + "strings" qs "github.com/google/go-querystring/query" ) @@ -19,12 +20,19 @@ import ( // Each method takes a query string defining the search keywords and any search qualifiers. // For example, when searching issues, the query "gopher is:issue language:go" will search // for issues containing the word "gopher" in Go repositories. The method call -// opts := &github.SearchOptions{Sort: "created", Order: "asc"} -// cl.Search.Issues(ctx, "gopher is:issue language:go", opts) +// +// opts := &github.SearchOptions{Sort: "created", Order: "asc"} +// cl.Search.Issues(ctx, "gopher is:issue language:go", opts) +// // will search for such issues, sorting by creation date in ascending order // (i.e., oldest first). // -// GitHub API docs: https://developer.github.com/v3/search/ +// If query includes multiple conditions, it MUST NOT include "+" as the condition separator. +// You have to use " " as the separator instead. +// For example, querying with "language:c++" and "leveldb", then query should be +// "language:c++ leveldb" but not "language:c+++leveldb". +// +// GitHub API docs: https://docs.github.com/rest/search?apiVersion=2022-11-28 type SearchService service // SearchOptions specifies optional parameters to the SearchService methods. @@ -46,6 +54,13 @@ type SearchOptions struct { // Whether to retrieve text match metadata with a query TextMatch bool `url:"-"` + // Whether to enable advanced search for issues + AdvancedSearch *bool `url:"advanced_search,omitempty"` + + // The type of search to perform on issues. Possible values are: + // semantic, hybrid. Default is lexical search. + SearchType string `url:"search_type,omitempty"` + ListOptions } @@ -57,18 +72,62 @@ type searchParameters struct { // RepositoriesSearchResult represents the result of a repositories search. type RepositoriesSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Repositories []Repository `json:"items,omitempty"` + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Repositories []*Repository `json:"items,omitempty"` } // Repositories searches repositories via various criteria. // -// GitHub API docs: https://developer.github.com/v3/search/#search-repositories -func (s *SearchService) Repositories(ctx context.Context, query string, opt *SearchOptions) (*RepositoriesSearchResult, *Response, error) { - result := new(RepositoriesSearchResult) - resp, err := s.search(ctx, "repositories", &searchParameters{Query: query}, opt, result) - return result, resp, err +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-repositories +// +//meta:operation GET /search/repositories +func (s *SearchService) Repositories(ctx context.Context, query string, opts *SearchOptions) (*RepositoriesSearchResult, *Response, error) { + var result *RepositoriesSearchResult + resp, err := s.search(ctx, "repositories", &searchParameters{Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// TopicsSearchResult represents the result of a topics search. +type TopicsSearchResult struct { + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Topics []*TopicResult `json:"items,omitempty"` +} + +// TopicResult represents a topic search result. +type TopicResult struct { + Name *string `json:"name,omitempty"` + DisplayName *string `json:"display_name,omitempty"` + ShortDescription *string `json:"short_description,omitempty"` + Description *string `json:"description,omitempty"` + CreatedBy *string `json:"created_by,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *string `json:"updated_at,omitempty"` + Featured *bool `json:"featured,omitempty"` + Curated *bool `json:"curated,omitempty"` + Score *float64 `json:"score,omitempty"` +} + +// Topics finds topics via various criteria. Results are sorted by best match. +// Please see https://help.github.com/articles/searching-topics for more +// information about search qualifiers. +// +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-topics +// +//meta:operation GET /search/topics +func (s *SearchService) Topics(ctx context.Context, query string, opts *SearchOptions) (*TopicsSearchResult, *Response, error) { + var result *TopicsSearchResult + resp, err := s.search(ctx, "topics", &searchParameters{Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil } // CommitsSearchResult represents the result of a commits search. @@ -95,43 +154,62 @@ type CommitResult struct { // Commits searches commits via various criteria. // -// GitHub API docs: https://developer.github.com/v3/search/#search-commits -func (s *SearchService) Commits(ctx context.Context, query string, opt *SearchOptions) (*CommitsSearchResult, *Response, error) { - result := new(CommitsSearchResult) - resp, err := s.search(ctx, "commits", &searchParameters{Query: query}, opt, result) - return result, resp, err +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-commits +// +//meta:operation GET /search/commits +func (s *SearchService) Commits(ctx context.Context, query string, opts *SearchOptions) (*CommitsSearchResult, *Response, error) { + var result *CommitsSearchResult + resp, err := s.search(ctx, "commits", &searchParameters{Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil } // IssuesSearchResult represents the result of an issues search. type IssuesSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Issues []Issue `json:"items,omitempty"` + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + SearchType *string `json:"search_type,omitempty"` + Issues []*Issue `json:"items,omitempty"` } // Issues searches issues via various criteria. // -// GitHub API docs: https://developer.github.com/v3/search/#search-issues -func (s *SearchService) Issues(ctx context.Context, query string, opt *SearchOptions) (*IssuesSearchResult, *Response, error) { - result := new(IssuesSearchResult) - resp, err := s.search(ctx, "issues", &searchParameters{Query: query}, opt, result) - return result, resp, err +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-issues-and-pull-requests +// +//meta:operation GET /search/issues +func (s *SearchService) Issues(ctx context.Context, query string, opts *SearchOptions) (*IssuesSearchResult, *Response, error) { + var result *IssuesSearchResult + resp, err := s.search(ctx, "issues", &searchParameters{Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil } // UsersSearchResult represents the result of a users search. type UsersSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Users []User `json:"items,omitempty"` + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + Users []*User `json:"items,omitempty"` } // Users searches users via various criteria. // -// GitHub API docs: https://developer.github.com/v3/search/#search-users -func (s *SearchService) Users(ctx context.Context, query string, opt *SearchOptions) (*UsersSearchResult, *Response, error) { - result := new(UsersSearchResult) - resp, err := s.search(ctx, "users", &searchParameters{Query: query}, opt, result) - return result, resp, err +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-users +// +//meta:operation GET /search/users +func (s *SearchService) Users(ctx context.Context, query string, opts *SearchOptions) (*UsersSearchResult, *Response, error) { + var result *UsersSearchResult + resp, err := s.search(ctx, "users", &searchParameters{Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil } // Match represents a single text match. @@ -140,13 +218,13 @@ type Match struct { Indices []int `json:"indices,omitempty"` } -// TextMatch represents a text match for a SearchResult +// TextMatch represents a text match for a SearchResult. type TextMatch struct { - ObjectURL *string `json:"object_url,omitempty"` - ObjectType *string `json:"object_type,omitempty"` - Property *string `json:"property,omitempty"` - Fragment *string `json:"fragment,omitempty"` - Matches []Match `json:"matches,omitempty"` + ObjectURL *string `json:"object_url,omitempty"` + ObjectType *string `json:"object_type,omitempty"` + Property *string `json:"property,omitempty"` + Fragment *string `json:"fragment,omitempty"` + Matches []*Match `json:"matches,omitempty"` } func (tm TextMatch) String() string { @@ -155,19 +233,19 @@ func (tm TextMatch) String() string { // CodeSearchResult represents the result of a code search. type CodeSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - CodeResults []CodeResult `json:"items,omitempty"` + Total *int `json:"total_count,omitempty"` + IncompleteResults *bool `json:"incomplete_results,omitempty"` + CodeResults []*CodeResult `json:"items,omitempty"` } // CodeResult represents a single search result. type CodeResult struct { - Name *string `json:"name,omitempty"` - Path *string `json:"path,omitempty"` - SHA *string `json:"sha,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - Repository *Repository `json:"repository,omitempty"` - TextMatches []TextMatch `json:"text_matches,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + SHA *string `json:"sha,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + Repository *Repository `json:"repository,omitempty"` + TextMatches []*TextMatch `json:"text_matches,omitempty"` } func (c CodeResult) String() string { @@ -176,11 +254,17 @@ func (c CodeResult) String() string { // Code searches code via various criteria. // -// GitHub API docs: https://developer.github.com/v3/search/#search-code -func (s *SearchService) Code(ctx context.Context, query string, opt *SearchOptions) (*CodeSearchResult, *Response, error) { - result := new(CodeSearchResult) - resp, err := s.search(ctx, "code", &searchParameters{Query: query}, opt, result) - return result, resp, err +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-code +// +//meta:operation GET /search/code +func (s *SearchService) Code(ctx context.Context, query string, opts *SearchOptions) (*CodeSearchResult, *Response, error) { + var result *CodeSearchResult + resp, err := s.search(ctx, "code", &searchParameters{Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil } // LabelsSearchResult represents the result of a code search. @@ -207,11 +291,17 @@ func (l LabelResult) String() string { // Labels searches labels in the repository with ID repoID via various criteria. // -// GitHub API docs: https://developer.github.com/v3/search/#search-labels -func (s *SearchService) Labels(ctx context.Context, repoID int64, query string, opt *SearchOptions) (*LabelsSearchResult, *Response, error) { - result := new(LabelsSearchResult) - resp, err := s.search(ctx, "labels", &searchParameters{RepositoryID: &repoID, Query: query}, opt, result) - return result, resp, err +// GitHub API docs: https://docs.github.com/rest/search/search?apiVersion=2022-11-28#search-labels +// +//meta:operation GET /search/labels +func (s *SearchService) Labels(ctx context.Context, repoID int64, query string, opts *SearchOptions) (*LabelsSearchResult, *Response, error) { + var result *LabelsSearchResult + resp, err := s.search(ctx, "labels", &searchParameters{RepositoryID: &repoID, Query: query}, opts, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil } // Helper function that executes search queries against different @@ -219,40 +309,41 @@ func (s *SearchService) Labels(ctx context.Context, repoID int64, query string, // // If searchParameters.Query includes multiple condition, it MUST NOT include "+" as condition separator. // For example, querying with "language:c++" and "leveldb", then searchParameters.Query should be "language:c++ leveldb" but not "language:c+++leveldb". -func (s *SearchService) search(ctx context.Context, searchType string, parameters *searchParameters, opt *SearchOptions, result interface{}) (*Response, error) { - params, err := qs.Values(opt) +func (s *SearchService) search(ctx context.Context, searchType string, parameters *searchParameters, opts *SearchOptions, result any) (*Response, error) { + params, err := qs.Values(opts) if err != nil { return nil, err } + if parameters.RepositoryID != nil { params.Set("repository_id", strconv.FormatInt(*parameters.RepositoryID, 10)) } params.Set("q", parameters.Query) - u := fmt.Sprintf("search/%s?%s", searchType, params.Encode()) + u := fmt.Sprintf("search/%v?%v", searchType, params.Encode()) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, err } - - switch { - case searchType == "commits": + var acceptHeaders []string + switch searchType { + case "commits": // Accept header for search commits preview endpoint - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCommitSearchPreview) - case searchType == "repositories": + acceptHeaders = append(acceptHeaders, mediaTypeCommitSearchPreview) + case "topics", "repositories": // Accept header for search repositories based on topics preview endpoint - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTopicsPreview) - case searchType == "labels": - // Accept header for search labels based on label description preview endpoint. - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) - case opt != nil && opt.TextMatch: - // Accept header defaults to "application/vnd.github.v3+json" - // We change it here to fetch back text-match metadata - req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") + acceptHeaders = append(acceptHeaders, mediaTypeTopicsPreview) + case "issues": + // Accept header for search issues based on reactions preview endpoint + acceptHeaders = append(acceptHeaders, mediaTypeReactionsPreview) + } + // https://docs.github.com/rest/search?apiVersion=2022-11-28#search-repositories + // Accept header defaults to "application/vnd.github.v3+json" + // We change it here to fetch back text-match metadata + if opts != nil && opts.TextMatch { + acceptHeaders = append(acceptHeaders, "application/vnd.github.v3.text-match+json") } + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - return s.client.Do(ctx, req, result) + return s.client.Do(req, result) } diff --git a/github/search_test.go b/github/search_test.go index 4b686884e37..c7909134177 100644 --- a/github/search_test.go +++ b/github/search_test.go @@ -6,17 +6,17 @@ package github import ( - "context" "fmt" "net/http" - "reflect" - + "strings" "testing" + + "github.com/google/go-cmp/cmp" ) func TestSearchService_Repositories(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/repositories", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -32,24 +32,179 @@ func TestSearchService_Repositories(t *testing.T) { }) opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} - result, _, err := client.Search.Repositories(context.Background(), "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Repositories(ctx, "blah", opts) if err != nil { t.Errorf("Search.Repositories returned error: %v", err) } want := &RepositoriesSearchResult{ - Total: Int(4), - IncompleteResults: Bool(false), - Repositories: []Repository{{ID: Int64(1)}, {ID: Int64(2)}}, + Total: Ptr(4), + IncompleteResults: Ptr(false), + Repositories: []*Repository{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Repositories returned %+v, want %+v", result, want) } + const methodName = "Repositories" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Repositories(ctx, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Repositories_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Repositories" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Repositories(ctx, "\n", nil) + return err + }) +} + +func TestSearchService_RepositoriesTextMatch(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/search/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + textMatchResponse := ` + { + "total_count": 1, + "incomplete_results": false, + "items": [ + { + "name":"gopher1" + } + ] + } + ` + list := strings.Split(r.Header.Get("Accept"), ",") + aMap := make(map[string]struct{}) + for _, s := range list { + aMap[strings.TrimSpace(s)] = struct{}{} + } + if _, ok := aMap["application/vnd.github.v3.text-match+json"]; ok { + textMatchResponse = ` + { + "total_count": 1, + "incomplete_results": false, + "items": [ + { + "name":"gopher1", + "text_matches": [ + { + "fragment": "I'm afraid my friend what you have found\nIs a gopher who lives to feed", + "matches": [ + { + "text": "gopher", + "indices": [ + 14, + 21 + ] + } + ] + } + ] + } + ] + } + ` + } + + fmt.Fprint(w, textMatchResponse) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}, TextMatch: true} + ctx := t.Context() + result, _, err := client.Search.Repositories(ctx, "blah", opts) + if err != nil { + t.Errorf("Search.Repositories returned error: %v", err) + } + + wantedRepoResult := &Repository{ + Name: Ptr("gopher1"), + TextMatches: []*TextMatch{ + { + Fragment: Ptr("I'm afraid my friend what you have found\nIs a gopher who lives to feed"), + Matches: []*Match{{Text: Ptr("gopher"), Indices: []int{14, 21}}}, + }, + }, + } + + want := &RepositoriesSearchResult{ + Total: Ptr(1), + IncompleteResults: Ptr(false), + Repositories: []*Repository{wantedRepoResult}, + } + if !cmp.Equal(result, want) { + t.Errorf("Search.Repo returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Topics(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/search/topics", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "page": "2", + "per_page": "2", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": false, "items": [{"name":"blah"},{"name":"blahblah"}]}`) + }) + + opts := &SearchOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} + ctx := t.Context() + result, _, err := client.Search.Topics(ctx, "blah", opts) + if err != nil { + t.Errorf("Search.Topics returned error: %v", err) + } + + want := &TopicsSearchResult{ + Total: Ptr(4), + IncompleteResults: Ptr(false), + Topics: []*TopicResult{{Name: Ptr("blah")}, {Name: Ptr("blahblah")}}, + } + if !cmp.Equal(result, want) { + t.Errorf("Search.Topics returned %+v, want %+v", result, want) + } + const methodName = "Topics" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Topics(ctx, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Topics_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Topics" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Topics(ctx, "\n", nil) + return err + }) } func TestSearchService_Commits(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/commits", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -63,24 +218,46 @@ func TestSearchService_Commits(t *testing.T) { }) opts := &SearchOptions{Sort: "author-date", Order: "desc"} - result, _, err := client.Search.Commits(context.Background(), "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Commits(ctx, "blah", opts) if err != nil { t.Errorf("Search.Commits returned error: %v", err) } want := &CommitsSearchResult{ - Total: Int(4), - IncompleteResults: Bool(false), - Commits: []*CommitResult{{SHA: String("random_hash1")}, {SHA: String("random_hash2")}}, + Total: Ptr(4), + IncompleteResults: Ptr(false), + Commits: []*CommitResult{{SHA: Ptr("random_hash1")}, {SHA: Ptr("random_hash2")}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Commits returned %+v, want %+v", result, want) } + const methodName = "Commits" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Commits(ctx, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Commits_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Commits" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Commits(ctx, "\n", nil) + return err + }) } func TestSearchService_Issues(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -96,24 +273,117 @@ func TestSearchService_Issues(t *testing.T) { }) opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} - result, _, err := client.Search.Issues(context.Background(), "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Issues(ctx, "blah", opts) if err != nil { t.Errorf("Search.Issues returned error: %v", err) } want := &IssuesSearchResult{ - Total: Int(4), - IncompleteResults: Bool(true), - Issues: []Issue{{Number: Int(1)}, {Number: Int(2)}}, + Total: Ptr(4), + IncompleteResults: Ptr(true), + Issues: []*Issue{{Number: Ptr(1)}, {Number: Ptr(2)}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Issues returned %+v, want %+v", result, want) } + const methodName = "Issues" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Issues(ctx, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Issues_advancedSearch(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/search/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "forks", + "order": "desc", + "page": "2", + "per_page": "2", + "advanced_search": "true", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": true, "items": [{"number":1},{"number":2}]}`) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}, AdvancedSearch: Ptr(true)} + ctx := t.Context() + result, _, err := client.Search.Issues(ctx, "blah", opts) + if err != nil { + t.Errorf("Search.Issues_advancedSearch returned error: %v", err) + } + + want := &IssuesSearchResult{ + Total: Ptr(4), + IncompleteResults: Ptr(true), + Issues: []*Issue{{Number: Ptr(1)}, {Number: Ptr(2)}}, + } + if !cmp.Equal(result, want) { + t.Errorf("Search.Issues_advancedSearch returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Issues_searchType(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/search/issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "q": "blah", + "sort": "forks", + "order": "desc", + "page": "2", + "per_page": "2", + "search_type": "hybrid", + }) + + fmt.Fprint(w, `{"total_count": 4, "incomplete_results": true, "search_type": "hybrid", "items": [{"number":1},{"number":2}]}`) + }) + + opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}, SearchType: "hybrid"} + ctx := t.Context() + result, _, err := client.Search.Issues(ctx, "blah", opts) + if err != nil { + t.Errorf("Search.Issues_searchType returned error: %v", err) + } + + want := &IssuesSearchResult{ + Total: Ptr(4), + IncompleteResults: Ptr(true), + SearchType: Ptr("hybrid"), + Issues: []*Issue{{Number: Ptr(1)}, {Number: Ptr(2)}}, + } + if !cmp.Equal(result, want) { + t.Errorf("Search.Issues_searchType returned %+v, want %+v", result, want) + } +} + +func TestSearchService_Issues_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Issues" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Issues(ctx, "\n", nil) + return err + }) } func TestSearchService_Issues_withQualifiersNoOpts(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) const q = "gopher is:issue label:bug language:c++ pushed:>=2018-01-01 stars:>=200" @@ -129,7 +399,8 @@ func TestSearchService_Issues_withQualifiersNoOpts(t *testing.T) { }) opts := &SearchOptions{} - result, _, err := client.Search.Issues(context.Background(), q, opts) + ctx := t.Context() + result, _, err := client.Search.Issues(ctx, q, opts) if err != nil { t.Errorf("Search.Issues returned error: %v", err) } @@ -139,18 +410,18 @@ func TestSearchService_Issues_withQualifiersNoOpts(t *testing.T) { } want := &IssuesSearchResult{ - Total: Int(4), - IncompleteResults: Bool(true), - Issues: []Issue{{Number: Int(1)}, {Number: Int(2)}}, + Total: Ptr(4), + IncompleteResults: Ptr(true), + Issues: []*Issue{{Number: Ptr(1)}, {Number: Ptr(2)}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Issues returned %+v, want %+v", result, want) } } func TestSearchService_Issues_withQualifiersAndOpts(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) const q = "gopher is:issue label:bug language:c++ pushed:>=2018-01-01 stars:>=200" @@ -167,7 +438,8 @@ func TestSearchService_Issues_withQualifiersAndOpts(t *testing.T) { }) opts := &SearchOptions{Sort: "forks"} - result, _, err := client.Search.Issues(context.Background(), q, opts) + ctx := t.Context() + result, _, err := client.Search.Issues(ctx, q, opts) if err != nil { t.Errorf("Search.Issues returned error: %v", err) } @@ -177,18 +449,18 @@ func TestSearchService_Issues_withQualifiersAndOpts(t *testing.T) { } want := &IssuesSearchResult{ - Total: Int(4), - IncompleteResults: Bool(true), - Issues: []Issue{{Number: Int(1)}, {Number: Int(2)}}, + Total: Ptr(4), + IncompleteResults: Ptr(true), + Issues: []*Issue{{Number: Ptr(1)}, {Number: Ptr(2)}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Issues returned %+v, want %+v", result, want) } } func TestSearchService_Users(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/users", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -204,24 +476,46 @@ func TestSearchService_Users(t *testing.T) { }) opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} - result, _, err := client.Search.Users(context.Background(), "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Users(ctx, "blah", opts) if err != nil { - t.Errorf("Search.Issues returned error: %v", err) + t.Errorf("Search.Users returned error: %v", err) } want := &UsersSearchResult{ - Total: Int(4), - IncompleteResults: Bool(false), - Users: []User{{ID: Int64(1)}, {ID: Int64(2)}}, + Total: Ptr(4), + IncompleteResults: Ptr(false), + Users: []*User{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Users returned %+v, want %+v", result, want) } + const methodName = "Users" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Users(ctx, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Users_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Users" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Users(ctx, "\n", nil) + return err + }) } func TestSearchService_Code(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/code", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -237,24 +531,46 @@ func TestSearchService_Code(t *testing.T) { }) opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} - result, _, err := client.Search.Code(context.Background(), "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Code(ctx, "blah", opts) if err != nil { t.Errorf("Search.Code returned error: %v", err) } want := &CodeSearchResult{ - Total: Int(4), - IncompleteResults: Bool(false), - CodeResults: []CodeResult{{Name: String("1")}, {Name: String("2")}}, + Total: Ptr(4), + IncompleteResults: Ptr(false), + CodeResults: []*CodeResult{{Name: Ptr("1")}, {Name: Ptr("2")}}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Code returned %+v, want %+v", result, want) } + const methodName = "Code" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Code(ctx, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Code_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Code" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Code(ctx, "\n", nil) + return err + }) } func TestSearchService_CodeTextMatch(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/code", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -289,33 +605,35 @@ func TestSearchService_CodeTextMatch(t *testing.T) { }) opts := &SearchOptions{Sort: "forks", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}, TextMatch: true} - result, _, err := client.Search.Code(context.Background(), "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Code(ctx, "blah", opts) if err != nil { t.Errorf("Search.Code returned error: %v", err) } - wantedCodeResult := CodeResult{ - Name: String("gopher1"), - TextMatches: []TextMatch{{ - Fragment: String("I'm afraid my friend what you have found\nIs a gopher who lives to feed"), - Matches: []Match{{Text: String("gopher"), Indices: []int{14, 21}}}, - }, + wantedCodeResult := &CodeResult{ + Name: Ptr("gopher1"), + TextMatches: []*TextMatch{ + { + Fragment: Ptr("I'm afraid my friend what you have found\nIs a gopher who lives to feed"), + Matches: []*Match{{Text: Ptr("gopher"), Indices: []int{14, 21}}}, + }, }, } want := &CodeSearchResult{ - Total: Int(1), - IncompleteResults: Bool(false), - CodeResults: []CodeResult{wantedCodeResult}, + Total: Ptr(1), + IncompleteResults: Ptr(false), + CodeResults: []*CodeResult{wantedCodeResult}, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Code returned %+v, want %+v", result, want) } } func TestSearchService_Labels(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/search/labels", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -332,20 +650,42 @@ func TestSearchService_Labels(t *testing.T) { }) opts := &SearchOptions{Sort: "updated", Order: "desc", ListOptions: ListOptions{Page: 2, PerPage: 2}} - result, _, err := client.Search.Labels(context.Background(), 1234, "blah", opts) + ctx := t.Context() + result, _, err := client.Search.Labels(ctx, 1234, "blah", opts) if err != nil { - t.Errorf("Search.Code returned error: %v", err) + t.Errorf("Search.Labels returned error: %v", err) } want := &LabelsSearchResult{ - Total: Int(4), - IncompleteResults: Bool(false), + Total: Ptr(4), + IncompleteResults: Ptr(false), Labels: []*LabelResult{ - {ID: Int64(1234), Name: String("bug"), Description: String("some text")}, - {ID: Int64(4567), Name: String("feature")}, + {ID: Ptr(int64(1234)), Name: Ptr("bug"), Description: Ptr("some text")}, + {ID: Ptr(int64(4567)), Name: Ptr("feature")}, }, } - if !reflect.DeepEqual(result, want) { + if !cmp.Equal(result, want) { t.Errorf("Search.Labels returned %+v, want %+v", result, want) } + const methodName = "Labels" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Search.Labels(ctx, 1234, "blah", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSearchService_Labels_coverage(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + + const methodName = "Labels" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Search.Labels(ctx, -1234, "\n", nil) + return err + }) } diff --git a/github/secret_scanning.go b/github/secret_scanning.go new file mode 100644 index 00000000000..e0013593de6 --- /dev/null +++ b/github/secret_scanning.go @@ -0,0 +1,383 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SecretScanningService handles communication with the secret scanning related +// methods of the GitHub API. +type SecretScanningService service + +// SecretScanningAlert represents a GitHub secret scanning alert. +type SecretScanningAlert struct { + Number *int `json:"number,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + LocationsURL *string `json:"locations_url,omitempty"` + FirstLocationDetected *SecretScanningAlertLocationDetails `json:"first_location_detected,omitempty"` + HasMoreLocations *bool `json:"has_more_locations,omitempty"` + State *string `json:"state,omitempty"` + Resolution *string `json:"resolution,omitempty"` + ResolvedAt *Timestamp `json:"resolved_at,omitempty"` + ResolvedBy *User `json:"resolved_by,omitempty"` + SecretType *string `json:"secret_type,omitempty"` + SecretTypeDisplayName *string `json:"secret_type_display_name,omitempty"` + Secret *string `json:"secret,omitempty"` + Repository *Repository `json:"repository,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + IsBase64Encoded *bool `json:"is_base64_encoded,omitempty"` + MultiRepo *bool `json:"multi_repo,omitempty"` + PubliclyLeaked *bool `json:"publicly_leaked,omitempty"` + PushProtectionBypassed *bool `json:"push_protection_bypassed,omitempty"` + PushProtectionBypassedBy *User `json:"push_protection_bypassed_by,omitempty"` + PushProtectionBypassedAt *Timestamp `json:"push_protection_bypassed_at,omitempty"` + ResolutionComment *string `json:"resolution_comment,omitempty"` + PushProtectionBypassRequestComment *string `json:"push_protection_bypass_request_comment,omitempty"` + PushProtectionBypassRequestHTMLURL *string `json:"push_protection_bypass_request_html_url,omitempty"` + PushProtectionBypassRequestReviewer *User `json:"push_protection_bypass_request_reviewer,omitempty"` + PushProtectionBypassRequestReviewerComment *string `json:"push_protection_bypass_request_reviewer_comment,omitempty"` + Validity *string `json:"validity,omitempty"` +} + +// SecretScanningAlertLocation represents the location for a secret scanning alert. +type SecretScanningAlertLocation struct { + Type *string `json:"type,omitempty"` + Details *SecretScanningAlertLocationDetails `json:"details,omitempty"` +} + +// SecretScanningAlertLocationDetails represents the location details for a secret scanning alert. +type SecretScanningAlertLocationDetails struct { + Path *string `json:"path,omitempty"` + Startline *int `json:"start_line,omitempty"` + EndLine *int `json:"end_line,omitempty"` + StartColumn *int `json:"start_column,omitempty"` + EndColumn *int `json:"end_column,omitempty"` + BlobSHA *string `json:"blob_sha,omitempty"` + BlobURL *string `json:"blob_url,omitempty"` + CommitSHA *string `json:"commit_sha,omitempty"` + CommitURL *string `json:"commit_url,omitempty"` + PullRequestCommentURL *string `json:"pull_request_comment_url,omitempty"` +} + +// SecretScanningAlertListOptions specifies optional parameters to the SecretScanningService.ListAlertsForEnterprise method. +type SecretScanningAlertListOptions struct { + // State of the secret scanning alerts to list. Set to open or resolved to only list secret scanning alerts in a specific state. + State string `url:"state,omitempty"` + + // A comma-separated list of secret types to return. By default all secret types are returned. + SecretType string `url:"secret_type,omitempty"` + + // A comma-separated list of resolutions. Only secret scanning alerts with one of these resolutions are listed. + // Valid resolutions are false_positive, wont_fix, revoked, pattern_edited, pattern_deleted or used_in_tests. + Resolution string `url:"resolution,omitempty"` + + // A comma-separated list of validities that, when present, will return alerts that match the validities in this list. + // Valid options are active, inactive, and unknown. + Validity string `url:"validity,omitempty"` + + // A boolean value representing whether or not to filter alerts by the publicly-leaked tag being present. Default: false. + IsPubliclyLeaked bool `url:"is_publicly_leaked,omitempty"` + + // A boolean value representing whether or not to filter alerts by the multi-repo tag being present. Default: false. + IsMultiRepo bool `url:"is_multi_repo,omitempty"` + + // The direction to sort the results by. Possible values are: asc, desc. Default: desc. + Direction string `url:"direction,omitempty"` + + // The property by which to sort the results. Possible values are: created, updated. Default: created. + Sort string `url:"sort,omitempty"` + + ListCursorOptions + + // List options can vary on the Enterprise type. + // On Enterprise Cloud, Secret Scan alerts support requesting by page number + // along with providing a cursor for an "after" param. + // See: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning?apiVersion=2022-11-28#list-secret-scanning-alerts-for-an-organization + // Whereas on Enterprise Server, pagination is by index. + // See: https://docs.github.com/enterprise-server@3.6/rest/secret-scanning?apiVersion=2022-11-28#list-secret-scanning-alerts-for-an-organization + ListOptions +} + +// SecretScanningAlertUpdateOptions specifies optional parameters to the SecretScanningService.UpdateAlert method. +type SecretScanningAlertUpdateOptions struct { + // State is required and sets the state of the secret scanning alert. + // Can be either "open" or "resolved". + // You must provide resolution when you set the state to "resolved". + State string `json:"state"` + + // Required when the state is "resolved" and represents the reason for resolving the alert. + // Can be one of: "false_positive", "wont_fix", "revoked", or "used_in_tests". + Resolution *string `json:"resolution,omitempty"` + + // An optional comment when closing an alert. + ResolutionComment *string `json:"resolution_comment,omitempty"` +} + +// PushProtectionBypassRequest represents the parameters for CreatePushProtectionBypass. +type PushProtectionBypassRequest struct { + // The reason for bypassing push protection. + // Can be one of: false_positive, used_in_tests, will_fix_later + Reason string `json:"reason"` + // PlaceholderID is an identifier used for the bypass request. + // GitHub Secret Scanning provides you with a unique PlaceholderID associated with that specific blocked push. + PlaceholderID string `json:"placeholder_id"` +} + +// PushProtectionBypass represents the response from CreatePushProtectionBypass. +type PushProtectionBypass struct { + // The reason for bypassing push protection. + Reason string `json:"reason"` + // The time that the bypass will expire in ISO 8601 format. + ExpireAt *Timestamp `json:"expire_at"` + // The token type this bypass is for. + TokenType string `json:"token_type"` +} + +// SecretsScan represents the common fields for a secret scanning scan. +type SecretsScan struct { + Type string `json:"type"` + Status string `json:"status"` + CompletedAt *Timestamp `json:"completed_at,omitempty"` + StartedAt *Timestamp `json:"started_at,omitempty"` +} + +// CustomPatternBackfillScan represents a scan with an associated custom pattern. +type CustomPatternBackfillScan struct { + SecretsScan + PatternSlug *string `json:"pattern_slug,omitempty"` + PatternScope *string `json:"pattern_scope,omitempty"` +} + +// SecretScanningScanHistory is the top-level struct for the secret scanning API response. +type SecretScanningScanHistory struct { + // Information on incremental scan performed by secret scanning on the repository. + IncrementalScans []*SecretsScan `json:"incremental_scans,omitempty"` + // Information on backfill scan performed by secret scanning on the repository. + BackfillScans []*SecretsScan `json:"backfill_scans,omitempty"` + // Information on pattern update scan performed by secret scanning on the repository. + PatternUpdateScans []*SecretsScan `json:"pattern_update_scans,omitempty"` + // Information on custom pattern backfill scan performed by secret scanning on the repository. + CustomPatternBackfillScans []*CustomPatternBackfillScan `json:"custom_pattern_backfill_scans,omitempty"` +} + +// ListAlertsForEnterprise lists secret scanning alerts for eligible repositories in an enterprise, from newest to oldest. +// +// To use this endpoint, you must be a member of the enterprise, and you must use an access token with the repo scope or +// security_events scope. Alerts are only returned for organizations in the enterprise for which you are an organization owner or a security manager. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#list-secret-scanning-alerts-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/secret-scanning/alerts +func (s *SecretScanningService) ListAlertsForEnterprise(ctx context.Context, enterprise string, opts *SecretScanningAlertListOptions) ([]*SecretScanningAlert, *Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/alerts", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*SecretScanningAlert + resp, err := s.client.Do(req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + +// ListAlertsForOrg lists secret scanning alerts for eligible repositories in an organization, from newest to oldest. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#list-secret-scanning-alerts-for-an-organization +// +//meta:operation GET /orgs/{org}/secret-scanning/alerts +func (s *SecretScanningService) ListAlertsForOrg(ctx context.Context, org string, opts *SecretScanningAlertListOptions) ([]*SecretScanningAlert, *Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/alerts", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*SecretScanningAlert + resp, err := s.client.Do(req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + +// ListAlertsForRepo lists secret scanning alerts for a private repository, from newest to oldest. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#list-secret-scanning-alerts-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/secret-scanning/alerts +func (s *SecretScanningService) ListAlertsForRepo(ctx context.Context, owner, repo string, opts *SecretScanningAlertListOptions) ([]*SecretScanningAlert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/alerts", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alerts []*SecretScanningAlert + resp, err := s.client.Do(req, &alerts) + if err != nil { + return nil, resp, err + } + + return alerts, resp, nil +} + +// GetAlert gets a single secret scanning alert detected in a private repository. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#get-a-secret-scanning-alert +// +//meta:operation GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number} +func (s *SecretScanningService) GetAlert(ctx context.Context, owner, repo string, number int64) (*SecretScanningAlert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/alerts/%v", owner, repo, number) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var alert *SecretScanningAlert + resp, err := s.client.Do(req, &alert) + if err != nil { + return nil, resp, err + } + + return alert, resp, nil +} + +// UpdateAlert updates the status of a secret scanning alert in a private repository. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#update-a-secret-scanning-alert +// +//meta:operation PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number} +func (s *SecretScanningService) UpdateAlert(ctx context.Context, owner, repo string, number int64, body *SecretScanningAlertUpdateOptions) (*SecretScanningAlert, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/alerts/%v", owner, repo, number) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var alert *SecretScanningAlert + resp, err := s.client.Do(req, &alert) + if err != nil { + return nil, resp, err + } + + return alert, resp, nil +} + +// ListLocationsForAlert lists all locations for a given secret scanning alert for a private repository. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#list-locations-for-a-secret-scanning-alert +// +//meta:operation GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations +func (s *SecretScanningService) ListLocationsForAlert(ctx context.Context, owner, repo string, number int64, opts *ListOptions) ([]*SecretScanningAlertLocation, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/alerts/%v/locations", owner, repo, number) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var locations []*SecretScanningAlertLocation + resp, err := s.client.Do(req, &locations) + if err != nil { + return nil, resp, err + } + + return locations, resp, nil +} + +// CreatePushProtectionBypass creates a push protection bypass for a given repository. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#create-a-push-protection-bypass +// +//meta:operation POST /repos/{owner}/{repo}/secret-scanning/push-protection-bypasses +func (s *SecretScanningService) CreatePushProtectionBypass(ctx context.Context, owner, repo string, body PushProtectionBypassRequest) (*PushProtectionBypass, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/push-protection-bypasses", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var pushProtectionBypass *PushProtectionBypass + resp, err := s.client.Do(req, &pushProtectionBypass) + if err != nil { + return nil, resp, err + } + return pushProtectionBypass, resp, nil +} + +// GetScanHistory fetches the secret scanning history for a given repository. +// +// To use this endpoint, you must be an administrator for the repository or organization, and you must use an access token with +// the repo scope or security_events scope and gitHub advanced security or secret scanning must be enabled. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/secret-scanning?apiVersion=2022-11-28#get-secret-scanning-scan-history-for-a-repository +// +//meta:operation GET /repos/{owner}/{repo}/secret-scanning/scan-history +func (s *SecretScanningService) GetScanHistory(ctx context.Context, owner, repo string) (*SecretScanningScanHistory, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/scan-history", owner, repo) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var secretScanningHistory *SecretScanningScanHistory + resp, err := s.client.Do(req, &secretScanningHistory) + if err != nil { + return nil, resp, err + } + + return secretScanningHistory, resp, nil +} diff --git a/github/secret_scanning_custom_patterns.go b/github/secret_scanning_custom_patterns.go new file mode 100644 index 00000000000..4725f2f7da8 --- /dev/null +++ b/github/secret_scanning_custom_patterns.go @@ -0,0 +1,424 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SecretScanningCustomPattern represents a custom pattern for secret scanning, +// as returned by the GitHub API. +// +// GitHub API docs: https://docs.github.com/en/rest/secret-scanning/secret-scanning +type SecretScanningCustomPattern struct { + // ID is the unique identifier of the custom pattern. + ID int64 `json:"id"` + + // Name is the name of the custom pattern. + Name string `json:"name"` + + // Pattern is the regular expression that defines the custom pattern. + Pattern string `json:"pattern"` + + // Slug is the URL-friendly identifier derived from the pattern name. + Slug string `json:"slug"` + + // State is the publish state of the pattern. Possible values are: + // "published" or "unpublished". + State string `json:"state"` + + // PushProtectionEnabled reports whether push protection is enabled for + // this custom pattern. + PushProtectionEnabled bool `json:"push_protection_enabled"` + + // StartDelimiter is the start delimiter regex for the custom pattern. + StartDelimiter *string `json:"start_delimiter,omitempty"` + + // EndDelimiter is the end delimiter regex for the custom pattern. + EndDelimiter *string `json:"end_delimiter,omitempty"` + + // MustMatch is the list of regexes that the secret must match. + MustMatch []string `json:"must_match,omitempty"` + + // MustNotMatch is the list of regexes that the secret must not match. + MustNotMatch []string `json:"must_not_match,omitempty"` + + // CustomPatternVersion is used to confirm you're updating the current + // version of the pattern and to avoid unintentionally overriding + // someone else's update. + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` + + // CreatedAt is the time the custom pattern was created. + CreatedAt *Timestamp `json:"created_at,omitempty"` + + // UpdatedAt is the time the custom pattern was last updated. + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +// SecretScanningCustomPatternRequest represents a single custom pattern to +// be created for secret scanning. +type SecretScanningCustomPatternRequest struct { + // Name is the name of the custom pattern. + Name string `json:"name"` + + // Pattern is the regular expression that defines the custom pattern. + Pattern string `json:"pattern"` + + // StartDelimiter is the start delimiter regex for the custom pattern. + // Defaults to a sensible boundary regex when not specified. + StartDelimiter *string `json:"start_delimiter,omitempty"` + + // EndDelimiter is the end delimiter regex for the custom pattern. + // Defaults to a sensible boundary regex when not specified. + EndDelimiter *string `json:"end_delimiter,omitempty"` + + // MustMatch is the list of regexes that the secret must match. + MustMatch []string `json:"must_match,omitempty"` + + // MustNotMatch is the list of regexes that the secret must not match. + MustNotMatch []string `json:"must_not_match,omitempty"` +} + +// SecretScanningCreateCustomPatternsRequest represents the bulk request body +// used to create one or more custom patterns. +type SecretScanningCreateCustomPatternsRequest struct { + // Patterns is the list of custom patterns to create. + Patterns []*SecretScanningCustomPatternRequest `json:"patterns"` +} + +// SecretScanningCreateCustomPatternsResponse represents the bulk response +// returned after creating one or more custom patterns. +type SecretScanningCreateCustomPatternsResponse struct { + // CreatedPatterns is the list of successfully created custom patterns. + CreatedPatterns []*SecretScanningCustomPattern `json:"created_patterns"` +} + +// SecretScanningCustomPatternToDelete identifies a single custom pattern to +// remove in a bulk delete operation. +type SecretScanningCustomPatternToDelete struct { + // PatternID is the ID of the custom pattern to delete. + PatternID int64 `json:"pattern_id"` + + // CustomPatternVersion is used to confirm you're deleting the current + // version of the pattern. + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` +} + +// SecretScanningDeleteCustomPatternsRequest represents the bulk request body +// used to delete one or more custom patterns. +type SecretScanningDeleteCustomPatternsRequest struct { + // Patterns is the list of custom patterns to delete. + Patterns []*SecretScanningCustomPatternToDelete `json:"patterns"` + + // PostDeleteAction controls what happens to alerts associated with the + // deleted patterns. Possible values are: "delete_alerts" (default) or + // "resolve_alerts". + PostDeleteAction *string `json:"post_delete_action,omitempty"` +} + +// SecretScanningUpdateCustomPatternRequest represents the fields that can be +// updated on an existing custom pattern. At least one of Pattern, +// StartDelimiter, EndDelimiter, MustMatch, or MustNotMatch must be set. +type SecretScanningUpdateCustomPatternRequest struct { + // Pattern is the updated regular expression of the custom pattern. + Pattern *string `json:"pattern,omitempty"` + + // StartDelimiter is the updated start delimiter regex for the custom + // pattern. + StartDelimiter *string `json:"start_delimiter,omitempty"` + + // EndDelimiter is the updated end delimiter regex for the custom + // pattern. + EndDelimiter *string `json:"end_delimiter,omitempty"` + + // MustMatch is the updated list of regexes that the secret must match. + MustMatch []string `json:"must_match,omitempty"` + + // MustNotMatch is the updated list of regexes that the secret must not + // match. + MustNotMatch []string `json:"must_not_match,omitempty"` + + // CustomPatternVersion is required and is used to confirm you're + // updating the current version of the pattern. + CustomPatternVersion *string `json:"custom_pattern_version"` +} + +// SecretScanningCustomPatternListOptions specifies the optional parameters +// to the SecretScanningService.ListCustomPatternsForRepo method. +type SecretScanningCustomPatternListOptions struct { + // State filters custom patterns by state. Possible values are: + // "published" or "unpublished". When absent, returns patterns in all + // states. + State string `url:"state,omitempty"` + + // PushProtection filters custom patterns by whether push protection is + // enabled. Possible values are: "enabled" or "disabled". When absent, + // returns patterns regardless of push protection status. + PushProtection string `url:"push_protection,omitempty"` + + // Sort is the property to sort the results by. Possible values are: + // "created", "updated", or "name". Default: "created". + Sort string `url:"sort,omitempty"` + + // Direction is the direction to sort the results by. Possible values + // are: "asc" or "desc". Default: "desc". + Direction string `url:"direction,omitempty"` + + // ListOptions controls pagination via Page and PerPage. + ListOptions +} + +// ListCustomPatternsForRepo lists the secret scanning custom patterns +// defined at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#list-repository-custom-patterns +// +//meta:operation GET /repos/{owner}/{repo}/secret-scanning/custom-patterns +func (s *SecretScanningService) ListCustomPatternsForRepo(ctx context.Context, owner, repo string, opts *SecretScanningCustomPatternListOptions) ([]*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var patterns []*SecretScanningCustomPattern + resp, err := s.client.Do(req, &patterns) + if err != nil { + return nil, resp, err + } + + return patterns, resp, nil +} + +// CreateCustomPatternsForRepo creates one or more secret scanning custom +// patterns at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-create-repository-custom-patterns +// +//meta:operation POST /repos/{owner}/{repo}/secret-scanning/custom-patterns +func (s *SecretScanningService) CreateCustomPatternsForRepo(ctx context.Context, owner, repo string, body SecretScanningCreateCustomPatternsRequest) (*SecretScanningCreateCustomPatternsResponse, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var result *SecretScanningCreateCustomPatternsResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// UpdateCustomPatternForRepo updates a single secret scanning custom pattern +// at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#update-a-repository-custom-pattern +// +//meta:operation PATCH /repos/{owner}/{repo}/secret-scanning/custom-patterns/{pattern_id} +func (s *SecretScanningService) UpdateCustomPatternForRepo(ctx context.Context, owner, repo string, patternID int64, body SecretScanningUpdateCustomPatternRequest) (*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns/%v", owner, repo, patternID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var pattern *SecretScanningCustomPattern + resp, err := s.client.Do(req, &pattern) + if err != nil { + return nil, resp, err + } + + return pattern, resp, nil +} + +// DeleteCustomPatternsForRepo deletes one or more secret scanning custom +// patterns at the repository level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-delete-repository-custom-patterns +// +//meta:operation DELETE /repos/{owner}/{repo}/secret-scanning/custom-patterns +func (s *SecretScanningService) DeleteCustomPatternsForRepo(ctx context.Context, owner, repo string, body SecretScanningDeleteCustomPatternsRequest) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/secret-scanning/custom-patterns", owner, repo) + + req, err := s.client.NewRequest(ctx, "DELETE", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListCustomPatternsForOrg lists the secret scanning custom patterns defined at the organization level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#list-organization-custom-patterns +// +//meta:operation GET /orgs/{org}/secret-scanning/custom-patterns +func (s *SecretScanningService) ListCustomPatternsForOrg(ctx context.Context, org string, opts *SecretScanningCustomPatternListOptions) ([]*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/custom-patterns", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var patterns []*SecretScanningCustomPattern + resp, err := s.client.Do(req, &patterns) + if err != nil { + return nil, resp, err + } + + return patterns, resp, nil +} + +// CreateCustomPatternsForOrg creates one or more secret scanning custom patterns at organization level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-create-organization-custom-patterns +// +//meta:operation POST /orgs/{org}/secret-scanning/custom-patterns +func (s *SecretScanningService) CreateCustomPatternsForOrg(ctx context.Context, org string, body SecretScanningCreateCustomPatternsRequest) (*SecretScanningCreateCustomPatternsResponse, *Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/custom-patterns", org) + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var result *SecretScanningCreateCustomPatternsResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// UpdateCustomPatternForOrg updates a single secret scanning custom pattern at organization level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#update-an-organization-custom-pattern +// +//meta:operation PATCH /orgs/{org}/secret-scanning/custom-patterns/{pattern_id} +func (s *SecretScanningService) UpdateCustomPatternForOrg(ctx context.Context, org string, patternID int64, body SecretScanningUpdateCustomPatternRequest) (*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/custom-patterns/%v", org, patternID) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var pattern *SecretScanningCustomPattern + resp, err := s.client.Do(req, &pattern) + if err != nil { + return nil, resp, err + } + + return pattern, resp, nil +} + +// DeleteCustomPatternsForOrg deletes one or more secret scanning custom patterns at the organization level. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-delete-organization-custom-patterns +// +//meta:operation DELETE /orgs/{org}/secret-scanning/custom-patterns +func (s *SecretScanningService) DeleteCustomPatternsForOrg(ctx context.Context, org string, body SecretScanningDeleteCustomPatternsRequest) (*Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/custom-patterns", org) + + req, err := s.client.NewRequest(ctx, "DELETE", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListCustomPatternsForEnterprise lists the secret scanning custom patterns defined at the enterprise level. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#list-enterprise-custom-patterns +// +//meta:operation GET /enterprises/{enterprise}/secret-scanning/custom-patterns +func (s *SecretScanningService) ListCustomPatternsForEnterprise(ctx context.Context, enterprise string, opts *SecretScanningCustomPatternListOptions) ([]*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/custom-patterns", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + var patterns []*SecretScanningCustomPattern + resp, err := s.client.Do(req, &patterns) + if err != nil { + return nil, resp, err + } + return patterns, resp, nil +} + +// CreateCustomPatternsForEnterprise creates one or more secret scanning custom patterns at the enterprise level. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-create-enterprise-custom-patterns +// +//meta:operation POST /enterprises/{enterprise}/secret-scanning/custom-patterns +func (s *SecretScanningService) CreateCustomPatternsForEnterprise(ctx context.Context, enterprise string, body SecretScanningCreateCustomPatternsRequest) (*SecretScanningCreateCustomPatternsResponse, *Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/custom-patterns", enterprise) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + var result *SecretScanningCreateCustomPatternsResponse + resp, err := s.client.Do(req, &result) + if err != nil { + return nil, resp, err + } + return result, resp, nil +} + +// UpdateCustomPatternForEnterprise updates a single secret scanning custom pattern at the enterprise level. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#update-an-enterprise-custom-pattern +// +//meta:operation PATCH /enterprises/{enterprise}/secret-scanning/custom-patterns/{pattern_id} +func (s *SecretScanningService) UpdateCustomPatternForEnterprise(ctx context.Context, enterprise string, patternID int64, body SecretScanningUpdateCustomPatternRequest) (*SecretScanningCustomPattern, *Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/custom-patterns/%v", enterprise, patternID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + var pattern *SecretScanningCustomPattern + resp, err := s.client.Do(req, &pattern) + if err != nil { + return nil, resp, err + } + return pattern, resp, nil +} + +// DeleteCustomPatternsForEnterprise deletes one or more secret scanning custom patterns at the enterprise level. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns?apiVersion=2022-11-28#bulk-delete-enterprise-custom-patterns +// +//meta:operation DELETE /enterprises/{enterprise}/secret-scanning/custom-patterns +func (s *SecretScanningService) DeleteCustomPatternsForEnterprise(ctx context.Context, enterprise string, body SecretScanningDeleteCustomPatternsRequest) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/custom-patterns", enterprise) + req, err := s.client.NewRequest(ctx, "DELETE", u, body) + if err != nil { + return nil, err + } + return s.client.Do(req, nil) +} diff --git a/github/secret_scanning_custom_patterns_test.go b/github/secret_scanning_custom_patterns_test.go new file mode 100644 index 00000000000..051280e4b86 --- /dev/null +++ b/github/secret_scanning_custom_patterns_test.go @@ -0,0 +1,710 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSecretScanningService_ListCustomPatternsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "published", + "push_protection": "enabled", + "sort": "created", + "direction": "desc", + "page": "2", + }) + fmt.Fprint(w, `[ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": true, + "start_delimiter": "\\b", + "end_delimiter": "\\b", + "must_match": ["ID-.*"], + "must_not_match": ["TEST-.*"], + "custom_pattern_version": "v1", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+` + } + ]`) + }) + + ctx := t.Context() + opts := &SecretScanningCustomPatternListOptions{ + State: "published", + PushProtection: "enabled", + Sort: "created", + Direction: "desc", + ListOptions: ListOptions{Page: 2}, + } + patterns, _, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r", opts) + if err != nil { + t.Errorf("SecretScanning.ListCustomPatternsForRepo returned error: %v", err) + } + + want := []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: true, + StartDelimiter: Ptr(`\b`), + EndDelimiter: Ptr(`\b`), + MustMatch: []string{"ID-.*"}, + MustNotMatch: []string{"TEST-.*"}, + CustomPatternVersion: Ptr("v1"), + CreatedAt: &referenceTimestamp, + UpdatedAt: &referenceTimestamp, + }, + } + if !cmp.Equal(patterns, want) { + t.Errorf("SecretScanning.ListCustomPatternsForRepo returned %+v, want %+v", patterns, want) + } + + const methodName = "ListCustomPatternsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListCustomPatternsForRepo(ctx, "\n", "\n", &SecretScanningCustomPatternListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListCustomPatternsForRepo(ctx, "o", "r", &SecretScanningCustomPatternListOptions{}) + return resp, err + }) +} + +func TestSecretScanningService_CreateCustomPatternsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningCreateCustomPatternsRequest{ + Patterns: []*SecretScanningCustomPatternRequest{ + { + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + }, + }, + } + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "created_patterns": [ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false + } + ] + }`) + }) + + ctx := t.Context() + result, _, err := client.SecretScanning.CreateCustomPatternsForRepo(ctx, "o", "r", input) + if err != nil { + t.Errorf("SecretScanning.CreateCustomPatternsForRepo returned error: %v", err) + } + + want := &SecretScanningCreateCustomPatternsResponse{ + CreatedPatterns: []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: false, + }, + }, + } + if !cmp.Equal(result, want) { + t.Errorf("SecretScanning.CreateCustomPatternsForRepo returned %+v, want %+v", result, want) + } + + const methodName = "CreateCustomPatternsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.CreateCustomPatternsForRepo(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.CreateCustomPatternsForRepo(ctx, "o", "r", input) + return resp, err + }) +} + +func TestSecretScanningService_UpdateCustomPatternForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningUpdateCustomPatternRequest{ + Pattern: Ptr("[A-Z]{3}-[0-9]{4}"), + CustomPatternVersion: Ptr("v1"), + } + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{3}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false, + "custom_pattern_version": "v2" + }`) + }) + + ctx := t.Context() + pattern, _, err := client.SecretScanning.UpdateCustomPatternForRepo(ctx, "o", "r", 1, input) + if err != nil { + t.Errorf("SecretScanning.UpdateCustomPatternForRepo returned error: %v", err) + } + + want := &SecretScanningCustomPattern{ + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{3}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: false, + CustomPatternVersion: Ptr("v2"), + } + if !cmp.Equal(pattern, want) { + t.Errorf("SecretScanning.UpdateCustomPatternForRepo returned %+v, want %+v", pattern, want) + } + + const methodName = "UpdateCustomPatternForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdateCustomPatternForRepo(ctx, "\n", "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdateCustomPatternForRepo(ctx, "o", "r", 1, input) + return resp, err + }) +} + +func TestSecretScanningService_DeleteCustomPatternsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningDeleteCustomPatternsRequest{ + Patterns: []*SecretScanningCustomPatternToDelete{ + {PatternID: 1}, + }, + } + + mux.HandleFunc("/repos/o/r/secret-scanning/custom-patterns", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + _, err := client.SecretScanning.DeleteCustomPatternsForRepo(ctx, "o", "r", input) + if err != nil { + t.Errorf("SecretScanning.DeleteCustomPatternsForRepo returned error: %v", err) + } + + const methodName = "DeleteCustomPatternsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.SecretScanning.DeleteCustomPatternsForRepo(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.SecretScanning.DeleteCustomPatternsForRepo(ctx, "o", "r", input) + }) +} + +func TestSecretScanningService_ListCustomPatternsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "published", + "push_protection": "enabled", + "sort": "created", + "direction": "desc", + "page": "2", + }) + fmt.Fprint(w, `[ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": true, + "start_delimiter": "\\b", + "end_delimiter": "\\b", + "must_match": ["ID-.*"], + "must_not_match": ["TEST-.*"], + "custom_pattern_version": "v1", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+` + } + ]`) + }) + + ctx := t.Context() + opts := &SecretScanningCustomPatternListOptions{ + State: "published", + PushProtection: "enabled", + Sort: "created", + Direction: "desc", + ListOptions: ListOptions{Page: 2}, + } + patterns, _, err := client.SecretScanning.ListCustomPatternsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("SecretScanning.ListCustomPatternsForOrg returned error: %v", err) + } + + want := []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: true, + StartDelimiter: Ptr(`\b`), + EndDelimiter: Ptr(`\b`), + MustMatch: []string{"ID-.*"}, + MustNotMatch: []string{"TEST-.*"}, + CustomPatternVersion: Ptr("v1"), + CreatedAt: &referenceTimestamp, + UpdatedAt: &referenceTimestamp, + }, + } + if !cmp.Equal(patterns, want) { + t.Errorf("SecretScanning.ListCustomPatternsForOrg returned %+v, want %+v", patterns, want) + } + + const methodName = "ListCustomPatternsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListCustomPatternsForOrg(ctx, "\n", &SecretScanningCustomPatternListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListCustomPatternsForOrg(ctx, "o", &SecretScanningCustomPatternListOptions{}) + return resp, err + }) +} + +func TestSecretScanningService_CreateCustomPatternsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningCreateCustomPatternsRequest{ + Patterns: []*SecretScanningCustomPatternRequest{ + { + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + }, + }, + } + + mux.HandleFunc("/orgs/o/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "created_patterns": [ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false + } + ] + }`) + }) + + ctx := t.Context() + result, _, err := client.SecretScanning.CreateCustomPatternsForOrg(ctx, "o", input) + if err != nil { + t.Errorf("SecretScanning.CreateCustomPatternsForOrg returned error: %v", err) + } + + want := &SecretScanningCreateCustomPatternsResponse{ + CreatedPatterns: []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: false, + }, + }, + } + if !cmp.Equal(result, want) { + t.Errorf("SecretScanning.CreateCustomPatternsForOrg returned %+v, want %+v", result, want) + } + + const methodName = "CreateCustomPatternsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.CreateCustomPatternsForOrg(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.CreateCustomPatternsForOrg(ctx, "o", input) + return resp, err + }) +} + +func TestSecretScanningService_UpdateCustomPatternForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningUpdateCustomPatternRequest{ + Pattern: Ptr("[A-Z]{3}-[0-9]{4}"), + CustomPatternVersion: Ptr("v1"), + } + + mux.HandleFunc("/orgs/o/secret-scanning/custom-patterns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{3}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false, + "custom_pattern_version": "v2" + }`) + }) + + ctx := t.Context() + pattern, _, err := client.SecretScanning.UpdateCustomPatternForOrg(ctx, "o", 1, input) + if err != nil { + t.Errorf("SecretScanning.UpdateCustomPatternForOrg returned error: %v", err) + } + + want := &SecretScanningCustomPattern{ + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{3}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: false, + CustomPatternVersion: Ptr("v2"), + } + if !cmp.Equal(pattern, want) { + t.Errorf("SecretScanning.UpdateCustomPatternForOrg returned %+v, want %+v", pattern, want) + } + + const methodName = "UpdateCustomPatternForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdateCustomPatternForOrg(ctx, "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdateCustomPatternForOrg(ctx, "o", 1, input) + return resp, err + }) +} + +func TestSecretScanningService_DeleteCustomPatternsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningDeleteCustomPatternsRequest{ + Patterns: []*SecretScanningCustomPatternToDelete{ + {PatternID: 1}, + }, + } + + mux.HandleFunc("/orgs/o/secret-scanning/custom-patterns", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + _, err := client.SecretScanning.DeleteCustomPatternsForOrg(ctx, "o", input) + if err != nil { + t.Errorf("SecretScanning.DeleteCustomPatternsForOrg returned error: %v", err) + } + + const methodName = "DeleteCustomPatternsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.SecretScanning.DeleteCustomPatternsForOrg(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.SecretScanning.DeleteCustomPatternsForOrg(ctx, "o", input) + }) +} + +func TestSecretScanningService_ListCustomPatternsForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "state": "published", + "push_protection": "enabled", + "sort": "created", + "direction": "desc", + "page": "2", + }) + fmt.Fprint(w, `[ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": true, + "start_delimiter": "\\b", + "end_delimiter": "\\b", + "must_match": ["ID-.*"], + "must_not_match": ["TEST-.*"], + "custom_pattern_version": "v1", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+` + } + ]`) + }) + + ctx := t.Context() + opts := &SecretScanningCustomPatternListOptions{ + State: "published", + PushProtection: "enabled", + Sort: "created", + Direction: "desc", + ListOptions: ListOptions{Page: 2}, + } + patterns, _, err := client.SecretScanning.ListCustomPatternsForEnterprise(ctx, "e", opts) + if err != nil { + t.Errorf("SecretScanning.ListCustomPatternsForEnterprise returned error: %v", err) + } + + want := []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: true, + StartDelimiter: Ptr(`\b`), + EndDelimiter: Ptr(`\b`), + MustMatch: []string{"ID-.*"}, + MustNotMatch: []string{"TEST-.*"}, + CustomPatternVersion: Ptr("v1"), + CreatedAt: &referenceTimestamp, + UpdatedAt: &referenceTimestamp, + }, + } + if !cmp.Equal(patterns, want) { + t.Errorf("SecretScanning.ListCustomPatternsForEnterprise returned %+v, want %+v", patterns, want) + } + + const methodName = "ListCustomPatternsForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListCustomPatternsForEnterprise(ctx, "\n", &SecretScanningCustomPatternListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListCustomPatternsForEnterprise(ctx, "e", &SecretScanningCustomPatternListOptions{}) + return resp, err + }) +} + +func TestSecretScanningService_CreateCustomPatternsForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningCreateCustomPatternsRequest{ + Patterns: []*SecretScanningCustomPatternRequest{ + { + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + }, + }, + } + + mux.HandleFunc("/enterprises/e/secret-scanning/custom-patterns", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "created_patterns": [ + { + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{2}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false + } + ] + }`) + }) + + ctx := t.Context() + result, _, err := client.SecretScanning.CreateCustomPatternsForEnterprise(ctx, "e", input) + if err != nil { + t.Errorf("SecretScanning.CreateCustomPatternsForEnterprise returned error: %v", err) + } + + want := &SecretScanningCreateCustomPatternsResponse{ + CreatedPatterns: []*SecretScanningCustomPattern{ + { + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{2}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: false, + }, + }, + } + if !cmp.Equal(result, want) { + t.Errorf("SecretScanning.CreateCustomPatternsForEnterprise returned %+v, want %+v", result, want) + } + + const methodName = "CreateCustomPatternsForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.CreateCustomPatternsForEnterprise(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.CreateCustomPatternsForEnterprise(ctx, "e", input) + return resp, err + }) +} + +func TestSecretScanningService_UpdateCustomPatternForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningUpdateCustomPatternRequest{ + Pattern: Ptr("[A-Z]{3}-[0-9]{4}"), + CustomPatternVersion: Ptr("v1"), + } + + mux.HandleFunc("/enterprises/e/secret-scanning/custom-patterns/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{ + "id": 1, + "name": "Custom pattern", + "pattern": "[A-Z]{3}-[0-9]{4}", + "slug": "custom-pattern", + "state": "published", + "push_protection_enabled": false, + "custom_pattern_version": "v2" + }`) + }) + + ctx := t.Context() + pattern, _, err := client.SecretScanning.UpdateCustomPatternForEnterprise(ctx, "e", 1, input) + if err != nil { + t.Errorf("SecretScanning.UpdateCustomPatternForEnterprise returned error: %v", err) + } + + want := &SecretScanningCustomPattern{ + ID: 1, + Name: "Custom pattern", + Pattern: "[A-Z]{3}-[0-9]{4}", + Slug: "custom-pattern", + State: "published", + PushProtectionEnabled: false, + CustomPatternVersion: Ptr("v2"), + } + if !cmp.Equal(pattern, want) { + t.Errorf("SecretScanning.UpdateCustomPatternForEnterprise returned %+v, want %+v", pattern, want) + } + + const methodName = "UpdateCustomPatternForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdateCustomPatternForEnterprise(ctx, "\n", 1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdateCustomPatternForEnterprise(ctx, "e", 1, input) + return resp, err + }) +} + +func TestSecretScanningService_DeleteCustomPatternsForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := SecretScanningDeleteCustomPatternsRequest{ + Patterns: []*SecretScanningCustomPatternToDelete{ + {PatternID: 1}, + }, + } + + mux.HandleFunc("/enterprises/e/secret-scanning/custom-patterns", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + }) + + ctx := t.Context() + _, err := client.SecretScanning.DeleteCustomPatternsForEnterprise(ctx, "e", input) + if err != nil { + t.Errorf("SecretScanning.DeleteCustomPatternsForEnterprise returned error: %v", err) + } + + const methodName = "DeleteCustomPatternsForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, err = client.SecretScanning.DeleteCustomPatternsForEnterprise(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.SecretScanning.DeleteCustomPatternsForEnterprise(ctx, "e", input) + }) +} diff --git a/github/secret_scanning_pattern_configs.go b/github/secret_scanning_pattern_configs.go new file mode 100644 index 00000000000..fe880700ff8 --- /dev/null +++ b/github/secret_scanning_pattern_configs.go @@ -0,0 +1,165 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SecretScanningPatternConfigs represents a collection of GitHub secret scanning patterns +// and their settings related to push protection. +type SecretScanningPatternConfigs struct { + PatternConfigVersion *string `json:"pattern_config_version,omitempty"` + ProviderPatternOverrides []*SecretScanningPatternOverride `json:"provider_pattern_overrides,omitempty"` + CustomPatternOverrides []*SecretScanningPatternOverride `json:"custom_pattern_overrides,omitempty"` +} + +// SecretScanningPatternOverride represents an override for provider partner or custom organization patterns. +type SecretScanningPatternOverride struct { + TokenType *string `json:"token_type,omitempty"` + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` + Slug *string `json:"slug,omitempty"` + DisplayName *string `json:"display_name,omitempty"` + AlertTotal *int `json:"alert_total,omitempty"` + AlertTotalPercentage *int `json:"alert_total_percentage,omitempty"` + FalsePositives *int `json:"false_positives,omitempty"` + FalsePositiveRate *int `json:"false_positive_rate,omitempty"` + Bypassrate *int `json:"bypass_rate,omitempty"` + DefaultSetting *string `json:"default_setting,omitempty"` + EnterpriseSetting *string `json:"enterprise_setting,omitempty"` + Setting *string `json:"setting,omitempty"` +} + +// SecretScanningPatternConfigsUpdate represents a secret scanning pattern configurations update. +type SecretScanningPatternConfigsUpdate struct { + PatternConfigVersion *string `json:"pattern_config_version,omitempty"` +} + +// SecretScanningPatternConfigsUpdateOptions specifies optional parameters to +// the SecretScanningService.UpdatePatternConfigsForEnterprise method and +// the SecretScanningService.UpdatePatternConfigsForOrg method. +type SecretScanningPatternConfigsUpdateOptions struct { + // The version of the entity. + PatternConfigVersion *string `json:"pattern_config_version,omitempty"` + + // Pattern settings for provider patterns. + ProviderPatternSettings []*SecretScanningProviderPatternSetting `json:"provider_pattern_settings,omitempty"` + + // Pattern settings for custom patterns. + CustomPatternSettings []*SecretScanningCustomPatternSetting `json:"custom_pattern_settings,omitempty"` +} + +// SecretScanningProviderPatternSetting defines an optional pattern setting for provider patterns. +type SecretScanningProviderPatternSetting struct { + // The ID of the pattern to configure. + TokenType string `json:"token_type"` + + // Push protection setting to set for the pattern. + // Can be one of: "not-set", "disabled", "enabled" + PushProtectionSetting string `json:"push_protection_setting"` +} + +// SecretScanningCustomPatternSetting defines an optional pattern setting for custom patterns. +type SecretScanningCustomPatternSetting struct { + // The ID of the pattern to configure. + TokenType string `json:"token_type"` + + // The version of the entity + CustomPatternVersion *string `json:"custom_pattern_version,omitempty"` + + // Push protection setting to set for the pattern. + // Can be one of: "not-set", "disabled", "enabled" + PushProtectionSetting string `json:"push_protection_setting"` +} + +// ListPatternConfigsForEnterprise lists the secret scanning pattern configurations for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/push-protection?apiVersion=2022-11-28#list-enterprise-pattern-configurations +// +//meta:operation GET /enterprises/{enterprise}/secret-scanning/pattern-configurations +func (s *SecretScanningService) ListPatternConfigsForEnterprise(ctx context.Context, enterprise string) (*SecretScanningPatternConfigs, *Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/pattern-configurations", enterprise) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var patternConfigs *SecretScanningPatternConfigs + resp, err := s.client.Do(req, &patternConfigs) + if err != nil { + return nil, resp, err + } + + return patternConfigs, resp, nil +} + +// ListPatternConfigsForOrg lists the secret scanning pattern configurations for an organization. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/push-protection?apiVersion=2022-11-28#list-organization-pattern-configurations +// +//meta:operation GET /orgs/{org}/secret-scanning/pattern-configurations +func (s *SecretScanningService) ListPatternConfigsForOrg(ctx context.Context, org string) (*SecretScanningPatternConfigs, *Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/pattern-configurations", org) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var patternConfigs *SecretScanningPatternConfigs + resp, err := s.client.Do(req, &patternConfigs) + if err != nil { + return nil, resp, err + } + + return patternConfigs, resp, nil +} + +// UpdatePatternConfigsForEnterprise updates the secret scanning pattern configurations for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/push-protection?apiVersion=2022-11-28#update-enterprise-pattern-configurations +// +//meta:operation PATCH /enterprises/{enterprise}/secret-scanning/pattern-configurations +func (s *SecretScanningService) UpdatePatternConfigsForEnterprise(ctx context.Context, enterprise string, body *SecretScanningPatternConfigsUpdateOptions) (*SecretScanningPatternConfigsUpdate, *Response, error) { + u := fmt.Sprintf("enterprises/%v/secret-scanning/pattern-configurations", enterprise) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var patternConfigsUpdate *SecretScanningPatternConfigsUpdate + resp, err := s.client.Do(req, &patternConfigsUpdate) + if err != nil { + return nil, resp, err + } + + return patternConfigsUpdate, resp, nil +} + +// UpdatePatternConfigsForOrg updates the secret scanning pattern configurations for an organization. +// +// GitHub API docs: https://docs.github.com/rest/secret-scanning/push-protection?apiVersion=2022-11-28#update-organization-pattern-configurations +// +//meta:operation PATCH /orgs/{org}/secret-scanning/pattern-configurations +func (s *SecretScanningService) UpdatePatternConfigsForOrg(ctx context.Context, org string, body *SecretScanningPatternConfigsUpdateOptions) (*SecretScanningPatternConfigsUpdate, *Response, error) { + u := fmt.Sprintf("orgs/%v/secret-scanning/pattern-configurations", org) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var patternConfigsUpdate *SecretScanningPatternConfigsUpdate + resp, err := s.client.Do(req, &patternConfigsUpdate) + if err != nil { + return nil, resp, err + } + + return patternConfigsUpdate, resp, nil +} diff --git a/github/secret_scanning_pattern_configs_test.go b/github/secret_scanning_pattern_configs_test.go new file mode 100644 index 00000000000..d41dfb4770a --- /dev/null +++ b/github/secret_scanning_pattern_configs_test.go @@ -0,0 +1,331 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSecretScanningService_ListPatternConfigsForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/secret-scanning/pattern-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `{ + "pattern_config_version": "0ujsswThIGTUYm2K8FjOOfXtY1K", + "provider_pattern_overrides": [ + { + "token_type": "GITHUB_PERSONAL_ACCESS_TOKEN", + "slug": "github_personal_access_token_legacy_v2", + "display_name": "GitHub Personal Access Token (Legacy v2)", + "alert_total": 15, + "alert_total_percentage": 36, + "false_positives": 2, + "false_positive_rate": 13, + "bypass_rate": 13, + "default_setting": "enabled", + "setting": "enabled", + "enterprise_setting": "enabled" + } + ], + "custom_pattern_overrides": [ + { + "token_type": "cp_2", + "custom_pattern_version": "0ujsswThIGTUYm2K8FjOOfXtY1K", + "slug": "custom-api-key", + "display_name": "Custom API Key", + "alert_total": 15, + "alert_total_percentage": 36, + "false_positives": 3, + "false_positive_rate": 20, + "bypass_rate": 20, + "default_setting": "disabled", + "setting": "enabled" + } + ] + }`) + }) + + ctx := t.Context() + + patternConfigs, _, err := client.SecretScanning.ListPatternConfigsForEnterprise(ctx, "e") + if err != nil { + t.Errorf("SecretScanning.ListPatternConfigsForEnterprise returned error: %v", err) + } + + want := &SecretScanningPatternConfigs{ + PatternConfigVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + ProviderPatternOverrides: []*SecretScanningPatternOverride{ + { + TokenType: Ptr("GITHUB_PERSONAL_ACCESS_TOKEN"), + CustomPatternVersion: nil, + Slug: Ptr("github_personal_access_token_legacy_v2"), + DisplayName: Ptr("GitHub Personal Access Token (Legacy v2)"), + AlertTotal: Ptr(15), + AlertTotalPercentage: Ptr(36), + FalsePositives: Ptr(2), + FalsePositiveRate: Ptr(13), + Bypassrate: Ptr(13), + DefaultSetting: Ptr("enabled"), + EnterpriseSetting: Ptr("enabled"), + Setting: Ptr("enabled"), + }, + }, + CustomPatternOverrides: []*SecretScanningPatternOverride{ + { + TokenType: Ptr("cp_2"), + CustomPatternVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + Slug: Ptr("custom-api-key"), + DisplayName: Ptr("Custom API Key"), + AlertTotal: Ptr(15), + AlertTotalPercentage: Ptr(36), + FalsePositives: Ptr(3), + FalsePositiveRate: Ptr(20), + Bypassrate: Ptr(20), + DefaultSetting: Ptr("disabled"), + EnterpriseSetting: nil, + Setting: Ptr("enabled"), + }, + }, + } + + if !cmp.Equal(patternConfigs, want) { + t.Errorf("SecretScanning.ListPatternConfigsForEnterprise returned %+v, want %+v", patternConfigs, want) + } + + const methodName = "ListPatternConfigsForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListPatternConfigsForEnterprise(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListPatternConfigsForEnterprise(ctx, "e") + return resp, err + }) +} + +func TestSecretScanningService_ListPatternConfigsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/secret-scanning/pattern-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `{ + "pattern_config_version": "0ujsswThIGTUYm2K8FjOOfXtY1K", + "provider_pattern_overrides": [ + { + "token_type": "GITHUB_PERSONAL_ACCESS_TOKEN", + "slug": "github_personal_access_token_legacy_v2", + "display_name": "GitHub Personal Access Token (Legacy v2)", + "alert_total": 15, + "alert_total_percentage": 36, + "false_positives": 2, + "false_positive_rate": 13, + "bypass_rate": 13, + "default_setting": "enabled", + "setting": "enabled", + "enterprise_setting": "enabled" + } + ], + "custom_pattern_overrides": [ + { + "token_type": "cp_2", + "custom_pattern_version": "0ujsswThIGTUYm2K8FjOOfXtY1K", + "slug": "custom-api-key", + "display_name": "Custom API Key", + "alert_total": 15, + "alert_total_percentage": 36, + "false_positives": 3, + "false_positive_rate": 20, + "bypass_rate": 20, + "default_setting": "disabled", + "setting": "enabled" + } + ] + }`) + }) + ctx := t.Context() + + patternConfigs, _, err := client.SecretScanning.ListPatternConfigsForOrg(ctx, "o") + if err != nil { + t.Errorf("SecretScanning.ListPatternConfigsForOrg returned error: %v", err) + } + + want := &SecretScanningPatternConfigs{ + PatternConfigVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + ProviderPatternOverrides: []*SecretScanningPatternOverride{ + { + TokenType: Ptr("GITHUB_PERSONAL_ACCESS_TOKEN"), + CustomPatternVersion: nil, + Slug: Ptr("github_personal_access_token_legacy_v2"), + DisplayName: Ptr("GitHub Personal Access Token (Legacy v2)"), + AlertTotal: Ptr(15), + AlertTotalPercentage: Ptr(36), + FalsePositives: Ptr(2), + FalsePositiveRate: Ptr(13), + Bypassrate: Ptr(13), + DefaultSetting: Ptr("enabled"), + EnterpriseSetting: Ptr("enabled"), + Setting: Ptr("enabled"), + }, + }, + CustomPatternOverrides: []*SecretScanningPatternOverride{ + { + TokenType: Ptr("cp_2"), + CustomPatternVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + Slug: Ptr("custom-api-key"), + DisplayName: Ptr("Custom API Key"), + AlertTotal: Ptr(15), + AlertTotalPercentage: Ptr(36), + FalsePositives: Ptr(3), + FalsePositiveRate: Ptr(20), + Bypassrate: Ptr(20), + DefaultSetting: Ptr("disabled"), + EnterpriseSetting: nil, + Setting: Ptr("enabled"), + }, + }, + } + + if !cmp.Equal(patternConfigs, want) { + t.Errorf("SecretScanning.ListPatternConfigsForOrg returned %+v, want %+v", patternConfigs, want) + } + + const methodName = "ListPatternConfigsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListPatternConfigsForOrg(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListPatternConfigsForOrg(ctx, "o") + return resp, err + }) +} + +func TestSecretScanningService_UpdatePatternConfigsForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/secret-scanning/pattern-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + + fmt.Fprint(w, `{ + "pattern_config_version": "0ujsswThIGTUYm2K8FjOOfXtY1K" + }`) + }) + + ctx := t.Context() + + opts := &SecretScanningPatternConfigsUpdateOptions{ + PatternConfigVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + ProviderPatternSettings: []*SecretScanningProviderPatternSetting{ + { + TokenType: "GITHUB_PERSONAL_ACCESS_TOKEN", + PushProtectionSetting: "enabled", + }, + }, + CustomPatternSettings: []*SecretScanningCustomPatternSetting{ + { + TokenType: "cp_2", + CustomPatternVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + PushProtectionSetting: "enabled", + }, + }, + } + + configsUpdate, _, err := client.SecretScanning.UpdatePatternConfigsForEnterprise(ctx, "e", opts) + if err != nil { + t.Errorf("SecretScanning.UpdatePatternConfigsForEnterprise returned error: %v", err) + } + + want := &SecretScanningPatternConfigsUpdate{ + PatternConfigVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + } + + if !cmp.Equal(configsUpdate, want) { + t.Errorf("SecretScanning.UpdatePatternConfigsForEnterprise returned %+v, want %+v", configsUpdate, want) + } + + const methodName = "UpdatePatternConfigsForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdatePatternConfigsForEnterprise(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdatePatternConfigsForEnterprise(ctx, "o", opts) + return resp, err + }) +} + +func TestSecretScanningService_UpdatePatternConfigsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/secret-scanning/pattern-configurations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + + fmt.Fprint(w, `{ + "pattern_config_version": "0ujsswThIGTUYm2K8FjOOfXtY1K" + }`) + }) + + ctx := t.Context() + + opts := &SecretScanningPatternConfigsUpdateOptions{ + PatternConfigVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + ProviderPatternSettings: []*SecretScanningProviderPatternSetting{ + { + TokenType: "GITHUB_PERSONAL_ACCESS_TOKEN", + PushProtectionSetting: "enabled", + }, + }, + CustomPatternSettings: []*SecretScanningCustomPatternSetting{ + { + TokenType: "cp_2", + CustomPatternVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + PushProtectionSetting: "enabled", + }, + }, + } + + configsUpdate, _, err := client.SecretScanning.UpdatePatternConfigsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("SecretScanning.UpdatePatternConfigsForOrg returned err: %v", err) + } + + want := &SecretScanningPatternConfigsUpdate{ + PatternConfigVersion: Ptr("0ujsswThIGTUYm2K8FjOOfXtY1K"), + } + + if !cmp.Equal(configsUpdate, want) { + t.Errorf("SecretScanning.UpdatePatternConfigsForOrg returned %+v, want %+v", configsUpdate, want) + } + + const methodName = "UpdatePatternConfigsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdatePatternConfigsForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdatePatternConfigsForOrg(ctx, "o", opts) + return resp, err + }) +} diff --git a/github/secret_scanning_test.go b/github/secret_scanning_test.go new file mode 100644 index 00000000000..852e3da073f --- /dev/null +++ b/github/secret_scanning_test.go @@ -0,0 +1,585 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSecretScanningService_ListAlertsForEnterprise(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/enterprises/e/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key", "sort": "updated", "direction": "asc"}) + + fmt.Fprint(w, `[{ + "number": 1, + "created_at": `+refTimeStr(1136178000)+`, + "url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1", + "html_url": "https://github.com/o/r/security/secret-scanning/1", + "locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations", + "state": "open", + "resolution": null, + "resolved_at": null, + "resolved_by": null, + "secret_type": "mailchimp_api_key", + "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2", + "repository": { + "id": 1, + "name": "n", + "url": "url" + } + }]`) + }) + + ctx := t.Context() + opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key", Direction: "asc", Sort: "updated"} + + alerts, _, err := client.SecretScanning.ListAlertsForEnterprise(ctx, "e", opts) + if err != nil { + t.Errorf("SecretScanning.ListAlertsForEnterprise returned error: %v", err) + } + + want := []*SecretScanningAlert{ + { + Number: Ptr(1), + CreatedAt: refTimestamp(1136178000), + URL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1"), + HTMLURL: Ptr("https://github.com/o/r/security/secret-scanning/1"), + LocationsURL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"), + State: Ptr("open"), + Resolution: nil, + ResolvedAt: nil, + ResolvedBy: nil, + SecretType: Ptr("mailchimp_api_key"), + Secret: Ptr("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"), + Repository: &Repository{ + ID: Ptr(int64(1)), + URL: Ptr("url"), + Name: Ptr("n"), + }, + }, + } + + if !cmp.Equal(alerts, want) { + t.Errorf("SecretScanning.ListAlertsForEnterprise returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForEnterprise" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListAlertsForEnterprise(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListAlertsForEnterprise(ctx, "e", opts) + return resp, err + }) +} + +func TestSecretScanningService_ListAlertsForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key", "sort": "updated", "direction": "asc"}) + + fmt.Fprint(w, `[{ + "number": 1, + "created_at": `+refTimeStr(1136178000)+`, + "url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1", + "html_url": "https://github.com/o/r/security/secret-scanning/1", + "locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations", + "state": "open", + "resolution": null, + "resolved_at": null, + "resolved_by": null, + "secret_type": "mailchimp_api_key", + "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2" + }]`) + }) + + ctx := t.Context() + opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key", Direction: "asc", Sort: "updated"} + + alerts, _, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("SecretScanning.ListAlertsForOrg returned error: %v", err) + } + + want := []*SecretScanningAlert{ + { + Number: Ptr(1), + CreatedAt: refTimestamp(1136178000), + URL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1"), + HTMLURL: Ptr("https://github.com/o/r/security/secret-scanning/1"), + LocationsURL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"), + State: Ptr("open"), + Resolution: nil, + ResolvedAt: nil, + ResolvedBy: nil, + SecretType: Ptr("mailchimp_api_key"), + Secret: Ptr("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"), + }, + } + + if !cmp.Equal(alerts, want) { + t.Errorf("SecretScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListAlertsForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts) + return resp, err + }) +} + +func TestSecretScanningService_ListAlertsForOrgListOptions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key", "per_page": "1", "page": "1", "sort": "updated", "direction": "asc"}) + + fmt.Fprint(w, `[{ + "number": 1, + "created_at": `+refTimeStr(1136178000)+`, + "url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1", + "html_url": "https://github.com/o/r/security/secret-scanning/1", + "locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations", + "state": "open", + "resolution": null, + "resolved_at": null, + "resolved_by": null, + "secret_type": "mailchimp_api_key", + "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2" + }]`) + }) + + ctx := t.Context() + + // Testing pagination by index + opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key", ListOptions: ListOptions{Page: 1, PerPage: 1}, Direction: "asc", Sort: "updated"} + + alerts, _, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts) + if err != nil { + t.Errorf("SecretScanning.ListAlertsForOrg returned error: %v", err) + } + + want := []*SecretScanningAlert{ + { + Number: Ptr(1), + CreatedAt: refTimestamp(1136178000), + URL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1"), + HTMLURL: Ptr("https://github.com/o/r/security/secret-scanning/1"), + LocationsURL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"), + State: Ptr("open"), + Resolution: nil, + ResolvedAt: nil, + ResolvedBy: nil, + SecretType: Ptr("mailchimp_api_key"), + Secret: Ptr("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"), + }, + } + + if !cmp.Equal(alerts, want) { + t.Errorf("SecretScanning.ListAlertsForOrg returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForOrg" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListAlertsForOrg(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListAlertsForOrg(ctx, "o", opts) + return resp, err + }) +} + +func TestSecretScanningService_ListAlertsForRepo(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/secret-scanning/alerts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "open", "secret_type": "mailchimp_api_key", "sort": "updated", "direction": "asc"}) + + fmt.Fprint(w, `[{ + "number": 1, + "created_at": `+refTimeStr(1136178000)+`, + "url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1", + "html_url": "https://github.com/o/r/security/secret-scanning/1", + "locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations", + "state": "open", + "resolution": null, + "resolved_at": null, + "resolved_by": null, + "secret_type": "mailchimp_api_key", + "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2" + }]`) + }) + + ctx := t.Context() + opts := &SecretScanningAlertListOptions{State: "open", SecretType: "mailchimp_api_key", Direction: "asc", Sort: "updated"} + + alerts, _, err := client.SecretScanning.ListAlertsForRepo(ctx, "o", "r", opts) + if err != nil { + t.Errorf("SecretScanning.ListAlertsForRepo returned error: %v", err) + } + + want := []*SecretScanningAlert{ + { + Number: Ptr(1), + CreatedAt: refTimestamp(1136178000), + URL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1"), + HTMLURL: Ptr("https://github.com/o/r/security/secret-scanning/1"), + LocationsURL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"), + State: Ptr("open"), + Resolution: nil, + ResolvedAt: nil, + ResolvedBy: nil, + SecretType: Ptr("mailchimp_api_key"), + Secret: Ptr("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"), + }, + } + + if !cmp.Equal(alerts, want) { + t.Errorf("SecretScanning.ListAlertsForRepo returned %+v, want %+v", alerts, want) + } + + const methodName = "ListAlertsForRepo" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListAlertsForRepo(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListAlertsForRepo(ctx, "o", "r", opts) + return resp, err + }) +} + +func TestSecretScanningService_GetAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/secret-scanning/alerts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `{ + "number": 1, + "created_at": `+refTimeStr(1136178000)+`, + "url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1", + "html_url": "https://github.com/o/r/security/secret-scanning/1", + "locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations", + "state": "open", + "resolution": null, + "resolved_at": null, + "resolved_by": null, + "secret_type": "mailchimp_api_key", + "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2" + }`) + }) + + ctx := t.Context() + + alert, _, err := client.SecretScanning.GetAlert(ctx, "o", "r", 1) + if err != nil { + t.Errorf("SecretScanning.GetAlert returned error: %v", err) + } + + want := &SecretScanningAlert{ + Number: Ptr(1), + CreatedAt: refTimestamp(1136178000), + URL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1"), + HTMLURL: Ptr("https://github.com/o/r/security/secret-scanning/1"), + LocationsURL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"), + State: Ptr("open"), + Resolution: nil, + ResolvedAt: nil, + ResolvedBy: nil, + SecretType: Ptr("mailchimp_api_key"), + Secret: Ptr("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"), + } + + if !cmp.Equal(alert, want) { + t.Errorf("SecretScanning.GetAlert returned %+v, want %+v", alert, want) + } + + const methodName = "GetAlert" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.GetAlert(ctx, "\n", "\n", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.GetAlert(ctx, "o", "r", 1) + return resp, err + }) +} + +func TestSecretScanningService_UpdateAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opts := &SecretScanningAlertUpdateOptions{State: "resolved", Resolution: Ptr("used_in_tests")} + + mux.HandleFunc("/repos/o/r/secret-scanning/alerts/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, opts) + fmt.Fprint(w, `{ + "number": 1, + "created_at": `+refTimeStr(1136178000)+`, + "url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1", + "html_url": "https://github.com/o/r/security/secret-scanning/1", + "locations_url": "https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations", + "state": "resolved", + "resolution": "used_in_tests", + "resolution_comment": "resolution comment", + "resolved_at": `+refTimeStr(1136178001)+`, + "resolved_by": null, + "secret_type": "mailchimp_api_key", + "secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2" + }`) + }) + + ctx := t.Context() + alert, _, err := client.SecretScanning.UpdateAlert(ctx, "o", "r", 1, opts) + if err != nil { + t.Errorf("SecretScanning.UpdateAlert returned error: %v", err) + } + + want := &SecretScanningAlert{ + Number: Ptr(1), + CreatedAt: refTimestamp(1136178000), + URL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1"), + HTMLURL: Ptr("https://github.com/o/r/security/secret-scanning/1"), + LocationsURL: Ptr("https://api.github.com/repos/o/r/secret-scanning/alerts/1/locations"), + State: Ptr("resolved"), + Resolution: Ptr("used_in_tests"), + ResolutionComment: Ptr("resolution comment"), + ResolvedAt: refTimestamp(1136178001), + ResolvedBy: nil, + SecretType: Ptr("mailchimp_api_key"), + Secret: Ptr("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-us2"), + } + + if !cmp.Equal(alert, want) { + t.Errorf("SecretScanning.UpdateAlert returned %+v, want %+v", alert, want) + } + + const methodName = "UpdateAlert" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.UpdateAlert(ctx, "\n", "\n", 1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.UpdateAlert(ctx, "o", "r", 1, opts) + return resp, err + }) +} + +func TestSecretScanningService_ListLocationsForAlert(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/secret-scanning/alerts/1/locations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1", "per_page": "100"}) + + fmt.Fprint(w, `[{ + "type": "commit", + "details": { + "path": "/example/secrets.txt", + "start_line": 1, + "end_line": 1, + "start_column": 1, + "end_column": 64, + "blob_sha": "af5626b4a114abcb82d63db7c8082c3c4756e51b", + "blob_url": "https://api.github.com/repos/o/r/git/blobs/af5626b4a114abcb82d63db7c8082c3c4756e51b", + "commit_sha": "f14d7debf9775f957cf4f1e8176da0786431f72b", + "commit_url": "https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b" + } + }]`) + }) + + ctx := t.Context() + opts := &ListOptions{Page: 1, PerPage: 100} + + locations, _, err := client.SecretScanning.ListLocationsForAlert(ctx, "o", "r", 1, opts) + if err != nil { + t.Errorf("SecretScanning.ListLocationsForAlert returned error: %v", err) + } + + want := []*SecretScanningAlertLocation{ + { + Type: Ptr("commit"), + Details: &SecretScanningAlertLocationDetails{ + Path: Ptr("/example/secrets.txt"), + Startline: Ptr(1), + EndLine: Ptr(1), + StartColumn: Ptr(1), + EndColumn: Ptr(64), + BlobSHA: Ptr("af5626b4a114abcb82d63db7c8082c3c4756e51b"), + BlobURL: Ptr("https://api.github.com/repos/o/r/git/blobs/af5626b4a114abcb82d63db7c8082c3c4756e51b"), + CommitSHA: Ptr("f14d7debf9775f957cf4f1e8176da0786431f72b"), + CommitURL: Ptr("https://api.github.com/repos/o/r/git/commits/f14d7debf9775f957cf4f1e8176da0786431f72b"), + }, + }, + } + + if !cmp.Equal(locations, want) { + t.Errorf("SecretScanning.ListLocationsForAlert returned %+v, want %+v", locations, want) + } + + const methodName = "ListLocationsForAlert" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.ListLocationsForAlert(ctx, "\n", "\n", 1, opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.ListLocationsForAlert(ctx, "o", "r", 1, opts) + return resp, err + }) +} + +func TestSecretScanningService_CreatePushProtectionBypass(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + owner := "o" + repo := "r" + opts := PushProtectionBypassRequest{Reason: "valid reason", PlaceholderID: "bypass-123"} + + mux.HandleFunc(fmt.Sprintf("/repos/%v/%v/secret-scanning/push-protection-bypasses", owner, repo), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, opts) + fmt.Fprint(w, `{ + "reason": "valid reason", + "expire_at": `+refTimeStr(1136178000)+`, + "token_type": "github_token" + }`) + }) + + ctx := t.Context() + + bypass, _, err := client.SecretScanning.CreatePushProtectionBypass(ctx, owner, repo, opts) + if err != nil { + t.Errorf("SecretScanning.CreatePushProtectionBypass returned error: %v", err) + } + + want := &PushProtectionBypass{ + Reason: "valid reason", + ExpireAt: refTimestamp(1136178000), + TokenType: "github_token", + } + + if !cmp.Equal(bypass, want) { + t.Errorf("SecretScanning.CreatePushProtectionBypass returned %+v, want %+v", bypass, want) + } + const methodName = "CreatePushProtectionBypass" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.CreatePushProtectionBypass(ctx, "\n", "\n", opts) + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.CreatePushProtectionBypass(ctx, "o", "r", opts) + return resp, err + }) +} + +func TestSecretScanningService_GetScanHistory(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + owner := "o" + repo := "r" + + mux.HandleFunc(fmt.Sprintf("/repos/%v/%v/secret-scanning/scan-history", owner, repo), func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "incremental_scans": [ + { + "type": "incremental", + "status": "success", + "completed_at": `+refTimeStr(1136178000)+`, + "started_at": `+refTimeStr(1136178001)+` + } + ], + "backfill_scans": [], + "pattern_update_scans": [], + "custom_pattern_backfill_scans": [ + { + "type": "custom_backfill", + "status": "in_progress", + "completed_at": null, + "started_at": `+refTimeStr(1136178002)+`, + "pattern_slug": "my-custom-pattern", + "pattern_scope": "organization" + } + ] + }`) + }) + + ctx := t.Context() + + history, _, err := client.SecretScanning.GetScanHistory(ctx, owner, repo) + if err != nil { + t.Errorf("SecretScanning.GetScanHistory returned error: %v", err) + } + + want := &SecretScanningScanHistory{ + IncrementalScans: []*SecretsScan{ + {Type: "incremental", Status: "success", CompletedAt: refTimestamp(1136178000), StartedAt: refTimestamp(1136178001)}, + }, + BackfillScans: []*SecretsScan{}, + PatternUpdateScans: []*SecretsScan{}, + CustomPatternBackfillScans: []*CustomPatternBackfillScan{ + { + SecretsScan: SecretsScan{Type: "custom_backfill", Status: "in_progress", CompletedAt: nil, StartedAt: refTimestamp(1136178002)}, + PatternSlug: Ptr("my-custom-pattern"), + PatternScope: Ptr("organization"), + }, + }, + } + + if !cmp.Equal(history, want) { + t.Errorf("SecretScanning.GetScanHistory returned %+v, want %+v", history, want) + } + const methodName = "GetScanHistory" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecretScanning.GetScanHistory(ctx, "\n", "\n") + return err + }) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecretScanning.GetScanHistory(ctx, "o", "r") + return resp, err + }) +} diff --git a/github/security_advisories.go b/github/security_advisories.go new file mode 100644 index 00000000000..f705d1adc5e --- /dev/null +++ b/github/security_advisories.go @@ -0,0 +1,285 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "errors" + "fmt" +) + +// SecurityAdvisoriesService handles communication with the security advisories +// related methods of the GitHub API. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories?apiVersion=2022-11-28 +type SecurityAdvisoriesService service + +// SecurityAdvisorySubmission represents the Security Advisory Submission. +type SecurityAdvisorySubmission struct { + // Accepted represents whether a private vulnerability report was accepted by the repository's administrators. + Accepted *bool `json:"accepted,omitempty"` +} + +// RepoAdvisoryCredit represents the credit object for a repository Security Advisory. +type RepoAdvisoryCredit struct { + Login *string `json:"login,omitempty"` + Type *string `json:"type,omitempty"` +} + +// RepoAdvisoryCreditDetailed represents a credit given to a user for a repository Security Advisory. +type RepoAdvisoryCreditDetailed struct { + User *User `json:"user,omitempty"` + Type *string `json:"type,omitempty"` + State *string `json:"state,omitempty"` +} + +// ListRepositorySecurityAdvisoriesOptions specifies the optional parameters to list the repository security advisories. +type ListRepositorySecurityAdvisoriesOptions struct { + ListCursorOptions + + // Direction in which to sort advisories. Possible values are: asc, desc. + // Default is "asc". + Direction string `url:"direction,omitempty"` + + // Sort specifies how to sort advisories. Possible values are: created, updated, + // and published. Default value is "created". + Sort string `url:"sort,omitempty"` + + // State filters advisories based on their state. Possible values are: triage, draft, published, closed. + State string `url:"state,omitempty"` +} + +// ListGlobalSecurityAdvisoriesOptions specifies the optional parameters to list the global security advisories. +type ListGlobalSecurityAdvisoriesOptions struct { + ListCursorOptions + + // If specified, only advisories with this GHSA (GitHub Security Advisory) identifier will be returned. + GHSAID *string `url:"ghsa_id,omitempty"` + + // If specified, only advisories of this type will be returned. + // By default, a request with no other parameters defined will only return reviewed advisories that are not malware. + // Default: reviewed + // Can be one of: reviewed, malware, unreviewed + Type *string `url:"type,omitempty"` + + // If specified, only advisories with this CVE (Common Vulnerabilities and Exposures) identifier will be returned. + CVEID *string `url:"cve_id,omitempty"` + + // If specified, only advisories for these ecosystems will be returned. + // Can be one of: actions, composer, erlang, go, maven, npm, nuget, other, pip, pub, rubygems, rust + Ecosystem *string `url:"ecosystem,omitempty"` + + // If specified, only advisories with these severities will be returned. + // Can be one of: unknown, low, medium, high, critical + Severity *string `url:"severity,omitempty"` + + // If specified, only advisories with these Common Weakness Enumerations (CWEs) will be returned. + // Example: cwes=79,284,22 or cwes[]=79&cwes[]=284&cwes[]=22 + CWEs []string `url:"cwes,omitempty"` + + // Whether to only return advisories that have been withdrawn. + IsWithdrawn *bool `url:"is_withdrawn,omitempty"` + + // If specified, only return advisories that affect any of package or package@version. + // A maximum of 1000 packages can be specified. If the query parameter causes + // the URL to exceed the maximum URL length supported by your client, you must specify fewer packages. + // Example: affects=package1,package2@1.0.0,package3@^2.0.0 or affects[]=package1&affects[]=package2@1.0.0 + Affects *string `url:"affects,omitempty"` + + // If specified, only return advisories that were published on a date or date range. + Published *string `url:"published,omitempty"` + + // If specified, only return advisories that were updated on a date or date range. + Updated *string `url:"updated,omitempty"` + + // If specified, only show advisories that were updated or published on a date or date range. + Modified *string `url:"modified,omitempty"` +} + +// GlobalSecurityAdvisory represents the global security advisory object response. +type GlobalSecurityAdvisory struct { + SecurityAdvisory + ID *int64 `json:"id,omitempty"` + RepositoryAdvisoryURL *string `json:"repository_advisory_url,omitempty"` + Type *string `json:"type,omitempty"` + SourceCodeLocation *string `json:"source_code_location,omitempty"` + References []string `json:"references,omitempty"` + Vulnerabilities []*GlobalSecurityVulnerability `json:"vulnerabilities,omitempty"` + GithubReviewedAt *Timestamp `json:"github_reviewed_at,omitempty"` + NVDPublishedAt *Timestamp `json:"nvd_published_at,omitempty"` + Credits []*Credit `json:"credits,omitempty"` +} + +// GlobalSecurityVulnerability represents a vulnerability for a global security advisory. +type GlobalSecurityVulnerability struct { + Package *VulnerabilityPackage `json:"package,omitempty"` + FirstPatchedVersion *string `json:"first_patched_version,omitempty"` + VulnerableVersionRange *string `json:"vulnerable_version_range,omitempty"` + VulnerableFunctions []string `json:"vulnerable_functions,omitempty"` +} + +// Credit represents the credit object for a global security advisory. +type Credit struct { + User *User `json:"user,omitempty"` + Type *string `json:"type,omitempty"` +} + +// RequestCVE requests a Common Vulnerabilities and Exposures (CVE) for a repository security advisory. +// The ghsaID is the GitHub Security Advisory identifier of the advisory. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#request-a-cve-for-a-repository-security-advisory +// +//meta:operation POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/cve +func (s *SecurityAdvisoriesService) RequestCVE(ctx context.Context, owner, repo, ghsaID string) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/security-advisories/%v/cve", owner, repo, ghsaID) + + req, err := s.client.NewRequest(ctx, "POST", url, nil) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(req, nil) + if err != nil { + if errors.As(err, new(*AcceptedError)) { + return resp, nil + } + + return resp, err + } + + return resp, nil +} + +// CreateTemporaryPrivateFork creates a temporary private fork to collaborate on fixing a security vulnerability in your repository. +// The ghsaID is the GitHub Security Advisory identifier of the advisory. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#create-a-temporary-private-fork +// +//meta:operation POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/forks +func (s *SecurityAdvisoriesService) CreateTemporaryPrivateFork(ctx context.Context, owner, repo, ghsaID string) (*Repository, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/security-advisories/%v/forks", owner, repo, ghsaID) + + req, err := s.client.NewRequest(ctx, "POST", url, nil) + if err != nil { + return nil, nil, err + } + + var fork *Repository + resp, err := s.client.Do(req, &fork) + if err != nil { + var aerr *AcceptedError + if errors.As(err, &aerr) { + if err := json.Unmarshal(aerr.Raw, &fork); err != nil { + return fork, resp, err + } + + return fork, resp, err + } + return nil, resp, err + } + + return fork, resp, nil +} + +// ListRepositorySecurityAdvisoriesForOrg lists the repository security advisories for an organization. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories-for-an-organization +// +//meta:operation GET /orgs/{org}/security-advisories +func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisoriesForOrg(ctx context.Context, org string, opts *ListRepositorySecurityAdvisoriesOptions) ([]*SecurityAdvisory, *Response, error) { + url := fmt.Sprintf("orgs/%v/security-advisories", org) + url, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisories []*SecurityAdvisory + resp, err := s.client.Do(req, &advisories) + if err != nil { + return nil, resp, err + } + + return advisories, resp, nil +} + +// ListRepositorySecurityAdvisories lists the security advisories in a repository. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/repository-advisories?apiVersion=2022-11-28#list-repository-security-advisories +// +//meta:operation GET /repos/{owner}/{repo}/security-advisories +func (s *SecurityAdvisoriesService) ListRepositorySecurityAdvisories(ctx context.Context, owner, repo string, opts *ListRepositorySecurityAdvisoriesOptions) ([]*SecurityAdvisory, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/security-advisories", owner, repo) + url, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisories []*SecurityAdvisory + resp, err := s.client.Do(req, &advisories) + if err != nil { + return nil, resp, err + } + + return advisories, resp, nil +} + +// ListGlobalSecurityAdvisories lists all global security advisories. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/global-advisories?apiVersion=2022-11-28#list-global-security-advisories +// +//meta:operation GET /advisories +func (s *SecurityAdvisoriesService) ListGlobalSecurityAdvisories(ctx context.Context, opts *ListGlobalSecurityAdvisoriesOptions) ([]*GlobalSecurityAdvisory, *Response, error) { + url := "advisories" + url, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisories []*GlobalSecurityAdvisory + resp, err := s.client.Do(req, &advisories) + if err != nil { + return nil, resp, err + } + + return advisories, resp, nil +} + +// GetGlobalSecurityAdvisories gets a global security advisory using its GitHub Security Advisory (GHSA) identifier. +// +// GitHub API docs: https://docs.github.com/rest/security-advisories/global-advisories?apiVersion=2022-11-28#get-a-global-security-advisory +// +//meta:operation GET /advisories/{ghsa_id} +func (s *SecurityAdvisoriesService) GetGlobalSecurityAdvisories(ctx context.Context, ghsaID string) (*GlobalSecurityAdvisory, *Response, error) { + url := fmt.Sprintf("advisories/%v", ghsaID) + req, err := s.client.NewRequest(ctx, "GET", url, nil) + if err != nil { + return nil, nil, err + } + + var advisory *GlobalSecurityAdvisory + resp, err := s.client.Do(req, &advisory) + if err != nil { + return nil, resp, err + } + + return advisory, resp, nil +} diff --git a/github/security_advisories_test.go b/github/security_advisories_test.go new file mode 100644 index 00000000000..cea5b9abe90 --- /dev/null +++ b/github/security_advisories_test.go @@ -0,0 +1,1145 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSecurityAdvisoriesService_RequestCVE(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories/ghsa_id_ok/cve", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusOK) + }) + + mux.HandleFunc("/repos/o/r/security-advisories/ghsa_id_accepted/cve", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusAccepted) + }) + + ctx := t.Context() + _, err := client.SecurityAdvisories.RequestCVE(ctx, "o", "r", "ghsa_id_ok") + if err != nil { + t.Errorf("SecurityAdvisoriesService.RequestCVE returned error: %v", err) + } + + _, err = client.SecurityAdvisories.RequestCVE(ctx, "o", "r", "ghsa_id_accepted") + if err != nil { + t.Errorf("SecurityAdvisoriesService.RequestCVE returned error: %v", err) + } + + const methodName = "RequestCVE" + testBadOptions(t, methodName, func() (err error) { + _, err = client.SecurityAdvisories.RequestCVE(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.SecurityAdvisories.RequestCVE(ctx, "o", "r", "ghsa_id") + if err == nil { + t.Errorf("testNewRequestAndDoFailure %v should have return err", methodName) + } + return resp, err + }) +} + +func TestSecurityAdvisoriesService_CreateTemporaryPrivateFork(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories/ghsa_id/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 1, + "node_id": "R_kgDPP3c6pQ", + "owner": { + "login": "owner", + "id": 2, + "node_id": "MDQ6VXFGcjYyMjcyMTQw", + "avatar_url": "https://avatars.githubusercontent.com/u/111111?v=4", + "html_url": "https://github.com/xxxxx", + "gravatar_id": "", + "type": "User", + "site_admin": false, + "url": "https://api.github.com/users/owner", + "events_url": "https://api.github.com/users/owner/events{/privacy}", + "following_url": "https://api.github.com/users/owner/following{/other_user}", + "followers_url": "https://api.github.com/users/owner/followers", + "gists_url": "https://api.github.com/users/owner/gists{/gist_id}", + "organizations_url": "https://api.github.com/users/owner/orgs", + "received_events_url": "https://api.github.com/users/owner/received_events", + "repos_url": "https://api.github.com/users/owner/repos", + "starred_url": "https://api.github.com/users/owner/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/owner/subscriptions" + }, + "name": "repo-ghsa-xxxx-xxxx-xxxx", + "full_name": "owner/repo-ghsa-xxxx-xxxx-xxxx", + "default_branch": "master", + "created_at": `+refTimeStr(1136178000)+`, + "pushed_at": `+refTimeStr(1136178001)+`, + "updated_at": `+refTimeStr(1136178002)+`, + "html_url": "https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx", + "clone_url": "https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git", + "git_url": "git://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git", + "ssh_url": "git@github.com:owner/repo-ghsa-xxxx-xxxx-xxxx.git", + "svn_url": "https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx", + "fork": false, + "forks_count": 0, + "network_count": 0, + "open_issues_count": 0, + "open_issues": 0, + "stargazers_count": 0, + "subscribers_count": 0, + "watchers_count": 0, + "watchers": 0, + "size": 0, + "permissions": { + "admin": true, + "maintain": true, + "pull": true, + "push": true, + "triage": true + }, + "allow_forking": true, + "web_commit_signoff_required": false, + "archived": false, + "disabled": false, + "private": true, + "has_issues": false, + "has_wiki": false, + "has_pages": false, + "has_projects": false, + "has_downloads": false, + "has_discussions": false, + "is_template": false, + "url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx", + "archive_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/assignees{/user}", + "blobs_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/comments{/number}", + "commits_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/commits{/sha}", + "compare_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contents/{+path}", + "contributors_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contributors", + "deployments_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/deployments", + "downloads_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/downloads", + "events_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/events", + "forks_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/forks", + "git_commits_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/tags{/sha}", + "hooks_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/hooks", + "issue_comment_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/events{/number}", + "issues_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues{/number}", + "keys_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/keys{/key_id}", + "labels_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/labels{/name}", + "languages_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/languages", + "merges_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/merges", + "milestones_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/milestones{/number}", + "notifications_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/pulls{/number}", + "releases_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/releases{/id}", + "stargazers_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/stargazers", + "statuses_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscribers", + "subscription_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscription", + "tags_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/tags", + "teams_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/teams", + "visibility": "private" + }`) + }) + + ctx := t.Context() + fork, _, err := client.SecurityAdvisories.CreateTemporaryPrivateFork(ctx, "o", "r", "ghsa_id") + if err != nil { + t.Errorf("SecurityAdvisoriesService.CreateTemporaryPrivateFork returned error: %v", err) + } + + want := &Repository{ + ID: Ptr(int64(1)), + NodeID: Ptr("R_kgDPP3c6pQ"), + Owner: &User{ + Login: Ptr("owner"), + ID: Ptr(int64(2)), + NodeID: Ptr("MDQ6VXFGcjYyMjcyMTQw"), + AvatarURL: Ptr("https://avatars.githubusercontent.com/u/111111?v=4"), + HTMLURL: Ptr("https://github.com/xxxxx"), + GravatarID: Ptr(""), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + URL: Ptr("https://api.github.com/users/owner"), + EventsURL: Ptr("https://api.github.com/users/owner/events{/privacy}"), + FollowingURL: Ptr("https://api.github.com/users/owner/following{/other_user}"), + FollowersURL: Ptr("https://api.github.com/users/owner/followers"), + GistsURL: Ptr("https://api.github.com/users/owner/gists{/gist_id}"), + OrganizationsURL: Ptr("https://api.github.com/users/owner/orgs"), + ReceivedEventsURL: Ptr("https://api.github.com/users/owner/received_events"), + ReposURL: Ptr("https://api.github.com/users/owner/repos"), + StarredURL: Ptr("https://api.github.com/users/owner/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/owner/subscriptions"), + }, + Name: Ptr("repo-ghsa-xxxx-xxxx-xxxx"), + FullName: Ptr("owner/repo-ghsa-xxxx-xxxx-xxxx"), + DefaultBranch: Ptr("master"), + CreatedAt: refTimestamp(1136178000), + PushedAt: refTimestamp(1136178001), + UpdatedAt: refTimestamp(1136178002), + HTMLURL: Ptr("https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx"), + CloneURL: Ptr("https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git"), + GitURL: Ptr("git://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git"), + SSHURL: Ptr("git@github.com:owner/repo-ghsa-xxxx-xxxx-xxxx.git"), + SVNURL: Ptr("https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx"), + Fork: Ptr(false), + ForksCount: Ptr(0), + NetworkCount: Ptr(0), + OpenIssuesCount: Ptr(0), + OpenIssues: Ptr(0), + StargazersCount: Ptr(0), + SubscribersCount: Ptr(0), + WatchersCount: Ptr(0), + Watchers: Ptr(0), + Size: Ptr(0), + Permissions: &RepositoryPermissions{ + Admin: Ptr(true), + Maintain: Ptr(true), + Pull: Ptr(true), + Push: Ptr(true), + Triage: Ptr(true), + }, + AllowForking: Ptr(true), + WebCommitSignoffRequired: Ptr(false), + Archived: Ptr(false), + Disabled: Ptr(false), + Private: Ptr(true), + HasIssues: Ptr(false), + HasWiki: Ptr(false), + HasPages: Ptr(false), + HasProjects: Ptr(false), + HasDownloads: Ptr(false), + HasDiscussions: Ptr(false), + IsTemplate: Ptr(false), + URL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx"), + ArchiveURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/{archive_format}{/ref}"), + AssigneesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/assignees{/user}"), + BlobsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/blobs{/sha}"), + BranchesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/branches{/branch}"), + CollaboratorsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/collaborators{/collaborator}"), + CommentsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/comments{/number}"), + CommitsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/commits{/sha}"), + CompareURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/compare/{base}...{head}"), + ContentsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contents/{+path}"), + ContributorsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contributors"), + DeploymentsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/deployments"), + DownloadsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/downloads"), + EventsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/events"), + ForksURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/forks"), + GitCommitsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/commits{/sha}"), + GitRefsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/refs{/sha}"), + GitTagsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/tags{/sha}"), + HooksURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/hooks"), + IssueCommentURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/comments{/number}"), + IssueEventsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/events{/number}"), + IssuesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues{/number}"), + KeysURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/keys{/key_id}"), + LabelsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/labels{/name}"), + LanguagesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/languages"), + MergesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/merges"), + MilestonesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/milestones{/number}"), + NotificationsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/notifications{?since,all,participating}"), + PullsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/pulls{/number}"), + ReleasesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/releases{/id}"), + StargazersURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/stargazers"), + StatusesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/statuses/{sha}"), + SubscribersURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscribers"), + SubscriptionURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscription"), + TagsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/tags"), + TeamsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/teams"), + Visibility: Ptr("private"), + } + if !cmp.Equal(fork, want) { + t.Errorf("SecurityAdvisoriesService.CreateTemporaryPrivateFork returned %+v, want %+v", fork, want) + } + + const methodName = "CreateTemporaryPrivateFork" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.CreateTemporaryPrivateFork(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.CreateTemporaryPrivateFork(ctx, "o", "r", "ghsa_id") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSecurityAdvisoriesService_CreateTemporaryPrivateFork_deferred(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories/ghsa_id/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, `{ + "id": 1, + "node_id": "R_kgDPP3c6pQ", + "owner": { + "login": "owner", + "id": 2, + "node_id": "MDQ6VXFGcjYyMjcyMTQw", + "avatar_url": "https://avatars.githubusercontent.com/u/111111?v=4", + "html_url": "https://github.com/xxxxx", + "gravatar_id": "", + "type": "User", + "site_admin": false, + "url": "https://api.github.com/users/owner", + "events_url": "https://api.github.com/users/owner/events{/privacy}", + "following_url": "https://api.github.com/users/owner/following{/other_user}", + "followers_url": "https://api.github.com/users/owner/followers", + "gists_url": "https://api.github.com/users/owner/gists{/gist_id}", + "organizations_url": "https://api.github.com/users/owner/orgs", + "received_events_url": "https://api.github.com/users/owner/received_events", + "repos_url": "https://api.github.com/users/owner/repos", + "starred_url": "https://api.github.com/users/owner/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/owner/subscriptions" + }, + "name": "repo-ghsa-xxxx-xxxx-xxxx", + "full_name": "owner/repo-ghsa-xxxx-xxxx-xxxx", + "default_branch": "master", + "created_at": `+refTimeStr(1136178000)+`, + "pushed_at": `+refTimeStr(1136178001)+`, + "updated_at": `+refTimeStr(1136178002)+`, + "html_url": "https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx", + "clone_url": "https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git", + "git_url": "git://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git", + "ssh_url": "git@github.com:owner/repo-ghsa-xxxx-xxxx-xxxx.git", + "svn_url": "https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx", + "fork": false, + "forks_count": 0, + "network_count": 0, + "open_issues_count": 0, + "open_issues": 0, + "stargazers_count": 0, + "subscribers_count": 0, + "watchers_count": 0, + "watchers": 0, + "size": 0, + "permissions": { + "admin": true, + "maintain": true, + "pull": true, + "push": true, + "triage": true + }, + "allow_forking": true, + "web_commit_signoff_required": false, + "archived": false, + "disabled": false, + "private": true, + "has_issues": false, + "has_wiki": false, + "has_pages": false, + "has_projects": false, + "has_downloads": false, + "has_discussions": false, + "is_template": false, + "url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx", + "archive_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/assignees{/user}", + "blobs_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/comments{/number}", + "commits_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/commits{/sha}", + "compare_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contents/{+path}", + "contributors_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contributors", + "deployments_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/deployments", + "downloads_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/downloads", + "events_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/events", + "forks_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/forks", + "git_commits_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/tags{/sha}", + "hooks_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/hooks", + "issue_comment_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/events{/number}", + "issues_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues{/number}", + "keys_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/keys{/key_id}", + "labels_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/labels{/name}", + "languages_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/languages", + "merges_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/merges", + "milestones_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/milestones{/number}", + "notifications_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/pulls{/number}", + "releases_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/releases{/id}", + "stargazers_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/stargazers", + "statuses_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscribers", + "subscription_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscription", + "tags_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/tags", + "teams_url": "https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/teams", + "visibility": "private" + }`) + }) + + ctx := t.Context() + fork, _, err := client.SecurityAdvisories.CreateTemporaryPrivateFork(ctx, "o", "r", "ghsa_id") + if !errors.As(err, new(*AcceptedError)) { + t.Errorf("SecurityAdvisoriesService.CreateTemporaryPrivateFork returned error: %v (want AcceptedError)", err) + } + + want := &Repository{ + ID: Ptr(int64(1)), + NodeID: Ptr("R_kgDPP3c6pQ"), + Owner: &User{ + Login: Ptr("owner"), + ID: Ptr(int64(2)), + NodeID: Ptr("MDQ6VXFGcjYyMjcyMTQw"), + AvatarURL: Ptr("https://avatars.githubusercontent.com/u/111111?v=4"), + HTMLURL: Ptr("https://github.com/xxxxx"), + GravatarID: Ptr(""), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + URL: Ptr("https://api.github.com/users/owner"), + EventsURL: Ptr("https://api.github.com/users/owner/events{/privacy}"), + FollowingURL: Ptr("https://api.github.com/users/owner/following{/other_user}"), + FollowersURL: Ptr("https://api.github.com/users/owner/followers"), + GistsURL: Ptr("https://api.github.com/users/owner/gists{/gist_id}"), + OrganizationsURL: Ptr("https://api.github.com/users/owner/orgs"), + ReceivedEventsURL: Ptr("https://api.github.com/users/owner/received_events"), + ReposURL: Ptr("https://api.github.com/users/owner/repos"), + StarredURL: Ptr("https://api.github.com/users/owner/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/owner/subscriptions"), + }, + Name: Ptr("repo-ghsa-xxxx-xxxx-xxxx"), + FullName: Ptr("owner/repo-ghsa-xxxx-xxxx-xxxx"), + DefaultBranch: Ptr("master"), + CreatedAt: refTimestamp(1136178000), + PushedAt: refTimestamp(1136178001), + UpdatedAt: refTimestamp(1136178002), + HTMLURL: Ptr("https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx"), + CloneURL: Ptr("https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git"), + GitURL: Ptr("git://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx.git"), + SSHURL: Ptr("git@github.com:owner/repo-ghsa-xxxx-xxxx-xxxx.git"), + SVNURL: Ptr("https://github.com/owner/repo-ghsa-xxxx-xxxx-xxxx"), + Fork: Ptr(false), + ForksCount: Ptr(0), + NetworkCount: Ptr(0), + OpenIssuesCount: Ptr(0), + OpenIssues: Ptr(0), + StargazersCount: Ptr(0), + SubscribersCount: Ptr(0), + WatchersCount: Ptr(0), + Watchers: Ptr(0), + Size: Ptr(0), + Permissions: &RepositoryPermissions{ + Admin: Ptr(true), + Maintain: Ptr(true), + Pull: Ptr(true), + Push: Ptr(true), + Triage: Ptr(true), + }, + AllowForking: Ptr(true), + WebCommitSignoffRequired: Ptr(false), + Archived: Ptr(false), + Disabled: Ptr(false), + Private: Ptr(true), + HasIssues: Ptr(false), + HasWiki: Ptr(false), + HasPages: Ptr(false), + HasProjects: Ptr(false), + HasDownloads: Ptr(false), + HasDiscussions: Ptr(false), + IsTemplate: Ptr(false), + URL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx"), + ArchiveURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/{archive_format}{/ref}"), + AssigneesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/assignees{/user}"), + BlobsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/blobs{/sha}"), + BranchesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/branches{/branch}"), + CollaboratorsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/collaborators{/collaborator}"), + CommentsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/comments{/number}"), + CommitsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/commits{/sha}"), + CompareURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/compare/{base}...{head}"), + ContentsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contents/{+path}"), + ContributorsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/contributors"), + DeploymentsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/deployments"), + DownloadsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/downloads"), + EventsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/events"), + ForksURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/forks"), + GitCommitsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/commits{/sha}"), + GitRefsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/refs{/sha}"), + GitTagsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/git/tags{/sha}"), + HooksURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/hooks"), + IssueCommentURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/comments{/number}"), + IssueEventsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues/events{/number}"), + IssuesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/issues{/number}"), + KeysURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/keys{/key_id}"), + LabelsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/labels{/name}"), + LanguagesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/languages"), + MergesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/merges"), + MilestonesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/milestones{/number}"), + NotificationsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/notifications{?since,all,participating}"), + PullsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/pulls{/number}"), + ReleasesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/releases{/id}"), + StargazersURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/stargazers"), + StatusesURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/statuses/{sha}"), + SubscribersURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscribers"), + SubscriptionURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/subscription"), + TagsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/tags"), + TeamsURL: Ptr("https://api.github.com/repos/owner/repo-ghsa-xxxx-xxxx-xxxx/teams"), + Visibility: Ptr("private"), + } + if !cmp.Equal(fork, want) { + t.Errorf("SecurityAdvisoriesService.CreateTemporaryPrivateFork returned %+v, want %+v", fork, want) + } +} + +func TestSecurityAdvisoriesService_CreateTemporaryPrivateFork_deferred_badBody(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories/ghsa_id/forks", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + w.WriteHeader(http.StatusAccepted) + fmt.Fprint(w, `{invalid json`) + }) + + ctx := t.Context() + fork, _, err := client.SecurityAdvisories.CreateTemporaryPrivateFork(ctx, "o", "r", "ghsa_id") + if err == nil { + t.Fatal("SecurityAdvisories.CreateTemporaryPrivateFork returned nil error") + } + if fork != nil { + t.Errorf("SecurityAdvisories.CreateTemporaryPrivateFork returned non-nil fork: %+v", fork) + } +} + +func TestSecurityAdvisoriesService_CreateTemporaryPrivateFork_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.SecurityAdvisories.CreateTemporaryPrivateFork(ctx, "%", "r", "ghsa_id") + testURLParseError(t, err) +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg_BadRequest(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + http.Error(w, "Bad Request", 400) + }) + + ctx := t.Context() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if err == nil { + t.Error("Expected HTTP 400 response") + } + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %v, want %v", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg_NotFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "draft"}) + + http.NotFound(w, r) + }) + + ctx := t.Context() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", &ListRepositorySecurityAdvisoriesOptions{ + State: "draft", + }) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %v, want %v", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisoriesForOrg(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[ + { + "ghsa_id": "GHSA-abcd-1234-efgh", + "cve_id": "CVE-2050-00000" + } + ]`)) + }) + + ctx := t.Context() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if err != nil { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg returned error: %v, want nil", err) + } + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg return status %v, want %v", got, want) + } + + want := []*SecurityAdvisory{ + { + GHSAID: Ptr("GHSA-abcd-1234-efgh"), + CVEID: Ptr("CVE-2050-00000"), + }, + } + if !cmp.Equal(advisories, want) { + t.Errorf("ListRepositorySecurityAdvisoriesForOrg returned %+v, want %+v", advisories, want) + } + + methodName := "ListRepositorySecurityAdvisoriesForOrg" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "\n", &ListRepositorySecurityAdvisoriesOptions{ + Sort: "\n", + }) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisoriesForOrg(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories_BadRequest(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + http.Error(w, "Bad Request", 400) + }) + + ctx := t.Context() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if err == nil { + t.Error("Expected HTTP 400 response") + } + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %v, want %v", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisories return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories_NotFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"state": "draft"}) + + http.NotFound(w, r) + }) + + ctx := t.Context() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", &ListRepositorySecurityAdvisoriesOptions{ + State: "draft", + }) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %v, want %v", got, want) + } + if advisories != nil { + t.Errorf("ListRepositorySecurityAdvisories return %+v, want nil", advisories) + } +} + +func TestSecurityAdvisoriesService_ListRepositorySecurityAdvisories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/security-advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + w.WriteHeader(http.StatusOK) + assertWrite(t, w, []byte(`[ + { + "ghsa_id": "GHSA-abcd-1234-efgh", + "cve_id": "CVE-2050-00000" + } + ]`)) + }) + + ctx := t.Context() + advisories, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if err != nil { + t.Errorf("ListRepositorySecurityAdvisories returned error: %v, want nil", err) + } + if got, want := resp.Response.StatusCode, http.StatusOK; got != want { + t.Errorf("ListRepositorySecurityAdvisories return status %v, want %v", got, want) + } + + want := []*SecurityAdvisory{ + { + GHSAID: Ptr("GHSA-abcd-1234-efgh"), + CVEID: Ptr("CVE-2050-00000"), + }, + } + if !cmp.Equal(advisories, want) { + t.Errorf("ListRepositorySecurityAdvisories returned %+v, want %+v", advisories, want) + } + + methodName := "ListRepositorySecurityAdvisories" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "\n", "\n", &ListRepositorySecurityAdvisoriesOptions{ + Sort: "\n", + }) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.ListRepositorySecurityAdvisories(ctx, "o", "r", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestListGlobalSecurityAdvisories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/advisories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"cve_id": "CVE-xoxo-1234"}) + + fmt.Fprint(w, `[{ + "id": 1, + "ghsa_id": "GHSA-xoxo-1234-xoxo", + "cve_id": "CVE-xoxo-1234", + "url": "https://api.github.com/advisories/GHSA-xoxo-1234-xoxo", + "html_url": "https://github.com/advisories/GHSA-xoxo-1234-xoxo", + "repository_advisory_url": "https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo", + "summary": "Heartbleed security advisory", + "description": "This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information.", + "type": "reviewed", + "severity": "high", + "source_code_location": "https://github.com/project/a-package", + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-xoxo-1234-xoxo" + }, + { + "type": "CVE", + "value": "CVE-xoxo-1234" + } + ], + "references": ["https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"], + "published_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "github_reviewed_at": `+refTimeStr(1136178002)+`, + "nvd_published_at": `+refTimeStr(1136178003)+`, + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "a-package" + }, + "first_patched_version": "1.0.3", + "vulnerable_version_range": "<=1.0.2", + "vulnerable_functions": ["a_function"] + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H", + "score": 7.6 + }, + "cvss_severities": { + "cvss_v3": {"vector_string": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H", "score": 7.6}, + "cvss_v4": {"vector_string": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N", "score": 8.7} + }, + "cwes": [ + { + "cwe_id": "CWE-400", + "name": "Uncontrolled Resource Consumption" + } + ], + "credits": [ + { + "user": { + "login": "user", + "id": 1, + "node_id": "12=", + "avatar_url": "a", + "gravatar_id": "", + "url": "a", + "html_url": "b", + "followers_url": "b", + "following_url": "c", + "gists_url": "d", + "starred_url": "e", + "subscriptions_url": "f", + "organizations_url": "g", + "repos_url": "h", + "events_url": "i", + "received_events_url": "j", + "type": "User", + "site_admin": false + }, + "type": "analyst" + } + ] + } + ]`) + }) + + ctx := t.Context() + opts := &ListGlobalSecurityAdvisoriesOptions{CVEID: Ptr("CVE-xoxo-1234")} + + advisories, _, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(ctx, opts) + if err != nil { + t.Errorf("SecurityAdvisories.ListGlobalSecurityAdvisories returned error: %v", err) + } + + want := []*GlobalSecurityAdvisory{ + { + ID: Ptr(int64(1)), + SecurityAdvisory: SecurityAdvisory{ + GHSAID: Ptr("GHSA-xoxo-1234-xoxo"), + CVEID: Ptr("CVE-xoxo-1234"), + URL: Ptr("https://api.github.com/advisories/GHSA-xoxo-1234-xoxo"), + HTMLURL: Ptr("https://github.com/advisories/GHSA-xoxo-1234-xoxo"), + Severity: Ptr("high"), + Summary: Ptr("Heartbleed security advisory"), + Description: Ptr("This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information."), + Identifiers: []*AdvisoryIdentifier{ + { + Type: Ptr("GHSA"), + Value: Ptr("GHSA-xoxo-1234-xoxo"), + }, + { + Type: Ptr("CVE"), + Value: Ptr("CVE-xoxo-1234"), + }, + }, + PublishedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + WithdrawnAt: nil, + CVSS: &AdvisoryCVSS{ + VectorString: Ptr("CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H"), + Score: Ptr(7.6), + }, + CVSSSeverities: &AdvisoryCVSSSeverities{ + CVSSV3: &AdvisoryCVSS{ + VectorString: Ptr("CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H"), + Score: Ptr(7.6), + }, + CVSSV4: &AdvisoryCVSS{ + VectorString: Ptr("CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"), + Score: Ptr(8.7), + }, + }, + CWEs: []*AdvisoryCWEs{ + { + CWEID: Ptr("CWE-400"), + Name: Ptr("Uncontrolled Resource Consumption"), + }, + }, + }, + References: []string{"https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"}, + Vulnerabilities: []*GlobalSecurityVulnerability{ + { + Package: &VulnerabilityPackage{ + Ecosystem: Ptr("npm"), + Name: Ptr("a-package"), + }, + FirstPatchedVersion: Ptr("1.0.3"), + VulnerableVersionRange: Ptr("<=1.0.2"), + VulnerableFunctions: []string{"a_function"}, + }, + }, + RepositoryAdvisoryURL: Ptr("https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo"), + Type: Ptr("reviewed"), + SourceCodeLocation: Ptr("https://github.com/project/a-package"), + GithubReviewedAt: refTimestamp(1136178002), + NVDPublishedAt: refTimestamp(1136178003), + Credits: []*Credit{ + { + User: &User{ + Login: Ptr("user"), + ID: Ptr(int64(1)), + NodeID: Ptr("12="), + AvatarURL: Ptr("a"), + GravatarID: Ptr(""), + URL: Ptr("a"), + HTMLURL: Ptr("b"), + FollowersURL: Ptr("b"), + FollowingURL: Ptr("c"), + GistsURL: Ptr("d"), + StarredURL: Ptr("e"), + SubscriptionsURL: Ptr("f"), + OrganizationsURL: Ptr("g"), + ReposURL: Ptr("h"), + EventsURL: Ptr("i"), + ReceivedEventsURL: Ptr("j"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + Type: Ptr("analyst"), + }, + }, + }, + } + + if !cmp.Equal(advisories, want) { + t.Errorf("SecurityAdvisories.ListGlobalSecurityAdvisories %+v, want %+v", advisories, want) + } + + const methodName = "ListGlobalSecurityAdvisories" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + _, resp, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(ctx, nil) + return resp, err + }) +} + +func TestGetGlobalSecurityAdvisories(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/advisories/GHSA-xoxo-1234-xoxo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `{ + "id": 1, + "ghsa_id": "GHSA-xoxo-1234-xoxo", + "cve_id": "CVE-xoxo-1234", + "url": "https://api.github.com/advisories/GHSA-xoxo-1234-xoxo", + "html_url": "https://github.com/advisories/GHSA-xoxo-1234-xoxo", + "repository_advisory_url": "https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo", + "summary": "Heartbleed security advisory", + "description": "This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information.", + "type": "reviewed", + "severity": "high", + "source_code_location": "https://github.com/project/a-package", + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-xoxo-1234-xoxo" + }, + { + "type": "CVE", + "value": "CVE-xoxo-1234" + } + ], + "references": ["https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"], + "published_at": `+refTimeStr(1136178000)+`, + "updated_at": `+refTimeStr(1136178001)+`, + "github_reviewed_at": `+refTimeStr(1136178002)+`, + "nvd_published_at": `+refTimeStr(1136178003)+`, + "withdrawn_at": null, + "vulnerabilities": [ + { + "package": { + "ecosystem": "npm", + "name": "a-package" + }, + "first_patched_version": "1.0.3", + "vulnerable_version_range": "<=1.0.2", + "vulnerable_functions": ["a_function"] + } + ], + "cvss": { + "vector_string": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H", + "score": 7.6 + }, + "cvss_severities": { + "cvss_v3": {"vector_string": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H", "score": 7.6}, + "cvss_v4": {"vector_string": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N", "score": 8.7} + }, + "cwes": [ + { + "cwe_id": "CWE-400", + "name": "Uncontrolled Resource Consumption" + } + ], + "credits": [ + { + "user": { + "login": "user", + "id": 1, + "node_id": "12=", + "avatar_url": "a", + "gravatar_id": "", + "url": "a", + "html_url": "b", + "followers_url": "b", + "following_url": "c", + "gists_url": "d", + "starred_url": "e", + "subscriptions_url": "f", + "organizations_url": "g", + "repos_url": "h", + "events_url": "i", + "received_events_url": "j", + "type": "User", + "site_admin": false + }, + "type": "analyst" + } + ] + }`) + }) + + ctx := t.Context() + advisory, _, err := client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, "GHSA-xoxo-1234-xoxo") + if err != nil { + t.Errorf("SecurityAdvisories.GetGlobalSecurityAdvisories returned error: %v", err) + } + + want := &GlobalSecurityAdvisory{ + ID: Ptr(int64(1)), + SecurityAdvisory: SecurityAdvisory{ + GHSAID: Ptr("GHSA-xoxo-1234-xoxo"), + CVEID: Ptr("CVE-xoxo-1234"), + URL: Ptr("https://api.github.com/advisories/GHSA-xoxo-1234-xoxo"), + HTMLURL: Ptr("https://github.com/advisories/GHSA-xoxo-1234-xoxo"), + Severity: Ptr("high"), + Summary: Ptr("Heartbleed security advisory"), + Description: Ptr("This bug allows an attacker to read portions of the affected server’s memory, potentially disclosing sensitive information."), + Identifiers: []*AdvisoryIdentifier{ + { + Type: Ptr("GHSA"), + Value: Ptr("GHSA-xoxo-1234-xoxo"), + }, + { + Type: Ptr("CVE"), + Value: Ptr("CVE-xoxo-1234"), + }, + }, + PublishedAt: refTimestamp(1136178000), + UpdatedAt: refTimestamp(1136178001), + WithdrawnAt: nil, + CVSS: &AdvisoryCVSS{ + VectorString: Ptr("CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H"), + Score: Ptr(7.6), + }, + CVSSSeverities: &AdvisoryCVSSSeverities{ + CVSSV3: &AdvisoryCVSS{ + VectorString: Ptr("CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:H"), + Score: Ptr(7.6), + }, + CVSSV4: &AdvisoryCVSS{ + VectorString: Ptr("CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"), + Score: Ptr(8.7), + }, + }, + CWEs: []*AdvisoryCWEs{ + { + CWEID: Ptr("CWE-400"), + Name: Ptr("Uncontrolled Resource Consumption"), + }, + }, + }, + RepositoryAdvisoryURL: Ptr("https://api.github.com/repos/project/a-package/security-advisories/GHSA-xoxo-1234-xoxo"), + Type: Ptr("reviewed"), + SourceCodeLocation: Ptr("https://github.com/project/a-package"), + References: []string{"https://nvd.nist.gov/vuln/detail/CVE-xoxo-1234"}, + GithubReviewedAt: refTimestamp(1136178002), + NVDPublishedAt: refTimestamp(1136178003), + + Vulnerabilities: []*GlobalSecurityVulnerability{ + { + Package: &VulnerabilityPackage{ + Ecosystem: Ptr("npm"), + Name: Ptr("a-package"), + }, + FirstPatchedVersion: Ptr("1.0.3"), + VulnerableVersionRange: Ptr("<=1.0.2"), + VulnerableFunctions: []string{"a_function"}, + }, + }, + Credits: []*Credit{ + { + User: &User{ + Login: Ptr("user"), + ID: Ptr(int64(1)), + NodeID: Ptr("12="), + AvatarURL: Ptr("a"), + GravatarID: Ptr(""), + URL: Ptr("a"), + HTMLURL: Ptr("b"), + FollowersURL: Ptr("b"), + FollowingURL: Ptr("c"), + GistsURL: Ptr("d"), + StarredURL: Ptr("e"), + SubscriptionsURL: Ptr("f"), + OrganizationsURL: Ptr("g"), + ReposURL: Ptr("h"), + EventsURL: Ptr("i"), + ReceivedEventsURL: Ptr("j"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + Type: Ptr("analyst"), + }, + }, + } + + if !cmp.Equal(advisory, want) { + t.Errorf("SecurityAdvisories.GetGlobalSecurityAdvisories %+v, want %+v", advisory, want) + } + + const methodName = "GetGlobalSecurityAdvisories" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, "CVE-\n-1234") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SecurityAdvisories.GetGlobalSecurityAdvisories(ctx, "e") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/strings.go b/github/strings.go index 431e1cc6c1d..dfbe3c685f9 100644 --- a/github/strings.go +++ b/github/strings.go @@ -8,83 +8,109 @@ package github import ( "bytes" "fmt" - "io" - "reflect" + "strconv" + "sync" ) -var timestampType = reflect.TypeOf(Timestamp{}) +var timestampType = reflect.TypeFor[Timestamp]() + +var bufferPool = sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, +} // Stringify attempts to create a reasonable string representation of types in // the GitHub library. It does things like resolve pointers to their values // and omits struct fields with nil values. -func Stringify(message interface{}) string { - var buf bytes.Buffer +func Stringify(message any) string { + buf := bufferPool.Get().(*bytes.Buffer) + defer func() { + buf.Reset() + bufferPool.Put(buf) + }() + v := reflect.ValueOf(message) - stringifyValue(&buf, v) + stringifyValue(buf, v) return buf.String() } // stringifyValue was heavily inspired by the goprotobuf library. -func stringifyValue(w io.Writer, val reflect.Value) { - if val.Kind() == reflect.Ptr && val.IsNil() { - w.Write([]byte("")) +func stringifyValue(w *bytes.Buffer, val reflect.Value) { + if val.Kind() == reflect.Pointer && val.IsNil() { + w.WriteString("") return } v := reflect.Indirect(val) switch v.Kind() { + case reflect.Bool: + w.Write(strconv.AppendBool(w.Bytes(), v.Bool())[w.Len():]) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + w.Write(strconv.AppendInt(w.Bytes(), v.Int(), 10)[w.Len():]) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + w.Write(strconv.AppendUint(w.Bytes(), v.Uint(), 10)[w.Len():]) + case reflect.Float32: + w.Write(strconv.AppendFloat(w.Bytes(), v.Float(), 'g', -1, 32)[w.Len():]) + case reflect.Float64: + w.Write(strconv.AppendFloat(w.Bytes(), v.Float(), 'g', -1, 64)[w.Len():]) case reflect.String: - fmt.Fprintf(w, `"%s"`, v) + w.WriteByte('"') + w.WriteString(v.String()) + w.WriteByte('"') case reflect.Slice: - w.Write([]byte{'['}) - for i := 0; i < v.Len(); i++ { + w.WriteByte('[') + for i := range v.Len() { if i > 0 { - w.Write([]byte{' '}) + w.WriteByte(' ') } stringifyValue(w, v.Index(i)) } - w.Write([]byte{']'}) + w.WriteByte(']') return case reflect.Struct: if v.Type().Name() != "" { - w.Write([]byte(v.Type().String())) + w.WriteString(v.Type().String()) } // special handling of Timestamp values if v.Type() == timestampType { - fmt.Fprintf(w, "{%s}", v.Interface()) + fmt.Fprintf(w, "{%v}", v.Interface()) return } - w.Write([]byte{'{'}) + w.WriteByte('{') var sep bool - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { fv := v.Field(i) - if fv.Kind() == reflect.Ptr && fv.IsNil() { + if fv.Kind() == reflect.Pointer && fv.IsNil() { continue } if fv.Kind() == reflect.Slice && fv.IsNil() { continue } + if fv.Kind() == reflect.Map && fv.IsNil() { + continue + } if sep { - w.Write([]byte(", ")) + w.WriteString(", ") } else { sep = true } - w.Write([]byte(v.Type().Field(i).Name)) - w.Write([]byte{':'}) + w.WriteString(v.Type().Field(i).Name) + w.WriteByte(':') stringifyValue(w, fv) } - w.Write([]byte{'}'}) + w.WriteByte('}') default: if v.CanInterface() { fmt.Fprint(w, v.Interface()) diff --git a/github/strings_benchmark_test.go b/github/strings_benchmark_test.go new file mode 100644 index 00000000000..b19db42c51e --- /dev/null +++ b/github/strings_benchmark_test.go @@ -0,0 +1,36 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "testing" +) + +type BenchmarkStruct struct { + Name string + Age int + Active bool + Score float32 + Rank float64 + Tags []string + Pointer *int +} + +func BenchmarkStringify(b *testing.B) { + s := &BenchmarkStruct{ + Name: "benchmark", + Age: 30, + Active: true, + Score: 1.1, + Rank: 99.999999, + Tags: []string{"go", "github", "api"}, + Pointer: Ptr(42), + } + b.ResetTimer() + for b.Loop() { + Stringify(s) + } +} diff --git a/github/strings_test.go b/github/strings_test.go index e16d4a5475b..83392f1f80e 100644 --- a/github/strings_test.go +++ b/github/strings_test.go @@ -12,10 +12,11 @@ import ( ) func TestStringify(t *testing.T) { + t.Parallel() var nilPointer *string - var tests = []struct { - in interface{} + tests := []struct { + in any out string }{ // basic types @@ -44,29 +45,30 @@ func TestStringify(t *testing.T) { // pointers {nilPointer, ``}, - {String("foo"), `"foo"`}, - {Int(123), `123`}, - {Bool(false), `false`}, + {Ptr("foo"), `"foo"`}, + {Ptr(123), `123`}, + {Ptr(false), `false`}, { - []*string{String("a"), String("b")}, + //nolint:sliceofpointers + []*string{Ptr("a"), Ptr("b")}, `["a" "b"]`, }, // actual GitHub structs { - Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, + Timestamp{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, `github.Timestamp{2006-01-02 15:04:05 +0000 UTC}`, }, { - &Timestamp{time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC)}, + &Timestamp{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, `github.Timestamp{2006-01-02 15:04:05 +0000 UTC}`, }, { - User{ID: Int64(123), Name: String("n")}, + User{ID: Ptr(int64(123)), Name: Ptr("n")}, `github.User{ID:123, Name:"n"}`, }, { - Repository{Owner: &User{ID: Int64(123)}}, + Repository{Owner: &User{ID: Ptr(int64(123))}}, `github.Repository{Owner:github.User{ID:123}}`, }, } @@ -74,68 +76,176 @@ func TestStringify(t *testing.T) { for i, tt := range tests { s := Stringify(tt.in) if s != tt.out { - t.Errorf("%d. Stringify(%q) => %q, want %q", i, tt.in, s, tt.out) + t.Errorf("%v. Stringify(%q) => %q, want %q", i, tt.in, s, tt.out) + } + } +} + +func TestStringify_Primitives(t *testing.T) { + t.Parallel() + tests := []struct { + in any + out string + }{ + // Bool + {true, "true"}, + {false, "false"}, + + // Int variants + {int(1), "1"}, + {int8(2), "2"}, + {int16(3), "3"}, + {int32(4), "4"}, + {int64(5), "5"}, + + // Uint variants + {uint(6), "6"}, + {uint8(7), "7"}, + {uint16(8), "8"}, + {uint32(9), "9"}, + {uint64(10), "10"}, + {uintptr(11), "11"}, + + // Float variants (Precision Correctness) + {float32(1.1), "1.1"}, + {float64(1.1), "1.1"}, + {float32(1.0000001), "1.0000001"}, + {float64(1.000000000000001), "1.000000000000001"}, + + // Boundary Cases + {int8(-128), "-128"}, + {int8(127), "127"}, + {uint64(18446744073709551615), "18446744073709551615"}, + + // String Optimization + {"hello", `"hello"`}, + {"", `""`}, + } + + for i, tt := range tests { + s := Stringify(tt.in) + if s != tt.out { + t.Errorf("%v. Stringify(%T) => %q, want %q", i, tt.in, s, tt.out) + } + } +} + +func TestStringify_BufferPool(t *testing.T) { + t.Parallel() + // Verify that concurrent usage of Stringify is safe and doesn't corrupt buffers. + // While we can't easily verify reuse without exposing internal metrics, + // we can verify correctness under load which implies proper Reset() handling. + const goroutines = 10 + const iterations = 100 + + errCh := make(chan error, goroutines) + + for range goroutines { + go func() { + for range iterations { + // Use a mix of types to exercise different code paths + s1 := Stringify(123) + if s1 != "123" { + errCh <- fmt.Errorf("got %q, want %q", s1, "123") + return + } + + s2 := Stringify("test") + if s2 != `"test"` { + errCh <- fmt.Errorf("got %q, want %q", s2, `"test"`) + return + } + } + errCh <- nil + }() + } + + for range goroutines { + if err := <-errCh; err != nil { + t.Error(err) } } } // Directly test the String() methods on various GitHub types. We don't do an -// exaustive test of all the various field types, since TestStringify() above +// exhaustive test of all the various field types, since TestStringify() above // takes care of that. Rather, we just make sure that Stringify() is being // used to build the strings, which we do by verifying that pointers are // stringified as their underlying value. func TestString(t *testing.T) { - var tests = []struct { - in interface{} + t.Parallel() + tests := []struct { + in any out string }{ - {CodeResult{Name: String("n")}, `github.CodeResult{Name:"n"}`}, - {CommitAuthor{Name: String("n")}, `github.CommitAuthor{Name:"n"}`}, - {CommitFile{SHA: String("s")}, `github.CommitFile{SHA:"s"}`}, - {CommitStats{Total: Int(1)}, `github.CommitStats{Total:1}`}, - {CommitsComparison{TotalCommits: Int(1)}, `github.CommitsComparison{TotalCommits:1}`}, - {Commit{SHA: String("s")}, `github.Commit{SHA:"s"}`}, - {Event{ID: String("1")}, `github.Event{ID:"1"}`}, - {GistComment{ID: Int64(1)}, `github.GistComment{ID:1}`}, - {GistFile{Size: Int(1)}, `github.GistFile{Size:1}`}, - {Gist{ID: String("1")}, `github.Gist{ID:"1", Files:map[]}`}, - {GitObject{SHA: String("s")}, `github.GitObject{SHA:"s"}`}, - {Gitignore{Name: String("n")}, `github.Gitignore{Name:"n"}`}, - {Hook{ID: Int64(1)}, `github.Hook{ID:1, Config:map[]}`}, - {IssueComment{ID: Int64(1)}, `github.IssueComment{ID:1}`}, - {Issue{Number: Int(1)}, `github.Issue{Number:1}`}, - {Key{ID: Int64(1)}, `github.Key{ID:1}`}, - {Label{ID: Int64(1), Name: String("l")}, `github.Label{ID:1, Name:"l"}`}, - {Organization{ID: Int64(1)}, `github.Organization{ID:1}`}, - {PullRequestComment{ID: Int64(1)}, `github.PullRequestComment{ID:1}`}, - {PullRequest{Number: Int(1)}, `github.PullRequest{Number:1}`}, - {PullRequestReview{ID: Int64(1)}, `github.PullRequestReview{ID:1}`}, - {DraftReviewComment{Position: Int(1)}, `github.DraftReviewComment{Position:1}`}, - {PullRequestReviewRequest{Body: String("r")}, `github.PullRequestReviewRequest{Body:"r"}`}, - {PullRequestReviewDismissalRequest{Message: String("r")}, `github.PullRequestReviewDismissalRequest{Message:"r"}`}, - {PushEventCommit{SHA: String("s")}, `github.PushEventCommit{SHA:"s"}`}, - {PushEvent{PushID: Int64(1)}, `github.PushEvent{PushID:1}`}, - {Reference{Ref: String("r")}, `github.Reference{Ref:"r"}`}, - {ReleaseAsset{ID: Int64(1)}, `github.ReleaseAsset{ID:1}`}, - {RepoStatus{ID: Int64(1)}, `github.RepoStatus{ID:1}`}, - {RepositoryComment{ID: Int64(1)}, `github.RepositoryComment{ID:1}`}, - {RepositoryCommit{SHA: String("s")}, `github.RepositoryCommit{SHA:"s"}`}, - {RepositoryContent{Name: String("n")}, `github.RepositoryContent{Name:"n"}`}, - {RepositoryRelease{ID: Int64(1)}, `github.RepositoryRelease{ID:1}`}, - {Repository{ID: Int64(1)}, `github.Repository{ID:1}`}, - {Team{ID: Int64(1)}, `github.Team{ID:1}`}, - {TreeEntry{SHA: String("s")}, `github.TreeEntry{SHA:"s"}`}, - {Tree{SHA: String("s")}, `github.Tree{SHA:"s"}`}, - {User{ID: Int64(1)}, `github.User{ID:1}`}, - {WebHookAuthor{Name: String("n")}, `github.WebHookAuthor{Name:"n"}`}, - {WebHookCommit{ID: String("1")}, `github.WebHookCommit{ID:"1"}`}, - {WebHookPayload{Ref: String("r")}, `github.WebHookPayload{Ref:"r"}`}, + {CodeResult{Name: Ptr("n")}, `github.CodeResult{Name:"n"}`}, + {CommitAuthor{Name: Ptr("n")}, `github.CommitAuthor{Name:"n"}`}, + {CommitFile{SHA: Ptr("s")}, `github.CommitFile{SHA:"s"}`}, + {CommitStats{Total: Ptr(1)}, `github.CommitStats{Total:1}`}, + {CommitsComparison{TotalCommits: Ptr(1)}, `github.CommitsComparison{TotalCommits:1}`}, + {Commit{SHA: Ptr("s")}, `github.Commit{SHA:"s"}`}, + {Event{ID: Ptr("1")}, `github.Event{ID:"1"}`}, + {GistComment{ID: Ptr(int64(1))}, `github.GistComment{ID:1}`}, + {GistFile{Size: Ptr(1)}, `github.GistFile{Size:1}`}, + {Gist{ID: Ptr("1")}, `github.Gist{ID:"1"}`}, + {GitObject{SHA: Ptr("s")}, `github.GitObject{SHA:"s"}`}, + {Gitignore{Name: Ptr("n")}, `github.Gitignore{Name:"n"}`}, + {Hook{ID: Ptr(int64(1))}, `github.Hook{ID:1}`}, + {IssueComment{ID: Ptr(int64(1))}, `github.IssueComment{ID:1}`}, + {Issue{Number: Ptr(1)}, `github.Issue{Number:1}`}, + {SubIssue{ID: Ptr(int64(1))}, `github.SubIssue{ID:1}`}, + {Key{ID: Ptr(int64(1))}, `github.Key{ID:1}`}, + {Label{ID: 1, Name: "l"}, `github.Label{ID:1, URL:"", Name:"l", Color:"", Default:false, NodeID:""}`}, + {Organization{ID: Ptr(int64(1))}, `github.Organization{ID:1}`}, + {PullRequestComment{ID: Ptr(int64(1))}, `github.PullRequestComment{ID:1}`}, + {PullRequest{Number: Ptr(1)}, `github.PullRequest{Number:1}`}, + {PullRequestReview{ID: Ptr(int64(1))}, `github.PullRequestReview{ID:1}`}, + {DraftReviewComment{Position: Ptr(1)}, `github.DraftReviewComment{Position:1}`}, + {PullRequestReviewRequest{Body: Ptr("r")}, `github.PullRequestReviewRequest{Body:"r"}`}, + {PullRequestDismissReviewRequest{Message: "r"}, `github.PullRequestDismissReviewRequest{Message:"r"}`}, + {HeadCommit{SHA: Ptr("s")}, `github.HeadCommit{SHA:"s"}`}, + {PushEvent{PushID: Ptr(int64(1))}, `github.PushEvent{PushID:1}`}, + {Reference{Ref: Ptr("r")}, `github.Reference{Ref:"r"}`}, + {ReleaseAsset{ID: Ptr(int64(1))}, `github.ReleaseAsset{ID:1}`}, + {RepoStatus{ID: Ptr(int64(1))}, `github.RepoStatus{ID:1}`}, + {RepositoryComment{ID: Ptr(int64(1))}, `github.RepositoryComment{ID:1}`}, + {RepositoryCommit{SHA: Ptr("s")}, `github.RepositoryCommit{SHA:"s"}`}, + {RepositoryContent{Name: Ptr("n")}, `github.RepositoryContent{Name:"n"}`}, + {RepositoryRelease{ID: 1}, `github.RepositoryRelease{TagName:"", TargetCommitish:"", Draft:false, Prerelease:false, ID:1, CreatedAt:github.Timestamp{0001-01-01 00:00:00 +0000 UTC}, URL:"", HTMLURL:"", AssetsURL:"", UploadURL:"", NodeID:""}`}, + {Repository{ID: Ptr(int64(1))}, `github.Repository{ID:1}`}, + {Team{ID: Ptr(int64(1))}, `github.Team{ID:1}`}, + {TreeEntry{SHA: Ptr("s")}, `github.TreeEntry{SHA:"s"}`}, + {Tree{SHA: Ptr("s")}, `github.Tree{SHA:"s"}`}, + {User{ID: Ptr(int64(1))}, `github.User{ID:1}`}, + {WebHookAuthor{Name: Ptr("n")}, `github.CommitAuthor{Name:"n"}`}, + {WebHookCommit{ID: Ptr("1")}, `github.HeadCommit{ID:"1"}`}, + {WebHookPayload{Ref: Ptr("r")}, `github.PushEvent{Ref:"r"}`}, } for i, tt := range tests { s := tt.in.(fmt.Stringer).String() if s != tt.out { - t.Errorf("%d. String() => %q, want %q", i, tt.in, tt.out) + t.Errorf("%v. String() => %q, want %q", i, tt.in, tt.out) + } + } +} + +func TestStringify_Floats(t *testing.T) { + t.Parallel() + tests := []struct { + in any + out string + }{ + {float32(1.1), "1.1"}, + {float64(1.1), "1.1"}, + {float32(1.0000001), "1.0000001"}, + {struct{ F float32 }{1.1}, "{F:1.1}"}, + } + + for i, tt := range tests { + s := Stringify(tt.in) + if s != tt.out { + t.Errorf("%v. Stringify(%v) = %q, want %q", i, tt.in, s, tt.out) } } } diff --git a/github/sub_issue.go b/github/sub_issue.go new file mode 100644 index 00000000000..210cee501ba --- /dev/null +++ b/github/sub_issue.go @@ -0,0 +1,161 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SubIssueService handles communication with the sub-issue related +// methods of the GitHub API. +// +// Sub-issues help you group and manage your issues with a parent/child relationship. +// +// GitHub API docs: https://docs.github.com/rest/issues/sub-issues?apiVersion=2022-11-28 +type SubIssueService service + +// SubIssue represents a GitHub sub-issue on a repository. +// Note: As far as the GitHub API is concerned, every pull request is an issue, +// but not every issue is a pull request. Some endpoints, events, and webhooks +// may also return pull requests via this struct. If PullRequestLinks is nil, +// this is an issue, and if PullRequestLinks is not nil, this is a pull request. +// The IsPullRequest helper method can be used to check that. +type SubIssue Issue + +func (i SubIssue) String() string { + return Stringify(i) +} + +// SubIssueListByIssueOptions specifies the optional parameters to the +// SubIssueService.ListByIssue method. +type SubIssueListByIssueOptions struct { + IssueListByRepoOptions +} + +// SubIssueRequest represents a request to add, remove, or reprioritize sub-issues. +type SubIssueRequest struct { + SubIssueID int64 `json:"sub_issue_id"` // Required: The ID of the sub-issue + AfterID *int64 `json:"after_id,omitempty"` // Optional: Position after this sub-issue ID + BeforeID *int64 `json:"before_id,omitempty"` // Optional: Position before this sub-issue ID + ReplaceParent *bool `json:"replace_parent,omitempty"` // Optional: Whether to replace the existing parent +} + +// Remove a sub-issue from the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/issues/sub-issues?apiVersion=2022-11-28#remove-sub-issue +// +//meta:operation DELETE /repos/{owner}/{repo}/issues/{issue_number}/sub_issue +func (s *SubIssueService) Remove(ctx context.Context, owner, repo string, issueNumber int64, subIssue SubIssueRequest) (*SubIssue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issue", owner, repo, issueNumber) + + req, err := s.client.NewRequest(ctx, "DELETE", u, subIssue) + if err != nil { + return nil, nil, err + } + + var si *SubIssue + resp, err := s.client.Do(req, &si) + if err != nil { + return nil, resp, err + } + + return si, resp, nil +} + +// ListByIssue lists all sub-issues for the specified issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/sub-issues?apiVersion=2022-11-28#list-sub-issues +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/sub_issues +func (s *SubIssueService) ListByIssue(ctx context.Context, owner, repo string, issueNumber int64, opts *ListOptions) ([]*SubIssue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues", owner, repo, issueNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var subIssues []*SubIssue + resp, err := s.client.Do(req, &subIssues) + if err != nil { + return nil, resp, err + } + + return subIssues, resp, nil +} + +// Add adds a sub-issue to the specified issue. +// +// The sub-issue to be added must belong to the same repository owner as the parent issue. +// To replace the existing parent of a sub-issue, set replaceParent to true. +// +// GitHub API docs: https://docs.github.com/rest/issues/sub-issues?apiVersion=2022-11-28#add-sub-issue +// +//meta:operation POST /repos/{owner}/{repo}/issues/{issue_number}/sub_issues +func (s *SubIssueService) Add(ctx context.Context, owner, repo string, issueNumber int64, body SubIssueRequest) (*SubIssue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues", owner, repo, issueNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var si *SubIssue + resp, err := s.client.Do(req, &si) + if err != nil { + return nil, resp, err + } + + return si, resp, nil +} + +// Reprioritize changes a sub-issue's priority to a different position in the parent list. +// +// Either afterId or beforeId must be specified to determine the new position of the sub-issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/sub-issues?apiVersion=2022-11-28#reprioritize-sub-issue +// +//meta:operation PATCH /repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority +func (s *SubIssueService) Reprioritize(ctx context.Context, owner, repo string, issueNumber int64, body SubIssueRequest) (*SubIssue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/sub_issues/priority", owner, repo, issueNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var si *SubIssue + resp, err := s.client.Do(req, &si) + if err != nil { + return nil, resp, err + } + + return si, resp, nil +} + +// GetParentIssue gets the parent issue of a sub-issue. +// +// GitHub API docs: https://docs.github.com/rest/issues/sub-issues?apiVersion=2022-11-28#get-parent-issue +// +//meta:operation GET /repos/{owner}/{repo}/issues/{issue_number}/parent +func (s *SubIssueService) GetParentIssue(ctx context.Context, owner, repo string, subIssueNumber int64) (*Issue, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/issues/%v/parent", owner, repo, subIssueNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var parentIssue *Issue + resp, err := s.client.Do(req, &parentIssue) + if err != nil { + return nil, resp, err + } + + return parentIssue, resp, nil +} diff --git a/github/sub_issue_test.go b/github/sub_issue_test.go new file mode 100644 index 00000000000..faeea077d6e --- /dev/null +++ b/github/sub_issue_test.go @@ -0,0 +1,188 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestSubIssuesService_Add(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SubIssueRequest{SubIssueID: 42} + + mux.HandleFunc("/repos/o/r/issues/1/sub_issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":42, "number":1}`) + }) + + ctx := t.Context() + got, _, err := client.SubIssue.Add(ctx, "o", "r", 1, *input) + if err != nil { + t.Errorf("SubIssues.Add returned error: %v", err) + } + + want := &SubIssue{Number: Ptr(1), ID: Ptr(int64(42))} + if !cmp.Equal(got, want) { + t.Errorf("SubIssues.Add = %+v, want %+v", got, want) + } + + const methodName = "Add" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SubIssue.Add(ctx, "o", "r", 1, *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSubIssuesService_ListByIssue(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/1/sub_issues", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "2", + "per_page": "50", + }) + + fmt.Fprint(w, `[{"id":1},{"id":2}]`) + }) + + ctx := t.Context() + opt := &ListOptions{ + Page: 2, + PerPage: 50, + } + issues, _, err := client.SubIssue.ListByIssue(ctx, "o", "r", 1, opt) + if err != nil { + t.Errorf("SubIssues.ListByIssue returned error: %v", err) + } + + want := []*SubIssue{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(issues, want) { + t.Errorf("SubIssues.ListByIssue = %+v, want %+v", issues, want) + } + + const methodName = "ListByIssue" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.SubIssue.ListByIssue(ctx, "\n", "\n", 1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SubIssue.ListByIssue(ctx, "o", "r", 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSubIssuesService_Remove(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SubIssueRequest{SubIssueID: 42} + + mux.HandleFunc("/repos/o/r/issues/1/sub_issue", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":42, "number":1}`) + }) + + ctx := t.Context() + got, _, err := client.SubIssue.Remove(ctx, "o", "r", 1, *input) + if err != nil { + t.Errorf("SubIssues.Remove returned error: %v", err) + } + + want := &SubIssue{ID: Ptr(int64(42)), Number: Ptr(1)} + if !cmp.Equal(got, want) { + t.Errorf("SubIssues.Remove = %+v, want %+v", got, want) + } + + const methodName = "Remove" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SubIssue.Remove(ctx, "o", "r", 1, *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSubIssuesService_Reprioritize(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &SubIssueRequest{SubIssueID: 42, AfterID: Ptr(int64(5))} + + mux.HandleFunc("/repos/o/r/issues/1/sub_issues/priority", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":42, "number":1}`) + }) + + ctx := t.Context() + got, _, err := client.SubIssue.Reprioritize(ctx, "o", "r", 1, *input) + if err != nil { + t.Errorf("SubIssues.Reprioritize returned error: %v", err) + } + + want := &SubIssue{ID: Ptr(int64(42)), Number: Ptr(1)} + if !cmp.Equal(got, want) { + t.Errorf("SubIssues.Reprioritize = %+v, want %+v", got, want) + } + + const methodName = "Reprioritize" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SubIssue.Reprioritize(ctx, "o", "r", 1, *input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestSubIssuesService_GetParentIssue(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/repos/o/r/issues/42/parent", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1, "number":1}`) + }) + + ctx := t.Context() + got, _, err := client.SubIssue.GetParentIssue(ctx, "o", "r", 42) + if err != nil { + t.Errorf("SubIssues.GetParentIssue returned error: %v", err) + } + + want := &Issue{Number: Ptr(1), ID: Ptr(int64(1))} + if !cmp.Equal(got, want) { + t.Errorf("SubIssues.GetParentIssue = %+v, want %+v", got, want) + } + + const methodName = "GetParentIssue" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.SubIssue.GetParentIssue(ctx, "o", "r", 42) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/teams.go b/github/teams.go index b4953e63871..63ffbe6e1a3 100644 --- a/github/teams.go +++ b/github/teams.go @@ -7,15 +7,14 @@ package github import ( "context" + "encoding/json" "fmt" - "strings" - "time" ) // TeamsService provides access to the team-related functions // in the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/teams/ +// GitHub API docs: https://docs.github.com/rest/teams?apiVersion=2022-11-28 type TeamsService service // Team represents a team within a GitHub organization. Teams are used to @@ -38,9 +37,13 @@ type Team struct { // Default is "secret". Privacy *string `json:"privacy,omitempty"` + // NotificationSetting can be one of: "notifications_enabled", "notifications_disabled". + NotificationSetting *string `json:"notification_setting,omitempty"` + MembersCount *int `json:"members_count,omitempty"` ReposCount *int `json:"repos_count,omitempty"` Organization *Organization `json:"organization,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` MembersURL *string `json:"members_url,omitempty"` RepositoriesURL *string `json:"repositories_url,omitempty"` Parent *Team `json:"parent,omitempty"` @@ -48,6 +51,24 @@ type Team struct { // LDAPDN is only available in GitHub Enterprise and when the team // membership is synchronized with LDAP. LDAPDN *string `json:"ldap_dn,omitempty"` + + // Permissions identifies the permissions that a team has on a given + // repository. This is only populated when calling Repositories.ListTeams. + Permissions map[string]bool `json:"permissions,omitempty"` + + // Assignment identifies how a team was assigned to an organization role. Its + // possible values are: "direct", "indirect", "mixed". This is only populated when + // calling the ListTeamsAssignedToOrgRole method. + Assignment *string `json:"assignment,omitempty"` + + // Type identifies the ownership type of the team + // Possible values are: "organization", "enterprise". + Type *string `json:"type,omitempty"` + + // AccessSource identifies the source of the team's access to a specific + // repository. Possible values are: "direct", "organization" or "enterprise". + // This is only populated when calling [RepositoriesService.ListTeams]. + AccessSource *string `json:"access_source,omitempty"` } func (t Team) String() string { @@ -62,10 +83,12 @@ type Invitation struct { Email *string `json:"email,omitempty"` // Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'. Role *string `json:"role,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` Inviter *User `json:"inviter,omitempty"` TeamCount *int `json:"team_count,omitempty"` InvitationTeamURL *string `json:"invitation_team_url,omitempty"` + FailedAt *Timestamp `json:"failed_at,omitempty"` + FailedReason *string `json:"failed_reason,omitempty"` } func (i Invitation) String() string { @@ -74,24 +97,23 @@ func (i Invitation) String() string { // ListTeams lists all of the teams for an organization. // -// GitHub API docs: https://developer.github.com/v3/teams/#list-teams -func (s *TeamsService) ListTeams(ctx context.Context, org string, opt *ListOptions) ([]*Team, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-teams +// +//meta:operation GET /orgs/{org}/teams +func (s *TeamsService) ListTeams(ctx context.Context, org string, opts *ListOptions) ([]*Team, *Response, error) { u := fmt.Sprintf("orgs/%v/teams", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) + resp, err := s.client.Do(req, &teams) if err != nil { return nil, resp, err } @@ -99,21 +121,20 @@ func (s *TeamsService) ListTeams(ctx context.Context, org string, opt *ListOptio return teams, resp, nil } -// GetTeam fetches a team by ID. +// GetTeamByID fetches a team, given a specified organization ID, by ID. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#get-a-team-by-name // -// GitHub API docs: https://developer.github.com/v3/teams/#get-team -func (s *TeamsService) GetTeam(ctx context.Context, team int64) (*Team, *Response, error) { - u := fmt.Sprintf("teams/%v", team) - req, err := s.client.NewRequest("GET", u, nil) +//meta:operation GET /organizations/{organization_id}/team/{team_id} +func (s *TeamsService) GetTeamByID(ctx context.Context, orgID, teamID int64) (*Team, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v", orgID, teamID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Team) - resp, err := s.client.Do(ctx, req, t) + var t *Team + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -121,18 +142,20 @@ func (s *TeamsService) GetTeam(ctx context.Context, team int64) (*Team, *Respons return t, resp, nil } -// GetTeamBySlug fetches a team by slug. +// GetTeamBySlug fetches a team, given a specified organization name, by slug. // -// GitHub API docs: https://developer.github.com/v3/teams/#get-team-by-name +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#get-a-team-by-name +// +//meta:operation GET /orgs/{org}/teams/{team_slug} func (s *TeamsService) GetTeamBySlug(ctx context.Context, org, slug string) (*Team, *Response, error) { u := fmt.Sprintf("orgs/%v/teams/%v", org, slug) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - t := new(Team) - resp, err := s.client.Do(ctx, req, t) + var t *Team + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -140,13 +163,29 @@ func (s *TeamsService) GetTeamBySlug(ctx context.Context, org, slug string) (*Te return t, resp, nil } -// NewTeam represents a team to be created or modified. -type NewTeam struct { - Name string `json:"name"` // Name of the team. (Required.) - Description *string `json:"description,omitempty"` - Maintainers []string `json:"maintainers,omitempty"` - RepoNames []string `json:"repo_names,omitempty"` - ParentTeamID *int64 `json:"parent_team_id,omitempty"` +// CreateTeamRequest represents a team to be created. +type CreateTeamRequest struct { + // The name of the team (required). + Name string `json:"name"` + + // The description of the team. + Description *string `json:"description,omitempty"` + + // List GitHub usernames for organization members who will become team maintainers. + Maintainers []string `json:"maintainers,omitempty"` + + // The full name (e.g., "organization-name/repository-name") of repositories to add the team to. + RepoNames []string `json:"repo_names,omitempty"` + + // Privacy identifies the level of privacy this team should have. + // Possible values are: + // secret - only visible to organization owners and members of this team + // closed - visible to all members of this organization + // Default is "secret". + Privacy *string `json:"privacy,omitempty"` + + // NotificationSetting can be one of: "notifications_enabled", "notifications_disabled". + NotificationSetting *string `json:"notification_setting,omitempty"` // Deprecated: Permission is deprecated when creating or editing a team in an org // using the new GitHub permission model. It no longer identifies the @@ -155,6 +194,47 @@ type NewTeam struct { // specifying a permission value when calling AddTeamRepo. Permission *string `json:"permission,omitempty"` + // The ID of a team to set as the parent team. + ParentTeamID *int64 `json:"parent_team_id,omitempty"` + + // The slug of a team to set as the parent team. + // Ignored when ParentTeamID is also provided. + ParentTeamSlug *string `json:"parent_team_slug,omitempty"` +} + +func (r CreateTeamRequest) String() string { + return Stringify(r) +} + +// CreateTeam creates a new team within an organization. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#create-a-team +// +//meta:operation POST /orgs/{org}/teams +func (s *TeamsService) CreateTeam(ctx context.Context, org string, body CreateTeamRequest) (*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams", org) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var t *Team + resp, err := s.client.Do(req, &t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// UpdateTeamRequest represents a team to be updated. +type UpdateTeamRequest struct { + // The name of the team (required). + Name *string `json:"name"` + + // The description of the team. + Description *string `json:"description,omitempty"` + // Privacy identifies the level of privacy this team should have. // Possible values are: // secret - only visible to organization owners and members of this team @@ -162,30 +242,60 @@ type NewTeam struct { // Default is "secret". Privacy *string `json:"privacy,omitempty"` - // LDAPDN may be used in GitHub Enterprise when the team membership - // is synchronized with LDAP. - LDAPDN *string `json:"ldap_dn,omitempty"` + // NotificationSetting can be one of: "notifications_enabled", "notifications_disabled". + NotificationSetting *string `json:"notification_setting,omitempty"` + + // Deprecated: Permission is deprecated when creating or editing a team in an org + // using the new GitHub permission model. It no longer identifies the + // permission a team has on its repos, but only specifies the default + // permission a repo is initially added with. Avoid confusion by + // specifying a permission value when calling AddTeamRepo. + Permission *string `json:"permission,omitempty"` + + // The ID of a team to set as the parent team. + ParentTeamID *int64 `json:"parent_team_id,omitempty"` + + // The slug of a team to set as the parent team. + // Ignored when ParentTeamID is also provided. + ParentTeamSlug *string `json:"parent_team_slug,omitempty"` + + // If set to true, the parent team will be removed from the team. + RemoveParentTeam bool `json:"-"` } -func (s NewTeam) String() string { - return Stringify(s) +func (r UpdateTeamRequest) String() string { + return Stringify(r) } -// CreateTeam creates a new team within an organization. +// MarshalJSON implements the json.Marshaler interface. +func (r UpdateTeamRequest) MarshalJSON() ([]byte, error) { + type alias UpdateTeamRequest + if !r.RemoveParentTeam { + return json.Marshal(alias(r)) + } + return json.Marshal(&struct { + alias + ParentTeamID *int64 `json:"parent_team_id"` + ParentTeamSlug *string `json:"parent_team_slug"` + }{ + alias: alias(r), + }) +} + +// UpdateTeamByID updates a team, given an organization ID, selected by ID. // -// GitHub API docs: https://developer.github.com/v3/teams/#create-team -func (s *TeamsService) CreateTeam(ctx context.Context, org string, team NewTeam) (*Team, *Response, error) { - u := fmt.Sprintf("orgs/%v/teams", org) - req, err := s.client.NewRequest("POST", u, team) +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#update-a-team +// +//meta:operation PATCH /organizations/{organization_id}/team/{team_id} +func (s *TeamsService) UpdateTeamByID(ctx context.Context, orgID, teamID int64, body UpdateTeamRequest) (*Team, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v", orgID, teamID) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Team) - resp, err := s.client.Do(ctx, req, t) + var t *Team + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -193,21 +303,20 @@ func (s *TeamsService) CreateTeam(ctx context.Context, org string, team NewTeam) return t, resp, nil } -// EditTeam edits a team. +// UpdateTeamBySlug updates a team, given an organization name, by slug. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#update-a-team // -// GitHub API docs: https://developer.github.com/v3/teams/#edit-team -func (s *TeamsService) EditTeam(ctx context.Context, id int64, team NewTeam) (*Team, *Response, error) { - u := fmt.Sprintf("teams/%v", id) - req, err := s.client.NewRequest("PATCH", u, team) +//meta:operation PATCH /orgs/{org}/teams/{team_slug} +func (s *TeamsService) UpdateTeamBySlug(ctx context.Context, org, slug string, body UpdateTeamRequest) (*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v", org, slug) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Team) - resp, err := s.client.Do(ctx, req, t) + var t *Team + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -215,40 +324,81 @@ func (s *TeamsService) EditTeam(ctx context.Context, id int64, team NewTeam) (*T return t, resp, nil } -// DeleteTeam deletes a team. +// DeleteTeamByID deletes a team referenced by ID. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#delete-a-team // -// GitHub API docs: https://developer.github.com/v3/teams/#delete-team -func (s *TeamsService) DeleteTeam(ctx context.Context, team int64) (*Response, error) { - u := fmt.Sprintf("teams/%v", team) - req, err := s.client.NewRequest("DELETE", u, nil) +//meta:operation DELETE /organizations/{organization_id}/team/{team_id} +func (s *TeamsService) DeleteTeamByID(ctx context.Context, orgID, teamID int64) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v", orgID, teamID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + return s.client.Do(req, nil) +} + +// DeleteTeamBySlug deletes a team reference by slug. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#delete-a-team +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug} +func (s *TeamsService) DeleteTeamBySlug(ctx context.Context, org, slug string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v", org, slug) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } -// ListChildTeams lists child teams for a team. +// ListChildTeamsByParentID lists child teams for a parent team given parent ID. // -// GitHub API docs: https://developer.github.com/v3/teams/#list-child-teams -func (s *TeamsService) ListChildTeams(ctx context.Context, teamID int64, opt *ListOptions) ([]*Team, *Response, error) { - u := fmt.Sprintf("teams/%v/teams", teamID) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-child-teams +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/teams +func (s *TeamsService) ListChildTeamsByParentID(ctx context.Context, orgID, teamID int64, opts *ListOptions) ([]*Team, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/teams", orgID, teamID) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + var teams []*Team + resp, err := s.client.Do(req, &teams) + if err != nil { + return nil, resp, err + } + + return teams, resp, nil +} + +// ListChildTeamsByParentSlug lists child teams for a parent team given parent slug. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-child-teams +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/teams +func (s *TeamsService) ListChildTeamsByParentSlug(ctx context.Context, org, slug string, opts *ListOptions) ([]*Team, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/teams", org, slug) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) + resp, err := s.client.Do(req, &teams) if err != nil { return nil, resp, err } @@ -256,27 +406,27 @@ func (s *TeamsService) ListChildTeams(ctx context.Context, teamID int64, opt *Li return teams, resp, nil } -// ListTeamRepos lists the repositories that the specified team has access to. +// ListTeamReposByID lists the repositories given a team ID that the specified team has access to. // -// GitHub API docs: https://developer.github.com/v3/teams/#list-team-repos -func (s *TeamsService) ListTeamRepos(ctx context.Context, team int64, opt *ListOptions) ([]*Repository, *Response, error) { - u := fmt.Sprintf("teams/%v/repos", team) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-team-repositories +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/repos +func (s *TeamsService) ListTeamReposByID(ctx context.Context, orgID, teamID int64, opts *ListOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/repos", orgID, teamID) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when topics API fully launches. - headers := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview} - req.Header.Set("Accept", strings.Join(headers, ", ")) + req.Header.Set("Accept", mediaTypeTopicsPreview) var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) + resp, err := s.client.Do(req, &repos) if err != nil { return nil, resp, err } @@ -284,23 +434,77 @@ func (s *TeamsService) ListTeamRepos(ctx context.Context, team int64, opt *ListO return repos, resp, nil } -// IsTeamRepo checks if a team manages the specified repository. If the +// ListTeamReposBySlug lists the repositories given a team slug that the specified team has access to. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-team-repositories +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/repos +func (s *TeamsService) ListTeamReposBySlug(ctx context.Context, org, slug string, opts *ListOptions) ([]*Repository, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/repos", org, slug) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeTopicsPreview) + + var repos []*Repository + resp, err := s.client.Do(req, &repos) + if err != nil { + return nil, resp, err + } + + return repos, resp, nil +} + +// IsTeamRepoByID checks if a team, given its ID, manages the specified repository. If the // repository is managed by team, a Repository is returned which includes the // permissions team has for that repo. // -// GitHub API docs: https://developer.github.com/v3/teams/#check-if-a-team-manages-a-repository -func (s *TeamsService) IsTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Repository, *Response, error) { - u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#check-team-permissions-for-a-repository +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/repos/{owner}/{repo} +func (s *TeamsService) IsTeamRepoByID(ctx context.Context, orgID, teamID int64, owner, repo string) (*Repository, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/repos/%v/%v", orgID, teamID, owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - headers := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview} - req.Header.Set("Accept", strings.Join(headers, ", ")) + req.Header.Set("Accept", mediaTypeOrgPermissionRepo) - repository := new(Repository) - resp, err := s.client.Do(ctx, req, repository) + var repository *Repository + resp, err := s.client.Do(req, &repository) + if err != nil { + return nil, resp, err + } + + return repository, resp, nil +} + +// IsTeamRepoBySlug checks if a team, given its slug, manages the specified repository. If the +// repository is managed by team, a Repository is returned which includes the +// permissions team has for that repo. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#check-team-permissions-for-a-repository +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} +func (s *TeamsService) IsTeamRepoBySlug(ctx context.Context, org, slug, owner, repo string) (*Repository, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/repos/%v/%v", org, slug, owner, repo) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeOrgPermissionRepo) + + var repository *Repository + resp, err := s.client.Do(req, &repository) if err != nil { return nil, resp, err } @@ -309,67 +513,107 @@ func (s *TeamsService) IsTeamRepo(ctx context.Context, team int64, owner string, } // TeamAddTeamRepoOptions specifies the optional parameters to the -// TeamsService.AddTeamRepo method. +// TeamsService.AddTeamRepoByID and TeamsService.AddTeamRepoBySlug methods. type TeamAddTeamRepoOptions struct { // Permission specifies the permission to grant the team on this repository. // Possible values are: // pull - team members can pull, but not push to or administer this repository - // push - team members can pull and push, but not administer this repository + // push - team members can push and pull, but not administer this repository // admin - team members can pull, push and administer this repository + // maintain - team members can manage the repository without access to sensitive or destructive actions. + // triage - team members can proactively manage issues and pull requests without write access. // // If not specified, the team's permission attribute will be used. Permission string `json:"permission,omitempty"` } -// AddTeamRepo adds a repository to be managed by the specified team. The -// specified repository must be owned by the organization to which the team +// AddTeamRepoByID adds a repository to be managed by the specified team given the team ID. +// The specified repository must be owned by the organization to which the team +// belongs, or a direct fork of a repository owned by the organization. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#add-or-update-team-repository-permissions +// +//meta:operation PUT /organizations/{organization_id}/team/{team_id}/repos/{owner}/{repo} +func (s *TeamsService) AddTeamRepoByID(ctx context.Context, orgID, teamID int64, owner, repo string, body *TeamAddTeamRepoOptions) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/repos/%v/%v", orgID, teamID, owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// AddTeamRepoBySlug adds a repository to be managed by the specified team given the team slug. +// The specified repository must be owned by the organization to which the team // belongs, or a direct fork of a repository owned by the organization. // -// GitHub API docs: https://developer.github.com/v3/teams/#add-team-repo -func (s *TeamsService) AddTeamRepo(ctx context.Context, team int64, owner string, repo string, opt *TeamAddTeamRepoOptions) (*Response, error) { - u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) - req, err := s.client.NewRequest("PUT", u, opt) +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#add-or-update-team-repository-permissions +// +//meta:operation PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} +func (s *TeamsService) AddTeamRepoBySlug(ctx context.Context, org, slug, owner, repo string, body *TeamAddTeamRepoOptions) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/repos/%v/%v", org, slug, owner, repo) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveTeamRepoByID removes a repository from being managed by the specified +// team given the team ID. Note that this does not delete the repository, it +// just removes it from the team. +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#remove-a-repository-from-a-team +// +//meta:operation DELETE /organizations/{organization_id}/team/{team_id}/repos/{owner}/{repo} +func (s *TeamsService) RemoveTeamRepoByID(ctx context.Context, orgID, teamID int64, owner, repo string) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/repos/%v/%v", orgID, teamID, owner, repo) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } -// RemoveTeamRepo removes a repository from being managed by the specified -// team. Note that this does not delete the repository, it just removes it -// from the team. +// RemoveTeamRepoBySlug removes a repository from being managed by the specified +// team given the team slug. Note that this does not delete the repository, it +// just removes it from the team. // -// GitHub API docs: https://developer.github.com/v3/teams/#remove-team-repo -func (s *TeamsService) RemoveTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Response, error) { - u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#remove-a-repository-from-a-team +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} +func (s *TeamsService) RemoveTeamRepoBySlug(ctx context.Context, org, slug, owner, repo string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/repos/%v/%v", org, slug, owner, repo) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // ListUserTeams lists a user's teams -// GitHub API docs: https://developer.github.com/v3/teams/#list-user-teams -func (s *TeamsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]*Team, *Response, error) { +// +// GitHub API docs: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user +// +//meta:operation GET /user/teams +func (s *TeamsService) ListUserTeams(ctx context.Context, opts *ListOptions) ([]*Team, *Response, error) { u := "user/teams" - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) + resp, err := s.client.Do(req, &teams) if err != nil { return nil, resp, err } @@ -377,23 +621,25 @@ func (s *TeamsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]* return teams, resp, nil } -// ListTeamProjects lists the organization projects for a team. +// ListTeamProjectsByID lists the organization projects for a team given the team ID. +// +// Deprecated: This endpoint has been deprecated by GitHub. // -// GitHub API docs: https://developer.github.com/v3/teams/#list-team-projects -func (s *TeamsService) ListTeamProjects(ctx context.Context, teamID int64) ([]*Project, *Response, error) { - u := fmt.Sprintf("teams/%v/projects", teamID) +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#list-team-projects +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/projects +func (s *TeamsService) ListTeamProjectsByID(ctx context.Context, orgID, teamID int64) ([]*ProjectV2, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/projects", orgID, teamID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeProjectsPreview) - var projects []*Project - resp, err := s.client.Do(ctx, req, &projects) + var projects []*ProjectV2 + resp, err := s.client.Do(req, &projects) if err != nil { return nil, resp, err } @@ -401,23 +647,77 @@ func (s *TeamsService) ListTeamProjects(ctx context.Context, teamID int64) ([]*P return projects, resp, nil } -// ReviewTeamProjects checks whether a team has read, write, or admin +// ListTeamProjectsBySlug lists the organization projects for a team given the team slug. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#list-team-projects +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/projects +func (s *TeamsService) ListTeamProjectsBySlug(ctx context.Context, org, slug string) ([]*ProjectV2, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/projects", org, slug) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeProjectsPreview) + + var projects []*ProjectV2 + resp, err := s.client.Do(req, &projects) + if err != nil { + return nil, resp, err + } + + return projects, resp, nil +} + +// ReviewTeamProjectsByID checks whether a team, given its ID, has read, write, or admin // permissions for an organization project. // -// GitHub API docs: https://developer.github.com/v3/teams/#review-a-team-project -func (s *TeamsService) ReviewTeamProjects(ctx context.Context, teamID, projectID int64) (*Project, *Response, error) { - u := fmt.Sprintf("teams/%v/projects/%v", teamID, projectID) - req, err := s.client.NewRequest("GET", u, nil) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#check-team-permissions-for-a-project +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/projects/{project_id} +func (s *TeamsService) ReviewTeamProjectsByID(ctx context.Context, orgID, teamID, projectID int64) (*ProjectV2, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/projects/%v", orgID, teamID, projectID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeProjectsPreview) - projects := &Project{} - resp, err := s.client.Do(ctx, req, &projects) + var projects *ProjectV2 + resp, err := s.client.Do(req, &projects) + if err != nil { + return nil, resp, err + } + + return projects, resp, nil +} + +// ReviewTeamProjectsBySlug checks whether a team, given its slug, has read, write, or admin +// permissions for an organization project. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#check-team-permissions-for-a-project +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/projects/{project_id} +func (s *TeamsService) ReviewTeamProjectsBySlug(ctx context.Context, org, slug string, projectID int64) (*ProjectV2, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/projects/%v", org, slug, projectID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeProjectsPreview) + + var projects *ProjectV2 + resp, err := s.client.Do(req, &projects) if err != nil { return nil, resp, err } @@ -437,49 +737,107 @@ type TeamProjectOptions struct { Permission *string `json:"permission,omitempty"` } -// AddTeamProject adds an organization project to a team. To add a project to a team or -// update the team's permission on a project, the authenticated user must have admin -// permissions for the project. +// AddTeamProjectByID adds an organization project to a team given the team ID. +// To add a project to a team or update the team's permission on a project, the +// authenticated user must have admin permissions for the project. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#add-or-update-team-project-permissions +// +//meta:operation PUT /organizations/{organization_id}/team/{team_id}/projects/{project_id} +func (s *TeamsService) AddTeamProjectByID(ctx context.Context, orgID, teamID, projectID int64, body *TeamProjectOptions) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/projects/%v", orgID, teamID, projectID) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(req, nil) +} + +// AddTeamProjectBySlug adds an organization project to a team given the team slug. +// To add a project to a team or update the team's permission on a project, the +// authenticated user must have admin permissions for the project. +// +// Deprecated: This endpoint has been deprecated by GitHub. // -// GitHub API docs: https://developer.github.com/v3/teams/#add-or-update-team-project -func (s *TeamsService) AddTeamProject(ctx context.Context, teamID, projectID int64, opt *TeamProjectOptions) (*Response, error) { - u := fmt.Sprintf("teams/%v/projects/%v", teamID, projectID) - req, err := s.client.NewRequest("PUT", u, opt) +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#add-or-update-team-project-permissions +// +//meta:operation PUT /orgs/{org}/teams/{team_slug}/projects/{project_id} +func (s *TeamsService) AddTeamProjectBySlug(ctx context.Context, org, slug string, projectID int64, body *TeamProjectOptions) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/projects/%v", org, slug, projectID) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeProjectsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } -// RemoveTeamProject removes an organization project from a team. An organization owner or -// a team maintainer can remove any project from the team. To remove a project from a team -// as an organization member, the authenticated user must have "read" access to both the team -// and project, or "admin" access to the team or project. +// RemoveTeamProjectByID removes an organization project from a team given team ID. +// An organization owner or a team maintainer can remove any project from the team. +// To remove a project from a team as an organization member, the authenticated user +// must have "read" access to both the team and project, or "admin" access to the team +// or project. // Note: This endpoint removes the project from the team, but does not delete it. // -// GitHub API docs: https://developer.github.com/v3/teams/#remove-team-project -func (s *TeamsService) RemoveTeamProject(ctx context.Context, teamID int64, projectID int64) (*Response, error) { - u := fmt.Sprintf("teams/%v/projects/%v", teamID, projectID) - req, err := s.client.NewRequest("DELETE", u, nil) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#remove-a-project-from-a-team +// +//meta:operation DELETE /organizations/{organization_id}/team/{team_id}/projects/{project_id} +func (s *TeamsService) RemoveTeamProjectByID(ctx context.Context, orgID, teamID, projectID int64) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/projects/%v", orgID, teamID, projectID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) + req.Header.Set("Accept", mediaTypeProjectsPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// RemoveTeamProjectBySlug removes an organization project from a team given team slug. +// An organization owner or a team maintainer can remove any project from the team. +// To remove a project from a team as an organization member, the authenticated user +// must have "read" access to both the team and project, or "admin" access to the team +// or project. +// Note: This endpoint removes the project from the team, but does not delete it. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#remove-a-project-from-a-team +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id} +func (s *TeamsService) RemoveTeamProjectBySlug(ctx context.Context, org, slug string, projectID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/projects/%v", org, slug, projectID) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + req.Header.Set("Accept", mediaTypeProjectsPreview) + + return s.client.Do(req, nil) +} + +// ListIDPGroupsOptions specifies the optional parameters to the ListIDPGroupsInOrganization method. +type ListIDPGroupsOptions struct { + // Filters the results to return only those that begin with the value specified by this parameter. + Query string `url:"q,omitempty"` + + ListCursorOptions } // IDPGroupList represents a list of external identity provider (IDP) groups. type IDPGroupList struct { - Groups []*IDPGroup `json:"groups,omitempty"` + Groups []*IDPGroup `json:"groups"` } // IDPGroup represents an external identity provider (IDP) group. @@ -491,72 +849,267 @@ type IDPGroup struct { // ListIDPGroupsInOrganization lists IDP groups available in an organization. // -// GitHub API docs: https://developer.github.com/v3/teams/team_sync/#list-idp-groups-in-an-organization -func (s *TeamsService) ListIDPGroupsInOrganization(ctx context.Context, org string, opt *ListOptions) (*IDPGroupList, *Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#list-idp-groups-for-an-organization +// +//meta:operation GET /orgs/{org}/team-sync/groups +func (s *TeamsService) ListIDPGroupsInOrganization(ctx context.Context, org string, opts *ListIDPGroupsOptions) (*IDPGroupList, *Response, error) { u := fmt.Sprintf("orgs/%v/team-sync/groups", org) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamSyncPreview) - - groups := new(IDPGroupList) - resp, err := s.client.Do(ctx, req, groups) + var groups *IDPGroupList + resp, err := s.client.Do(req, &groups) if err != nil { return nil, resp, err } + return groups, resp, nil } -// ListIDPGroupsForTeam lists IDP groups connected to a team on GitHub. +// ListIDPGroupsForTeamByID lists IDP groups connected to a team on GitHub +// given organization and team IDs. // -// GitHub API docs: https://developer.github.com/v3/teams/team_sync/#list-idp-groups-for-a-team -func (s *TeamsService) ListIDPGroupsForTeam(ctx context.Context, teamID string) (*IDPGroupList, *Response, error) { - u := fmt.Sprintf("teams/%v/team-sync/group-mappings", teamID) +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#list-idp-groups-for-a-team +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/team-sync/group-mappings +func (s *TeamsService) ListIDPGroupsForTeamByID(ctx context.Context, orgID, teamID int64) (*IDPGroupList, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/team-sync/group-mappings", orgID, teamID) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamSyncPreview) + var groups *IDPGroupList + resp, err := s.client.Do(req, &groups) + if err != nil { + return nil, resp, err + } + + return groups, resp, nil +} + +// ListIDPGroupsForTeamBySlug lists IDP groups connected to a team on GitHub +// given organization name and team slug. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#list-idp-groups-for-a-team +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/team-sync/group-mappings +func (s *TeamsService) ListIDPGroupsForTeamBySlug(ctx context.Context, org, slug string) (*IDPGroupList, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/team-sync/group-mappings", org, slug) - groups := new(IDPGroupList) - resp, err := s.client.Do(ctx, req, groups) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var groups *IDPGroupList + resp, err := s.client.Do(req, &groups) if err != nil { return nil, resp, err } - return groups, resp, err + + return groups, resp, nil } -// CreateOrUpdateIDPGroupConnections creates, updates, or removes a connection between a team -// and an IDP group. +// CreateOrUpdateIDPGroupConnectionsByID creates, updates, or removes a connection +// between a team and an IDP group given organization and team IDs. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#create-or-update-idp-group-connections // -// GitHub API docs: https://developer.github.com/v3/teams/team_sync/#create-or-update-idp-group-connections -func (s *TeamsService) CreateOrUpdateIDPGroupConnections(ctx context.Context, teamID string, opt IDPGroupList) (*IDPGroupList, *Response, error) { - u := fmt.Sprintf("teams/%v/team-sync/group-mappings", teamID) +//meta:operation PATCH /organizations/{organization_id}/team/{team_id}/team-sync/group-mappings +func (s *TeamsService) CreateOrUpdateIDPGroupConnectionsByID(ctx context.Context, orgID, teamID int64, body IDPGroupList) (*IDPGroupList, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/team-sync/group-mappings", orgID, teamID) - req, err := s.client.NewRequest("PATCH", u, opt) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamSyncPreview) + var groups *IDPGroupList + resp, err := s.client.Do(req, &groups) + if err != nil { + return nil, resp, err + } + + return groups, resp, nil +} + +// CreateOrUpdateIDPGroupConnectionsBySlug creates, updates, or removes a connection +// between a team and an IDP group given organization name and team slug. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#create-or-update-idp-group-connections +// +//meta:operation PATCH /orgs/{org}/teams/{team_slug}/team-sync/group-mappings +func (s *TeamsService) CreateOrUpdateIDPGroupConnectionsBySlug(ctx context.Context, org, slug string, body IDPGroupList) (*IDPGroupList, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/team-sync/group-mappings", org, slug) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } - groups := new(IDPGroupList) - resp, err := s.client.Do(ctx, req, groups) + var groups *IDPGroupList + resp, err := s.client.Do(req, &groups) if err != nil { return nil, resp, err } return groups, resp, nil } + +// ExternalGroupMember represents a member of an external group. +type ExternalGroupMember struct { + MemberID *int64 `json:"member_id,omitempty"` + MemberLogin *string `json:"member_login,omitempty"` + MemberName *string `json:"member_name,omitempty"` + MemberEmail *string `json:"member_email,omitempty"` +} + +// ExternalGroupTeam represents a team connected to an external group. +type ExternalGroupTeam struct { + TeamID *int64 `json:"team_id,omitempty"` + TeamName *string `json:"team_name,omitempty"` +} + +// ExternalGroup represents an external group. +type ExternalGroup struct { + GroupID *int64 `json:"group_id,omitempty"` + GroupName *string `json:"group_name,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Teams []*ExternalGroupTeam `json:"teams,omitempty"` + Members []*ExternalGroupMember `json:"members,omitempty"` +} + +// ExternalGroupList represents a list of external groups. +type ExternalGroupList struct { + Groups []*ExternalGroup `json:"groups"` +} + +// UpdateConnectedExternalGroupRequest represents a request to update the connection +// between an external group and a team. +type UpdateConnectedExternalGroupRequest struct { + GroupID int64 `json:"group_id"` +} + +// GetExternalGroup fetches an external group. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#get-an-external-group +// +//meta:operation GET /orgs/{org}/external-group/{group_id} +func (s *TeamsService) GetExternalGroup(ctx context.Context, org string, groupID int64) (*ExternalGroup, *Response, error) { + u := fmt.Sprintf("orgs/%v/external-group/%v", org, groupID) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var externalGroup *ExternalGroup + resp, err := s.client.Do(req, &externalGroup) + if err != nil { + return nil, resp, err + } + + return externalGroup, resp, nil +} + +// ListExternalGroupsOptions specifies the optional parameters to the +// TeamsService.ListExternalGroups method. +type ListExternalGroupsOptions struct { + DisplayName *string `url:"display_name,omitempty"` + + ListOptions +} + +// ListExternalGroups lists external groups in an organization on GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#list-external-groups-available-to-an-organization +// +//meta:operation GET /orgs/{org}/external-groups +func (s *TeamsService) ListExternalGroups(ctx context.Context, org string, opts *ListExternalGroupsOptions) (*ExternalGroupList, *Response, error) { + u := fmt.Sprintf("orgs/%v/external-groups", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var externalGroups *ExternalGroupList + resp, err := s.client.Do(req, &externalGroups) + if err != nil { + return nil, resp, err + } + + return externalGroups, resp, nil +} + +// ListExternalGroupsForTeamBySlug lists external groups connected to a team on GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#list-a-connection-between-an-external-group-and-a-team +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/external-groups +func (s *TeamsService) ListExternalGroupsForTeamBySlug(ctx context.Context, org, slug string) (*ExternalGroupList, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/external-groups", org, slug) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var externalGroups *ExternalGroupList + resp, err := s.client.Do(req, &externalGroups) + if err != nil { + return nil, resp, err + } + + return externalGroups, resp, nil +} + +// UpdateConnectedExternalGroup updates the connection between an external group and a team. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#update-the-connection-between-an-external-group-and-a-team +// +//meta:operation PATCH /orgs/{org}/teams/{team_slug}/external-groups +func (s *TeamsService) UpdateConnectedExternalGroup(ctx context.Context, org, slug string, body UpdateConnectedExternalGroupRequest) (*ExternalGroup, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/external-groups", org, slug) + + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var externalGroup *ExternalGroup + resp, err := s.client.Do(req, &externalGroup) + if err != nil { + return nil, resp, err + } + + return externalGroup, resp, nil +} + +// RemoveConnectedExternalGroup removes the connection between an external group and a team. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups?apiVersion=2022-11-28#remove-the-connection-between-an-external-group-and-a-team +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/external-groups +func (s *TeamsService) RemoveConnectedExternalGroup(ctx context.Context, org, slug string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/external-groups", org, slug) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/teams_discussion_comments.go b/github/teams_discussion_comments.go index a0206b9c92b..84e5e98a8be 100644 --- a/github/teams_discussion_comments.go +++ b/github/teams_discussion_comments.go @@ -10,7 +10,7 @@ import ( "fmt" ) -// DiscussionComment represents a GitHub dicussion in a team. +// DiscussionComment represents a GitHub discussion in a team. type DiscussionComment struct { Author *User `json:"author,omitempty"` Body *string `json:"body,omitempty"` @@ -37,29 +37,60 @@ type DiscussionCommentListOptions struct { // Sorts the discussion comments by the date they were created. // Accepted values are asc and desc. Default is desc. Direction string `url:"direction,omitempty"` + ListOptions } -// ListComments lists all comments on a team discussion. +// ListCommentsByID lists all comments on a team discussion by team ID. // Authenticated user must grant read:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#list-comments -func (s *TeamsService) ListComments(ctx context.Context, teamID int64, discussionNumber int, options *DiscussionCommentListOptions) ([]*DiscussionComment, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v/comments", teamID, discussionNumber) - u, err := addOptions(u, options) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#list-discussion-comments +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments +func (s *TeamsService) ListCommentsByID(ctx context.Context, orgID, teamID int64, discussionNumber int, opts *DiscussionCommentListOptions) ([]*DiscussionComment, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/comments", orgID, teamID, discussionNumber) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var comments []*DiscussionComment + resp, err := s.client.Do(req, &comments) + if err != nil { + return nil, resp, err + } + + return comments, resp, nil +} + +// ListCommentsBySlug lists all comments on a team discussion by team slug. +// Authenticated user must grant read:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#list-discussion-comments +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments +func (s *TeamsService) ListCommentsBySlug(ctx context.Context, org, slug string, discussionNumber int, opts *DiscussionCommentListOptions) ([]*DiscussionComment, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments", org, slug, discussionNumber) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } var comments []*DiscussionComment - resp, err := s.client.Do(ctx, req, &comments) + resp, err := s.client.Do(req, &comments) if err != nil { return nil, resp, err } @@ -67,22 +98,48 @@ func (s *TeamsService) ListComments(ctx context.Context, teamID int64, discussio return comments, resp, nil } -// GetComment gets a specific comment on a team discussion. +// GetCommentByID gets a specific comment on a team discussion by team ID. // Authenticated user must grant read:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#get-a-single-comment -func (s *TeamsService) GetComment(ctx context.Context, teamID int64, discussionNumber, commentNumber int) (*DiscussionComment, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v", teamID, discussionNumber, commentNumber) - req, err := s.client.NewRequest("GET", u, nil) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#get-a-discussion-comment +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} +func (s *TeamsService) GetCommentByID(ctx context.Context, orgID, teamID int64, discussionNumber, commentNumber int) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/comments/%v", orgID, teamID, discussionNumber, commentNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var discussionComment *DiscussionComment + resp, err := s.client.Do(req, &discussionComment) + if err != nil { + return nil, resp, err + } + + return discussionComment, resp, nil +} + +// GetCommentBySlug gets a specific comment on a team discussion by team slug. +// Authenticated user must grant read:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#get-a-discussion-comment +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} +func (s *TeamsService) GetCommentBySlug(ctx context.Context, org, slug string, discussionNumber, commentNumber int) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments/%v", org, slug, discussionNumber, commentNumber) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - discussionComment := &DiscussionComment{} - resp, err := s.client.Do(ctx, req, discussionComment) + var discussionComment *DiscussionComment + resp, err := s.client.Do(req, &discussionComment) if err != nil { return nil, resp, err } @@ -90,22 +147,47 @@ func (s *TeamsService) GetComment(ctx context.Context, teamID int64, discussionN return discussionComment, resp, nil } -// CreateComment creates a new discussion post on a team discussion. +// CreateCommentByID creates a new comment on a team discussion by team ID. // Authenticated user must grant write:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#create-a-comment -func (s *TeamsService) CreateComment(ctx context.Context, teamID int64, discsusionNumber int, comment DiscussionComment) (*DiscussionComment, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v/comments", teamID, discsusionNumber) - req, err := s.client.NewRequest("POST", u, comment) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#create-a-discussion-comment +// +//meta:operation POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments +func (s *TeamsService) CreateCommentByID(ctx context.Context, orgID, teamID int64, discussionNumber int, body DiscussionComment) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/comments", orgID, teamID, discussionNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var discussionComment *DiscussionComment + resp, err := s.client.Do(req, &discussionComment) + if err != nil { + return nil, resp, err + } + + return discussionComment, resp, nil +} - discussionComment := &DiscussionComment{} - resp, err := s.client.Do(ctx, req, discussionComment) +// CreateCommentBySlug creates a new comment on a team discussion by team slug. +// Authenticated user must grant write:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#create-a-discussion-comment +// +//meta:operation POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments +func (s *TeamsService) CreateCommentBySlug(ctx context.Context, org, slug string, discussionNumber int, body DiscussionComment) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments", org, slug, discussionNumber) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var discussionComment *DiscussionComment + resp, err := s.client.Do(req, &discussionComment) if err != nil { return nil, resp, err } @@ -113,23 +195,49 @@ func (s *TeamsService) CreateComment(ctx context.Context, teamID int64, discsusi return discussionComment, resp, nil } -// EditComment edits the body text of a discussion comment. +// EditCommentByID edits the body text of a discussion comment by team ID. // Authenticated user must grant write:discussion scope. // User is allowed to edit body of a comment only. // -// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#edit-a-comment -func (s *TeamsService) EditComment(ctx context.Context, teamID int64, discussionNumber, commentNumber int, comment DiscussionComment) (*DiscussionComment, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v", teamID, discussionNumber, commentNumber) - req, err := s.client.NewRequest("PATCH", u, comment) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#update-a-discussion-comment +// +//meta:operation PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} +func (s *TeamsService) EditCommentByID(ctx context.Context, orgID, teamID int64, discussionNumber, commentNumber int, body DiscussionComment) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/comments/%v", orgID, teamID, discussionNumber, commentNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var discussionComment *DiscussionComment + resp, err := s.client.Do(req, &discussionComment) + if err != nil { + return nil, resp, err + } + + return discussionComment, resp, nil +} - discussionComment := &DiscussionComment{} - resp, err := s.client.Do(ctx, req, discussionComment) +// EditCommentBySlug edits the body text of a discussion comment by team slug. +// Authenticated user must grant write:discussion scope. +// User is allowed to edit body of a comment only. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#update-a-discussion-comment +// +//meta:operation PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} +func (s *TeamsService) EditCommentBySlug(ctx context.Context, org, slug string, discussionNumber, commentNumber int, body DiscussionComment) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments/%v", org, slug, discussionNumber, commentNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var discussionComment *DiscussionComment + resp, err := s.client.Do(req, &discussionComment) if err != nil { return nil, resp, err } @@ -137,19 +245,38 @@ func (s *TeamsService) EditComment(ctx context.Context, teamID int64, discussion return discussionComment, resp, nil } -// DeleteComment deletes a comment on a team discussion. +// DeleteCommentByID deletes a comment on a team discussion by team ID. // Authenticated user must grant write:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#delete-a-comment -func (s *TeamsService) DeleteComment(ctx context.Context, teamID int64, discussionNumber, commentNumber int) (*Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v", teamID, discussionNumber, commentNumber) - req, err := s.client.NewRequest("DELETE", u, nil) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#delete-a-discussion-comment +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} +func (s *TeamsService) DeleteCommentByID(ctx context.Context, orgID, teamID int64, discussionNumber, commentNumber int) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v/comments/%v", orgID, teamID, discussionNumber, commentNumber) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + return s.client.Do(req, nil) +} + +// DeleteCommentBySlug deletes a comment on a team discussion by team slug. +// Authenticated user must grant write:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#delete-a-discussion-comment +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} +func (s *TeamsService) DeleteCommentBySlug(ctx context.Context, org, slug string, discussionNumber, commentNumber int) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments/%v", org, slug, discussionNumber, commentNumber) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/teams_discussion_comments_test.go b/github/teams_discussion_comments_test.go index f4c9f96f4b5..c25ba0f12d9 100644 --- a/github/teams_discussion_comments_test.go +++ b/github/teams_discussion_comments_test.go @@ -6,26 +6,41 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) +// "Team Discussion Comments" endpoint, when using a teamID. +func tdcEndpointByID(orgID, teamID, discussionNumber, commentNumber string) string { + out := fmt.Sprintf("/organizations/%v/team/%v/discussions/%v/comments", orgID, teamID, discussionNumber) + if commentNumber != "" { + return fmt.Sprintf("%v/%v", out, commentNumber) + } + return out +} + +// "Team Discussion Comments" endpoint, when using a team slug. +func tdcEndpointBySlug(org, slug, discussionNumber, commentNumber string) string { + out := fmt.Sprintf("/orgs/%v/teams/%v/discussions/%v/comments", org, slug, discussionNumber) + if commentNumber != "" { + return fmt.Sprintf("%v/%v", out, commentNumber) + } + return out +} + func TestTeamsService_ListComments(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/2/discussions/3/comments", func(w http.ResponseWriter, r *http.Request) { + handleFunc := func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) testFormValues(t, r, values{ "direction": "desc", }) - fmt.Fprintf(w, + fmt.Fprint(w, `[ { "author": { @@ -50,154 +65,354 @@ func TestTeamsService_ListComments(t *testing.T) { "body": "comment", "body_html": "

comment

", "body_version": "version", - "created_at": "2018-01-01T00:00:00Z", + "created_at": `+refTimeStr(1136178000)+`, "last_edited_at": null, "discussion_url": "https://api.github.com/teams/2/discussions/3", "html_url": "https://github.com/orgs/1/teams/2/discussions/3/comments/4", "node_id": "node", "number": 4, - "updated_at": "2018-01-01T00:00:00Z", + "updated_at": `+refTimeStr(1136178001)+`, "url": "https://api.github.com/teams/2/discussions/3/comments/4" } ]`) - }) - - comments, _, err := client.Teams.ListComments(context.Background(), 2, 3, &DiscussionCommentListOptions{"desc"}) - if err != nil { - t.Errorf("Teams.ListComments returned error: %v", err) } want := []*DiscussionComment{ { Author: &User{ - Login: String("author"), - ID: Int64(0), - AvatarURL: String("https://avatars1.githubusercontent.com/u/0?v=4"), - GravatarID: String(""), - URL: String("https://api.github.com/users/author"), - HTMLURL: String("https://github.com/author"), - FollowersURL: String("https://api.github.com/users/author/followers"), - FollowingURL: String("https://api.github.com/users/author/following{/other_user}"), - GistsURL: String("https://api.github.com/users/author/gists{/gist_id}"), - StarredURL: String("https://api.github.com/users/author/starred{/owner}{/repo}"), - SubscriptionsURL: String("https://api.github.com/users/author/subscriptions"), - OrganizationsURL: String("https://api.github.com/users/author/orgs"), - ReposURL: String("https://api.github.com/users/author/repos"), - EventsURL: String("https://api.github.com/users/author/events{/privacy}"), - ReceivedEventsURL: String("https://api.github.com/users/author/received_events"), - Type: String("User"), - SiteAdmin: Bool(false), + Login: Ptr("author"), + ID: Ptr(int64(0)), + AvatarURL: Ptr("https://avatars1.githubusercontent.com/u/0?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/author"), + HTMLURL: Ptr("https://github.com/author"), + FollowersURL: Ptr("https://api.github.com/users/author/followers"), + FollowingURL: Ptr("https://api.github.com/users/author/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/author/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/author/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/author/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/author/orgs"), + ReposURL: Ptr("https://api.github.com/users/author/repos"), + EventsURL: Ptr("https://api.github.com/users/author/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/author/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), }, - Body: String("comment"), - BodyHTML: String("

comment

"), - BodyVersion: String("version"), - CreatedAt: &Timestamp{time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)}, + Body: Ptr("comment"), + BodyHTML: Ptr("

comment

"), + BodyVersion: Ptr("version"), + CreatedAt: refTimestamp(1136178000), LastEditedAt: nil, - DiscussionURL: String("https://api.github.com/teams/2/discussions/3"), - HTMLURL: String("https://github.com/orgs/1/teams/2/discussions/3/comments/4"), - NodeID: String("node"), - Number: Int(4), - UpdatedAt: &Timestamp{time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)}, - URL: String("https://api.github.com/teams/2/discussions/3/comments/4"), + DiscussionURL: Ptr("https://api.github.com/teams/2/discussions/3"), + HTMLURL: Ptr("https://github.com/orgs/1/teams/2/discussions/3/comments/4"), + NodeID: Ptr("node"), + Number: Ptr(4), + UpdatedAt: refTimestamp(1136178001), + URL: Ptr("https://api.github.com/teams/2/discussions/3/comments/4"), }, } - if !reflect.DeepEqual(comments, want) { - t.Errorf("Teams.ListComments returned %+v, want %+v", comments, want) + + e := tdcEndpointByID("1", "2", "3", "") + mux.HandleFunc(e, handleFunc) + + ctx := t.Context() + commentsByID, _, err := client.Teams.ListCommentsByID(ctx, 1, 2, 3, + &DiscussionCommentListOptions{Direction: "desc"}) + if err != nil { + t.Errorf("Teams.ListCommentsByID returned error: %v", err) } + + if !cmp.Equal(commentsByID, want) { + t.Errorf("Teams.ListCommentsByID returned %+v, want %+v", commentsByID, want) + } + + e = tdcEndpointBySlug("a", "b", "3", "") + mux.HandleFunc(e, handleFunc) + + commentsBySlug, _, err := client.Teams.ListCommentsBySlug(ctx, "a", "b", 3, + &DiscussionCommentListOptions{Direction: "desc"}) + if err != nil { + t.Errorf("Teams.ListCommentsBySlug returned error: %v", err) + } + + if !cmp.Equal(commentsBySlug, want) { + t.Errorf("Teams.ListCommentsBySlug returned %+v, want %+v", commentsBySlug, want) + } + + methodName := "ListCommentsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListCommentsByID(ctx, -1, -2, -3, + &DiscussionCommentListOptions{Direction: "desc"}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListCommentsByID(ctx, 1, 2, 3, + &DiscussionCommentListOptions{Direction: "desc"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + methodName = "ListCommentsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListCommentsBySlug(ctx, "a\na", "b\nb", -3, + &DiscussionCommentListOptions{Direction: "desc"}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListCommentsBySlug(ctx, "a", "b", 3, + &DiscussionCommentListOptions{Direction: "desc"}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestTeamsService_GetComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/2/discussions/3/comments/4", func(w http.ResponseWriter, r *http.Request) { + handlerFunc := func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) fmt.Fprint(w, `{"number":4}`) - }) + } + want := &DiscussionComment{Number: Ptr(4)} + + e := tdcEndpointByID("1", "2", "3", "4") + mux.HandleFunc(e, handlerFunc) - comment, _, err := client.Teams.GetComment(context.Background(), 2, 3, 4) + ctx := t.Context() + commentByID, _, err := client.Teams.GetCommentByID(ctx, 1, 2, 3, 4) if err != nil { - t.Errorf("Teams.GetComment returned error: %v", err) + t.Errorf("Teams.GetCommentByID returned error: %v", err) } - want := &DiscussionComment{Number: Int(4)} - if !reflect.DeepEqual(comment, want) { - t.Errorf("Teams.GetComment returned %+v, want %+v", comment, want) + if !cmp.Equal(commentByID, want) { + t.Errorf("Teams.GetCommentByID returned %+v, want %+v", commentByID, want) } -} -func TestTeamsService_CreateComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + e = tdcEndpointBySlug("a", "b", "3", "4") + mux.HandleFunc(e, handlerFunc) - input := DiscussionComment{Body: String("c")} + commentBySlug, _, err := client.Teams.GetCommentBySlug(ctx, "a", "b", 3, 4) + if err != nil { + t.Errorf("Teams.GetCommentBySlug returned error: %v", err) + } - mux.HandleFunc("/teams/2/discussions/3/comments", func(w http.ResponseWriter, r *http.Request) { - v := new(DiscussionComment) - json.NewDecoder(r.Body).Decode(v) + if !cmp.Equal(commentBySlug, want) { + t.Errorf("Teams.GetCommentBySlug returned %+v, want %+v", commentBySlug, want) + } - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) - if !reflect.DeepEqual(v, &input) { - t.Errorf("Request body = %+v, want %+v", v, input) + methodName := "GetCommentByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetCommentByID(ctx, -1, -2, -3, -4) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetCommentByID(ctx, 1, 2, 3, 4) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) - fmt.Fprint(w, `{"number":4}`) + methodName = "ListCommentsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetCommentBySlug(ctx, "a\na", "b\nb", -3, -4) + return err }) - comment, _, err := client.Teams.CreateComment(context.Background(), 2, 3, input) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetCommentBySlug(ctx, "a", "b", 3, 4) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_CreateComment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := DiscussionComment{Body: Ptr("c")} + + handlerFunc := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"number":4}`) + } + want := &DiscussionComment{Number: Ptr(4)} + + e := tdcEndpointByID("1", "2", "3", "") + mux.HandleFunc(e, handlerFunc) + + ctx := t.Context() + commentByID, _, err := client.Teams.CreateCommentByID(ctx, 1, 2, 3, input) if err != nil { - t.Errorf("Teams.CreateComment returned error: %v", err) + t.Errorf("Teams.CreateCommentByID returned error: %v", err) } - want := &DiscussionComment{Number: Int(4)} - if !reflect.DeepEqual(comment, want) { - t.Errorf("Teams.CreateComment returned %+v, want %+v", comment, want) + if !cmp.Equal(commentByID, want) { + t.Errorf("Teams.CreateCommentByID returned %+v, want %+v", commentByID, want) } -} -func TestTeamsService_EditComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + e = tdcEndpointBySlug("a", "b", "3", "") + mux.HandleFunc(e, handlerFunc) + + commentBySlug, _, err := client.Teams.CreateCommentBySlug(ctx, "a", "b", 3, input) + if err != nil { + t.Errorf("Teams.CreateCommentBySlug returned error: %v", err) + } - input := DiscussionComment{Body: String("e")} + if !cmp.Equal(commentBySlug, want) { + t.Errorf("Teams.CreateCommentBySlug returned %+v, want %+v", commentBySlug, want) + } - mux.HandleFunc("/teams/2/discussions/3/comments/4", func(w http.ResponseWriter, r *http.Request) { - v := new(DiscussionComment) - json.NewDecoder(r.Body).Decode(v) + methodName := "CreateCommentByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateCommentByID(ctx, -1, -2, -3, input) + return err + }) - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) - if !reflect.DeepEqual(v, &input) { - t.Errorf("Request body = %+v, want %+v", v, input) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateCommentByID(ctx, 1, 2, 3, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) - fmt.Fprint(w, `{"number":4}`) + methodName = "CreateCommentBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateCommentBySlug(ctx, "a\na", "b\nb", -3, input) + return err }) - comment, _, err := client.Teams.EditComment(context.Background(), 2, 3, 4, input) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateCommentBySlug(ctx, "a", "b", 3, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_EditComment(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := DiscussionComment{Body: Ptr("e")} + handlerFunc := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"number":4}`) + } + want := &DiscussionComment{Number: Ptr(4)} + + e := tdcEndpointByID("1", "2", "3", "4") + mux.HandleFunc(e, handlerFunc) + + ctx := t.Context() + commentByID, _, err := client.Teams.EditCommentByID(ctx, 1, 2, 3, 4, input) + if err != nil { + t.Errorf("Teams.EditCommentByID returned error: %v", err) + } + + if !cmp.Equal(commentByID, want) { + t.Errorf("Teams.EditCommentByID returned %+v, want %+v", commentByID, want) + } + + e = tdcEndpointBySlug("a", "b", "3", "4") + mux.HandleFunc(e, handlerFunc) + + commentBySlug, _, err := client.Teams.EditCommentBySlug(ctx, "a", "b", 3, 4, input) if err != nil { - t.Errorf("Teams.EditComment returned error: %v", err) + t.Errorf("Teams.EditCommentBySlug returned error: %v", err) } - want := &DiscussionComment{Number: Int(4)} - if !reflect.DeepEqual(comment, want) { - t.Errorf("Teams.EditComment returned %+v, want %+v", comment, want) + if !cmp.Equal(commentBySlug, want) { + t.Errorf("Teams.EditCommentBySlug returned %+v, want %+v", commentBySlug, want) } + + methodName := "EditCommentByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.EditCommentByID(ctx, -1, -2, -3, -4, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.EditCommentByID(ctx, 1, 2, 3, 4, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) + + methodName = "EditCommentBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.EditCommentBySlug(ctx, "a\na", "b\nb", -3, -4, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.EditCommentBySlug(ctx, "a", "b", 3, 4, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestTeamsService_DeleteComment(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/2/discussions/3/comments/4", func(w http.ResponseWriter, r *http.Request) { + handlerFunc := func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) - }) + } - _, err := client.Teams.DeleteComment(context.Background(), 2, 3, 4) + e := tdcEndpointByID("1", "2", "3", "4") + mux.HandleFunc(e, handlerFunc) + + ctx := t.Context() + _, err := client.Teams.DeleteCommentByID(ctx, 1, 2, 3, 4) if err != nil { - t.Errorf("Teams.DeleteComment returned error: %v", err) + t.Errorf("Teams.DeleteCommentByID returned error: %v", err) } + + e = tdcEndpointBySlug("a", "b", "3", "4") + mux.HandleFunc(e, handlerFunc) + + _, err = client.Teams.DeleteCommentBySlug(ctx, "a", "b", 3, 4) + if err != nil { + t.Errorf("Teams.DeleteCommentBySlug returned error: %v", err) + } + + methodName := "DeleteCommentByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.DeleteCommentByID(ctx, -1, -2, -3, -4) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Teams.DeleteCommentByID(ctx, 1, 2, 3, 4) + return resp, err + }) + + methodName = "DeleteCommentBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.DeleteCommentBySlug(ctx, "a\na", "b\nb", -3, -4) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + resp, err := client.Teams.DeleteCommentBySlug(ctx, "a", "b", 3, 4) + return resp, err + }) } diff --git a/github/teams_discussions.go b/github/teams_discussions.go index f491c9d1d6e..7a17997aa07 100644 --- a/github/teams_discussions.go +++ b/github/teams_discussions.go @@ -10,7 +10,7 @@ import ( "fmt" ) -// TeamDiscussion represents a GitHub dicussion in a team. +// TeamDiscussion represents a GitHub discussion in a team. type TeamDiscussion struct { Author *User `json:"author,omitempty"` Body *string `json:"body,omitempty"` @@ -42,29 +42,61 @@ type DiscussionListOptions struct { // Sorts the discussion by the date they were created. // Accepted values are asc and desc. Default is desc. Direction string `url:"direction,omitempty"` + + ListOptions } -// ListDiscussions lists all discussions on team's page. +// ListDiscussionsByID lists all discussions on team's page given Organization and Team ID. // Authenticated user must grant read:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussions/#list-discussions -func (s *TeamsService) ListDiscussions(ctx context.Context, teamID int64, options *DiscussionListOptions) ([]*TeamDiscussion, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions", teamID) - u, err := addOptions(u, options) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#list-discussions +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions +func (s *TeamsService) ListDiscussionsByID(ctx context.Context, orgID, teamID int64, opts *DiscussionListOptions) ([]*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions", orgID, teamID) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var teamDiscussions []*TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussions) + if err != nil { + return nil, resp, err + } + + return teamDiscussions, resp, nil +} + +// ListDiscussionsBySlug lists all discussions on team's page given Organization name and Team's slug. +// Authenticated user must grant read:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#list-discussions +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions +func (s *TeamsService) ListDiscussionsBySlug(ctx context.Context, org, slug string, opts *DiscussionListOptions) ([]*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions", org, slug) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } var teamDiscussions []*TeamDiscussion - resp, err := s.client.Do(ctx, req, &teamDiscussions) + resp, err := s.client.Do(req, &teamDiscussions) if err != nil { return nil, resp, err } @@ -72,22 +104,47 @@ func (s *TeamsService) ListDiscussions(ctx context.Context, teamID int64, option return teamDiscussions, resp, nil } -// GetDiscussion gets a specific discussion on a team's page. +// GetDiscussionByID gets a specific discussion on a team's page given Organization and Team ID. // Authenticated user must grant read:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussions/#get-a-single-discussion -func (s *TeamsService) GetDiscussion(ctx context.Context, teamID int64, discussionNumber int) (*TeamDiscussion, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v", teamID, discussionNumber) - req, err := s.client.NewRequest("GET", u, nil) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#get-a-discussion +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} +func (s *TeamsService) GetDiscussionByID(ctx context.Context, orgID, teamID int64, discussionNumber int) (*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v", orgID, teamID, discussionNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var teamDiscussion *TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussion) + if err != nil { + return nil, resp, err + } + + return teamDiscussion, resp, nil +} + +// GetDiscussionBySlug gets a specific discussion on a team's page given Organization name and Team's slug. +// Authenticated user must grant read:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#get-a-discussion +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} +func (s *TeamsService) GetDiscussionBySlug(ctx context.Context, org, slug string, discussionNumber int) (*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v", org, slug, discussionNumber) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } - teamDiscussion := &TeamDiscussion{} - resp, err := s.client.Do(ctx, req, teamDiscussion) + var teamDiscussion *TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussion) if err != nil { return nil, resp, err } @@ -95,22 +152,47 @@ func (s *TeamsService) GetDiscussion(ctx context.Context, teamID int64, discussi return teamDiscussion, resp, nil } -// CreateDiscussion creates a new discussion post on a team's page. +// CreateDiscussionByID creates a new discussion post on a team's page given Organization and Team ID. // Authenticated user must grant write:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussions/#create-a-discussion -func (s *TeamsService) CreateDiscussion(ctx context.Context, teamID int64, discussion TeamDiscussion) (*TeamDiscussion, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions", teamID) - req, err := s.client.NewRequest("POST", u, discussion) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#create-a-discussion +// +//meta:operation POST /orgs/{org}/teams/{team_slug}/discussions +func (s *TeamsService) CreateDiscussionByID(ctx context.Context, orgID, teamID int64, body TeamDiscussion) (*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions", orgID, teamID) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var teamDiscussion *TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussion) + if err != nil { + return nil, resp, err + } + + return teamDiscussion, resp, nil +} - teamDiscussion := &TeamDiscussion{} - resp, err := s.client.Do(ctx, req, teamDiscussion) +// CreateDiscussionBySlug creates a new discussion post on a team's page given Organization name and Team's slug. +// Authenticated user must grant write:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#create-a-discussion +// +//meta:operation POST /orgs/{org}/teams/{team_slug}/discussions +func (s *TeamsService) CreateDiscussionBySlug(ctx context.Context, org, slug string, body TeamDiscussion) (*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions", org, slug) + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var teamDiscussion *TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussion) if err != nil { return nil, resp, err } @@ -118,23 +200,49 @@ func (s *TeamsService) CreateDiscussion(ctx context.Context, teamID int64, discu return teamDiscussion, resp, nil } -// EditDiscussion edits the title and body text of a discussion post. +// EditDiscussionByID edits the title and body text of a discussion post given Organization and Team ID. // Authenticated user must grant write:discussion scope. // User is allowed to change Title and Body of a discussion only. // -// GitHub API docs: https://developer.github.com/v3/teams/discussions/#edit-a-discussion -func (s *TeamsService) EditDiscussion(ctx context.Context, teamID int64, discussionNumber int, discussion TeamDiscussion) (*TeamDiscussion, *Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v", teamID, discussionNumber) - req, err := s.client.NewRequest("PATCH", u, discussion) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#update-a-discussion +// +//meta:operation PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} +func (s *TeamsService) EditDiscussionByID(ctx context.Context, orgID, teamID int64, discussionNumber int, body TeamDiscussion) (*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v", orgID, teamID, discussionNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + var teamDiscussion *TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussion) + if err != nil { + return nil, resp, err + } + + return teamDiscussion, resp, nil +} - teamDiscussion := &TeamDiscussion{} - resp, err := s.client.Do(ctx, req, teamDiscussion) +// EditDiscussionBySlug edits the title and body text of a discussion post given Organization name and Team's slug. +// Authenticated user must grant write:discussion scope. +// User is allowed to change Title and Body of a discussion only. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#update-a-discussion +// +//meta:operation PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} +func (s *TeamsService) EditDiscussionBySlug(ctx context.Context, org, slug string, discussionNumber int, body TeamDiscussion) (*TeamDiscussion, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v", org, slug, discussionNumber) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) + if err != nil { + return nil, nil, err + } + + var teamDiscussion *TeamDiscussion + resp, err := s.client.Do(req, &teamDiscussion) if err != nil { return nil, resp, err } @@ -142,19 +250,38 @@ func (s *TeamsService) EditDiscussion(ctx context.Context, teamID int64, discuss return teamDiscussion, resp, nil } -// DeleteDiscussion deletes a discussion from team's page. +// DeleteDiscussionByID deletes a discussion from team's page given Organization and Team ID. // Authenticated user must grant write:discussion scope. // -// GitHub API docs: https://developer.github.com/v3/teams/discussions/#delete-a-discussion -func (s *TeamsService) DeleteDiscussion(ctx context.Context, teamID int64, discussionNumber int) (*Response, error) { - u := fmt.Sprintf("teams/%v/discussions/%v", teamID, discussionNumber) - req, err := s.client.NewRequest("DELETE", u, nil) +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#delete-a-discussion +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} +func (s *TeamsService) DeleteDiscussionByID(ctx context.Context, orgID, teamID int64, discussionNumber int) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/discussions/%v", orgID, teamID, discussionNumber) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview) + return s.client.Do(req, nil) +} + +// DeleteDiscussionBySlug deletes a discussion from team's page given Organization name and Team's slug. +// Authenticated user must grant write:discussion scope. +// +// Deprecated: This endpoint has been deprecated by GitHub. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#delete-a-discussion +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} +func (s *TeamsService) DeleteDiscussionBySlug(ctx context.Context, org, slug string, discussionNumber int) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v", org, slug, discussionNumber) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/teams_discussions_test.go b/github/teams_discussions_test.go index c588c228954..11be8d3ddda 100644 --- a/github/teams_discussions_test.go +++ b/github/teams_discussions_test.go @@ -6,26 +6,139 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" - "time" + + "github.com/google/go-cmp/cmp" ) -func TestTeamsService_ListDiscussions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListDiscussionsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/discussions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "direction": "desc", + "page": "2", + }) + fmt.Fprint(w, + `[ + { + "author": { + "login": "author", + "id": 0, + "avatar_url": "https://avatars1.githubusercontent.com/u/0?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/author", + "html_url": "https://github.com/author", + "followers_url": "https://api.github.com/users/author/followers", + "following_url": "https://api.github.com/users/author/following{/other_user}", + "gists_url": "https://api.github.com/users/author/gists{/gist_id}", + "starred_url": "https://api.github.com/users/author/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/author/subscriptions", + "organizations_url": "https://api.github.com/users/author/orgs", + "repos_url": "https://api.github.com/users/author/repos", + "events_url": "https://api.github.com/users/author/events{/privacy}", + "received_events_url": "https://api.github.com/users/author/received_events", + "type": "User", + "site_admin": false + }, + "body": "test", + "body_html": "

test

", + "body_version": "version", + "comments_count": 1, + "comments_url": "https://api.github.com/teams/2/discussions/3/comments", + "created_at": `+refTimeStr(1136178000)+`, + "last_edited_at": null, + "html_url": "https://github.com/orgs/1/teams/2/discussions/3", + "node_id": "node", + "number": 3, + "pinned": false, + "private": false, + "team_url": "https://api.github.com/teams/2", + "title": "test", + "updated_at": `+refTimeStr(1136178001)+`, + "url": "https://api.github.com/teams/2/discussions/3" + } + ]`) + }) + ctx := t.Context() + discussions, _, err := client.Teams.ListDiscussionsByID(ctx, 1, 2, &DiscussionListOptions{"desc", ListOptions{Page: 2}}) + if err != nil { + t.Errorf("Teams.ListDiscussionsByID returned error: %v", err) + } + + want := []*TeamDiscussion{ + { + Author: &User{ + Login: Ptr("author"), + ID: Ptr(int64(0)), + AvatarURL: Ptr("https://avatars1.githubusercontent.com/u/0?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/author"), + HTMLURL: Ptr("https://github.com/author"), + FollowersURL: Ptr("https://api.github.com/users/author/followers"), + FollowingURL: Ptr("https://api.github.com/users/author/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/author/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/author/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/author/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/author/orgs"), + ReposURL: Ptr("https://api.github.com/users/author/repos"), + EventsURL: Ptr("https://api.github.com/users/author/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/author/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), + }, + Body: Ptr("test"), + BodyHTML: Ptr("

test

"), + BodyVersion: Ptr("version"), + CommentsCount: Ptr(1), + CommentsURL: Ptr("https://api.github.com/teams/2/discussions/3/comments"), + CreatedAt: refTimestamp(1136178000), + LastEditedAt: nil, + HTMLURL: Ptr("https://github.com/orgs/1/teams/2/discussions/3"), + NodeID: Ptr("node"), + Number: Ptr(3), + Pinned: Ptr(false), + Private: Ptr(false), + TeamURL: Ptr("https://api.github.com/teams/2"), + Title: Ptr("test"), + UpdatedAt: refTimestamp(1136178001), + URL: Ptr("https://api.github.com/teams/2/discussions/3"), + }, + } + if !cmp.Equal(discussions, want) { + t.Errorf("Teams.ListDiscussionsByID returned %+v, want %+v", discussions, want) + } + + const methodName = "ListDiscussionsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListDiscussionsByID(ctx, -1, -2, nil) + return err + }) - mux.HandleFunc("/teams/2/discussions", func(w http.ResponseWriter, r *http.Request) { + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListDiscussionsByID(ctx, 1, 2, &DiscussionListOptions{"desc", ListOptions{Page: 2}}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListDiscussionsBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/discussions", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) testFormValues(t, r, values{ "direction": "desc", + "page": "2", }) - fmt.Fprintf(w, + fmt.Fprint(w, `[ { "author": { @@ -52,7 +165,7 @@ func TestTeamsService_ListDiscussions(t *testing.T) { "body_version": "version", "comments_count": 1, "comments_url": "https://api.github.com/teams/2/discussions/3/comments", - "created_at": "2018-01-01T00:00:00Z", + "created_at": `+refTimeStr(1136178000)+`, "last_edited_at": null, "html_url": "https://github.com/orgs/1/teams/2/discussions/3", "node_id": "node", @@ -61,152 +174,343 @@ func TestTeamsService_ListDiscussions(t *testing.T) { "private": false, "team_url": "https://api.github.com/teams/2", "title": "test", - "updated_at": "2018-01-01T00:00:00Z", + "updated_at": `+refTimeStr(1136178001)+`, "url": "https://api.github.com/teams/2/discussions/3" } ]`) }) - discussions, _, err := client.Teams.ListDiscussions(context.Background(), 2, &DiscussionListOptions{"desc"}) + ctx := t.Context() + discussions, _, err := client.Teams.ListDiscussionsBySlug(ctx, "o", "s", &DiscussionListOptions{"desc", ListOptions{Page: 2}}) if err != nil { - t.Errorf("Teams.ListDiscussions returned error: %v", err) + t.Errorf("Teams.ListDiscussionsBySlug returned error: %v", err) } want := []*TeamDiscussion{ { Author: &User{ - Login: String("author"), - ID: Int64(0), - AvatarURL: String("https://avatars1.githubusercontent.com/u/0?v=4"), - GravatarID: String(""), - URL: String("https://api.github.com/users/author"), - HTMLURL: String("https://github.com/author"), - FollowersURL: String("https://api.github.com/users/author/followers"), - FollowingURL: String("https://api.github.com/users/author/following{/other_user}"), - GistsURL: String("https://api.github.com/users/author/gists{/gist_id}"), - StarredURL: String("https://api.github.com/users/author/starred{/owner}{/repo}"), - SubscriptionsURL: String("https://api.github.com/users/author/subscriptions"), - OrganizationsURL: String("https://api.github.com/users/author/orgs"), - ReposURL: String("https://api.github.com/users/author/repos"), - EventsURL: String("https://api.github.com/users/author/events{/privacy}"), - ReceivedEventsURL: String("https://api.github.com/users/author/received_events"), - Type: String("User"), - SiteAdmin: Bool(false), + Login: Ptr("author"), + ID: Ptr(int64(0)), + AvatarURL: Ptr("https://avatars1.githubusercontent.com/u/0?v=4"), + GravatarID: Ptr(""), + URL: Ptr("https://api.github.com/users/author"), + HTMLURL: Ptr("https://github.com/author"), + FollowersURL: Ptr("https://api.github.com/users/author/followers"), + FollowingURL: Ptr("https://api.github.com/users/author/following{/other_user}"), + GistsURL: Ptr("https://api.github.com/users/author/gists{/gist_id}"), + StarredURL: Ptr("https://api.github.com/users/author/starred{/owner}{/repo}"), + SubscriptionsURL: Ptr("https://api.github.com/users/author/subscriptions"), + OrganizationsURL: Ptr("https://api.github.com/users/author/orgs"), + ReposURL: Ptr("https://api.github.com/users/author/repos"), + EventsURL: Ptr("https://api.github.com/users/author/events{/privacy}"), + ReceivedEventsURL: Ptr("https://api.github.com/users/author/received_events"), + Type: Ptr("User"), + SiteAdmin: Ptr(false), }, - Body: String("test"), - BodyHTML: String("

test

"), - BodyVersion: String("version"), - CommentsCount: Int(1), - CommentsURL: String("https://api.github.com/teams/2/discussions/3/comments"), - CreatedAt: &Timestamp{time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)}, + Body: Ptr("test"), + BodyHTML: Ptr("

test

"), + BodyVersion: Ptr("version"), + CommentsCount: Ptr(1), + CommentsURL: Ptr("https://api.github.com/teams/2/discussions/3/comments"), + CreatedAt: refTimestamp(1136178000), LastEditedAt: nil, - HTMLURL: String("https://github.com/orgs/1/teams/2/discussions/3"), - NodeID: String("node"), - Number: Int(3), - Pinned: Bool(false), - Private: Bool(false), - TeamURL: String("https://api.github.com/teams/2"), - Title: String("test"), - UpdatedAt: &Timestamp{time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)}, - URL: String("https://api.github.com/teams/2/discussions/3"), + HTMLURL: Ptr("https://github.com/orgs/1/teams/2/discussions/3"), + NodeID: Ptr("node"), + Number: Ptr(3), + Pinned: Ptr(false), + Private: Ptr(false), + TeamURL: Ptr("https://api.github.com/teams/2"), + Title: Ptr("test"), + UpdatedAt: refTimestamp(1136178001), + URL: Ptr("https://api.github.com/teams/2/discussions/3"), }, } - if !reflect.DeepEqual(discussions, want) { - t.Errorf("Teams.ListDiscussions returned %+v, want %+v", discussions, want) + if !cmp.Equal(discussions, want) { + t.Errorf("Teams.ListDiscussionsBySlug returned %+v, want %+v", discussions, want) } + + const methodName = "ListDiscussionsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListDiscussionsBySlug(ctx, "o\no", "s\ns", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListDiscussionsBySlug(ctx, "o", "s", &DiscussionListOptions{"desc", ListOptions{Page: 2}}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_GetDiscussion(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_GetDiscussionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/2/discussions/3", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/2/discussions/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) fmt.Fprint(w, `{"number":3}`) }) - discussion, _, err := client.Teams.GetDiscussion(context.Background(), 2, 3) + ctx := t.Context() + discussion, _, err := client.Teams.GetDiscussionByID(ctx, 1, 2, 3) if err != nil { - t.Errorf("Teams.GetDiscussion returned error: %v", err) + t.Errorf("Teams.GetDiscussionByID returned error: %v", err) } - want := &TeamDiscussion{Number: Int(3)} - if !reflect.DeepEqual(discussion, want) { - t.Errorf("Teams.GetDiscussion returned %+v, want %+v", discussion, want) + want := &TeamDiscussion{Number: Ptr(3)} + if !cmp.Equal(discussion, want) { + t.Errorf("Teams.GetDiscussionByID returned %+v, want %+v", discussion, want) } + + const methodName = "GetDiscussionByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetDiscussionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetDiscussionByID(ctx, 1, 2, 3) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_CreateDiscussion(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_GetDiscussionBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := TeamDiscussion{Title: String("c_t"), Body: String("c_b")} + mux.HandleFunc("/orgs/o/teams/s/discussions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"number":3}`) + }) - mux.HandleFunc("/teams/2/discussions", func(w http.ResponseWriter, r *http.Request) { - v := new(TeamDiscussion) - json.NewDecoder(r.Body).Decode(v) + ctx := t.Context() + discussion, _, err := client.Teams.GetDiscussionBySlug(ctx, "o", "s", 3) + if err != nil { + t.Errorf("Teams.GetDiscussionBySlug returned error: %v", err) + } - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) - if !reflect.DeepEqual(v, &input) { - t.Errorf("Request body = %+v, want %+v", v, input) + want := &TeamDiscussion{Number: Ptr(3)} + if !cmp.Equal(discussion, want) { + t.Errorf("Teams.GetDiscussionBySlug returned %+v, want %+v", discussion, want) + } + + const methodName = "GetDiscussionBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetDiscussionBySlug(ctx, "o\no", "s\ns", -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetDiscussionBySlug(ctx, "o", "s", 3) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} + +func TestTeamsService_CreateDiscussionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := TeamDiscussion{Title: Ptr("c_t"), Body: Ptr("c_b")} + mux.HandleFunc("/organizations/1/team/2/discussions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":3}`) }) - comment, _, err := client.Teams.CreateDiscussion(context.Background(), 2, input) + ctx := t.Context() + comment, _, err := client.Teams.CreateDiscussionByID(ctx, 1, 2, input) if err != nil { - t.Errorf("Teams.CreateDiscussion returned error: %v", err) + t.Errorf("Teams.CreateDiscussionByID returned error: %v", err) } - want := &TeamDiscussion{Number: Int(3)} - if !reflect.DeepEqual(comment, want) { - t.Errorf("Teams.CreateDiscussion returned %+v, want %+v", comment, want) + want := &TeamDiscussion{Number: Ptr(3)} + if !cmp.Equal(comment, want) { + t.Errorf("Teams.CreateDiscussionByID returned %+v, want %+v", comment, want) } + + const methodName = "CreateDiscussionByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateDiscussionByID(ctx, -1, -2, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateDiscussionByID(ctx, 1, 2, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_EditDiscussion(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_CreateDiscussionBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := TeamDiscussion{Title: Ptr("c_t"), Body: Ptr("c_b")} + + mux.HandleFunc("/orgs/o/teams/s/discussions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"number":3}`) + }) + + ctx := t.Context() + comment, _, err := client.Teams.CreateDiscussionBySlug(ctx, "o", "s", input) + if err != nil { + t.Errorf("Teams.CreateDiscussionBySlug returned error: %v", err) + } + + want := &TeamDiscussion{Number: Ptr(3)} + if !cmp.Equal(comment, want) { + t.Errorf("Teams.CreateDiscussionBySlug returned %+v, want %+v", comment, want) + } + + const methodName = "CreateDiscussionBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateDiscussionBySlug(ctx, "o\no", "s\ns", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateDiscussionBySlug(ctx, "o", "s", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} - input := TeamDiscussion{Title: String("e_t"), Body: String("e_b")} +func TestTeamsService_EditDiscussionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/2/discussions/3", func(w http.ResponseWriter, r *http.Request) { - v := new(TeamDiscussion) - json.NewDecoder(r.Body).Decode(v) + input := TeamDiscussion{Title: Ptr("e_t"), Body: Ptr("e_b")} + mux.HandleFunc("/organizations/1/team/2/discussions/3", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) - if !reflect.DeepEqual(v, &input) { - t.Errorf("Request body = %+v, want %+v", v, input) + testJSONBody(t, r, input) + fmt.Fprint(w, `{"number":3}`) + }) + + ctx := t.Context() + comment, _, err := client.Teams.EditDiscussionByID(ctx, 1, 2, 3, input) + if err != nil { + t.Errorf("Teams.EditDiscussionByID returned error: %v", err) + } + + want := &TeamDiscussion{Number: Ptr(3)} + if !cmp.Equal(comment, want) { + t.Errorf("Teams.EditDiscussionByID returned %+v, want %+v", comment, want) + } + + const methodName = "EditDiscussionByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.EditDiscussionByID(ctx, -1, -2, -3, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.EditDiscussionByID(ctx, 1, 2, 3, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} + +func TestTeamsService_EditDiscussionBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := TeamDiscussion{Title: Ptr("e_t"), Body: Ptr("e_b")} + mux.HandleFunc("/orgs/o/teams/s/discussions/3", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) fmt.Fprint(w, `{"number":3}`) }) - comment, _, err := client.Teams.EditDiscussion(context.Background(), 2, 3, input) + ctx := t.Context() + comment, _, err := client.Teams.EditDiscussionBySlug(ctx, "o", "s", 3, input) if err != nil { - t.Errorf("Teams.EditDiscussion returned error: %v", err) + t.Errorf("Teams.EditDiscussionBySlug returned error: %v", err) + } + + want := &TeamDiscussion{Number: Ptr(3)} + if !cmp.Equal(comment, want) { + t.Errorf("Teams.EditDiscussionBySlug returned %+v, want %+v", comment, want) } - want := &TeamDiscussion{Number: Int(3)} - if !reflect.DeepEqual(comment, want) { - t.Errorf("Teams.EditDiscussion returned %+v, want %+v", comment, want) + const methodName = "EditDiscussionBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.EditDiscussionBySlug(ctx, "o\no", "s\ns", -3, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.EditDiscussionBySlug(ctx, "o", "s", 3, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_DeleteDiscussionByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/discussions/3", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Teams.DeleteDiscussionByID(ctx, 1, 2, 3) + if err != nil { + t.Errorf("Teams.DeleteDiscussionByID returned error: %v", err) } + + const methodName = "DeleteDiscussionByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.DeleteDiscussionByID(ctx, -1, -2, -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.DeleteDiscussionByID(ctx, 1, 2, 3) + }) } -func TestTeamsService_DeleteDiscussion(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_DeleteDiscussionBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/2/discussions/3", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/teams/s/discussions/3", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeTeamDiscussionsPreview) }) - _, err := client.Teams.DeleteDiscussion(context.Background(), 2, 3) + ctx := t.Context() + _, err := client.Teams.DeleteDiscussionBySlug(ctx, "o", "s", 3) if err != nil { - t.Errorf("Teams.DeleteDiscussion returned error: %v", err) + t.Errorf("Teams.DeleteDiscussionBySlug returned error: %v", err) } + + const methodName = "DeleteDiscussionBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.DeleteDiscussionBySlug(ctx, "o\no", "s\ns", -3) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.DeleteDiscussionBySlug(ctx, "o", "s", 3) + }) } diff --git a/github/teams_members.go b/github/teams_members.go index d5cfa0dc7dd..184ab06bbcb 100644 --- a/github/teams_members.go +++ b/github/teams_members.go @@ -20,26 +20,26 @@ type TeamListTeamMembersOptions struct { ListOptions } -// ListTeamMembers lists all of the users who are members of the specified -// team. +// ListTeamMembersByID lists all of the users who are members of a team, given a specified +// organization ID, by team ID. // -// GitHub API docs: https://developer.github.com/v3/teams/members/#list-team-members -func (s *TeamsService) ListTeamMembers(ctx context.Context, team int64, opt *TeamListTeamMembersOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("teams/%v/members", team) - u, err := addOptions(u, opt) +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#list-team-members +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/members +func (s *TeamsService) ListTeamMembersByID(ctx context.Context, orgID, teamID int64, opts *TeamListTeamMembersOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/members", orgID, teamID) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - var members []*User - resp, err := s.client.Do(ctx, req, &members) + resp, err := s.client.Do(req, &members) if err != nil { return nil, resp, err } @@ -47,38 +47,70 @@ func (s *TeamsService) ListTeamMembers(ctx context.Context, team int64, opt *Tea return members, resp, nil } -// IsTeamMember checks if a user is a member of the specified team. +// ListTeamMembersBySlug lists all of the users who are members of a team, given a specified +// organization name, by team slug. // -// GitHub API docs: https://developer.github.com/v3/teams/members/#get-team-member +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#list-team-members // -// Deprecated: This API has been marked as deprecated in the Github API docs, -// TeamsService.GetTeamMembership method should be used instead. -func (s *TeamsService) IsTeamMember(ctx context.Context, team int64, user string) (bool, *Response, error) { - u := fmt.Sprintf("teams/%v/members/%v", team, user) - req, err := s.client.NewRequest("GET", u, nil) +//meta:operation GET /orgs/{org}/teams/{team_slug}/members +func (s *TeamsService) ListTeamMembersBySlug(ctx context.Context, org, slug string, opts *TeamListTeamMembersOptions) ([]*User, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/members", org, slug) + u, err := addOptions(u, opts) if err != nil { - return false, nil, err + return nil, nil, err } - resp, err := s.client.Do(ctx, req, nil) - member, err := parseBoolResponse(err) - return member, resp, err + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var members []*User + resp, err := s.client.Do(req, &members) + if err != nil { + return nil, resp, err + } + + return members, resp, nil } -// GetTeamMembership returns the membership status for a user in a team. +// GetTeamMembershipByID returns the membership status for a user in a team, given a specified +// organization ID, by team ID. // -// GitHub API docs: https://developer.github.com/v3/teams/members/#get-team-membership -func (s *TeamsService) GetTeamMembership(ctx context.Context, team int64, user string) (*Membership, *Response, error) { - u := fmt.Sprintf("teams/%v/memberships/%v", team, user) - req, err := s.client.NewRequest("GET", u, nil) +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#get-team-membership-for-a-user +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/memberships/{username} +func (s *TeamsService) GetTeamMembershipByID(ctx context.Context, orgID, teamID int64, user string) (*Membership, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/memberships/%v", orgID, teamID, user) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) + var t *Membership + resp, err := s.client.Do(req, &t) + if err != nil { + return nil, resp, err + } - t := new(Membership) - resp, err := s.client.Do(ctx, req, t) + return t, resp, nil +} + +// GetTeamMembershipBySlug returns the membership status for a user in a team, given a specified +// organization name, by team slug. +// +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#get-team-membership-for-a-user +// +//meta:operation GET /orgs/{org}/teams/{team_slug}/memberships/{username} +func (s *TeamsService) GetTeamMembershipBySlug(ctx context.Context, org, slug, user string) (*Membership, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/memberships/%v", org, slug, user) + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var t *Membership + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -100,33 +132,43 @@ type TeamAddTeamMembershipOptions struct { Role string `json:"role,omitempty"` } -// AddTeamMembership adds or invites a user to a team. +// AddTeamMembershipByID adds or invites a user to a team, given a specified +// organization ID, by team ID. // -// In order to add a membership between a user and a team, the authenticated -// user must have 'admin' permissions to the team or be an owner of the -// organization that the team is associated with. +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#add-or-update-team-membership-for-a-user // -// If the user is already a part of the team's organization (meaning they're on -// at least one other team in the organization), this endpoint will add the -// user to the team. +//meta:operation PUT /organizations/{organization_id}/team/{team_id}/memberships/{username} +func (s *TeamsService) AddTeamMembershipByID(ctx context.Context, orgID, teamID int64, user string, body *TeamAddTeamMembershipOptions) (*Membership, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/memberships/%v", orgID, teamID, user) + req, err := s.client.NewRequest(ctx, "PUT", u, body) + if err != nil { + return nil, nil, err + } + + var t *Membership + resp, err := s.client.Do(req, &t) + if err != nil { + return nil, resp, err + } + + return t, resp, nil +} + +// AddTeamMembershipBySlug adds or invites a user to a team, given a specified +// organization name, by team slug. // -// If the user is completely unaffiliated with the team's organization (meaning -// they're on none of the organization's teams), this endpoint will send an -// invitation to the user via email. This newly-created membership will be in -// the "pending" state until the user accepts the invitation, at which point -// the membership will transition to the "active" state and the user will be -// added as a member of the team. +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#add-or-update-team-membership-for-a-user // -// GitHub API docs: https://developer.github.com/v3/teams/members/#add-or-update-team-membership -func (s *TeamsService) AddTeamMembership(ctx context.Context, team int64, user string, opt *TeamAddTeamMembershipOptions) (*Membership, *Response, error) { - u := fmt.Sprintf("teams/%v/memberships/%v", team, user) - req, err := s.client.NewRequest("PUT", u, opt) +//meta:operation PUT /orgs/{org}/teams/{team_slug}/memberships/{username} +func (s *TeamsService) AddTeamMembershipBySlug(ctx context.Context, org, slug, user string, body *TeamAddTeamMembershipOptions) (*Membership, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/memberships/%v", org, slug, user) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, nil, err } - t := new(Membership) - resp, err := s.client.Do(ctx, req, t) + var t *Membership + resp, err := s.client.Do(req, &t) if err != nil { return nil, resp, err } @@ -134,38 +176,85 @@ func (s *TeamsService) AddTeamMembership(ctx context.Context, team int64, user s return t, resp, nil } -// RemoveTeamMembership removes a user from a team. +// RemoveTeamMembershipByID removes a user from a team, given a specified +// organization ID, by team ID. +// +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#remove-team-membership-for-a-user +// +//meta:operation DELETE /organizations/{organization_id}/team/{team_id}/memberships/{username} +func (s *TeamsService) RemoveTeamMembershipByID(ctx context.Context, orgID, teamID int64, user string) (*Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/memberships/%v", orgID, teamID, user) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RemoveTeamMembershipBySlug removes a user from a team, given a specified +// organization name, by team slug. // -// GitHub API docs: https://developer.github.com/v3/teams/members/#remove-team-membership -func (s *TeamsService) RemoveTeamMembership(ctx context.Context, team int64, user string) (*Response, error) { - u := fmt.Sprintf("teams/%v/memberships/%v", team, user) - req, err := s.client.NewRequest("DELETE", u, nil) +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#remove-team-membership-for-a-user +// +//meta:operation DELETE /orgs/{org}/teams/{team_slug}/memberships/{username} +func (s *TeamsService) RemoveTeamMembershipBySlug(ctx context.Context, org, slug, user string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/memberships/%v", org, slug, user) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// ListPendingTeamInvitationsByID gets pending invitation list of a team, given a specified +// organization ID, by team ID. +// +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#list-pending-team-invitations +// +//meta:operation GET /organizations/{organization_id}/team/{team_id}/invitations +func (s *TeamsService) ListPendingTeamInvitationsByID(ctx context.Context, orgID, teamID int64, opts *ListOptions) ([]*Invitation, *Response, error) { + u := fmt.Sprintf("organizations/%v/team/%v/invitations", orgID, teamID) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pendingInvitations []*Invitation + resp, err := s.client.Do(req, &pendingInvitations) + if err != nil { + return nil, resp, err + } + + return pendingInvitations, resp, nil } -// ListPendingTeamInvitations get pending invitaion list in team. -// Warning: The API may change without advance notice during the preview period. -// Preview features are not supported for production use. +// ListPendingTeamInvitationsBySlug get pending invitation list of a team, given a specified +// organization name, by team slug. +// +// GitHub API docs: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#list-pending-team-invitations // -// GitHub API docs: https://developer.github.com/v3/teams/members/#list-pending-team-invitations -func (s *TeamsService) ListPendingTeamInvitations(ctx context.Context, team int64, opt *ListOptions) ([]*Invitation, *Response, error) { - u := fmt.Sprintf("teams/%v/invitations", team) - u, err := addOptions(u, opt) +//meta:operation GET /orgs/{org}/teams/{team_slug}/invitations +func (s *TeamsService) ListPendingTeamInvitationsBySlug(ctx context.Context, org, slug string, opts *ListOptions) ([]*Invitation, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/invitations", org, slug) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var pendingInvitations []*Invitation - resp, err := s.client.Do(ctx, req, &pendingInvitations) + resp, err := s.client.Do(req, &pendingInvitations) if err != nil { return nil, resp, err } diff --git a/github/teams_members_test.go b/github/teams_members_test.go index 2489da0e2b7..72f775e5028 100644 --- a/github/teams_members_test.go +++ b/github/teams_members_test.go @@ -6,162 +6,760 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) -func TestTeamsService__ListTeamMembers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamMembersByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/members", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/2/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) testFormValues(t, r, values{"role": "member", "page": "2"}) fmt.Fprint(w, `[{"id":1}]`) }) opt := &TeamListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}} - members, _, err := client.Teams.ListTeamMembers(context.Background(), 1, opt) + ctx := t.Context() + members, _, err := client.Teams.ListTeamMembersByID(ctx, 1, 2, opt) if err != nil { - t.Errorf("Teams.ListTeamMembers returned error: %v", err) + t.Errorf("Teams.ListTeamMembersByID returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(members, want) { - t.Errorf("Teams.ListTeamMembers returned %+v, want %+v", members, want) + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { + t.Errorf("Teams.ListTeamMembersByID returned %+v, want %+v", members, want) } + + const methodName = "ListTeamMembersByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamMembersByID(ctx, -1, -2, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamMembersByID(ctx, 1, 2, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService__IsTeamMember_true(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamMembersByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/2/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") + testFormValues(t, r, values{"role": "member", "page": "2"}) + w.WriteHeader(http.StatusNotFound) }) - member, _, err := client.Teams.IsTeamMember(context.Background(), 1, "u") - if err != nil { - t.Errorf("Teams.IsTeamMember returned error: %v", err) + opt := &TeamListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}} + ctx := t.Context() + members, resp, err := client.Teams.ListTeamMembersByID(ctx, 1, 2, opt) + if err == nil { + t.Error("Expected HTTP 404 response") } - if want := true; member != want { - t.Errorf("Teams.IsTeamMember returned %+v, want %+v", member, want) + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.ListTeamMembersByID returned status %v, want %v", got, want) } + if members != nil { + t.Errorf("Teams.ListTeamMembersByID returned %+v, want nil", members) + } + + const methodName = "ListTeamMembersByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamMembersByID(ctx, 1, 2, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamMembersByID(ctx, 1, 2, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -// ensure that a 404 response is interpreted as "false" and not an error -func TestTeamsService__IsTeamMember_false(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamMembersBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/teams/s/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - w.WriteHeader(http.StatusNotFound) + testFormValues(t, r, values{"role": "member", "page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) }) - member, _, err := client.Teams.IsTeamMember(context.Background(), 1, "u") + opt := &TeamListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}} + ctx := t.Context() + members, _, err := client.Teams.ListTeamMembersBySlug(ctx, "o", "s", opt) if err != nil { - t.Errorf("Teams.IsTeamMember returned error: %+v", err) + t.Errorf("Teams.ListTeamMembersBySlug returned error: %v", err) } - if want := false; member != want { - t.Errorf("Teams.IsTeamMember returned %+v, want %+v", member, want) + + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { + t.Errorf("Teams.ListTeamMembersBySlug returned %+v, want %+v", members, want) } + + const methodName = "ListTeamMembersBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamMembersBySlug(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamMembersBySlug(ctx, "o", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -// ensure that a 400 response is interpreted as an actual error, and not simply -// as "false" like the above case of a 404 -func TestTeamsService__IsTeamMember_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamMembersBySlug_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/teams/s/members", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - http.Error(w, "BadRequest", http.StatusBadRequest) + testFormValues(t, r, values{"role": "member", "page": "2"}) + w.WriteHeader(http.StatusNotFound) }) - member, _, err := client.Teams.IsTeamMember(context.Background(), 1, "u") + opt := &TeamListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}} + ctx := t.Context() + members, resp, err := client.Teams.ListTeamMembersBySlug(ctx, "o", "s", opt) if err == nil { - t.Errorf("Expected HTTP 400 response") + t.Error("Expected HTTP 404 response") } - if want := false; member != want { - t.Errorf("Teams.IsTeamMember returned %+v, want %+v", member, want) + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.ListTeamMembersBySlug returned status %v, want %v", got, want) } + if members != nil { + t.Errorf("Teams.ListTeamMembersBySlug returned %+v, want nil", members) + } + + const methodName = "ListTeamMembersBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamMembersBySlug(ctx, "o", "s", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamMembersBySlug(ctx, "o", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService__IsTeamMember_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamMembersBySlug_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Teams.IsTeamMember(context.Background(), 1, "%") + ctx := t.Context() + _, _, err := client.Teams.ListTeamMembersBySlug(ctx, "%", "s", nil) testURLParseError(t, err) } -func TestTeamsService__GetTeamMembership(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_GetTeamMembershipByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/2/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) fmt.Fprint(w, `{"url":"u", "state":"active"}`) }) - membership, _, err := client.Teams.GetTeamMembership(context.Background(), 1, "u") + ctx := t.Context() + membership, _, err := client.Teams.GetTeamMembershipByID(ctx, 1, 2, "u") if err != nil { - t.Errorf("Teams.GetTeamMembership returned error: %v", err) + t.Errorf("Teams.GetTeamMembershipByID returned error: %v", err) } - want := &Membership{URL: String("u"), State: String("active")} - if !reflect.DeepEqual(membership, want) { - t.Errorf("Teams.GetTeamMembership returned %+v, want %+v", membership, want) + want := &Membership{URL: Ptr("u"), State: Ptr("active")} + if !cmp.Equal(membership, want) { + t.Errorf("Teams.GetTeamMembershipByID returned %+v, want %+v", membership, want) + } + + const methodName = "GetTeamMembershipByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetTeamMembershipByID(ctx, -1, -2, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetTeamMembershipByID(ctx, 1, 2, "u") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_GetTeamMembershipByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + membership, resp, err := client.Teams.GetTeamMembershipByID(ctx, 1, 2, "u") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.GetTeamMembershipByID returned status %v, want %v", got, want) } + if membership != nil { + t.Errorf("Teams.GetTeamMembershipByID returned %+v, want nil", membership) + } + + const methodName = "GetTeamMembershipByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetTeamMembershipByID(ctx, 1, 2, "u") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetTeamMembershipByID(ctx, 1, 2, "u") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService__AddTeamMembership(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_GetTeamMembershipBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"url":"u", "state":"active"}`) + }) + + ctx := t.Context() + membership, _, err := client.Teams.GetTeamMembershipBySlug(ctx, "o", "s", "u") + if err != nil { + t.Errorf("Teams.GetTeamMembershipBySlug returned error: %v", err) + } + + want := &Membership{URL: Ptr("u"), State: Ptr("active")} + if !cmp.Equal(membership, want) { + t.Errorf("Teams.GetTeamMembershipBySlug returned %+v, want %+v", membership, want) + } + + const methodName = "GetTeamMembershipBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetTeamMembershipBySlug(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetTeamMembershipBySlug(ctx, "o", "s", "u") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_GetTeamMembershipBySlug_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + membership, resp, err := client.Teams.GetTeamMembershipBySlug(ctx, "o", "s", "u") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.GetTeamMembershipBySlug returned status %v, want %v", got, want) + } + if membership != nil { + t.Errorf("Teams.GetTeamMembershipBySlug returned %+v, want nil", membership) + } + + const methodName = "GetTeamMembershipBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetTeamMembershipBySlug(ctx, "o", "s", "u") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetTeamMembershipBySlug(ctx, "o", "s", "u") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_GetTeamMembershipBySlug_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Teams.GetTeamMembershipBySlug(ctx, "%v", "s", "u") + testURLParseError(t, err) +} + +func TestTeamsService_AddTeamMembershipByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) opt := &TeamAddTeamMembershipOptions{Role: "maintainer"} - mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { - v := new(TeamAddTeamMembershipOptions) - json.NewDecoder(r.Body).Decode(v) + mux.HandleFunc("/organizations/1/team/2/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, opt) + fmt.Fprint(w, `{"url":"u", "state":"pending"}`) + }) + + ctx := t.Context() + membership, _, err := client.Teams.AddTeamMembershipByID(ctx, 1, 2, "u", opt) + if err != nil { + t.Errorf("Teams.AddTeamMembershipByID returned error: %v", err) + } + + want := &Membership{URL: Ptr("u"), State: Ptr("pending")} + if !cmp.Equal(membership, want) { + t.Errorf("Teams.AddTeamMembershipByID returned %+v, want %+v", membership, want) + } + const methodName = "AddTeamMembershipByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.AddTeamMembershipByID(ctx, -1, -2, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.AddTeamMembershipByID(ctx, 1, 2, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_AddTeamMembershipByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &TeamAddTeamMembershipOptions{Role: "maintainer"} + + mux.HandleFunc("/organizations/1/team/2/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, opt) { - t.Errorf("Request body = %+v, want %+v", v, opt) + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + membership, resp, err := client.Teams.AddTeamMembershipByID(ctx, 1, 2, "u", opt) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.AddTeamMembershipByID returned status %v, want %v", got, want) + } + if membership != nil { + t.Errorf("Teams.AddTeamMembershipByID returned %+v, want nil", membership) + } + + const methodName = "AddTeamMembershipByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.AddTeamMembershipByID(ctx, 1, 2, "u", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.AddTeamMembershipByID(ctx, 1, 2, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} + +func TestTeamsService_AddTeamMembershipBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + opt := &TeamAddTeamMembershipOptions{Role: "maintainer"} + + mux.HandleFunc("/orgs/o/teams/s/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, opt) fmt.Fprint(w, `{"url":"u", "state":"pending"}`) }) - membership, _, err := client.Teams.AddTeamMembership(context.Background(), 1, "u", opt) + ctx := t.Context() + membership, _, err := client.Teams.AddTeamMembershipBySlug(ctx, "o", "s", "u", opt) + if err != nil { + t.Errorf("Teams.AddTeamMembershipBySlug returned error: %v", err) + } + + want := &Membership{URL: Ptr("u"), State: Ptr("pending")} + if !cmp.Equal(membership, want) { + t.Errorf("Teams.AddTeamMembershipBySlug returned %+v, want %+v", membership, want) + } + + const methodName = "AddTeamMembershipBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.AddTeamMembershipBySlug(ctx, "\n", "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.AddTeamMembershipBySlug(ctx, "o", "s", "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_AddTeamMembershipBySlug_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &TeamAddTeamMembershipOptions{Role: "maintainer"} + + mux.HandleFunc("/orgs/o/teams/s/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + membership, resp, err := client.Teams.AddTeamMembershipBySlug(ctx, "o", "s", "u", opt) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.AddTeamMembershipBySlug returned status %v, want %v", got, want) + } + if membership != nil { + t.Errorf("Teams.AddTeamMembershipBySlug returned %+v, want nil", membership) + } + + const methodName = "AddTeamMembershipBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.AddTeamMembershipBySlug(ctx, "o", "s", "u", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.AddTeamMembershipBySlug(ctx, "o", "s", "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_AddTeamMembershipBySlug_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Teams.AddTeamMembershipBySlug(ctx, "%", "s", "u", nil) + testURLParseError(t, err) +} + +func TestTeamsService_RemoveTeamMembershipByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamMembershipByID(ctx, 1, 2, "u") if err != nil { - t.Errorf("Teams.AddTeamMembership returned error: %v", err) + t.Errorf("Teams.RemoveTeamMembershipByID returned error: %v", err) } - want := &Membership{URL: String("u"), State: String("pending")} - if !reflect.DeepEqual(membership, want) { - t.Errorf("Teams.AddTeamMembership returned %+v, want %+v", membership, want) + const methodName = "RemoveTeamMembershipByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamMembershipByID(ctx, -1, -2, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamMembershipByID(ctx, 1, 2, "u") + }) +} + +func TestTeamsService_RemoveTeamMembershipByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, err := client.Teams.RemoveTeamMembershipByID(ctx, 1, 2, "u") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.RemoveTeamMembershipByID returned status %v, want %v", got, want) } + + const methodName = "RemoveTeamMembershipByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamMembershipByID(ctx, 1, 2, "u") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamMembershipByID(ctx, 1, 2, "u") + }) } -func TestTeamsService__RemoveTeamMembership(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_RemoveTeamMembershipBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/teams/s/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Teams.RemoveTeamMembership(context.Background(), 1, "u") + ctx := t.Context() + _, err := client.Teams.RemoveTeamMembershipBySlug(ctx, "o", "s", "u") if err != nil { - t.Errorf("Teams.RemoveTeamMembership returned error: %v", err) + t.Errorf("Teams.RemoveTeamMembershipBySlug returned error: %v", err) + } + + const methodName = "RemoveTeamMembershipBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamMembershipBySlug(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamMembershipBySlug(ctx, "o", "s", "u") + }) +} + +func TestTeamsService_RemoveTeamMembershipBySlug_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/memberships/u", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, err := client.Teams.RemoveTeamMembershipBySlug(ctx, "o", "s", "u") + if err == nil { + t.Error("Expected HTTP 404 response") } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.RemoveTeamMembershipBySlug returned status %v, want %v", got, want) + } + + const methodName = "RemoveTeamMembershipBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamMembershipBySlug(ctx, "o", "s", "u") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamMembershipBySlug(ctx, "o", "s", "u") + }) +} + +func TestTeamsService_RemoveTeamMembershipBySlug_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamMembershipBySlug(ctx, "%", "s", "u") + testURLParseError(t, err) +} + +func TestTeamsService_ListPendingTeamInvitationsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + invitations, _, err := client.Teams.ListPendingTeamInvitationsByID(ctx, 1, 2, opt) + if err != nil { + t.Errorf("Teams.ListPendingTeamInvitationsByID returned error: %v", err) + } + + want := []*Invitation{{ID: Ptr(int64(1))}} + if !cmp.Equal(invitations, want) { + t.Errorf("Teams.ListPendingTeamInvitationsByID returned %+v, want %+v", invitations, want) + } + + const methodName = "ListPendingTeamInvitationsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListPendingTeamInvitationsByID(ctx, -1, -2, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListPendingTeamInvitationsByID(ctx, 1, 2, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListPendingTeamInvitationsByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/2/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + w.WriteHeader(http.StatusNotFound) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + invitations, resp, err := client.Teams.ListPendingTeamInvitationsByID(ctx, 1, 2, opt) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.ListPendingTeamInvitationsByID returned status %v, want %v", got, want) + } + if invitations != nil { + t.Errorf("Teams.ListPendingTeamInvitationsByID returned %+v, want nil", invitations) + } + + const methodName = "ListPendingTeamInvitationsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListPendingTeamInvitationsByID(ctx, 1, 2, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListPendingTeamInvitationsByID(ctx, 1, 2, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListPendingTeamInvitationsBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + invitations, _, err := client.Teams.ListPendingTeamInvitationsBySlug(ctx, "o", "s", opt) + if err != nil { + t.Errorf("Teams.ListPendingTeamInvitationsBySlug returned error: %v", err) + } + + want := []*Invitation{{ID: Ptr(int64(1))}} + if !cmp.Equal(invitations, want) { + t.Errorf("Teams.ListPendingTeamInvitationsBySlug returned %+v, want %+v", invitations, want) + } + + const methodName = "ListPendingTeamInvitationsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListPendingTeamInvitationsBySlug(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListPendingTeamInvitationsBySlug(ctx, "o", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListPendingTeamInvitationsBySlug_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/invitations", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + w.WriteHeader(http.StatusNotFound) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + invitations, resp, err := client.Teams.ListPendingTeamInvitationsBySlug(ctx, "o", "s", opt) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.ListPendingTeamInvitationsBySlug returned status %v, want %v", got, want) + } + if invitations != nil { + t.Errorf("Teams.ListPendingTeamInvitationsBySlug returned %+v, want nil", invitations) + } + + const methodName = "ListPendingTeamInvitationsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListPendingTeamInvitationsBySlug(ctx, "o", "s", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListPendingTeamInvitationsBySlug(ctx, "o", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListPendingTeamInvitationsBySlug_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Teams.ListPendingTeamInvitationsBySlug(ctx, "%", "s", nil) + testURLParseError(t, err) } diff --git a/github/teams_test.go b/github/teams_test.go index 6210bb596ae..5cbede587e3 100644 --- a/github/teams_test.go +++ b/github/teams_test.go @@ -6,135 +6,177 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" - "strings" "testing" - "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" ) func TestTeamsService_ListTeams(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"id":1}]`) }) opt := &ListOptions{Page: 2} - teams, _, err := client.Teams.ListTeams(context.Background(), "o", opt) + ctx := t.Context() + teams, _, err := client.Teams.ListTeams(ctx, "o", opt) if err != nil { t.Errorf("Teams.ListTeams returned error: %v", err) } - want := []*Team{{ID: Int64(1)}} - if !reflect.DeepEqual(teams, want) { + want := []*Team{{ID: Ptr(int64(1))}} + if !cmp.Equal(teams, want) { t.Errorf("Teams.ListTeams returned %+v, want %+v", teams, want) } + + const methodName = "ListTeams" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeams(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeams(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestTeamsService_ListTeams_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Teams.ListTeams(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Teams.ListTeams(ctx, "%", nil) testURLParseError(t, err) } -func TestTeamsService_GetTeam(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_GetTeamByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) - fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", "ldap_dn":"cn=n,ou=groups,dc=example,dc=com", "parent":null}`) + fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", "ldap_dn":"cn=n,ou=groups,dc=example,dc=com", "type": "organization", "parent":null}`) }) - team, _, err := client.Teams.GetTeam(context.Background(), 1) + ctx := t.Context() + team, _, err := client.Teams.GetTeamByID(ctx, 1, 1) if err != nil { - t.Errorf("Teams.GetTeam returned error: %v", err) + t.Errorf("Teams.GetTeamByID returned error: %v", err) } - want := &Team{ID: Int64(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p"), LDAPDN: String("cn=n,ou=groups,dc=example,dc=com")} - if !reflect.DeepEqual(team, want) { - t.Errorf("Teams.GetTeam returned %+v, want %+v", team, want) + want := &Team{ID: Ptr(int64(1)), Name: Ptr("n"), Description: Ptr("d"), URL: Ptr("u"), Slug: Ptr("s"), Permission: Ptr("p"), LDAPDN: Ptr("cn=n,ou=groups,dc=example,dc=com"), Type: Ptr("organization")} + if !cmp.Equal(team, want) { + t.Errorf("Teams.GetTeamByID returned %+v, want %+v", team, want) } + + const methodName = "GetTeamByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetTeamByID(ctx, -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetTeamByID(ctx, 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_GetTeam_nestedTeams(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_GetTeamByID_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/2", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) - fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", - "parent": {"id":2, "name":"n", "description": "d", "parent": null}}`) + w.WriteHeader(http.StatusNotFound) }) - team, _, err := client.Teams.GetTeam(context.Background(), 1) - if err != nil { - t.Errorf("Teams.GetTeam returned error: %v", err) + ctx := t.Context() + team, resp, err := client.Teams.GetTeamByID(ctx, 1, 2) + if err == nil { + t.Error("Expected HTTP 404 response") } - - want := &Team{ID: Int64(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p"), - Parent: &Team{ID: Int64(2), Name: String("n"), Description: String("d")}, + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.GetTeamByID returned status %v, want %v", got, want) } - if !reflect.DeepEqual(team, want) { - t.Errorf("Teams.GetTeam returned %+v, want %+v", team, want) + if team != nil { + t.Errorf("Teams.GetTeamByID returned %+v, want nil", team) } } func TestTeamsService_GetTeamBySlug(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/teams/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", "ldap_dn":"cn=n,ou=groups,dc=example,dc=com", "parent":null}`) + fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p", "ldap_dn":"cn=n,ou=groups,dc=example,dc=com", "type": "organization", "parent":null}`) }) - team, _, err := client.Teams.GetTeamBySlug(context.Background(), "o", "s") + ctx := t.Context() + team, _, err := client.Teams.GetTeamBySlug(ctx, "o", "s") if err != nil { t.Errorf("Teams.GetTeamBySlug returned error: %v", err) } - want := &Team{ID: Int64(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p"), LDAPDN: String("cn=n,ou=groups,dc=example,dc=com")} - if !reflect.DeepEqual(team, want) { + want := &Team{ID: Ptr(int64(1)), Name: Ptr("n"), Description: Ptr("d"), URL: Ptr("u"), Slug: Ptr("s"), Permission: Ptr("p"), LDAPDN: Ptr("cn=n,ou=groups,dc=example,dc=com"), Type: Ptr("organization")} + if !cmp.Equal(team, want) { t.Errorf("Teams.GetTeamBySlug returned %+v, want %+v", team, want) } + + const methodName = "GetTeamBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetTeamBySlug(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetTeamBySlug(ctx, "o", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestTeamsService_GetTeamBySlug_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Teams.GetTeamBySlug(context.Background(), "%", "s") + ctx := t.Context() + _, _, err := client.Teams.GetTeamBySlug(ctx, "%", "s") testURLParseError(t, err) } func TestTeamsService_GetTeamBySlug_notFound(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/orgs/o/teams/s", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - team, resp, err := client.Teams.GetTeamBySlug(context.Background(), "o", "s") + ctx := t.Context() + team, resp, err := client.Teams.GetTeamBySlug(ctx, "o", "s") if err == nil { - t.Errorf("Expected HTTP 404 response") + t.Error("Expected HTTP 404 response") } if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { - t.Errorf("Teams.GetTeamBySlug returned status %d, want %d", got, want) + t.Errorf("Teams.GetTeamBySlug returned status %v, want %v", got, want) } if team != nil { t.Errorf("Teams.GetTeamBySlug returned %+v, want nil", team) @@ -142,563 +184,1747 @@ func TestTeamsService_GetTeamBySlug_notFound(t *testing.T) { } func TestTeamsService_CreateTeam(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := NewTeam{Name: "n", Privacy: String("closed"), RepoNames: []string{"r"}} + input := CreateTeamRequest{Name: "n", Privacy: Ptr("closed"), RepoNames: []string{"r"}} mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) { - v := new(NewTeam) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) - if !reflect.DeepEqual(v, &input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - team, _, err := client.Teams.CreateTeam(context.Background(), "o", input) + ctx := t.Context() + team, _, err := client.Teams.CreateTeam(ctx, "o", input) if err != nil { t.Errorf("Teams.CreateTeam returned error: %v", err) } - want := &Team{ID: Int64(1)} - if !reflect.DeepEqual(team, want) { + want := &Team{ID: Ptr(int64(1))} + if !cmp.Equal(team, want) { t.Errorf("Teams.CreateTeam returned %+v, want %+v", team, want) } + + const methodName = "CreateTeam" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateTeam(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateTeam(ctx, "o", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestTeamsService_CreateTeam_invalidOrg(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Teams.CreateTeam(context.Background(), "%", NewTeam{}) + ctx := t.Context() + _, _, err := client.Teams.CreateTeam(ctx, "%", CreateTeamRequest{}) testURLParseError(t, err) } -func TestTeamsService_EditTeam(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_UpdateTeamByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := NewTeam{Name: "n", Privacy: String("closed")} + input := UpdateTeamRequest{Name: Ptr("n"), Privacy: Ptr("closed")} - mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { - v := new(NewTeam) - json.NewDecoder(r.Body).Decode(v) - - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + mux.HandleFunc("/organizations/1/team/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, &input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - team, _, err := client.Teams.EditTeam(context.Background(), 1, input) + ctx := t.Context() + team, _, err := client.Teams.UpdateTeamByID(ctx, 1, 1, input) if err != nil { - t.Errorf("Teams.EditTeam returned error: %v", err) + t.Errorf("Teams.UpdateTeamByID returned error: %v", err) } - want := &Team{ID: Int64(1)} - if !reflect.DeepEqual(team, want) { - t.Errorf("Teams.EditTeam returned %+v, want %+v", team, want) + want := &Team{ID: Ptr(int64(1))} + if !cmp.Equal(team, want) { + t.Errorf("Teams.UpdateTeamByID returned %+v, want %+v", team, want) } -} -func TestTeamsService_DeleteTeam(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) + const methodName = "UpdateTeamByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.UpdateTeamByID(ctx, -1, -1, input) + return err }) - _, err := client.Teams.DeleteTeam(context.Background(), 1) - if err != nil { - t.Errorf("Teams.DeleteTeam returned error: %v", err) - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.UpdateTeamByID(ctx, 1, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_ListChildTeams(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_UpdateTeamByID_RemoveParent(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/teams", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) - testFormValues(t, r, values{"page": "2"}) - fmt.Fprint(w, `[{"id":2}]`) + input := UpdateTeamRequest{Name: Ptr("n"), NotificationSetting: Ptr("notifications_enabled"), Privacy: Ptr("closed"), RemoveParentTeam: true} + + mux.HandleFunc("/organizations/1/team/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input, cmpopts.IgnoreFields(UpdateTeamRequest{}, "RemoveParentTeam")) + + fmt.Fprint(w, `{"id":1}`) }) - opt := &ListOptions{Page: 2} - teams, _, err := client.Teams.ListChildTeams(context.Background(), 1, opt) + ctx := t.Context() + team, _, err := client.Teams.UpdateTeamByID(ctx, 1, 1, input) if err != nil { - t.Errorf("Teams.ListTeams returned error: %v", err) + t.Errorf("Teams.UpdateTeamByID returned error: %v", err) } - want := []*Team{{ID: Int64(2)}} - if !reflect.DeepEqual(teams, want) { - t.Errorf("Teams.ListTeams returned %+v, want %+v", teams, want) + want := &Team{ID: Ptr(int64(1))} + if !cmp.Equal(team, want) { + t.Errorf("Teams.UpdateTeamByID returned %+v, want %+v", team, want) } } -func TestTeamsService_ListTeamRepos(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_UpdateTeamBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/repos", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - wantAcceptHeaders := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview} - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - testFormValues(t, r, values{"page": "2"}) - fmt.Fprint(w, `[{"id":1}]`) + input := UpdateTeamRequest{Name: Ptr("n"), Privacy: Ptr("closed")} + + mux.HandleFunc("/orgs/o/teams/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1}`) }) - opt := &ListOptions{Page: 2} - members, _, err := client.Teams.ListTeamRepos(context.Background(), 1, opt) + ctx := t.Context() + team, _, err := client.Teams.UpdateTeamBySlug(ctx, "o", "s", input) if err != nil { - t.Errorf("Teams.ListTeamRepos returned error: %v", err) + t.Errorf("Teams.UpdateTeamBySlug returned error: %v", err) } - want := []*Repository{{ID: Int64(1)}} - if !reflect.DeepEqual(members, want) { - t.Errorf("Teams.ListTeamRepos returned %+v, want %+v", members, want) + want := &Team{ID: Ptr(int64(1))} + if !cmp.Equal(team, want) { + t.Errorf("Teams.UpdateTeamBySlug returned %+v, want %+v", team, want) } + + const methodName = "UpdateTeamBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.UpdateTeamBySlug(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.UpdateTeamBySlug(ctx, "o", "s", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_IsTeamRepo_true(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_UpdateTeamBySlug_RemoveParent(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := UpdateTeamRequest{Name: Ptr("n"), NotificationSetting: Ptr("notifications_disabled"), Privacy: Ptr("closed"), RemoveParentTeam: true} + + mux.HandleFunc("/orgs/o/teams/s", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input, cmpopts.IgnoreFields(UpdateTeamRequest{}, "RemoveParentTeam")) - mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - wantAcceptHeaders := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview} - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) fmt.Fprint(w, `{"id":1}`) }) - repo, _, err := client.Teams.IsTeamRepo(context.Background(), 1, "o", "r") + ctx := t.Context() + team, _, err := client.Teams.UpdateTeamBySlug(ctx, "o", "s", input) if err != nil { - t.Errorf("Teams.IsTeamRepo returned error: %v", err) + t.Errorf("Teams.UpdateTeamBySlug returned error: %v", err) } - want := &Repository{ID: Int64(1)} - if !reflect.DeepEqual(repo, want) { - t.Errorf("Teams.IsTeamRepo returned %+v, want %+v", repo, want) + want := &Team{ID: Ptr(int64(1))} + if !cmp.Equal(team, want) { + t.Errorf("Teams.UpdateTeamBySlug returned %+v, want %+v", team, want) } } -func TestTeamsService_IsTeamRepo_false(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_DeleteTeamByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - w.WriteHeader(http.StatusNotFound) + mux.HandleFunc("/organizations/1/team/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") }) - repo, resp, err := client.Teams.IsTeamRepo(context.Background(), 1, "o", "r") - if err == nil { - t.Errorf("Expected HTTP 404 response") - } - if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { - t.Errorf("Teams.IsTeamRepo returned status %d, want %d", got, want) - } - if repo != nil { - t.Errorf("Teams.IsTeamRepo returned %+v, want nil", repo) + ctx := t.Context() + _, err := client.Teams.DeleteTeamByID(ctx, 1, 1) + if err != nil { + t.Errorf("Teams.DeleteTeamByID returned error: %v", err) } -} -func TestTeamsService_IsTeamRepo_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - http.Error(w, "BadRequest", http.StatusBadRequest) + const methodName = "DeleteTeamByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.DeleteTeamByID(ctx, -1, -1) + return err }) - repo, resp, err := client.Teams.IsTeamRepo(context.Background(), 1, "o", "r") - if err == nil { - t.Errorf("Expected HTTP 400 response") - } - if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { - t.Errorf("Teams.IsTeamRepo returned status %d, want %d", got, want) - } - if repo != nil { - t.Errorf("Teams.IsTeamRepo returned %+v, want nil", repo) - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.DeleteTeamByID(ctx, 1, 1) + }) } -func TestTeamsService_IsTeamRepo_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() +func TestTeamsService_DeleteTeamBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - _, _, err := client.Teams.IsTeamRepo(context.Background(), 1, "%", "r") - testURLParseError(t, err) -} + mux.HandleFunc("/orgs/o/teams/s", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) -func TestTeamsService_AddTeamRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + ctx := t.Context() + _, err := client.Teams.DeleteTeamBySlug(ctx, "o", "s") + if err != nil { + t.Errorf("Teams.DeleteTeamBySlug returned error: %v", err) + } - opt := &TeamAddTeamRepoOptions{Permission: "admin"} + const methodName = "DeleteTeamBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.DeleteTeamBySlug(ctx, "\n", "\n") + return err + }) - mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - v := new(TeamAddTeamRepoOptions) - json.NewDecoder(r.Body).Decode(v) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.DeleteTeamBySlug(ctx, "o", "s") + }) +} - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, opt) { - t.Errorf("Request body = %+v, want %+v", v, opt) - } +func TestTeamsService_ListChildTeamsByParentID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - w.WriteHeader(http.StatusNoContent) + mux.HandleFunc("/organizations/1/team/2/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":2}]`) }) - _, err := client.Teams.AddTeamRepo(context.Background(), 1, "o", "r", opt) + opt := &ListOptions{Page: 2} + ctx := t.Context() + teams, _, err := client.Teams.ListChildTeamsByParentID(ctx, 1, 2, opt) if err != nil { - t.Errorf("Teams.AddTeamRepo returned error: %v", err) + t.Errorf("Teams.ListChildTeamsByParentID returned error: %v", err) } -} - -func TestTeamsService_AddTeamRepo_noAccess(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - w.WriteHeader(http.StatusUnprocessableEntity) - }) - _, err := client.Teams.AddTeamRepo(context.Background(), 1, "o", "r", nil) - if err == nil { - t.Errorf("Expcted error to be returned") + want := []*Team{{ID: Ptr(int64(2))}} + if !cmp.Equal(teams, want) { + t.Errorf("Teams.ListChildTeamsByParentID returned %+v, want %+v", teams, want) } -} -func TestTeamsService_AddTeamRepo_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + const methodName = "ListChildTeamsByParentID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListChildTeamsByParentID(ctx, -1, -2, opt) + return err + }) - _, err := client.Teams.AddTeamRepo(context.Background(), 1, "%", "r", nil) - testURLParseError(t, err) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListChildTeamsByParentID(ctx, 1, 2, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_RemoveTeamRepo(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListChildTeamsByParentSlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - w.WriteHeader(http.StatusNoContent) + mux.HandleFunc("/orgs/o/teams/s/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":2}]`) }) - _, err := client.Teams.RemoveTeamRepo(context.Background(), 1, "o", "r") + opt := &ListOptions{Page: 2} + ctx := t.Context() + teams, _, err := client.Teams.ListChildTeamsByParentSlug(ctx, "o", "s", opt) if err != nil { - t.Errorf("Teams.RemoveTeamRepo returned error: %v", err) + t.Errorf("Teams.ListChildTeamsByParentSlug returned error: %v", err) } -} -func TestTeamsService_RemoveTeamRepo_invalidOwner(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + want := []*Team{{ID: Ptr(int64(2))}} + if !cmp.Equal(teams, want) { + t.Errorf("Teams.ListChildTeamsByParentSlug returned %+v, want %+v", teams, want) + } - _, err := client.Teams.RemoveTeamRepo(context.Background(), 1, "%", "r") - testURLParseError(t, err) + const methodName = "ListChildTeamsByParentSlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListChildTeamsByParentSlug(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListChildTeamsByParentSlug(ctx, "o", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_ListUserTeams(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamReposByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/teams", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/1/repos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeNestedTeamsPreview) - testFormValues(t, r, values{"page": "1"}) + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"id":1}]`) }) - opt := &ListOptions{Page: 1} - teams, _, err := client.Teams.ListUserTeams(context.Background(), opt) + opt := &ListOptions{Page: 2} + ctx := t.Context() + members, _, err := client.Teams.ListTeamReposByID(ctx, 1, 1, opt) if err != nil { - t.Errorf("Teams.ListUserTeams returned error: %v", err) + t.Errorf("Teams.ListTeamReposByID returned error: %v", err) } - want := []*Team{{ID: Int64(1)}} - if !reflect.DeepEqual(teams, want) { - t.Errorf("Teams.ListUserTeams returned %+v, want %+v", teams, want) + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { + t.Errorf("Teams.ListTeamReposByID returned %+v, want %+v", members, want) } -} - -func TestTeamsService_ListPendingTeamInvitations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - mux.HandleFunc("/teams/1/invitations", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - testFormValues(t, r, values{"page": "1"}) - fmt.Fprint(w, `[ - { - "id": 1, - "login": "monalisa", - "email": "octocat@github.com", - "role": "direct_member", - "created_at": "2017-01-21T00:00:00Z", - "inviter": { - "login": "other_user", - "id": 1, - "avatar_url": "https://github.com/images/error/other_user_happy.gif", - "gravatar_id": "", - "url": "https://api.github.com/users/other_user", - "html_url": "https://github.com/other_user", - "followers_url": "https://api.github.com/users/other_user/followers", - "following_url": "https://api.github.com/users/other_user/following/other_user", - "gists_url": "https://api.github.com/users/other_user/gists/gist_id", - "starred_url": "https://api.github.com/users/other_user/starred/owner/repo", - "subscriptions_url": "https://api.github.com/users/other_user/subscriptions", - "organizations_url": "https://api.github.com/users/other_user/orgs", - "repos_url": "https://api.github.com/users/other_user/repos", - "events_url": "https://api.github.com/users/other_user/events/privacy", - "received_events_url": "https://api.github.com/users/other_user/received_events/privacy", - "type": "User", - "site_admin": false - } - } - ]`) + const methodName = "ListTeamReposByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamReposByID(ctx, -1, -1, opt) + return err }) - opt := &ListOptions{Page: 1} - invitations, _, err := client.Teams.ListPendingTeamInvitations(context.Background(), 1, opt) - if err != nil { - t.Errorf("Teams.ListPendingTeamInvitations returned error: %v", err) - } - - createdAt := time.Date(2017, time.January, 21, 0, 0, 0, 0, time.UTC) - want := []*Invitation{ - { - ID: Int64(1), - Login: String("monalisa"), - Email: String("octocat@github.com"), - Role: String("direct_member"), - CreatedAt: &createdAt, - Inviter: &User{ - Login: String("other_user"), - ID: Int64(1), - AvatarURL: String("https://github.com/images/error/other_user_happy.gif"), - GravatarID: String(""), - URL: String("https://api.github.com/users/other_user"), - HTMLURL: String("https://github.com/other_user"), - FollowersURL: String("https://api.github.com/users/other_user/followers"), - FollowingURL: String("https://api.github.com/users/other_user/following/other_user"), - GistsURL: String("https://api.github.com/users/other_user/gists/gist_id"), - StarredURL: String("https://api.github.com/users/other_user/starred/owner/repo"), - SubscriptionsURL: String("https://api.github.com/users/other_user/subscriptions"), - OrganizationsURL: String("https://api.github.com/users/other_user/orgs"), - ReposURL: String("https://api.github.com/users/other_user/repos"), - EventsURL: String("https://api.github.com/users/other_user/events/privacy"), - ReceivedEventsURL: String("https://api.github.com/users/other_user/received_events/privacy"), - Type: String("User"), - SiteAdmin: Bool(false), - }, - }} - - if !reflect.DeepEqual(invitations, want) { - t.Errorf("Teams.ListPendingTeamInvitations returned %+v, want %+v", invitations, want) - } + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamReposByID(ctx, 1, 1, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_ListProjects(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_ListTeamReposBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - mux.HandleFunc("/teams/1/projects", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/o/teams/s/repos", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeTopicsPreview) + testFormValues(t, r, values{"page": "2"}) fmt.Fprint(w, `[{"id":1}]`) }) - projects, _, err := client.Teams.ListTeamProjects(context.Background(), 1) + opt := &ListOptions{Page: 2} + ctx := t.Context() + members, _, err := client.Teams.ListTeamReposBySlug(ctx, "o", "s", opt) if err != nil { - t.Errorf("Teams.ListTeamProjects returned error: %v", err) + t.Errorf("Teams.ListTeamReposBySlug returned error: %v", err) } - want := []*Project{{ID: Int64(1)}} - if !reflect.DeepEqual(projects, want) { - t.Errorf("Teams.ListTeamProjects returned %+v, want %+v", projects, want) + want := []*Repository{{ID: Ptr(int64(1))}} + if !cmp.Equal(members, want) { + t.Errorf("Teams.ListTeamReposBySlug returned %+v, want %+v", members, want) } + + const methodName = "ListTeamReposBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamReposBySlug(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamReposBySlug(ctx, "o", "s", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_ReviewProjects(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_IsTeamRepoByID_true(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - mux.HandleFunc("/teams/1/projects/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/1/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + testHeader(t, r, "Accept", mediaTypeOrgPermissionRepo) fmt.Fprint(w, `{"id":1}`) }) - project, _, err := client.Teams.ReviewTeamProjects(context.Background(), 1, 1) + ctx := t.Context() + repo, _, err := client.Teams.IsTeamRepoByID(ctx, 1, 1, "owner", "repo") if err != nil { - t.Errorf("Teams.ReviewTeamProjects returned error: %v", err) + t.Errorf("Teams.IsTeamRepoByID returned error: %v", err) } - want := &Project{ID: Int64(1)} - if !reflect.DeepEqual(project, want) { - t.Errorf("Teams.ReviewTeamProjects returned %+v, want %+v", project, want) + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(repo, want) { + t.Errorf("Teams.IsTeamRepoByID returned %+v, want %+v", repo, want) } + + const methodName = "IsTeamRepoByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.IsTeamRepoByID(ctx, -1, -1, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.IsTeamRepoByID(ctx, 1, 1, "owner", "repo") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestTeamsService_AddTeamProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_IsTeamRepoBySlug_true(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - opt := &TeamProjectOptions{ - Permission: String("admin"), + mux.HandleFunc("/orgs/org/teams/slug/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeOrgPermissionRepo) + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + repo, _, err := client.Teams.IsTeamRepoBySlug(ctx, "org", "slug", "owner", "repo") + if err != nil { + t.Errorf("Teams.IsTeamRepoBySlug returned error: %v", err) } - wantAcceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - mux.HandleFunc("/teams/1/projects/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PUT") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) + want := &Repository{ID: Ptr(int64(1))} + if !cmp.Equal(repo, want) { + t.Errorf("Teams.IsTeamRepoBySlug returned %+v, want %+v", repo, want) + } + + const methodName = "IsTeamRepoBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.IsTeamRepoBySlug(ctx, "\n", "\n", "\n", "\n") + return err + }) - v := &TeamProjectOptions{} - json.NewDecoder(r.Body).Decode(v) - if !reflect.DeepEqual(v, opt) { - t.Errorf("Request body = %+v, want %+v", v, opt) + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.IsTeamRepoBySlug(ctx, "org", "slug", "owner", "repo") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } + return resp, err + }) +} - w.WriteHeader(http.StatusNoContent) +func TestTeamsService_IsTeamRepoByID_false(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) }) - _, err := client.Teams.AddTeamProject(context.Background(), 1, 1, opt) - if err != nil { - t.Errorf("Teams.AddTeamProject returned error: %v", err) + ctx := t.Context() + repo, resp, err := client.Teams.IsTeamRepoByID(ctx, 1, 1, "owner", "repo") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.IsTeamRepoByID returned status %v, want %v", got, want) + } + if repo != nil { + t.Errorf("Teams.IsTeamRepoByID returned %+v, want nil", repo) } } -func TestTeamsService_RemoveTeamProject(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_IsTeamRepoBySlug_false(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - wantAcceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview} - mux.HandleFunc("/teams/1/projects/1", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", strings.Join(wantAcceptHeaders, ", ")) - w.WriteHeader(http.StatusNoContent) + mux.HandleFunc("/orgs/org/teams/slug/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) }) - _, err := client.Teams.RemoveTeamProject(context.Background(), 1, 1) - if err != nil { - t.Errorf("Teams.RemoveTeamProject returned error: %v", err) + ctx := t.Context() + repo, resp, err := client.Teams.IsTeamRepoBySlug(ctx, "org", "slug", "owner", "repo") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.IsTeamRepoBySlug returned status %v, want %v", got, want) + } + if repo != nil { + t.Errorf("Teams.IsTeamRepoBySlug returned %+v, want nil", repo) } } -func TestTeamsService_ListIDPGroupsInOrganization(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_IsTeamRepoByID_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/orgs/o/team-sync/groups", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/organizations/1/team/1/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamSyncPreview) - testFormValues(t, r, values{ - "page": "2", - }) - fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + http.Error(w, "BadRequest", http.StatusBadRequest) }) - opt := &ListOptions{Page: 2} - groups, _, err := client.Teams.ListIDPGroupsInOrganization(context.Background(), "o", opt) - if err != nil { - t.Errorf("Teams.ListIDPGroupsInOrganization returned error: %v", err) + ctx := t.Context() + repo, resp, err := client.Teams.IsTeamRepoByID(ctx, 1, 1, "owner", "repo") + if err == nil { + t.Error("Expected HTTP 400 response") } - - want := &IDPGroupList{ - Groups: []*IDPGroup{ - { - GroupID: String("1"), - GroupName: String("n"), - GroupDescription: String("d"), - }, - }, + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("Teams.IsTeamRepoByID returned status %v, want %v", got, want) } - if !reflect.DeepEqual(groups, want) { - t.Errorf("Teams.ListIDPGroupsInOrganization returned %+v. want %+v", groups, want) + if repo != nil { + t.Errorf("Teams.IsTeamRepoByID returned %+v, want nil", repo) } } -func TestTeamsService_ListIDPGroupsForTeam(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_IsTeamRepoBySlug_error(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/teams/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/orgs/org/teams/slug/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamSyncPreview) - fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + http.Error(w, "BadRequest", http.StatusBadRequest) }) - groups, _, err := client.Teams.ListIDPGroupsForTeam(context.Background(), "1") - if err != nil { - t.Errorf("Teams.ListIDPGroupsForTeam returned error: %v", err) + ctx := t.Context() + repo, resp, err := client.Teams.IsTeamRepoBySlug(ctx, "org", "slug", "owner", "repo") + if err == nil { + t.Error("Expected HTTP 400 response") } - - want := &IDPGroupList{ - Groups: []*IDPGroup{ - { - GroupID: String("1"), - GroupName: String("n"), - GroupDescription: String("d"), - }, - }, + if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { + t.Errorf("Teams.IsTeamRepoBySlug returned status %v, want %v", got, want) } - if !reflect.DeepEqual(groups, want) { - t.Errorf("Teams.ListIDPGroupsForTeam returned %+v. want %+v", groups, want) + if repo != nil { + t.Errorf("Teams.IsTeamRepoBySlug returned %+v, want nil", repo) } } -func TestTeamsService_CreateOrUpdateIDPGroupConnections(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestTeamsService_IsTeamRepoByID_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) - mux.HandleFunc("/teams/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeTeamSyncPreview) - fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + ctx := t.Context() + _, _, err := client.Teams.IsTeamRepoByID(ctx, 1, 1, "%", "r") + testURLParseError(t, err) +} + +func TestTeamsService_IsTeamRepoBySlug_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Teams.IsTeamRepoBySlug(ctx, "o", "s", "%", "r") + testURLParseError(t, err) +} + +func TestTeamsService_AddTeamRepoByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &TeamAddTeamRepoOptions{Permission: "admin"} + + mux.HandleFunc("/organizations/1/team/1/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusNoContent) }) - input := IDPGroupList{ - Groups: []*IDPGroup{ - { - GroupID: String("1"), - GroupName: String("n"), - GroupDescription: String("d"), - }, - }, + ctx := t.Context() + _, err := client.Teams.AddTeamRepoByID(ctx, 1, 1, "owner", "repo", opt) + if err != nil { + t.Errorf("Teams.AddTeamRepoByID returned error: %v", err) } - groups, _, err := client.Teams.CreateOrUpdateIDPGroupConnections(context.Background(), "1", input) + const methodName = "AddTeamRepoByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.AddTeamRepoByID(ctx, 1, 1, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.AddTeamRepoByID(ctx, 1, 1, "owner", "repo", opt) + }) +} + +func TestTeamsService_AddTeamRepoBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &TeamAddTeamRepoOptions{Permission: "admin"} + + mux.HandleFunc("/orgs/org/teams/slug/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.AddTeamRepoBySlug(ctx, "org", "slug", "owner", "repo", opt) if err != nil { - t.Errorf("Teams.CreateOrUpdateIDPGroupConnections returned error: %v", err) + t.Errorf("Teams.AddTeamRepoBySlug returned error: %v", err) + } + + const methodName = "AddTeamRepoBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.AddTeamRepoBySlug(ctx, "\n", "\n", "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.AddTeamRepoBySlug(ctx, "org", "slug", "owner", "repo", opt) + }) +} + +func TestTeamsService_AddTeamRepoByID_noAccess(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusUnprocessableEntity) + }) + + ctx := t.Context() + _, err := client.Teams.AddTeamRepoByID(ctx, 1, 1, "owner", "repo", nil) + if err == nil { + t.Error("Expected error to be returned") + } +} + +func TestTeamsService_AddTeamRepoBySlug_noAccess(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/org/teams/slug/repos/o/r", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + w.WriteHeader(http.StatusUnprocessableEntity) + }) + + ctx := t.Context() + _, err := client.Teams.AddTeamRepoBySlug(ctx, "org", "slug", "owner", "repo", nil) + if err == nil { + t.Error("Expected error to be returned") + } +} + +func TestTeamsService_AddTeamRepoByID_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Teams.AddTeamRepoByID(ctx, 1, 1, "%", "r", nil) + testURLParseError(t, err) +} + +func TestTeamsService_AddTeamRepoBySlug_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Teams.AddTeamRepoBySlug(ctx, "o", "s", "%", "r", nil) + testURLParseError(t, err) +} + +func TestTeamsService_RemoveTeamRepoByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamRepoByID(ctx, 1, 1, "owner", "repo") + if err != nil { + t.Errorf("Teams.RemoveTeamRepoByID returned error: %v", err) + } + + const methodName = "RemoveTeamRepoByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamRepoByID(ctx, -1, -1, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamRepoByID(ctx, 1, 1, "owner", "repo") + }) +} + +func TestTeamsService_RemoveTeamRepoBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/org/teams/slug/repos/owner/repo", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamRepoBySlug(ctx, "org", "slug", "owner", "repo") + if err != nil { + t.Errorf("Teams.RemoveTeamRepoBySlug returned error: %v", err) + } + + const methodName = "RemoveTeamRepoBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamRepoBySlug(ctx, "\n", "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamRepoBySlug(ctx, "org", "slug", "owner", "repo") + }) +} + +func TestTeamsService_RemoveTeamRepoByID_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamRepoByID(ctx, 1, 1, "%", "r") + testURLParseError(t, err) +} + +func TestTeamsService_RemoveTeamRepoBySlug_invalidOwner(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamRepoBySlug(ctx, "o", "s", "%", "r") + testURLParseError(t, err) +} + +func TestTeamsService_ListUserTeams(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/teams", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "1"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 1} + ctx := t.Context() + teams, _, err := client.Teams.ListUserTeams(ctx, opt) + if err != nil { + t.Errorf("Teams.ListUserTeams returned error: %v", err) + } + + want := []*Team{{ID: Ptr(int64(1))}} + if !cmp.Equal(teams, want) { + t.Errorf("Teams.ListUserTeams returned %+v, want %+v", teams, want) + } + + const methodName = "ListUserTeams" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListUserTeams(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListProjectsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/projects", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + ctx := t.Context() + projects, _, err := client.Teams.ListTeamProjectsByID(ctx, 1, 1) + if err != nil { + t.Errorf("Teams.ListTeamProjectsByID returned error: %v", err) + } + + want := []*ProjectV2{{ID: Ptr(int64(1))}} + if !cmp.Equal(projects, want) { + t.Errorf("Teams.ListTeamProjectsByID returned %+v, want %+v", projects, want) + } + + const methodName = "ListTeamProjectsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamProjectsByID(ctx, -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamProjectsByID(ctx, 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListProjectsBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/projects", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `[{"id":1}]`) + }) + + ctx := t.Context() + projects, _, err := client.Teams.ListTeamProjectsBySlug(ctx, "o", "s") + if err != nil { + t.Errorf("Teams.ListTeamProjectsBySlug returned error: %v", err) + } + + want := []*ProjectV2{{ID: Ptr(int64(1))}} + if !cmp.Equal(projects, want) { + t.Errorf("Teams.ListTeamProjectsBySlug returned %+v, want %+v", projects, want) + } + + const methodName = "ListTeamProjectsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListTeamProjectsBySlug(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListTeamProjectsBySlug(ctx, "o", "s") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ReviewProjectsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + project, _, err := client.Teams.ReviewTeamProjectsByID(ctx, 1, 1, 1) + if err != nil { + t.Errorf("Teams.ReviewTeamProjectsByID returned error: %v", err) + } + + want := &ProjectV2{ID: Ptr(int64(1))} + if !cmp.Equal(project, want) { + t.Errorf("Teams.ReviewTeamProjectsByID returned %+v, want %+v", project, want) + } + + const methodName = "ReviewTeamProjectsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ReviewTeamProjectsByID(ctx, -1, -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ReviewTeamProjectsByID(ctx, 1, 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ReviewProjectsBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + project, _, err := client.Teams.ReviewTeamProjectsBySlug(ctx, "o", "s", 1) + if err != nil { + t.Errorf("Teams.ReviewTeamProjectsBySlug returned error: %v", err) + } + + want := &ProjectV2{ID: Ptr(int64(1))} + if !cmp.Equal(project, want) { + t.Errorf("Teams.ReviewTeamProjectsBySlug returned %+v, want %+v", project, want) + } + + const methodName = "ReviewTeamProjectsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ReviewTeamProjectsBySlug(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ReviewTeamProjectsBySlug(ctx, "o", "s", 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_AddTeamProjectByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &TeamProjectOptions{ + Permission: Ptr("admin"), + } + + mux.HandleFunc("/organizations/1/team/1/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.AddTeamProjectByID(ctx, 1, 1, 1, opt) + if err != nil { + t.Errorf("Teams.AddTeamProjectByID returned error: %v", err) + } + + const methodName = "AddTeamProjectByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.AddTeamProjectByID(ctx, -1, -1, -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.AddTeamProjectByID(ctx, 1, 1, 1, opt) + }) +} + +func TestTeamsService_AddTeamProjectBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + opt := &TeamProjectOptions{ + Permission: Ptr("admin"), + } + + mux.HandleFunc("/orgs/o/teams/s/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + testJSONBody(t, r, opt) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.AddTeamProjectBySlug(ctx, "o", "s", 1, opt) + if err != nil { + t.Errorf("Teams.AddTeamProjectBySlug returned error: %v", err) + } + + const methodName = "AddTeamProjectBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.AddTeamProjectBySlug(ctx, "\n", "\n", -1, opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.AddTeamProjectBySlug(ctx, "o", "s", 1, opt) + }) +} + +func TestTeamsService_RemoveTeamProjectByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamProjectByID(ctx, 1, 1, 1) + if err != nil { + t.Errorf("Teams.RemoveTeamProjectByID returned error: %v", err) + } + + const methodName = "RemoveTeamProjectByID" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamProjectByID(ctx, -1, -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamProjectByID(ctx, 1, 1, 1) + }) +} + +func TestTeamsService_RemoveTeamProjectBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/s/projects/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testHeader(t, r, "Accept", mediaTypeProjectsPreview) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.RemoveTeamProjectBySlug(ctx, "o", "s", 1) + if err != nil { + t.Errorf("Teams.RemoveTeamProjectBySlug returned error: %v", err) + } + + const methodName = "RemoveTeamProjectBySlug" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveTeamProjectBySlug(ctx, "\n", "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveTeamProjectBySlug(ctx, "o", "s", 1) + }) +} + +func TestTeamsService_ListIDPGroupsInOrganization(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/team-sync/groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "page": "url-encoded-next-page-token", + "q": "n", + }) + fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + }) + + opt := &ListIDPGroupsOptions{ + Query: "n", + ListCursorOptions: ListCursorOptions{Page: "url-encoded-next-page-token"}, + } + ctx := t.Context() + groups, _, err := client.Teams.ListIDPGroupsInOrganization(ctx, "o", opt) + if err != nil { + t.Errorf("Teams.ListIDPGroupsInOrganization returned error: %v", err) + } + + want := &IDPGroupList{ + Groups: []*IDPGroup{ + { + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.ListIDPGroupsInOrganization returned %+v. want %+v", groups, want) + } + + const methodName = "ListIDPGroupsInOrganization" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListIDPGroupsInOrganization(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListIDPGroupsInOrganization(ctx, "o", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListIDPGroupsForTeamByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + }) + + ctx := t.Context() + groups, _, err := client.Teams.ListIDPGroupsForTeamByID(ctx, 1, 1) + if err != nil { + t.Errorf("Teams.ListIDPGroupsForTeamByID returned error: %v", err) + } + + want := &IDPGroupList{ + Groups: []*IDPGroup{ + { + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.ListIDPGroupsForTeamByID returned %+v. want %+v", groups, want) + } + + const methodName = "ListIDPGroupsForTeamByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListIDPGroupsForTeamByID(ctx, -1, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListIDPGroupsForTeamByID(ctx, 1, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListIDPGroupsForTeamBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/slug/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + }) + + ctx := t.Context() + groups, _, err := client.Teams.ListIDPGroupsForTeamBySlug(ctx, "o", "slug") + if err != nil { + t.Errorf("Teams.ListIDPGroupsForTeamBySlug returned error: %v", err) + } + + want := &IDPGroupList{ + Groups: []*IDPGroup{ + { + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.ListIDPGroupsForTeamBySlug returned %+v. want %+v", groups, want) + } + + const methodName = "ListIDPGroupsForTeamBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListIDPGroupsForTeamBySlug(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListIDPGroupsForTeamBySlug(ctx, "o", "slug") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_CreateOrUpdateIDPGroupConnectionsByID(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + }) + + input := IDPGroupList{ + Groups: []*IDPGroup{ + { + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + + ctx := t.Context() + groups, _, err := client.Teams.CreateOrUpdateIDPGroupConnectionsByID(ctx, 1, 1, input) + if err != nil { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsByID returned error: %v", err) + } + + want := &IDPGroupList{ + Groups: []*IDPGroup{ + { + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsByID returned %+v. want %+v", groups, want) + } + + const methodName = "CreateOrUpdateIDPGroupConnectionsByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateOrUpdateIDPGroupConnectionsByID(ctx, -1, -1, input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateOrUpdateIDPGroupConnectionsByID(ctx, 1, 1, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_CreateOrUpdateIDPGroupConnectionsBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/slug/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"groups": [{"group_id": "1", "group_name": "n", "group_description": "d"}]}`) + }) + + input := IDPGroupList{ + Groups: []*IDPGroup{ + { + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + + ctx := t.Context() + groups, _, err := client.Teams.CreateOrUpdateIDPGroupConnectionsBySlug(ctx, "o", "slug", input) + if err != nil { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsBySlug returned error: %v", err) } want := &IDPGroupList{ Groups: []*IDPGroup{ { - GroupID: String("1"), - GroupName: String("n"), - GroupDescription: String("d"), + GroupID: Ptr("1"), + GroupName: Ptr("n"), + GroupDescription: Ptr("d"), + }, + }, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsBySlug returned %+v. want %+v", groups, want) + } + + const methodName = "CreateOrUpdateIDPGroupConnectionsBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.CreateOrUpdateIDPGroupConnectionsBySlug(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.CreateOrUpdateIDPGroupConnectionsBySlug(ctx, "o", "slug", input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_CreateOrUpdateIDPGroupConnectionsByID_empty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/organizations/1/team/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"groups": []}`) + }) + + input := IDPGroupList{ + Groups: []*IDPGroup{}, + } + + ctx := t.Context() + groups, _, err := client.Teams.CreateOrUpdateIDPGroupConnectionsByID(ctx, 1, 1, input) + if err != nil { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsByID returned error: %v", err) + } + + want := &IDPGroupList{ + Groups: []*IDPGroup{}, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsByID returned %+v. want %+v", groups, want) + } +} + +func TestTeamsService_CreateOrUpdateIDPGroupConnectionsBySlug_empty(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/slug/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{"groups": []}`) + }) + + input := IDPGroupList{ + Groups: []*IDPGroup{}, + } + + ctx := t.Context() + groups, _, err := client.Teams.CreateOrUpdateIDPGroupConnectionsBySlug(ctx, "o", "slug", input) + if err != nil { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsBySlug returned error: %v", err) + } + + want := &IDPGroupList{ + Groups: []*IDPGroup{}, + } + if !cmp.Equal(groups, want) { + t.Errorf("Teams.CreateOrUpdateIDPGroupConnectionsBySlug returned %+v. want %+v", groups, want) + } +} + +func TestTeamsService_GetExternalGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/external-group/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "group_id": 123, + "group_name": "Octocat admins", + "updated_at": "2006-01-02T15:04:05Z", + "teams": [ + { + "team_id": 1, + "team_name": "team-test" + }, + { + "team_id": 2, + "team_name": "team-test2" + } + ], + "members": [ + { + "member_id": 1, + "member_login": "mona-lisa_eocsaxrs", + "member_name": "Mona Lisa", + "member_email": "mona_lisa@github.com" + }, + { + "member_id": 2, + "member_login": "octo-lisa_eocsaxrs", + "member_name": "Octo Lisa", + "member_email": "octo_lisa@github.com" + } + ] + }`) + }) + + ctx := t.Context() + externalGroup, _, err := client.Teams.GetExternalGroup(ctx, "o", 123) + if err != nil { + t.Errorf("Teams.GetExternalGroup returned error: %v", err) + } + + want := &ExternalGroup{ + GroupID: Ptr(int64(123)), + GroupName: Ptr("Octocat admins"), + UpdatedAt: &Timestamp{Time: referenceTime}, + Teams: []*ExternalGroupTeam{ + { + TeamID: Ptr(int64(1)), + TeamName: Ptr("team-test"), + }, + { + TeamID: Ptr(int64(2)), + TeamName: Ptr("team-test2"), + }, + }, + Members: []*ExternalGroupMember{ + { + MemberID: Ptr(int64(1)), + MemberLogin: Ptr("mona-lisa_eocsaxrs"), + MemberName: Ptr("Mona Lisa"), + MemberEmail: Ptr("mona_lisa@github.com"), + }, + { + MemberID: Ptr(int64(2)), + MemberLogin: Ptr("octo-lisa_eocsaxrs"), + MemberName: Ptr("Octo Lisa"), + MemberEmail: Ptr("octo_lisa@github.com"), + }, + }, + } + if !cmp.Equal(externalGroup, want) { + t.Errorf("Teams.GetExternalGroup returned %+v, want %+v", externalGroup, want) + } + + const methodName = "GetExternalGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.GetExternalGroup(ctx, "\n", -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.GetExternalGroup(ctx, "o", 123) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_GetExternalGroup_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/external-group/123", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + eg, resp, err := client.Teams.GetExternalGroup(ctx, "o", 123) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.GetExternalGroup returned status %v, want %v", got, want) + } + if eg != nil { + t.Errorf("Teams.GetExternalGroup returned %+v, want nil", eg) + } +} + +func TestTeamsService_ListExternalGroups(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "groups": [ + { + "group_id": 123, + "group_name": "Octocat admins", + "updated_at": "2006-01-02T15:04:05Z" + } + ] + }`) + }) + + ctx := t.Context() + opts := &ListExternalGroupsOptions{ + DisplayName: Ptr("Octocat"), + } + list, _, err := client.Teams.ListExternalGroups(ctx, "o", opts) + if err != nil { + t.Errorf("Teams.ListExternalGroups returned error: %v", err) + } + + want := &ExternalGroupList{ + Groups: []*ExternalGroup{ + { + GroupID: Ptr(int64(123)), + GroupName: Ptr("Octocat admins"), + UpdatedAt: &Timestamp{Time: referenceTime}, + }, + }, + } + if !cmp.Equal(list, want) { + t.Errorf("Teams.ListExternalGroups returned %+v, want %+v", list, want) + } + + const methodName = "ListExternalGroups" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListExternalGroups(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListExternalGroups(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListExternalGroups_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + eg, resp, err := client.Teams.ListExternalGroups(ctx, "o", nil) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.ListExternalGroups returned status %v, want %v", got, want) + } + if eg != nil { + t.Errorf("Teams.ListExternalGroups returned %+v, want nil", eg) + } +} + +func TestTeamsService_ListExternalGroupsForTeamBySlug(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/t/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "groups": [ + { + "group_id": 123, + "group_name": "Octocat admins", + "updated_at": "2006-01-02T15:04:05Z" + } + ] + }`) + }) + + ctx := t.Context() + list, _, err := client.Teams.ListExternalGroupsForTeamBySlug(ctx, "o", "t") + if err != nil { + t.Errorf("Teams.ListExternalGroupsForTeamBySlug returned error: %v", err) + } + + want := &ExternalGroupList{ + Groups: []*ExternalGroup{ + { + GroupID: Ptr(int64(123)), + GroupName: Ptr("Octocat admins"), + UpdatedAt: &Timestamp{Time: referenceTime}, + }, + }, + } + if !cmp.Equal(list, want) { + t.Errorf("Teams.ListExternalGroupsForTeamBySlug returned %+v, want %+v", list, want) + } + + const methodName = "ListExternalGroupsForTeamBySlug" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.ListExternalGroupsForTeamBySlug(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.ListExternalGroupsForTeamBySlug(ctx, "o", "t") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_ListExternalGroupsForTeamBySlug_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/t/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + eg, resp, err := client.Teams.ListExternalGroupsForTeamBySlug(ctx, "o", "t") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.ListExternalGroupsForTeamBySlug returned status %v, want %v", got, want) + } + if eg != nil { + t.Errorf("Teams.ListExternalGroupsForTeamBySlug returned %+v, want nil", eg) + } +} + +func TestTeamsService_UpdateConnectedExternalGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/t/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + fmt.Fprint(w, `{ + "group_id": 123, + "group_name": "Octocat admins", + "updated_at": "2006-01-02T15:04:05Z", + "teams": [ + { + "team_id": 1, + "team_name": "team-test" + }, + { + "team_id": 2, + "team_name": "team-test2" + } + ], + "members": [ + { + "member_id": 1, + "member_login": "mona-lisa_eocsaxrs", + "member_name": "Mona Lisa", + "member_email": "mona_lisa@github.com" + }, + { + "member_id": 2, + "member_login": "octo-lisa_eocsaxrs", + "member_name": "Octo Lisa", + "member_email": "octo_lisa@github.com" + } + ] + }`) + }) + + ctx := t.Context() + body := UpdateConnectedExternalGroupRequest{ + GroupID: 123, + } + externalGroup, _, err := client.Teams.UpdateConnectedExternalGroup(ctx, "o", "t", body) + if err != nil { + t.Errorf("Teams.UpdateConnectedExternalGroup returned error: %v", err) + } + + want := &ExternalGroup{ + GroupID: Ptr(int64(123)), + GroupName: Ptr("Octocat admins"), + UpdatedAt: &Timestamp{Time: referenceTime}, + Teams: []*ExternalGroupTeam{ + { + TeamID: Ptr(int64(1)), + TeamName: Ptr("team-test"), + }, + { + TeamID: Ptr(int64(2)), + TeamName: Ptr("team-test2"), + }, + }, + Members: []*ExternalGroupMember{ + { + MemberID: Ptr(int64(1)), + MemberLogin: Ptr("mona-lisa_eocsaxrs"), + MemberName: Ptr("Mona Lisa"), + MemberEmail: Ptr("mona_lisa@github.com"), + }, + { + MemberID: Ptr(int64(2)), + MemberLogin: Ptr("octo-lisa_eocsaxrs"), + MemberName: Ptr("Octo Lisa"), + MemberEmail: Ptr("octo_lisa@github.com"), }, }, } - if !reflect.DeepEqual(groups, want) { - t.Errorf("Teams.CreateOrUpdateIDPGroupConnections returned %+v. want %+v", groups, want) + if !cmp.Equal(externalGroup, want) { + t.Errorf("Teams.GetExternalGroup returned %+v, want %+v", externalGroup, want) + } + + const methodName = "UpdateConnectedExternalGroup" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Teams.UpdateConnectedExternalGroup(ctx, "\n", "\n", body) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Teams.UpdateConnectedExternalGroup(ctx, "o", "t", body) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestTeamsService_UpdateConnectedExternalGroup_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/t/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + body := UpdateConnectedExternalGroupRequest{ + GroupID: 123, + } + eg, resp, err := client.Teams.UpdateConnectedExternalGroup(ctx, "o", "t", body) + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.UpdateConnectedExternalGroup returned status %v, want %v", got, want) + } + if eg != nil { + t.Errorf("Teams.UpdateConnectedExternalGroup returned %+v, want nil", eg) + } +} + +func TestTeamsService_RemoveConnectedExternalGroup(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/t/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Teams.RemoveConnectedExternalGroup(ctx, "o", "t") + if err != nil { + t.Errorf("Teams.RemoveConnectedExternalGroup returned error: %v", err) + } + + const methodName = "RemoveConnectedExternalGroup" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Teams.RemoveConnectedExternalGroup(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Teams.RemoveConnectedExternalGroup(ctx, "o", "t") + }) +} + +func TestTeamsService_RemoveConnectedExternalGroup_notFound(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/orgs/o/teams/t/external-groups", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNotFound) + }) + + ctx := t.Context() + resp, err := client.Teams.RemoveConnectedExternalGroup(ctx, "o", "t") + if err == nil { + t.Error("Expected HTTP 404 response") + } + if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { + t.Errorf("Teams.RemoveConnectedExternalGroup returned status %v, want %v", got, want) } } diff --git a/github/timestamp.go b/github/timestamp.go index 90929d5757d..a26077da255 100644 --- a/github/timestamp.go +++ b/github/timestamp.go @@ -10,7 +10,7 @@ import ( "time" ) -// Timestamp represents a time that can be unmarshalled from a JSON string +// Timestamp represents a time that can be unmarshaled from a JSON string // formatted as either an RFC3339 or Unix timestamp. This is necessary for some // fields since the GitHub API is inconsistent in how it represents times. All // exported methods of time.Time can be called on Timestamp. @@ -22,6 +22,14 @@ func (t Timestamp) String() string { return t.Time.String() } +// GetTime returns std time.Time. +func (t *Timestamp) GetTime() *time.Time { + if t == nil { + return nil + } + return &t.Time +} + // UnmarshalJSON implements the json.Unmarshaler interface. // Time is expected in RFC3339 or Unix format. func (t *Timestamp) UnmarshalJSON(data []byte) (err error) { @@ -29,13 +37,16 @@ func (t *Timestamp) UnmarshalJSON(data []byte) (err error) { i, err := strconv.ParseInt(str, 10, 64) if err == nil { t.Time = time.Unix(i, 0) + if t.Time.Year() > 3000 { + t.Time = time.Unix(0, i*1e6) + } } else { t.Time, err = time.Parse(`"`+time.RFC3339+`"`, str) } - return + return err } -// Equal reports whether t and u are equal based on time.Equal +// Equal reports whether t and u are equal based on time.Equal. func (t Timestamp) Equal(u Timestamp) bool { return t.Time.Equal(u.Time) } diff --git a/github/timestamp_test.go b/github/timestamp_test.go index dc09eaf993f..8b31320efbb 100644 --- a/github/timestamp_test.go +++ b/github/timestamp_test.go @@ -7,24 +7,29 @@ package github import ( "encoding/json" - "fmt" "testing" "time" ) const ( - emptyTimeStr = `"0001-01-01T00:00:00Z"` - referenceTimeStr = `"2006-01-02T15:04:05Z"` - referenceTimeStrFractional = `"2006-01-02T15:04:05.000Z"` // This format was returned by the Projects API before October 1, 2017. - referenceUnixTimeStr = `1136214245` + emptyTimeStr = `"0001-01-01T00:00:00Z"` + referenceTimeRaw = "2006-01-02T15:04:05Z" + referenceTimeStr = `"2006-01-02T15:04:05Z"` + referenceTimeStrFractional = `"2006-01-02T15:04:05.000Z"` // This format was returned by the Projects API before October 1, 2017. + referenceUnixTimeStr = `1136214245` + referenceUnixTimeStrMilliSeconds = `1136214245000` // Millisecond-granular timestamps were introduced in the Audit log API. ) var ( - referenceTime = time.Date(2006, time.January, 02, 15, 04, 05, 0, time.UTC) - unixOrigin = time.Unix(0, 0).In(time.UTC) + referenceTime = time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC) + referenceTimestamp = Timestamp{referenceTime} + refTimeStr = func(unix int64) string { return `"` + refTimestamp(unix).Format(time.RFC3339) + `"` } + refTimestamp = func(unix int64) *Timestamp { return &Timestamp{time.Unix(unix, 0).In(time.UTC).Local()} } + unixOrigin = time.Unix(0, 0).In(time.UTC) ) func TestTimestamp_Marshal(t *testing.T) { + t.Parallel() testCases := []struct { desc string data Timestamp @@ -39,17 +44,18 @@ func TestTimestamp_Marshal(t *testing.T) { for _, tc := range testCases { out, err := json.Marshal(tc.data) if gotErr := err != nil; gotErr != tc.wantErr { - t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + t.Errorf("%v: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) } got := string(out) equal := got == tc.want if (got == tc.want) != tc.equal { - t.Errorf("%s: got=%s, want=%s, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + t.Errorf("%v: got=%v, want=%v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) } } } func TestTimestamp_Unmarshal(t *testing.T) { + t.Parallel() testCases := []struct { desc string data string @@ -59,28 +65,31 @@ func TestTimestamp_Unmarshal(t *testing.T) { }{ {"Reference", referenceTimeStr, Timestamp{referenceTime}, false, true}, {"ReferenceUnix", referenceUnixTimeStr, Timestamp{referenceTime}, false, true}, + {"ReferenceUnixMillisecond", referenceUnixTimeStrMilliSeconds, Timestamp{referenceTime}, false, true}, {"ReferenceFractional", referenceTimeStrFractional, Timestamp{referenceTime}, false, true}, {"Empty", emptyTimeStr, Timestamp{}, false, true}, {"UnixStart", `0`, Timestamp{unixOrigin}, false, true}, {"Mismatch", referenceTimeStr, Timestamp{}, false, false}, {"MismatchUnix", `0`, Timestamp{}, false, false}, {"Invalid", `"asdf"`, Timestamp{referenceTime}, true, false}, + {"OffByMillisecond", `1136214245001`, Timestamp{referenceTime}, false, false}, } for _, tc := range testCases { var got Timestamp err := json.Unmarshal([]byte(tc.data), &got) if gotErr := err != nil; gotErr != tc.wantErr { - t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + t.Errorf("%v: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) continue } equal := got.Equal(tc.want) if equal != tc.equal { - t.Errorf("%s: got=%#v, want=%#v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + t.Errorf("%v: got=%#v, want=%#v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) } } } -func TestTimstamp_MarshalReflexivity(t *testing.T) { +func TestTimestamp_MarshalReflexivity(t *testing.T) { + t.Parallel() testCases := []struct { desc string data Timestamp @@ -91,50 +100,26 @@ func TestTimstamp_MarshalReflexivity(t *testing.T) { for _, tc := range testCases { data, err := json.Marshal(tc.data) if err != nil { - t.Errorf("%s: Marshal err=%v", tc.desc, err) + t.Errorf("%v: Marshal err=%v", tc.desc, err) } var got Timestamp err = json.Unmarshal(data, &got) if err != nil { - t.Errorf("%s: Unmarshal err=%v", tc.desc, err) + t.Errorf("%v: Unmarshal err=%v", tc.desc, err) } if !got.Equal(tc.data) { - t.Errorf("%s: %+v != %+v", tc.desc, got, data) + t.Errorf("%v: %+v != %+v", tc.desc, got, data) } } } type WrappedTimestamp struct { - A int - Time Timestamp + A int `json:"A"` + Time Timestamp `json:"Time"` } -func TestWrappedTimstamp_Marshal(t *testing.T) { - testCases := []struct { - desc string - data WrappedTimestamp - want string - wantErr bool - equal bool - }{ - {"Reference", WrappedTimestamp{0, Timestamp{referenceTime}}, fmt.Sprintf(`{"A":0,"Time":%s}`, referenceTimeStr), false, true}, - {"Empty", WrappedTimestamp{}, fmt.Sprintf(`{"A":0,"Time":%s}`, emptyTimeStr), false, true}, - {"Mismatch", WrappedTimestamp{}, fmt.Sprintf(`{"A":0,"Time":%s}`, referenceTimeStr), false, false}, - } - for _, tc := range testCases { - out, err := json.Marshal(tc.data) - if gotErr := err != nil; gotErr != tc.wantErr { - t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) - } - got := string(out) - equal := got == tc.want - if equal != tc.equal { - t.Errorf("%s: got=%s, want=%s, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) - } - } -} - -func TestWrappedTimstamp_Unmarshal(t *testing.T) { +func TestWrappedTimestamp_Unmarshal(t *testing.T) { + t.Parallel() testCases := []struct { desc string data string @@ -144,27 +129,42 @@ func TestWrappedTimstamp_Unmarshal(t *testing.T) { }{ {"Reference", referenceTimeStr, WrappedTimestamp{0, Timestamp{referenceTime}}, false, true}, {"ReferenceUnix", referenceUnixTimeStr, WrappedTimestamp{0, Timestamp{referenceTime}}, false, true}, + {"ReferenceUnixMillisecond", referenceUnixTimeStrMilliSeconds, WrappedTimestamp{0, Timestamp{referenceTime}}, false, true}, {"Empty", emptyTimeStr, WrappedTimestamp{0, Timestamp{}}, false, true}, {"UnixStart", `0`, WrappedTimestamp{0, Timestamp{unixOrigin}}, false, true}, {"Mismatch", referenceTimeStr, WrappedTimestamp{0, Timestamp{}}, false, false}, {"MismatchUnix", `0`, WrappedTimestamp{0, Timestamp{}}, false, false}, {"Invalid", `"asdf"`, WrappedTimestamp{0, Timestamp{referenceTime}}, true, false}, + {"OffByMillisecond", `1136214245001`, WrappedTimestamp{0, Timestamp{referenceTime}}, false, false}, } for _, tc := range testCases { var got Timestamp err := json.Unmarshal([]byte(tc.data), &got) if gotErr := err != nil; gotErr != tc.wantErr { - t.Errorf("%s: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) + t.Errorf("%v: gotErr=%v, wantErr=%v, err=%v", tc.desc, gotErr, tc.wantErr, err) continue } equal := got.Time.Equal(tc.want.Time.Time) if equal != tc.equal { - t.Errorf("%s: got=%#v, want=%#v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) + t.Errorf("%v: got=%#v, want=%#v, equal=%v, want=%v", tc.desc, got, tc.want, equal, tc.equal) } } } -func TestWrappedTimstamp_MarshalReflexivity(t *testing.T) { +func TestTimestamp_GetTime(t *testing.T) { + t.Parallel() + var t1 *Timestamp + if t1.GetTime() != nil { + t.Errorf("nil timestamp should return nil, got: %v", t1.GetTime()) + } + t1 = &Timestamp{referenceTime} + if !t1.GetTime().Equal(referenceTime) { + t.Errorf("want reference time, got: %v", t1.GetTime()) + } +} + +func TestWrappedTimestamp_MarshalReflexivity(t *testing.T) { + t.Parallel() testCases := []struct { desc string data WrappedTimestamp @@ -175,15 +175,15 @@ func TestWrappedTimstamp_MarshalReflexivity(t *testing.T) { for _, tc := range testCases { bytes, err := json.Marshal(tc.data) if err != nil { - t.Errorf("%s: Marshal err=%v", tc.desc, err) + t.Errorf("%v: Marshal err=%v", tc.desc, err) } var got WrappedTimestamp err = json.Unmarshal(bytes, &got) if err != nil { - t.Errorf("%s: Unmarshal err=%v", tc.desc, err) + t.Errorf("%v: Unmarshal err=%v", tc.desc, err) } if !got.Time.Equal(tc.data.Time) { - t.Errorf("%s: %+v != %+v", tc.desc, got, tc.data) + t.Errorf("%v: %+v != %+v", tc.desc, got, tc.data) } } } diff --git a/github/url.go b/github/url.go new file mode 100644 index 00000000000..f0411dccc12 --- /dev/null +++ b/github/url.go @@ -0,0 +1,33 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "errors" + "fmt" + "net/url" + "strings" +) + +// parseURL parses the given string as a URL and ensures that the path ends +// with a slash. If the input string is empty, it returns an error. If the URL +// cannot be parsed, it returns the parsing error. +func parseURL(s string) (*url.URL, error) { + if s == "" { + return nil, errors.New("url cannot be empty") + } + + u, err := url.Parse(s) + if err != nil { + return nil, fmt.Errorf("invalid url: %w", err) + } + + if !strings.HasSuffix(u.Path, "/") { + u.Path += "/" + } + + return u, nil +} diff --git a/github/url_test.go b/github/url_test.go new file mode 100644 index 00000000000..431d6ea05b3 --- /dev/null +++ b/github/url_test.go @@ -0,0 +1,74 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "strings" + "testing" +) + +func Test_parseURL(t *testing.T) { + t.Parallel() + + for _, tt := range []struct { + name string + input string + want string + wantErr string + }{ + { + name: "empty_string_returns_error", + input: "", + wantErr: "url cannot be empty", + }, + { + name: "invalid_url_returns_error", + input: "://invalid-url\n", + wantErr: "invalid url", + }, + { + name: "valid_url", + input: "https://api.github.com/", + want: "https://api.github.com/", + }, + { + name: "valid_url_without_trailing_slash_adds_slash", + input: "https://api.github.com", + want: "https://api.github.com/", + }, + } { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := parseURL(tt.input) + if err != nil { + if tt.wantErr == "" { + t.Fatalf("unexpected error: %v", err) + } + + if !strings.Contains(err.Error(), tt.wantErr) { + t.Fatalf("expected error to contain %v, got %v", tt.wantErr, err.Error()) + } + + return + } + + if tt.wantErr != "" { + t.Fatalf("expected error to contain %v, got nil", tt.wantErr) + return + } + + if got == nil { + t.Fatal("expected non-nil URL, got nil") + return + } + + if got.String() != tt.want { + t.Fatalf("expected URL %v, got %v", tt.want, got.String()) + } + }) + } +} diff --git a/github/users.go b/github/users.go index 2592aea0f4f..e5b92a158d7 100644 --- a/github/users.go +++ b/github/users.go @@ -13,13 +13,14 @@ import ( // UsersService handles communication with the user related // methods of the GitHub API. // -// GitHub API docs: https://developer.github.com/v3/users/ +// GitHub API docs: https://docs.github.com/rest/users?apiVersion=2022-11-28 type UsersService service // User represents a GitHub user. type User struct { Login *string `json:"login,omitempty"` ID *int64 `json:"id,omitempty"` + UserViewType *string `json:"user_view_type,omitempty"` NodeID *string `json:"node_id,omitempty"` AvatarURL *string `json:"avatar_url,omitempty"` HTMLURL *string `json:"html_url,omitempty"` @@ -29,8 +30,10 @@ type User struct { Blog *string `json:"blog,omitempty"` Location *string `json:"location,omitempty"` Email *string `json:"email,omitempty"` + NotificationEmail *string `json:"notification_email,omitempty"` Hireable *bool `json:"hireable,omitempty"` Bio *string `json:"bio,omitempty"` + TwitterUsername *string `json:"twitter_username,omitempty"` PublicRepos *int `json:"public_repos,omitempty"` PublicGists *int `json:"public_gists,omitempty"` Followers *int `json:"followers,omitempty"` @@ -40,13 +43,14 @@ type User struct { SuspendedAt *Timestamp `json:"suspended_at,omitempty"` Type *string `json:"type,omitempty"` SiteAdmin *bool `json:"site_admin,omitempty"` - TotalPrivateRepos *int `json:"total_private_repos,omitempty"` - OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` + TotalPrivateRepos *int64 `json:"total_private_repos,omitempty"` + OwnedPrivateRepos *int64 `json:"owned_private_repos,omitempty"` PrivateGists *int `json:"private_gists,omitempty"` DiskUsage *int `json:"disk_usage,omitempty"` Collaborators *int `json:"collaborators,omitempty"` TwoFactorAuthentication *bool `json:"two_factor_authentication,omitempty"` Plan *Plan `json:"plan,omitempty"` + BusinessPlus *bool `json:"business_plus,omitempty"` LdapDn *string `json:"ldap_dn,omitempty"` // API URLs @@ -62,12 +66,51 @@ type User struct { SubscriptionsURL *string `json:"subscriptions_url,omitempty"` // TextMatches is only populated from search results that request text matches - // See: search.go and https://developer.github.com/v3/search/#text-match-metadata - TextMatches []TextMatch `json:"text_matches,omitempty"` + // See: search.go and https://docs.github.com/rest/search?apiVersion=2022-11-28#text-match-metadata + TextMatches []*TextMatch `json:"text_matches,omitempty"` + + // Permissions identify the permissions that a user has on a given + // repository. + // This is only populated when calling [RepositoriesService.ListCollaborators]. + Permissions *RepositoryPermissions `json:"permissions,omitempty"` + + // RoleName identifies the role that a user has on a given repository. + // This is only populated when calling [RepositoriesService.ListCollaborators]. + RoleName *string `json:"role_name,omitempty"` + + // Assignment identifies how a user was assigned to an organization role. Its + // possible values are: "direct", "indirect", "mixed". This is only populated when + // calling the ListUsersAssignedToOrgRole method. + Assignment *string `json:"assignment,omitempty"` + // InheritedFrom identifies the team that a user inherited their organization role + // from. This is only populated when calling the ListUsersAssignedToOrgRole method. + InheritedFrom []*Team `json:"inherited_from,omitempty"` + + // Role identifies the role that a user has on a given team. + // Its possible values are: "member", "maintainer". + // This is only populated when calling the + // [TeamsService.ListTeamMembersBySlug] or [TeamsService.ListTeamMembersByID] + // methods. + Role *string `json:"role,omitempty"` + + // Inherited identifies whether a user inherited their team role from a child + // team. + // This is only populated when calling the + // [TeamsService.ListTeamMembersBySlug] or [TeamsService.ListTeamMembersByID] + // methods. + Inherited *bool `json:"inherited,omitempty"` +} - // Permissions identifies the permissions that a user has on a given - // repository. This is only populated when calling Repositories.ListCollaborators. - Permissions *map[string]bool `json:"permissions,omitempty"` +// UserUpdateRequest represents the request body for updating a user. +type UserUpdateRequest struct { + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` + Blog *string `json:"blog,omitempty"` + TwitterUsername *string `json:"twitter_username,omitempty"` + Company *string `json:"company,omitempty"` + Location *string `json:"location,omitempty"` + Hireable *bool `json:"hireable,omitempty"` + Bio *string `json:"bio,omitempty"` } func (u User) String() string { @@ -77,8 +120,12 @@ func (u User) String() string { // Get fetches a user. Passing the empty string will fetch the authenticated // user. // -// GitHub API docs: https://developer.github.com/v3/users/#get-a-single-user -// and: https://developer.github.com/v3/users/#get-the-authenticated-user +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#get-a-user +// +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user +// +//meta:operation GET /user +//meta:operation GET /users/{username} func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, error) { var u string if user != "" { @@ -86,13 +133,13 @@ func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, } else { u = "user" } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - uResp := new(User) - resp, err := s.client.Do(ctx, req, uResp) + var uResp *User + resp, err := s.client.Do(req, &uResp) if err != nil { return nil, resp, err } @@ -102,16 +149,18 @@ func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, // GetByID fetches a user. // -// Note: GetByID uses the undocumented GitHub API endpoint /user/:id. +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#get-a-user-using-their-id +// +//meta:operation GET /user/{account_id} func (s *UsersService) GetByID(ctx context.Context, id int64) (*User, *Response, error) { - u := fmt.Sprintf("user/%d", id) - req, err := s.client.NewRequest("GET", u, nil) + u := fmt.Sprintf("user/%v", id) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - user := new(User) - resp, err := s.client.Do(ctx, req, user) + var user *User + resp, err := s.client.Do(req, &user) if err != nil { return nil, resp, err } @@ -119,18 +168,20 @@ func (s *UsersService) GetByID(ctx context.Context, id int64) (*User, *Response, return user, resp, nil } -// Edit the authenticated user. +// Update the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#update-the-authenticated-user // -// GitHub API docs: https://developer.github.com/v3/users/#update-the-authenticated-user -func (s *UsersService) Edit(ctx context.Context, user *User) (*User, *Response, error) { +//meta:operation PATCH /user +func (s *UsersService) Update(ctx context.Context, body UserUpdateRequest) (*User, *Response, error) { u := "user" - req, err := s.client.NewRequest("PATCH", u, user) + req, err := s.client.NewRequest(ctx, "PATCH", u, body) if err != nil { return nil, nil, err } - uResp := new(User) - resp, err := s.client.Do(ctx, req, uResp) + var uResp *User + resp, err := s.client.Do(req, &uResp) if err != nil { return nil, resp, err } @@ -163,24 +214,23 @@ type UserContext struct { // GetHovercard fetches contextual information about user. It requires authentication // via Basic Auth or via OAuth with the repo scope. // -// GitHub API docs: https://developer.github.com/v3/users/#get-contextual-information-about-a-user -func (s *UsersService) GetHovercard(ctx context.Context, user string, opt *HovercardOptions) (*Hovercard, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#get-contextual-information-for-a-user +// +//meta:operation GET /users/{username}/hovercard +func (s *UsersService) GetHovercard(ctx context.Context, user string, opts *HovercardOptions) (*Hovercard, *Response, error) { u := fmt.Sprintf("users/%v/hovercard", user) - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeHovercardPreview) - - hc := new(Hovercard) - resp, err := s.client.Do(ctx, req, hc) + var hc *Hovercard + resp, err := s.client.Do(req, &hc) if err != nil { return nil, resp, err } @@ -191,33 +241,31 @@ func (s *UsersService) GetHovercard(ctx context.Context, user string, opt *Hover // UserListOptions specifies optional parameters to the UsersService.ListAll // method. type UserListOptions struct { - // ID of the last user seen - Since int64 `url:"since,omitempty"` - - // Note: Pagination is powered exclusively by the Since parameter, - // ListOptions.Page has no effect. - // ListOptions.PerPage controls an undocumented GitHub API parameter. - ListOptions + // A user ID. Only return users with an ID greater than this ID. + Since int64 `url:"since,omitempty"` + PerPage int `url:"per_page,omitempty"` } // ListAll lists all GitHub users. // // To paginate through all users, populate 'Since' with the ID of the last user. // -// GitHub API docs: https://developer.github.com/v3/users/#get-all-users -func (s *UsersService) ListAll(ctx context.Context, opt *UserListOptions) ([]*User, *Response, error) { - u, err := addOptions("users", opt) +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#list-users +// +//meta:operation GET /users +func (s *UsersService) ListAll(ctx context.Context, opts *UserListOptions) ([]*User, *Response, error) { + u, err := addOptions("users", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var users []*User - resp, err := s.client.Do(ctx, req, &users) + resp, err := s.client.Do(req, &users) if err != nil { return nil, resp, err } @@ -228,20 +276,22 @@ func (s *UsersService) ListAll(ctx context.Context, opt *UserListOptions) ([]*Us // ListInvitations lists all currently-open repository invitations for the // authenticated user. // -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-a-users-repository-invitations -func (s *UsersService) ListInvitations(ctx context.Context, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { - u, err := addOptions("user/repository_invitations", opt) +// GitHub API docs: https://docs.github.com/rest/collaborators/invitations?apiVersion=2022-11-28#list-repository-invitations-for-the-authenticated-user +// +//meta:operation GET /user/repository_invitations +func (s *UsersService) ListInvitations(ctx context.Context, opts *ListOptions) ([]*RepositoryInvitation, *Response, error) { + u, err := addOptions("user/repository_invitations", opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } invites := []*RepositoryInvitation{} - resp, err := s.client.Do(ctx, req, &invites) + resp, err := s.client.Do(req, &invites) if err != nil { return nil, resp, err } @@ -252,27 +302,31 @@ func (s *UsersService) ListInvitations(ctx context.Context, opt *ListOptions) ([ // AcceptInvitation accepts the currently-open repository invitation for the // authenticated user. // -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#accept-a-repository-invitation +// GitHub API docs: https://docs.github.com/rest/collaborators/invitations?apiVersion=2022-11-28#accept-a-repository-invitation +// +//meta:operation PATCH /user/repository_invitations/{invitation_id} func (s *UsersService) AcceptInvitation(ctx context.Context, invitationID int64) (*Response, error) { u := fmt.Sprintf("user/repository_invitations/%v", invitationID) - req, err := s.client.NewRequest("PATCH", u, nil) + req, err := s.client.NewRequest(ctx, "PATCH", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // DeclineInvitation declines the currently-open repository invitation for the // authenticated user. // -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#decline-a-repository-invitation +// GitHub API docs: https://docs.github.com/rest/collaborators/invitations?apiVersion=2022-11-28#decline-a-repository-invitation +// +//meta:operation DELETE /user/repository_invitations/{invitation_id} func (s *UsersService) DeclineInvitation(ctx context.Context, invitationID int64) (*Response, error) { u := fmt.Sprintf("user/repository_invitations/%v", invitationID) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/users_administration.go b/github/users_administration.go index 1c483a7b17a..5a5b8edb875 100644 --- a/github/users_administration.go +++ b/github/users_administration.go @@ -12,61 +12,69 @@ import ( // PromoteSiteAdmin promotes a user to a site administrator of a GitHub Enterprise instance. // -// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#promote-an-ordinary-user-to-a-site-administrator +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#promote-a-user-to-be-a-site-administrator +// +//meta:operation PUT /users/{username}/site_admin func (s *UsersService) PromoteSiteAdmin(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("users/%v/site_admin", user) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // DemoteSiteAdmin demotes a user from site administrator of a GitHub Enterprise instance. // -// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#demote-a-site-administrator-to-an-ordinary-user +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#demote-a-site-administrator +// +//meta:operation DELETE /users/{username}/site_admin func (s *UsersService) DemoteSiteAdmin(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("users/%v/site_admin", user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } -// UserSuspendOptions represents the reason a user is being suspended. +// UserSuspendOptions represents the reason to suspend a user. type UserSuspendOptions struct { Reason *string `json:"reason,omitempty"` } // Suspend a user on a GitHub Enterprise instance. // -// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#suspend-a-user -func (s *UsersService) Suspend(ctx context.Context, user string, opt *UserSuspendOptions) (*Response, error) { +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#suspend-a-user +// +//meta:operation PUT /users/{username}/suspended +func (s *UsersService) Suspend(ctx context.Context, user string, body *UserSuspendOptions) (*Response, error) { u := fmt.Sprintf("users/%v/suspended", user) - req, err := s.client.NewRequest("PUT", u, opt) + req, err := s.client.NewRequest(ctx, "PUT", u, body) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // Unsuspend a user on a GitHub Enterprise instance. // -// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#unsuspend-a-user +// GitHub API docs: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#unsuspend-a-user +// +//meta:operation DELETE /users/{username}/suspended func (s *UsersService) Unsuspend(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("users/%v/suspended", user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/users_administration_test.go b/github/users_administration_test.go index c1d9f975460..34ca232a269 100644 --- a/github/users_administration_test.go +++ b/github/users_administration_test.go @@ -6,93 +6,129 @@ package github import ( - "context" - "encoding/json" "net/http" - "reflect" "testing" ) func TestUsersService_PromoteSiteAdmin(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/site_admin", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.PromoteSiteAdmin(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.PromoteSiteAdmin(ctx, "u") if err != nil { t.Errorf("Users.PromoteSiteAdmin returned error: %v", err) } + + const methodName = "PromoteSiteAdmin" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.PromoteSiteAdmin(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.PromoteSiteAdmin(ctx, "u") + }) } func TestUsersService_DemoteSiteAdmin(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/site_admin", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.DemoteSiteAdmin(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.DemoteSiteAdmin(ctx, "u") if err != nil { t.Errorf("Users.DemoteSiteAdmin returned error: %v", err) } + + const methodName = "DemoteSiteAdmin" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DemoteSiteAdmin(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DemoteSiteAdmin(ctx, "u") + }) } func TestUsersService_Suspend(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/suspended", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.Suspend(context.Background(), "u", nil) + ctx := t.Context() + _, err := client.Users.Suspend(ctx, "u", nil) if err != nil { t.Errorf("Users.Suspend returned error: %v", err) } + + const methodName = "Suspend" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.Suspend(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.Suspend(ctx, "u", nil) + }) } func TestUsersServiceReason_Suspend(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &UserSuspendOptions{Reason: String("test")} + input := &UserSuspendOptions{Reason: Ptr("test")} mux.HandleFunc("/users/u/suspended", func(w http.ResponseWriter, r *http.Request) { - v := new(UserSuspendOptions) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PUT") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.Suspend(context.Background(), "u", input) + ctx := t.Context() + _, err := client.Users.Suspend(ctx, "u", input) if err != nil { t.Errorf("Users.Suspend returned error: %v", err) } } func TestUsersService_Unsuspend(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/suspended", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.Unsuspend(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.Unsuspend(ctx, "u") if err != nil { t.Errorf("Users.Unsuspend returned error: %v", err) } + + const methodName = "Unsuspend" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.Unsuspend(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.Unsuspend(ctx, "u") + }) } diff --git a/github/users_attestations.go b/github/users_attestations.go new file mode 100644 index 00000000000..23200cad36c --- /dev/null +++ b/github/users_attestations.go @@ -0,0 +1,40 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// ListAttestations returns a collection of artifact attestations +// with a given subject digest that are associated with repositories +// owned by a user. +// +// GitHub API docs: https://docs.github.com/rest/users/attestations?apiVersion=2022-11-28#list-attestations +// +//meta:operation GET /users/{username}/attestations/{subject_digest} +func (s *UsersService) ListAttestations(ctx context.Context, user, subjectDigest string, opts *ListOptions) (*AttestationsResponse, *Response, error) { + u := fmt.Sprintf("users/%v/attestations/%v", user, subjectDigest) + + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var attestations *AttestationsResponse + res, err := s.client.Do(req, &attestations) + if err != nil { + return nil, res, err + } + + return attestations, res, nil +} diff --git a/github/users_attestations_test.go b/github/users_attestations_test.go new file mode 100644 index 00000000000..347af245f8a --- /dev/null +++ b/github/users_attestations_test.go @@ -0,0 +1,71 @@ +// Copyright 2024 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestUsersService_ListAttestations(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/attestations/digest", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "attestations": [ + { + "repository_id": 1, + "bundle": {} + }, + { + "repository_id": 2, + "bundle": {} + } + ] + }`) + }) + ctx := t.Context() + attestations, _, err := client.Users.ListAttestations(ctx, "u", "digest", &ListOptions{}) + if err != nil { + t.Errorf("Users.ListAttestations returned error: %v", err) + } + + want := &AttestationsResponse{ + Attestations: []*Attestation{ + { + RepositoryID: 1, + Bundle: []byte(`{}`), + }, + { + RepositoryID: 2, + Bundle: []byte(`{}`), + }, + }, + } + + if !cmp.Equal(attestations, want) { + t.Errorf("Users.ListAttestations = %+v, want %+v", attestations, want) + } + const methodName = "ListAttestations" + + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListAttestations(ctx, "\n", "\n", &ListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListAttestations(ctx, "u", "digest", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/users_blocking.go b/github/users_blocking.go index 39e45601cc1..9fa4da63159 100644 --- a/github/users_blocking.go +++ b/github/users_blocking.go @@ -12,24 +12,25 @@ import ( // ListBlockedUsers lists all the blocked users by the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/blocking/#list-blocked-users -func (s *UsersService) ListBlockedUsers(ctx context.Context, opt *ListOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/blocking?apiVersion=2022-11-28#list-users-blocked-by-the-authenticated-user +// +//meta:operation GET /user/blocks +func (s *UsersService) ListBlockedUsers(ctx context.Context, opts *ListOptions) ([]*User, *Response, error) { u := "user/blocks" - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) var blockedUsers []*User - resp, err := s.client.Do(ctx, req, &blockedUsers) + resp, err := s.client.Do(req, &blockedUsers) if err != nil { return nil, resp, err } @@ -39,53 +40,56 @@ func (s *UsersService) ListBlockedUsers(ctx context.Context, opt *ListOptions) ( // IsBlocked reports whether specified user is blocked by the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/blocking/#check-whether-youve-blocked-a-user +// GitHub API docs: https://docs.github.com/rest/users/blocking?apiVersion=2022-11-28#check-if-a-user-is-blocked-by-the-authenticated-user +// +//meta:operation GET /user/blocks/{username} func (s *UsersService) IsBlocked(ctx context.Context, user string) (bool, *Response, error) { u := fmt.Sprintf("user/blocks/%v", user) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) isBlocked, err := parseBoolResponse(err) return isBlocked, resp, err } // BlockUser blocks specified user for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/blocking/#block-a-user +// GitHub API docs: https://docs.github.com/rest/users/blocking?apiVersion=2022-11-28#block-a-user +// +//meta:operation PUT /user/blocks/{username} func (s *UsersService) BlockUser(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("user/blocks/%v", user) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // UnblockUser unblocks specified user for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/blocking/#unblock-a-user +// GitHub API docs: https://docs.github.com/rest/users/blocking?apiVersion=2022-11-28#unblock-a-user +// +//meta:operation DELETE /user/blocks/{username} func (s *UsersService) UnblockUser(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("user/blocks/%v", user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - // TODO: remove custom Accept header when this API fully launches. req.Header.Set("Accept", mediaTypeBlockUsersPreview) - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/users_blocking_test.go b/github/users_blocking_test.go index 83e8e30c83d..79e0dbf6589 100644 --- a/github/users_blocking_test.go +++ b/github/users_blocking_test.go @@ -6,16 +6,16 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestUsersService_ListBlockedUsers(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/blocks", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -27,20 +27,30 @@ func TestUsersService_ListBlockedUsers(t *testing.T) { }) opt := &ListOptions{Page: 2} - blockedUsers, _, err := client.Users.ListBlockedUsers(context.Background(), opt) + ctx := t.Context() + blockedUsers, _, err := client.Users.ListBlockedUsers(ctx, opt) if err != nil { t.Errorf("Users.ListBlockedUsers returned error: %v", err) } - want := []*User{{Login: String("octocat")}} - if !reflect.DeepEqual(blockedUsers, want) { + want := []*User{{Login: Ptr("octocat")}} + if !cmp.Equal(blockedUsers, want) { t.Errorf("Users.ListBlockedUsers returned %+v, want %+v", blockedUsers, want) } + + const methodName = "ListBlockedUsers" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListBlockedUsers(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_IsBlocked(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/blocks/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -48,18 +58,33 @@ func TestUsersService_IsBlocked(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - isBlocked, _, err := client.Users.IsBlocked(context.Background(), "u") + ctx := t.Context() + isBlocked, _, err := client.Users.IsBlocked(ctx, "u") if err != nil { t.Errorf("Users.IsBlocked returned error: %v", err) } if want := true; isBlocked != want { t.Errorf("Users.IsBlocked returned %+v, want %+v", isBlocked, want) } + + const methodName = "IsBlocked" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.IsBlocked(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.IsBlocked(ctx, "u") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_BlockUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/blocks/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") @@ -67,15 +92,26 @@ func TestUsersService_BlockUser(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.BlockUser(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.BlockUser(ctx, "u") if err != nil { t.Errorf("Users.BlockUser returned error: %v", err) } + + const methodName = "BlockUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.BlockUser(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.BlockUser(ctx, "u") + }) } func TestUsersService_UnblockUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/blocks/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") @@ -83,8 +119,19 @@ func TestUsersService_UnblockUser(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - _, err := client.Users.UnblockUser(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.UnblockUser(ctx, "u") if err != nil { t.Errorf("Users.UnblockUser returned error: %v", err) } + + const methodName = "UnblockUser" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.UnblockUser(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.UnblockUser(ctx, "u") + }) } diff --git a/github/users_emails.go b/github/users_emails.go index 78d21491191..bccaa28c4b7 100644 --- a/github/users_emails.go +++ b/github/users_emails.go @@ -7,7 +7,7 @@ package github import "context" -// UserEmail represents user's email address +// UserEmail represents user's email address. type UserEmail struct { Email *string `json:"email,omitempty"` Primary *bool `json:"primary,omitempty"` @@ -17,21 +17,23 @@ type UserEmail struct { // ListEmails lists all email addresses for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user -func (s *UsersService) ListEmails(ctx context.Context, opt *ListOptions) ([]*UserEmail, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/emails?apiVersion=2022-11-28#list-email-addresses-for-the-authenticated-user +// +//meta:operation GET /user/emails +func (s *UsersService) ListEmails(ctx context.Context, opts *ListOptions) ([]*UserEmail, *Response, error) { u := "user/emails" - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var emails []*UserEmail - resp, err := s.client.Do(ctx, req, &emails) + resp, err := s.client.Do(req, &emails) if err != nil { return nil, resp, err } @@ -41,16 +43,18 @@ func (s *UsersService) ListEmails(ctx context.Context, opt *ListOptions) ([]*Use // AddEmails adds email addresses of the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/emails/#add-email-addresses -func (s *UsersService) AddEmails(ctx context.Context, emails []string) ([]*UserEmail, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/emails?apiVersion=2022-11-28#add-an-email-address-for-the-authenticated-user +// +//meta:operation POST /user/emails +func (s *UsersService) AddEmails(ctx context.Context, body []string) ([]*UserEmail, *Response, error) { u := "user/emails" - req, err := s.client.NewRequest("POST", u, emails) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } var e []*UserEmail - resp, err := s.client.Do(ctx, req, &e) + resp, err := s.client.Do(req, &e) if err != nil { return nil, resp, err } @@ -60,13 +64,42 @@ func (s *UsersService) AddEmails(ctx context.Context, emails []string) ([]*UserE // DeleteEmails deletes email addresses from authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/emails/#delete-email-addresses +// GitHub API docs: https://docs.github.com/rest/users/emails?apiVersion=2022-11-28#delete-an-email-address-for-the-authenticated-user +// +//meta:operation DELETE /user/emails func (s *UsersService) DeleteEmails(ctx context.Context, emails []string) (*Response, error) { u := "user/emails" - req, err := s.client.NewRequest("DELETE", u, emails) + req, err := s.client.NewRequest(ctx, "DELETE", u, emails) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) +} + +// SetEmailVisibility sets the visibility for the primary email address of the authenticated user. +// `visibility` can be "private" or "public". +// +// GitHub API docs: https://docs.github.com/rest/users/emails?apiVersion=2022-11-28#set-primary-email-visibility-for-the-authenticated-user +// +//meta:operation PATCH /user/email/visibility +func (s *UsersService) SetEmailVisibility(ctx context.Context, visibility string) ([]*UserEmail, *Response, error) { + u := "user/email/visibility" + + updateVisibilityReq := &UserEmail{ + Visibility: &visibility, + } + + req, err := s.client.NewRequest(ctx, "PATCH", u, updateVisibilityReq) + if err != nil { + return nil, nil, err + } + + var e []*UserEmail + resp, err := s.client.Do(req, &e) + if err != nil { + return nil, resp, err + } + + return e, resp, nil } diff --git a/github/users_emails_test.go b/github/users_emails_test.go index 3f72b409bb1..3d24c1bdd4a 100644 --- a/github/users_emails_test.go +++ b/github/users_emails_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestUsersService_ListEmails(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/emails", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -29,67 +28,120 @@ func TestUsersService_ListEmails(t *testing.T) { }) opt := &ListOptions{Page: 2} - emails, _, err := client.Users.ListEmails(context.Background(), opt) + ctx := t.Context() + emails, _, err := client.Users.ListEmails(ctx, opt) if err != nil { t.Errorf("Users.ListEmails returned error: %v", err) } - want := []*UserEmail{{Email: String("user@example.com"), Verified: Bool(false), Primary: Bool(true)}} - if !reflect.DeepEqual(emails, want) { + want := []*UserEmail{{Email: Ptr("user@example.com"), Verified: Ptr(false), Primary: Ptr(true)}} + if !cmp.Equal(emails, want) { t.Errorf("Users.ListEmails returned %+v, want %+v", emails, want) } + + const methodName = "ListEmails" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListEmails(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_AddEmails(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := []string{"new@example.com"} mux.HandleFunc("/user/emails", func(w http.ResponseWriter, r *http.Request) { - var v []string - json.NewDecoder(r.Body).Decode(&v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `[{"email":"old@example.com"}, {"email":"new@example.com"}]`) }) - emails, _, err := client.Users.AddEmails(context.Background(), input) + ctx := t.Context() + emails, _, err := client.Users.AddEmails(ctx, input) if err != nil { t.Errorf("Users.AddEmails returned error: %v", err) } want := []*UserEmail{ - {Email: String("old@example.com")}, - {Email: String("new@example.com")}, + {Email: Ptr("old@example.com")}, + {Email: Ptr("new@example.com")}, } - if !reflect.DeepEqual(emails, want) { + if !cmp.Equal(emails, want) { t.Errorf("Users.AddEmails returned %+v, want %+v", emails, want) } + + const methodName = "AddEmails" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.AddEmails(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_DeleteEmails(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := []string{"user@example.com"} - mux.HandleFunc("/user/emails", func(w http.ResponseWriter, r *http.Request) { - var v []string - json.NewDecoder(r.Body).Decode(&v) - + mux.HandleFunc("/user/emails", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } + testJSONBody(t, r, input) }) - _, err := client.Users.DeleteEmails(context.Background(), input) + ctx := t.Context() + _, err := client.Users.DeleteEmails(ctx, input) if err != nil { t.Errorf("Users.DeleteEmails returned error: %v", err) } + + const methodName = "DeleteEmails" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteEmails(ctx, input) + }) +} + +func TestUsersService_SetEmailVisibility(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &UserEmail{Visibility: Ptr("private")} + + mux.HandleFunc("/user/email/visibility", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testJSONBody(t, r, input) + fmt.Fprint(w, `[{ + "email": "user@example.com", + "verified": false, + "primary": true, + "visibility": "private" + }]`) + }) + + ctx := t.Context() + emails, _, err := client.Users.SetEmailVisibility(ctx, "private") + if err != nil { + t.Errorf("Users.SetEmailVisibility returned error: %v", err) + } + + want := []*UserEmail{{Email: Ptr("user@example.com"), Verified: Ptr(false), Primary: Ptr(true), Visibility: Ptr("private")}} + if !cmp.Equal(emails, want) { + t.Errorf("Users.SetEmailVisibility returned %+v, want %+v", emails, want) + } + + const methodName = "SetEmailVisibility" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.SetEmailVisibility(ctx, "private") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } diff --git a/github/users_followers.go b/github/users_followers.go index c2224096a62..4543cb8ec56 100644 --- a/github/users_followers.go +++ b/github/users_followers.go @@ -13,26 +13,31 @@ import ( // ListFollowers lists the followers for a user. Passing the empty string will // fetch followers for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/followers/#list-followers-of-a-user -func (s *UsersService) ListFollowers(ctx context.Context, user string, opt *ListOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#list-followers-of-a-user +// +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#list-followers-of-the-authenticated-user +// +//meta:operation GET /user/followers +//meta:operation GET /users/{username}/followers +func (s *UsersService) ListFollowers(ctx context.Context, user string, opts *ListOptions) ([]*User, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/followers", user) } else { u = "user/followers" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var users []*User - resp, err := s.client.Do(ctx, req, &users) + resp, err := s.client.Do(req, &users) if err != nil { return nil, resp, err } @@ -43,26 +48,31 @@ func (s *UsersService) ListFollowers(ctx context.Context, user string, opt *List // ListFollowing lists the people that a user is following. Passing the empty // string will list people the authenticated user is following. // -// GitHub API docs: https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user -func (s *UsersService) ListFollowing(ctx context.Context, user string, opt *ListOptions) ([]*User, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#list-the-people-a-user-follows +// +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#list-the-people-the-authenticated-user-follows +// +//meta:operation GET /user/following +//meta:operation GET /users/{username}/following +func (s *UsersService) ListFollowing(ctx context.Context, user string, opts *ListOptions) ([]*User, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/following", user) } else { u = "user/following" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var users []*User - resp, err := s.client.Do(ctx, req, &users) + resp, err := s.client.Do(req, &users) if err != nil { return nil, resp, err } @@ -73,7 +83,12 @@ func (s *UsersService) ListFollowing(ctx context.Context, user string, opt *List // IsFollowing checks if "user" is following "target". Passing the empty // string for "user" will check if the authenticated user is following "target". // -// GitHub API docs: https://developer.github.com/v3/users/followers/#check-if-you-are-following-a-user +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#check-if-a-person-is-followed-by-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#check-if-a-user-follows-another-user +// +//meta:operation GET /user/following/{username} +//meta:operation GET /users/{username}/following/{target_user} func (s *UsersService) IsFollowing(ctx context.Context, user, target string) (bool, *Response, error) { var u string if user != "" { @@ -82,38 +97,42 @@ func (s *UsersService) IsFollowing(ctx context.Context, user, target string) (bo u = fmt.Sprintf("user/following/%v", target) } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return false, nil, err } - resp, err := s.client.Do(ctx, req, nil) + resp, err := s.client.Do(req, nil) following, err := parseBoolResponse(err) return following, resp, err } // Follow will cause the authenticated user to follow the specified user. // -// GitHub API docs: https://developer.github.com/v3/users/followers/#follow-a-user +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#follow-a-user +// +//meta:operation PUT /user/following/{username} func (s *UsersService) Follow(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("user/following/%v", user) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest(ctx, "PUT", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } // Unfollow will cause the authenticated user to unfollow the specified user. // -// GitHub API docs: https://developer.github.com/v3/users/followers/#unfollow-a-user +// GitHub API docs: https://docs.github.com/rest/users/followers?apiVersion=2022-11-28#unfollow-a-user +// +//meta:operation DELETE /user/following/{username} func (s *UsersService) Unfollow(ctx context.Context, user string) (*Response, error) { u := fmt.Sprintf("user/following/%v", user) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/users_followers_test.go b/github/users_followers_test.go index 777fe801438..21e42aab34c 100644 --- a/github/users_followers_test.go +++ b/github/users_followers_test.go @@ -6,16 +6,16 @@ package github import ( - "context" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestUsersService_ListFollowers_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/followers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -24,48 +24,79 @@ func TestUsersService_ListFollowers_authenticatedUser(t *testing.T) { }) opt := &ListOptions{Page: 2} - users, _, err := client.Users.ListFollowers(context.Background(), "", opt) + ctx := t.Context() + users, _, err := client.Users.ListFollowers(ctx, "", opt) if err != nil { t.Errorf("Users.ListFollowers returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(users, want) { t.Errorf("Users.ListFollowers returned %+v, want %+v", users, want) } + + const methodName = "ListFollowers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListFollowers(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListFollowers(ctx, "", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListFollowers_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/followers", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":1}]`) }) - users, _, err := client.Users.ListFollowers(context.Background(), "u", nil) + ctx := t.Context() + users, _, err := client.Users.ListFollowers(ctx, "u", nil) if err != nil { t.Errorf("Users.ListFollowers returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(users, want) { t.Errorf("Users.ListFollowers returned %+v, want %+v", users, want) } + + const methodName = "ListFollowers" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListFollowers(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListFollowers(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListFollowers_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Users.ListFollowers(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Users.ListFollowers(ctx, "%", nil) testURLParseError(t, err) } func TestUsersService_ListFollowing_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/following", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -74,165 +105,281 @@ func TestUsersService_ListFollowing_authenticatedUser(t *testing.T) { }) opts := &ListOptions{Page: 2} - users, _, err := client.Users.ListFollowing(context.Background(), "", opts) + ctx := t.Context() + users, _, err := client.Users.ListFollowing(ctx, "", opts) if err != nil { t.Errorf("Users.ListFollowing returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(users, want) { t.Errorf("Users.ListFollowing returned %+v, want %+v", users, want) } + + const methodName = "ListFollowing" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListFollowing(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListFollowing(ctx, "", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListFollowing_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/following", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":1}]`) }) - users, _, err := client.Users.ListFollowing(context.Background(), "u", nil) + ctx := t.Context() + users, _, err := client.Users.ListFollowing(ctx, "u", nil) if err != nil { t.Errorf("Users.ListFollowing returned error: %v", err) } - want := []*User{{ID: Int64(1)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(1))}} + if !cmp.Equal(users, want) { t.Errorf("Users.ListFollowing returned %+v, want %+v", users, want) } + + const methodName = "ListFollowing" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListFollowing(ctx, "\n", nil) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListFollowing(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListFollowing_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Users.ListFollowing(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Users.ListFollowing(ctx, "%", nil) testURLParseError(t, err) } func TestUsersService_IsFollowing_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/following/t", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - following, _, err := client.Users.IsFollowing(context.Background(), "", "t") + ctx := t.Context() + following, _, err := client.Users.IsFollowing(ctx, "", "t") if err != nil { t.Errorf("Users.IsFollowing returned error: %v", err) } if want := true; following != want { t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) } + + const methodName = "IsFollowing" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.IsFollowing(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.IsFollowing(ctx, "", "t") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestUsersService_IsFollowing_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/following/t", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNoContent) }) - following, _, err := client.Users.IsFollowing(context.Background(), "u", "t") + ctx := t.Context() + following, _, err := client.Users.IsFollowing(ctx, "u", "t") if err != nil { t.Errorf("Users.IsFollowing returned error: %v", err) } if want := true; following != want { t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) } + + const methodName = "IsFollowing" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.IsFollowing(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.IsFollowing(ctx, "u", "t") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestUsersService_IsFollowing_false(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/following/t", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") w.WriteHeader(http.StatusNotFound) }) - following, _, err := client.Users.IsFollowing(context.Background(), "u", "t") + ctx := t.Context() + following, _, err := client.Users.IsFollowing(ctx, "u", "t") if err != nil { t.Errorf("Users.IsFollowing returned error: %v", err) } if want := false; following != want { t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) } + + const methodName = "IsFollowing" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.IsFollowing(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.IsFollowing(ctx, "u", "t") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestUsersService_IsFollowing_error(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/following/t", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") http.Error(w, "BadRequest", http.StatusBadRequest) }) - following, _, err := client.Users.IsFollowing(context.Background(), "u", "t") + ctx := t.Context() + following, _, err := client.Users.IsFollowing(ctx, "u", "t") if err == nil { - t.Errorf("Expected HTTP 400 response") + t.Error("Expected HTTP 400 response") } if want := false; following != want { t.Errorf("Users.IsFollowing returned %+v, want %+v", following, want) } + + const methodName = "IsFollowing" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.IsFollowing(ctx, "u", "t") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.IsFollowing(ctx, "u", "t") + if got { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got) + } + return resp, err + }) } func TestUsersService_IsFollowing_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Users.IsFollowing(context.Background(), "%", "%") + ctx := t.Context() + _, _, err := client.Users.IsFollowing(ctx, "%", "%") testURLParseError(t, err) } func TestUsersService_Follow(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/following/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/user/following/u", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") }) - _, err := client.Users.Follow(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.Follow(ctx, "u") if err != nil { t.Errorf("Users.Follow returned error: %v", err) } + + const methodName = "Follow" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.Follow(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.Follow(ctx, "u") + }) } func TestUsersService_Follow_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Users.Follow(context.Background(), "%") + ctx := t.Context() + _, err := client.Users.Follow(ctx, "%") testURLParseError(t, err) } func TestUsersService_Unfollow(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/following/u", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/user/following/u", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Users.Unfollow(context.Background(), "u") + ctx := t.Context() + _, err := client.Users.Unfollow(ctx, "u") if err != nil { - t.Errorf("Users.Follow returned error: %v", err) + t.Errorf("Users.Unfollow returned error: %v", err) } + + const methodName = "Unfollow" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.Unfollow(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.Unfollow(ctx, "u") + }) } func TestUsersService_Unfollow_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, err := client.Users.Unfollow(context.Background(), "%") + ctx := t.Context() + _, err := client.Users.Unfollow(ctx, "%") testURLParseError(t, err) } diff --git a/github/users_gpg_keys.go b/github/users_gpg_keys.go index 07ed38dcbef..f22ab81b2df 100644 --- a/github/users_gpg_keys.go +++ b/github/users_gpg_keys.go @@ -8,25 +8,25 @@ package github import ( "context" "fmt" - "time" ) // GPGKey represents a GitHub user's public GPG key used to verify GPG signed commits and tags. // // https://developer.github.com/changes/2016-04-04-git-signing-api-preview/ type GPGKey struct { - ID *int64 `json:"id,omitempty"` - PrimaryKeyID *int64 `json:"primary_key_id,omitempty"` - KeyID *string `json:"key_id,omitempty"` - PublicKey *string `json:"public_key,omitempty"` - Emails []GPGEmail `json:"emails,omitempty"` - Subkeys []GPGKey `json:"subkeys,omitempty"` - CanSign *bool `json:"can_sign,omitempty"` - CanEncryptComms *bool `json:"can_encrypt_comms,omitempty"` - CanEncryptStorage *bool `json:"can_encrypt_storage,omitempty"` - CanCertify *bool `json:"can_certify,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` + ID *int64 `json:"id,omitempty"` + PrimaryKeyID *int64 `json:"primary_key_id,omitempty"` + KeyID *string `json:"key_id,omitempty"` + RawKey *string `json:"raw_key,omitempty"` + PublicKey *string `json:"public_key,omitempty"` + Emails []*GPGEmail `json:"emails,omitempty"` + Subkeys []*GPGKey `json:"subkeys,omitempty"` + CanSign *bool `json:"can_sign,omitempty"` + CanEncryptComms *bool `json:"can_encrypt_comms,omitempty"` + CanEncryptStorage *bool `json:"can_encrypt_storage,omitempty"` + CanCertify *bool `json:"can_certify,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + ExpiresAt *Timestamp `json:"expires_at,omitempty"` } // String stringifies a GPGKey. @@ -44,26 +44,31 @@ type GPGEmail struct { // string will fetch keys for the authenticated user. It requires authentication // via Basic Auth or via OAuth with at least read:gpg_key scope. // -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#list-gpg-keys-for-a-user -func (s *UsersService) ListGPGKeys(ctx context.Context, user string, opt *ListOptions) ([]*GPGKey, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/gpg-keys?apiVersion=2022-11-28#list-gpg-keys-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/users/gpg-keys?apiVersion=2022-11-28#list-gpg-keys-for-the-authenticated-user +// +//meta:operation GET /user/gpg_keys +//meta:operation GET /users/{username}/gpg_keys +func (s *UsersService) ListGPGKeys(ctx context.Context, user string, opts *ListOptions) ([]*GPGKey, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/gpg_keys", user) } else { u = "user/gpg_keys" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var keys []*GPGKey - resp, err := s.client.Do(ctx, req, &keys) + resp, err := s.client.Do(req, &keys) if err != nil { return nil, resp, err } @@ -74,16 +79,18 @@ func (s *UsersService) ListGPGKeys(ctx context.Context, user string, opt *ListOp // GetGPGKey gets extended details for a single GPG key. It requires authentication // via Basic Auth or via OAuth with at least read:gpg_key scope. // -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#get-a-single-gpg-key +// GitHub API docs: https://docs.github.com/rest/users/gpg-keys?apiVersion=2022-11-28#get-a-gpg-key-for-the-authenticated-user +// +//meta:operation GET /user/gpg_keys/{gpg_key_id} func (s *UsersService) GetGPGKey(ctx context.Context, id int64) (*GPGKey, *Response, error) { u := fmt.Sprintf("user/gpg_keys/%v", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - key := &GPGKey{} - resp, err := s.client.Do(ctx, req, key) + var key *GPGKey + resp, err := s.client.Do(req, &key) if err != nil { return nil, resp, err } @@ -91,21 +98,23 @@ func (s *UsersService) GetGPGKey(ctx context.Context, id int64) (*GPGKey, *Respo return key, resp, nil } -// CreateGPGKey creates a GPG key. It requires authenticatation via Basic Auth +// CreateGPGKey creates a GPG key. It requires authentication via Basic Auth // or OAuth with at least write:gpg_key scope. // -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#create-a-gpg-key +// GitHub API docs: https://docs.github.com/rest/users/gpg-keys?apiVersion=2022-11-28#create-a-gpg-key-for-the-authenticated-user +// +//meta:operation POST /user/gpg_keys func (s *UsersService) CreateGPGKey(ctx context.Context, armoredPublicKey string) (*GPGKey, *Response, error) { gpgKey := &struct { ArmoredPublicKey string `json:"armored_public_key"` }{ArmoredPublicKey: armoredPublicKey} - req, err := s.client.NewRequest("POST", "user/gpg_keys", gpgKey) + req, err := s.client.NewRequest(ctx, "POST", "user/gpg_keys", gpgKey) if err != nil { return nil, nil, err } - key := &GPGKey{} - resp, err := s.client.Do(ctx, req, key) + var key *GPGKey + resp, err := s.client.Do(req, &key) if err != nil { return nil, resp, err } @@ -116,13 +125,15 @@ func (s *UsersService) CreateGPGKey(ctx context.Context, armoredPublicKey string // DeleteGPGKey deletes a GPG key. It requires authentication via Basic Auth or // via OAuth with at least admin:gpg_key scope. // -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#delete-a-gpg-key +// GitHub API docs: https://docs.github.com/rest/users/gpg-keys?apiVersion=2022-11-28#delete-a-gpg-key-for-the-authenticated-user +// +//meta:operation DELETE /user/gpg_keys/{gpg_key_id} func (s *UsersService) DeleteGPGKey(ctx context.Context, id int64) (*Response, error) { u := fmt.Sprintf("user/gpg_keys/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/users_gpg_keys_test.go b/github/users_gpg_keys_test.go index dfabab257ea..0c87f31f761 100644 --- a/github/users_gpg_keys_test.go +++ b/github/users_gpg_keys_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestUsersService_ListGPGKeys_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/gpg_keys", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,68 +24,100 @@ func TestUsersService_ListGPGKeys_authenticatedUser(t *testing.T) { }) opt := &ListOptions{Page: 2} - keys, _, err := client.Users.ListGPGKeys(context.Background(), "", opt) + ctx := t.Context() + keys, _, err := client.Users.ListGPGKeys(ctx, "", opt) if err != nil { t.Errorf("Users.ListGPGKeys returned error: %v", err) } - want := []*GPGKey{{ID: Int64(1), PrimaryKeyID: Int64(2)}} - if !reflect.DeepEqual(keys, want) { + want := []*GPGKey{{ID: Ptr(int64(1)), PrimaryKeyID: Ptr(int64(2))}} + if !cmp.Equal(keys, want) { t.Errorf("Users.ListGPGKeys = %+v, want %+v", keys, want) } + + const methodName = "ListGPGKeys" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListGPGKeys(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListGPGKeys(ctx, "", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListGPGKeys_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/gpg_keys", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":1,"primary_key_id":2}]`) }) - keys, _, err := client.Users.ListGPGKeys(context.Background(), "u", nil) + ctx := t.Context() + keys, _, err := client.Users.ListGPGKeys(ctx, "u", nil) if err != nil { t.Errorf("Users.ListGPGKeys returned error: %v", err) } - want := []*GPGKey{{ID: Int64(1), PrimaryKeyID: Int64(2)}} - if !reflect.DeepEqual(keys, want) { + want := []*GPGKey{{ID: Ptr(int64(1)), PrimaryKeyID: Ptr(int64(2))}} + if !cmp.Equal(keys, want) { t.Errorf("Users.ListGPGKeys = %+v, want %+v", keys, want) } } func TestUsersService_ListGPGKeys_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Users.ListGPGKeys(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Users.ListGPGKeys(ctx, "%", nil) testURLParseError(t, err) } func TestUsersService_GetGPGKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/gpg_keys/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - key, _, err := client.Users.GetGPGKey(context.Background(), 1) + ctx := t.Context() + key, _, err := client.Users.GetGPGKey(ctx, 1) if err != nil { t.Errorf("Users.GetGPGKey returned error: %v", err) } - want := &GPGKey{ID: Int64(1)} - if !reflect.DeepEqual(key, want) { + want := &GPGKey{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { t.Errorf("Users.GetGPGKey = %+v, want %+v", key, want) } + + const methodName = "GetGPGKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetGPGKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetGPGKey(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_CreateGPGKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) input := ` -----BEGIN PGP PUBLIC KEY BLOCK----- @@ -98,40 +129,59 @@ mQINBFcEd9kBEACo54TDbGhKlXKWMvJgecEUKPPcv7XdnpKdGb3LRw5MvFwT0V0f -----END PGP PUBLIC KEY BLOCK-----` mux.HandleFunc("/user/gpg_keys", func(w http.ResponseWriter, r *http.Request) { - var gpgKey struct { + gpgKey := struct { ArmoredPublicKey *string `json:"armored_public_key,omitempty"` + }{ + ArmoredPublicKey: &input, } - json.NewDecoder(r.Body).Decode(&gpgKey) - testMethod(t, r, "POST") - if gpgKey.ArmoredPublicKey == nil || *gpgKey.ArmoredPublicKey != input { - t.Errorf("gpgKey = %+v, want %q", gpgKey, input) - } + testJSONBody(t, r, gpgKey) fmt.Fprint(w, `{"id":1}`) }) - gpgKey, _, err := client.Users.CreateGPGKey(context.Background(), input) + ctx := t.Context() + gpgKey, _, err := client.Users.CreateGPGKey(ctx, input) if err != nil { - t.Errorf("Users.GetGPGKey returned error: %v", err) + t.Errorf("Users.CreateGPGKey returned error: %v", err) } - want := &GPGKey{ID: Int64(1)} - if !reflect.DeepEqual(gpgKey, want) { + want := &GPGKey{ID: Ptr(int64(1))} + if !cmp.Equal(gpgKey, want) { t.Errorf("Users.GetGPGKey = %+v, want %+v", gpgKey, want) } + + const methodName = "CreateGPGKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.CreateGPGKey(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_DeleteGPGKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/gpg_keys/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/user/gpg_keys/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Users.DeleteGPGKey(context.Background(), 1) + ctx := t.Context() + _, err := client.Users.DeleteGPGKey(ctx, 1) if err != nil { t.Errorf("Users.DeleteGPGKey returned error: %v", err) } + + const methodName = "DeleteGPGKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeleteGPGKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteGPGKey(ctx, 1) + }) } diff --git a/github/users_keys.go b/github/users_keys.go index 39a4f54d6c2..c8952fbdb5f 100644 --- a/github/users_keys.go +++ b/github/users_keys.go @@ -17,7 +17,10 @@ type Key struct { URL *string `json:"url,omitempty"` Title *string `json:"title,omitempty"` ReadOnly *bool `json:"read_only,omitempty"` + Verified *bool `json:"verified,omitempty"` CreatedAt *Timestamp `json:"created_at,omitempty"` + AddedBy *string `json:"added_by,omitempty"` + LastUsed *Timestamp `json:"last_used,omitempty"` } func (k Key) String() string { @@ -27,26 +30,31 @@ func (k Key) String() string { // ListKeys lists the verified public keys for a user. Passing the empty // string will fetch keys for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user -func (s *UsersService) ListKeys(ctx context.Context, user string, opt *ListOptions) ([]*Key, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/keys?apiVersion=2022-11-28#list-public-keys-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/users/keys?apiVersion=2022-11-28#list-public-ssh-keys-for-the-authenticated-user +// +//meta:operation GET /user/keys +//meta:operation GET /users/{username}/keys +func (s *UsersService) ListKeys(ctx context.Context, user string, opts *ListOptions) ([]*Key, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/keys", user) } else { u = "user/keys" } - u, err := addOptions(u, opt) + u, err := addOptions(u, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } var keys []*Key - resp, err := s.client.Do(ctx, req, &keys) + resp, err := s.client.Do(req, &keys) if err != nil { return nil, resp, err } @@ -56,17 +64,19 @@ func (s *UsersService) ListKeys(ctx context.Context, user string, opt *ListOptio // GetKey fetches a single public key. // -// GitHub API docs: https://developer.github.com/v3/users/keys/#get-a-single-public-key +// GitHub API docs: https://docs.github.com/rest/users/keys?apiVersion=2022-11-28#get-a-public-ssh-key-for-the-authenticated-user +// +//meta:operation GET /user/keys/{key_id} func (s *UsersService) GetKey(ctx context.Context, id int64) (*Key, *Response, error) { u := fmt.Sprintf("user/keys/%v", id) - req, err := s.client.NewRequest("GET", u, nil) + req, err := s.client.NewRequest(ctx, "GET", u, nil) if err != nil { return nil, nil, err } - key := new(Key) - resp, err := s.client.Do(ctx, req, key) + var key *Key + resp, err := s.client.Do(req, &key) if err != nil { return nil, resp, err } @@ -76,17 +86,19 @@ func (s *UsersService) GetKey(ctx context.Context, id int64) (*Key, *Response, e // CreateKey adds a public key for the authenticated user. // -// GitHub API docs: https://developer.github.com/v3/users/keys/#create-a-public-key -func (s *UsersService) CreateKey(ctx context.Context, key *Key) (*Key, *Response, error) { +// GitHub API docs: https://docs.github.com/rest/users/keys?apiVersion=2022-11-28#create-a-public-ssh-key-for-the-authenticated-user +// +//meta:operation POST /user/keys +func (s *UsersService) CreateKey(ctx context.Context, body *Key) (*Key, *Response, error) { u := "user/keys" - req, err := s.client.NewRequest("POST", u, key) + req, err := s.client.NewRequest(ctx, "POST", u, body) if err != nil { return nil, nil, err } - k := new(Key) - resp, err := s.client.Do(ctx, req, k) + var k *Key + resp, err := s.client.Do(req, &k) if err != nil { return nil, resp, err } @@ -96,14 +108,16 @@ func (s *UsersService) CreateKey(ctx context.Context, key *Key) (*Key, *Response // DeleteKey deletes a public key. // -// GitHub API docs: https://developer.github.com/v3/users/keys/#delete-a-public-key +// GitHub API docs: https://docs.github.com/rest/users/keys?apiVersion=2022-11-28#delete-a-public-ssh-key-for-the-authenticated-user +// +//meta:operation DELETE /user/keys/{key_id} func (s *UsersService) DeleteKey(ctx context.Context, id int64) (*Response, error) { u := fmt.Sprintf("user/keys/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) if err != nil { return nil, err } - return s.client.Do(ctx, req, nil) + return s.client.Do(req, nil) } diff --git a/github/users_keys_test.go b/github/users_keys_test.go index 3115fdb2e14..245d99516a2 100644 --- a/github/users_keys_test.go +++ b/github/users_keys_test.go @@ -6,17 +6,16 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" + + "github.com/google/go-cmp/cmp" ) func TestUsersService_ListKeys_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/keys", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") @@ -25,104 +24,151 @@ func TestUsersService_ListKeys_authenticatedUser(t *testing.T) { }) opt := &ListOptions{Page: 2} - keys, _, err := client.Users.ListKeys(context.Background(), "", opt) + ctx := t.Context() + keys, _, err := client.Users.ListKeys(ctx, "", opt) if err != nil { t.Errorf("Users.ListKeys returned error: %v", err) } - want := []*Key{{ID: Int64(1)}} - if !reflect.DeepEqual(keys, want) { + want := []*Key{{ID: Ptr(int64(1))}} + if !cmp.Equal(keys, want) { t.Errorf("Users.ListKeys returned %+v, want %+v", keys, want) } + + const methodName = "ListKeys" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListKeys(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListKeys(ctx, "", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListKeys_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/keys", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `[{"id":1}]`) }) - keys, _, err := client.Users.ListKeys(context.Background(), "u", nil) + ctx := t.Context() + keys, _, err := client.Users.ListKeys(ctx, "u", nil) if err != nil { t.Errorf("Users.ListKeys returned error: %v", err) } - want := []*Key{{ID: Int64(1)}} - if !reflect.DeepEqual(keys, want) { + want := []*Key{{ID: Ptr(int64(1))}} + if !cmp.Equal(keys, want) { t.Errorf("Users.ListKeys returned %+v, want %+v", keys, want) } } func TestUsersService_ListKeys_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Users.ListKeys(context.Background(), "%", nil) + ctx := t.Context() + _, _, err := client.Users.ListKeys(ctx, "%", nil) testURLParseError(t, err) } func TestUsersService_GetKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/keys/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - key, _, err := client.Users.GetKey(context.Background(), 1) + ctx := t.Context() + key, _, err := client.Users.GetKey(ctx, 1) if err != nil { t.Errorf("Users.GetKey returned error: %v", err) } - want := &Key{ID: Int64(1)} - if !reflect.DeepEqual(key, want) { + want := &Key{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { t.Errorf("Users.GetKey returned %+v, want %+v", key, want) } + + const methodName = "GetKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetKey(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_CreateKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - input := &Key{Key: String("k"), Title: String("t")} + input := &Key{Key: Ptr("k"), Title: Ptr("t")} mux.HandleFunc("/user/keys", func(w http.ResponseWriter, r *http.Request) { - v := new(Key) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "POST") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - key, _, err := client.Users.CreateKey(context.Background(), input) + ctx := t.Context() + key, _, err := client.Users.CreateKey(ctx, input) if err != nil { - t.Errorf("Users.GetKey returned error: %v", err) + t.Errorf("Users.CreateKey returned error: %v", err) } - want := &Key{ID: Int64(1)} - if !reflect.DeepEqual(key, want) { - t.Errorf("Users.GetKey returned %+v, want %+v", key, want) + want := &Key{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { + t.Errorf("Users.CreateKey returned %+v, want %+v", key, want) } + + const methodName = "CreateKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.CreateKey(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_DeleteKey(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) - mux.HandleFunc("/user/keys/1", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/user/keys/1", func(_ http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") }) - _, err := client.Users.DeleteKey(context.Background(), 1) + ctx := t.Context() + _, err := client.Users.DeleteKey(ctx, 1) if err != nil { t.Errorf("Users.DeleteKey returned error: %v", err) } + + const methodName = "DeleteKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeleteKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteKey(ctx, 1) + }) } diff --git a/github/users_packages.go b/github/users_packages.go new file mode 100644 index 00000000000..8a1fad3b648 --- /dev/null +++ b/github/users_packages.go @@ -0,0 +1,265 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/url" +) + +// ListPackages lists the packages for a user. Passing the empty string for "user" will +// list packages for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#list-packages-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#list-packages-for-the-authenticated-users-namespace +// +//meta:operation GET /user/packages +//meta:operation GET /users/{username}/packages +func (s *UsersService) ListPackages(ctx context.Context, user string, opts *PackageListOptions) ([]*Package, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages", user) + } else { + u = "user/packages" + } + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var packages []*Package + resp, err := s.client.Do(req, &packages) + if err != nil { + return nil, resp, err + } + + return packages, resp, nil +} + +// GetPackage gets a package by name for a user. Passing the empty string for "user" will +// get the package for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#get-a-package-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#get-a-package-for-the-authenticated-user +// +//meta:operation GET /user/packages/{package_type}/{package_name} +//meta:operation GET /users/{username}/packages/{package_type}/{package_name} +func (s *UsersService) GetPackage(ctx context.Context, user, packageType, packageName string) (*Package, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages/%v/%v", user, packageType, url.PathEscape(packageName)) + } else { + u = fmt.Sprintf("user/packages/%v/%v", packageType, url.PathEscape(packageName)) + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var pack *Package + resp, err := s.client.Do(req, &pack) + if err != nil { + return nil, resp, err + } + + return pack, resp, nil +} + +// DeletePackage deletes a package from a user. Passing the empty string for "user" will +// delete the package for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#delete-a-package-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#delete-a-package-for-the-authenticated-user +// +//meta:operation DELETE /user/packages/{package_type}/{package_name} +//meta:operation DELETE /users/{username}/packages/{package_type}/{package_name} +func (s *UsersService) DeletePackage(ctx context.Context, user, packageType, packageName string) (*Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages/%v/%v", user, packageType, packageName) + } else { + u = fmt.Sprintf("user/packages/%v/%v", packageType, packageName) + } + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// RestorePackage restores a package to a user. Passing the empty string for "user" will +// restore the package for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#restore-a-package-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#restore-a-package-for-the-authenticated-user +// +//meta:operation POST /user/packages/{package_type}/{package_name}/restore +//meta:operation POST /users/{username}/packages/{package_type}/{package_name}/restore +func (s *UsersService) RestorePackage(ctx context.Context, user, packageType, packageName string) (*Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages/%v/%v/restore", user, packageType, packageName) + } else { + u = fmt.Sprintf("user/packages/%v/%v/restore", packageType, packageName) + } + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListPackageVersionsOptions specifies the optional parameters to the UsersService.ListPackageVersions. +type ListPackageVersionsOptions struct { + // State of package either "active" or "deleted". + State string `url:"state,omitempty"` + + ListOptions +} + +// ListPackageVersions gets all versions of a package for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#list-package-versions-for-a-package-owned-by-the-authenticated-user +// +//meta:operation GET /user/packages/{package_type}/{package_name}/versions +func (s *UsersService) ListPackageVersions(ctx context.Context, packageType, packageName string, opts *ListPackageVersionsOptions) ([]*PackageVersion, *Response, error) { + u := fmt.Sprintf("user/packages/%v/%v/versions", packageType, packageName) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var versions []*PackageVersion + resp, err := s.client.Do(req, &versions) + if err != nil { + return nil, resp, err + } + + return versions, resp, nil +} + +// ListUserPackageVersions returns package versions for a public package owned by a specified user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#list-package-versions-for-a-package-owned-by-a-user +// +//meta:operation GET /users/{username}/packages/{package_type}/{package_name}/versions +func (s *UsersService) ListUserPackageVersions(ctx context.Context, user, packageType, packageName string) ([]*PackageVersion, *Response, error) { + u := fmt.Sprintf("users/%v/packages/%v/%v/versions", user, packageType, packageName) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var versions []*PackageVersion + resp, err := s.client.Do(req, &versions) + if err != nil { + return nil, resp, err + } + + return versions, resp, nil +} + +// PackageGetVersion gets a specific version of a package for a user. Passing the empty string for "user" will +// get the version for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#get-a-package-version-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#get-a-package-version-for-the-authenticated-user +// +//meta:operation GET /user/packages/{package_type}/{package_name}/versions/{package_version_id} +//meta:operation GET /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id} +func (s *UsersService) PackageGetVersion(ctx context.Context, user, packageType, packageName string, packageVersionID int64) (*PackageVersion, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages/%v/%v/versions/%v", user, packageType, packageName, packageVersionID) + } else { + u = fmt.Sprintf("user/packages/%v/%v/versions/%v", packageType, packageName, packageVersionID) + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var version *PackageVersion + resp, err := s.client.Do(req, &version) + if err != nil { + return nil, resp, err + } + + return version, resp, nil +} + +// PackageDeleteVersion deletes a package version for a user. Passing the empty string for "user" will +// delete the version for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#delete-a-package-version-for-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#delete-package-version-for-a-user +// +//meta:operation DELETE /user/packages/{package_type}/{package_name}/versions/{package_version_id} +//meta:operation DELETE /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id} +func (s *UsersService) PackageDeleteVersion(ctx context.Context, user, packageType, packageName string, packageVersionID int64) (*Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages/%v/%v/versions/%v", user, packageType, packageName, packageVersionID) + } else { + u = fmt.Sprintf("user/packages/%v/%v/versions/%v", packageType, packageName, packageVersionID) + } + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// PackageRestoreVersion restores a package version to a user. Passing the empty string for "user" will +// restore the version for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#restore-a-package-version-for-the-authenticated-user +// +// GitHub API docs: https://docs.github.com/rest/packages/packages?apiVersion=2022-11-28#restore-package-version-for-a-user +// +//meta:operation POST /user/packages/{package_type}/{package_name}/versions/{package_version_id}/restore +//meta:operation POST /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore +func (s *UsersService) PackageRestoreVersion(ctx context.Context, user, packageType, packageName string, packageVersionID int64) (*Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/packages/%v/%v/versions/%v/restore", user, packageType, packageName, packageVersionID) + } else { + u = fmt.Sprintf("user/packages/%v/%v/versions/%v/restore", packageType, packageName, packageVersionID) + } + + req, err := s.client.NewRequest(ctx, "POST", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/users_packages_test.go b/github/users_packages_test.go new file mode 100644 index 00000000000..939344fd93d --- /dev/null +++ b/github/users_packages_test.go @@ -0,0 +1,695 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestUsersService_Authenticated_ListPackages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/packages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"package_type": "container", "visibility": "private"}) + fmt.Fprint(w, `[{ + "id": 197, + "name": "hello_docker", + "package_type": "container", + "version_count": 1, + "visibility": "private", + "url": "https://api.github.com/orgs/github/packages/container/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/orgs/github/packages/container/package/hello_docker" + }]`) + }) + + ctx := t.Context() + packages, _, err := client.Users.ListPackages(ctx, "", &PackageListOptions{PackageType: Ptr("container"), Visibility: Ptr("private")}) + if err != nil { + t.Errorf("Users.Authenticated_ListPackages returned error: %v", err) + } + + want := []*Package{{ + ID: Ptr(int64(197)), + Name: Ptr("hello_docker"), + PackageType: Ptr("container"), + VersionCount: Ptr(int64(1)), + Visibility: Ptr("private"), + URL: Ptr("https://api.github.com/orgs/github/packages/container/hello_docker"), + HTMLURL: Ptr("https://github.com/orgs/github/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + }} + if !cmp.Equal(packages, want) { + t.Errorf("Users.Authenticated_ListPackages returned %+v, want %+v", packages, want) + } + + const methodName = "ListPackages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListPackages(ctx, "\n", &PackageListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListPackages(ctx, "", &PackageListOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_specifiedUser_ListPackages(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/packages", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"package_type": "container", "visibility": "public"}) + fmt.Fprint(w, `[{ + "id": 197, + "name": "hello_docker", + "package_type": "container", + "version_count": 1, + "visibility": "public", + "url": "https://api.github.com/orgs/github/packages/container/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/orgs/github/packages/container/package/hello_docker" + }]`) + }) + + ctx := t.Context() + packages, _, err := client.Users.ListPackages(ctx, "u", &PackageListOptions{PackageType: Ptr("container"), Visibility: Ptr("public")}) + if err != nil { + t.Errorf("Users.specifiedUser_ListPackages returned error: %v", err) + } + + want := []*Package{{ + ID: Ptr(int64(197)), + Name: Ptr("hello_docker"), + PackageType: Ptr("container"), + VersionCount: Ptr(int64(1)), + Visibility: Ptr("public"), + URL: Ptr("https://api.github.com/orgs/github/packages/container/hello_docker"), + HTMLURL: Ptr("https://github.com/orgs/github/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + }} + if !cmp.Equal(packages, want) { + t.Errorf("Users.specifiedUser_ListPackages returned %+v, want %+v", packages, want) + } + + const methodName = "ListPackages" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListPackages(ctx, "\n", &PackageListOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListPackages(ctx, "", &PackageListOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_specifiedUser_GetPackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/packages/container/hello%2fhello_docker", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + _, err := io.WriteString(w, `{ + "id": 197, + "name": "hello/hello_docker", + "package_type": "container", + "version_count": 1, + "visibility": "private", + "url": "https://api.github.com/orgs/github/packages/container/hello%2Fhello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/orgs/github/packages/container/package/hello%2Fhello_docker" + }`) + if err != nil { + t.Fatal("Failed to write test response: ", err) + } + }) + + ctx := t.Context() + packages, _, err := client.Users.GetPackage(ctx, "u", "container", "hello/hello_docker") + if err != nil { + t.Errorf("Users.GetPackage returned error: %v", err) + } + + want := &Package{ + ID: Ptr(int64(197)), + Name: Ptr("hello/hello_docker"), + PackageType: Ptr("container"), + VersionCount: Ptr(int64(1)), + Visibility: Ptr("private"), + URL: Ptr("https://api.github.com/orgs/github/packages/container/hello%2Fhello_docker"), + HTMLURL: Ptr("https://github.com/orgs/github/packages/container/package/hello%2Fhello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + } + if !cmp.Equal(packages, want) { + t.Errorf("Users.specifiedUser_GetPackage returned %+v, want %+v", packages, want) + } + const methodName = "GetPackage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetPackage(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetPackage(ctx, "", "", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_Authenticated_GetPackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/packages/container/hello_docker", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 197, + "name": "hello_docker", + "package_type": "container", + "version_count": 1, + "visibility": "private", + "url": "https://api.github.com/orgs/github/packages/container/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/orgs/github/packages/container/package/hello_docker" + }`) + }) + + ctx := t.Context() + packages, _, err := client.Users.GetPackage(ctx, "", "container", "hello_docker") + if err != nil { + t.Errorf("Users.Authenticated_GetPackage returned error: %v", err) + } + + want := &Package{ + ID: Ptr(int64(197)), + Name: Ptr("hello_docker"), + PackageType: Ptr("container"), + VersionCount: Ptr(int64(1)), + Visibility: Ptr("private"), + URL: Ptr("https://api.github.com/orgs/github/packages/container/hello_docker"), + HTMLURL: Ptr("https://github.com/orgs/github/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + } + if !cmp.Equal(packages, want) { + t.Errorf("Users.Authenticated_GetPackage returned %+v, want %+v", packages, want) + } + + const methodName = "GetPackage" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetPackage(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetPackage(ctx, "", "", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_Authenticated_DeletePackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/packages/container/hello_docker", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Users.DeletePackage(ctx, "", "container", "hello_docker") + if err != nil { + t.Errorf("Users.Authenticated_DeletePackage returned error: %v", err) + } + + const methodName = "DeletePackage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeletePackage(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeletePackage(ctx, "", "", "") + }) +} + +func TestUsersService_specifiedUser_DeletePackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/packages/container/hello_docker", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Users.DeletePackage(ctx, "u", "container", "hello_docker") + if err != nil { + t.Errorf("Users.specifiedUser_DeletePackage returned error: %v", err) + } + + const methodName = "DeletePackage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeletePackage(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeletePackage(ctx, "", "", "") + }) +} + +func TestUsersService_Authenticated_RestorePackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/packages/container/hello_docker/restore", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + _, err := client.Users.RestorePackage(ctx, "", "container", "hello_docker") + if err != nil { + t.Errorf("Users.Authenticated_RestorePackage returned error: %v", err) + } + + const methodName = "RestorePackage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.RestorePackage(ctx, "\n", "", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.RestorePackage(ctx, "", "container", "hello_docker") + }) +} + +func TestUsersService_specifiedUser_RestorePackage(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/packages/container/hello_docker/restore", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + _, err := client.Users.RestorePackage(ctx, "u", "container", "hello_docker") + if err != nil { + t.Errorf("Users.specifiedUser_RestorePackage returned error: %v", err) + } + + const methodName = "RestorePackage" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.RestorePackage(ctx, "\n", "", "") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.RestorePackage(ctx, "", "container", "hello_docker") + }) +} + +func TestUsersService_ListPackageVersions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + m := `{ + "package_type": "container", + "container": { + "tags": [ + "latest" + ] + } + }` + + mux.HandleFunc("/user/packages/container/hello_docker/versions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "id": 45763, + "name": "sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9", + "url": "https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763", + "package_html_url": "https://github.com/users/octocat/packages/container/package/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763", + "metadata": `+m+` + }]`) + }) + + ctx := t.Context() + opts := &ListPackageVersionsOptions{ + ListOptions: ListOptions{Page: 1, PerPage: 2}, + } + packages, _, err := client.Users.ListPackageVersions(ctx, "container", "hello_docker", opts) + if err != nil { + t.Errorf("Users.ListPackageVersions returned error: %v", err) + } + + want := []*PackageVersion{{ + ID: Ptr(int64(45763)), + Name: Ptr("sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9"), + URL: Ptr("https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763"), + PackageHTMLURL: Ptr("https://github.com/users/octocat/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + HTMLURL: Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"), + Metadata: json.RawMessage(m), + }} + if !cmp.Equal(packages, want) { + t.Errorf("Users.ListPackageVersions returned %+v, want %+v", packages, want) + } + + const methodName = "ListPackageVersions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListPackageVersions(ctx, "\n", "\n", &ListPackageVersionsOptions{}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListPackageVersions(ctx, "", "", &ListPackageVersionsOptions{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_ListUserPackageVersions(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + m := `{ + "package_type": "container", + "container": { + "tags": [ + "latest" + ] + } + }` + + mux.HandleFunc("/users/u/packages/container/hello_docker/versions", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "id": 45763, + "name": "sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9", + "url": "https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763", + "package_html_url": "https://github.com/users/octocat/packages/container/package/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763", + "metadata": `+m+` + }]`) + }) + + ctx := t.Context() + packages, _, err := client.Users.ListUserPackageVersions(ctx, "u", "container", "hello_docker") + if err != nil { + t.Errorf("Users.ListUserPackageVersions returned error: %v", err) + } + + want := []*PackageVersion{{ + ID: Ptr(int64(45763)), + Name: Ptr("sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9"), + URL: Ptr("https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763"), + PackageHTMLURL: Ptr("https://github.com/users/octocat/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + HTMLURL: Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"), + Metadata: json.RawMessage(m), + }} + if !cmp.Equal(packages, want) { + t.Errorf("Users.ListUserPackageVersions returned %+v, want %+v", packages, want) + } + + const methodName = "ListUserPackageVersions" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListUserPackageVersions(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListUserPackageVersions(ctx, "", "", "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_Authenticated_PackageGetVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + m := `{ + "package_type": "container", + "container": { + "tags": [ + "latest" + ] + } + }` + + mux.HandleFunc("/user/packages/container/hello_docker/versions/45763", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, ` + { + "id": 45763, + "name": "sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9", + "url": "https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763", + "package_html_url": "https://github.com/users/octocat/packages/container/package/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763", + "metadata": `+m+` + }`) + }) + + ctx := t.Context() + packages, _, err := client.Users.PackageGetVersion(ctx, "", "container", "hello_docker", 45763) + if err != nil { + t.Errorf("Users.PackageGetVersion returned error: %v", err) + } + + want := &PackageVersion{ + ID: Ptr(int64(45763)), + Name: Ptr("sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9"), + URL: Ptr("https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763"), + PackageHTMLURL: Ptr("https://github.com/users/octocat/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + HTMLURL: Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"), + Metadata: json.RawMessage(m), + } + if !cmp.Equal(packages, want) { + t.Errorf("Users.Authenticated_PackageGetVersion returned %+v, want %+v", packages, want) + } + + const methodName = "PackageGetVersion" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.PackageGetVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.PackageGetVersion(ctx, "", "", "", 45763) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_specifiedUser_PackageGetVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + m := `{ + "package_type": "container", + "container": { + "tags": [ + "latest" + ] + } + }` + + mux.HandleFunc("/users/u/packages/container/hello_docker/versions/45763", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, ` + { + "id": 45763, + "name": "sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9", + "url": "https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763", + "package_html_url": "https://github.com/users/octocat/packages/container/package/hello_docker", + "created_at": `+referenceTimeStr+`, + "updated_at": `+referenceTimeStr+`, + "html_url": "https://github.com/users/octocat/packages/container/hello_docker/45763", + "metadata": `+m+` + }`) + }) + + ctx := t.Context() + packages, _, err := client.Users.PackageGetVersion(ctx, "u", "container", "hello_docker", 45763) + if err != nil { + t.Errorf("Users.specifiedUser_PackageGetVersion returned error: %v", err) + } + + want := &PackageVersion{ + ID: Ptr(int64(45763)), + Name: Ptr("sha256:08a44bab0bddaddd8837a8b381aebc2e4b933768b981685a9e088360af0d3dd9"), + URL: Ptr("https://api.github.com/users/octocat/packages/container/hello_docker/versions/45763"), + PackageHTMLURL: Ptr("https://github.com/users/octocat/packages/container/package/hello_docker"), + CreatedAt: &Timestamp{referenceTime}, + UpdatedAt: &Timestamp{referenceTime}, + HTMLURL: Ptr("https://github.com/users/octocat/packages/container/hello_docker/45763"), + Metadata: json.RawMessage(m), + } + if !cmp.Equal(packages, want) { + t.Errorf("Users.specifiedUser_PackageGetVersion returned %+v, want %+v", packages, want) + } + + const methodName = "PackageGetVersion" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.PackageGetVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.PackageGetVersion(ctx, "", "", "", 45763) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_Authenticated_PackageDeleteVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/packages/container/hello_docker/versions/45763", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Users.PackageDeleteVersion(ctx, "", "container", "hello_docker", 45763) + if err != nil { + t.Errorf("Users.Authenticated_PackageDeleteVersion returned error: %v", err) + } + + const methodName = "PackageDeleteVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.PackageDeleteVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.PackageDeleteVersion(ctx, "", "", "", 45763) + }) +} + +func TestUsersService_specifiedUser_PackageDeleteVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/packages/container/hello_docker/versions/45763", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Users.PackageDeleteVersion(ctx, "u", "container", "hello_docker", 45763) + if err != nil { + t.Errorf("Users.specifiedUser_PackageDeleteVersion returned error: %v", err) + } + + const methodName = "PackageDeleteVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.PackageDeleteVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.PackageDeleteVersion(ctx, "", "", "", 45763) + }) +} + +func TestUsersService_Authenticated_PackageRestoreVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/packages/container/hello_docker/versions/45763/restore", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + _, err := client.Users.PackageRestoreVersion(ctx, "", "container", "hello_docker", 45763) + if err != nil { + t.Errorf("Users.Authenticated_PackageRestoreVersion returned error: %v", err) + } + + const methodName = "PackageRestoreVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.PackageRestoreVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.PackageRestoreVersion(ctx, "", "", "", 45763) + }) +} + +func TestUsersService_specifiedUser_PackageRestoreVersion(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/packages/container/hello_docker/versions/45763/restore", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + }) + + ctx := t.Context() + _, err := client.Users.PackageRestoreVersion(ctx, "u", "container", "hello_docker", 45763) + if err != nil { + t.Errorf("Users.specifiedUser_PackageRestoreVersion returned error: %v", err) + } + + const methodName = "PackageRestoreVersion" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.PackageRestoreVersion(ctx, "\n", "", "", 0) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.PackageRestoreVersion(ctx, "", "", "", 45763) + }) +} diff --git a/github/users_social_accounts.go b/github/users_social_accounts.go new file mode 100644 index 00000000000..ebef40265d9 --- /dev/null +++ b/github/users_social_accounts.go @@ -0,0 +1,110 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SocialAccount represents a social account linked to a user. +type SocialAccount struct { + Provider *string `json:"provider,omitempty"` + URL *string `json:"url,omitempty"` +} + +// socialAccountsRequest represents the request body for adding or deleting social accounts. +type socialAccountsRequest struct { + AccountURLs []string `json:"account_urls"` +} + +// ListSocialAccounts lists all social accounts for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts?apiVersion=2022-11-28#list-social-accounts-for-the-authenticated-user +// +//meta:operation GET /user/social_accounts +func (s *UsersService) ListSocialAccounts(ctx context.Context, opts *ListOptions) ([]*SocialAccount, *Response, error) { + u := "user/social_accounts" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var socialAccounts []*SocialAccount + resp, err := s.client.Do(req, &socialAccounts) + if err != nil { + return nil, resp, err + } + + return socialAccounts, resp, nil +} + +// AddSocialAccounts adds social accounts for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts?apiVersion=2022-11-28#add-social-accounts-for-the-authenticated-user +// +//meta:operation POST /user/social_accounts +func (s *UsersService) AddSocialAccounts(ctx context.Context, accountURLs []string) ([]*SocialAccount, *Response, error) { + u := "user/social_accounts" + req, err := s.client.NewRequest(ctx, "POST", u, &socialAccountsRequest{AccountURLs: accountURLs}) + if err != nil { + return nil, nil, err + } + + var accounts []*SocialAccount + resp, err := s.client.Do(req, &accounts) + if err != nil { + return nil, resp, err + } + + return accounts, resp, nil +} + +// DeleteSocialAccounts deletes social accounts for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts?apiVersion=2022-11-28#delete-social-accounts-for-the-authenticated-user +// +//meta:operation DELETE /user/social_accounts +func (s *UsersService) DeleteSocialAccounts(ctx context.Context, accountURLs []string) (*Response, error) { + u := "user/social_accounts" + req, err := s.client.NewRequest(ctx, "DELETE", u, &socialAccountsRequest{AccountURLs: accountURLs}) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} + +// ListUserSocialAccounts lists all social accounts for a user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts?apiVersion=2022-11-28#list-social-accounts-for-a-user +// +//meta:operation GET /users/{username}/social_accounts +func (s *UsersService) ListUserSocialAccounts(ctx context.Context, username string, opts *ListOptions) ([]*SocialAccount, *Response, error) { + u := fmt.Sprintf("users/%v/social_accounts", username) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var addedAccounts []*SocialAccount + resp, err := s.client.Do(req, &addedAccounts) + if err != nil { + return nil, resp, err + } + + return addedAccounts, resp, nil +} diff --git a/github/users_social_accounts_test.go b/github/users_social_accounts_test.go new file mode 100644 index 00000000000..5cb8631593d --- /dev/null +++ b/github/users_social_accounts_test.go @@ -0,0 +1,151 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestUsersService_ListSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + mux.HandleFunc("/user/social_accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "provider": "example", + "url": "https://example.com" + }]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + accounts, _, err := client.Users.ListSocialAccounts(ctx, opt) + if err != nil { + t.Errorf("Users.ListSocialAccounts returned error: %v", err) + } + + want := []*SocialAccount{{Provider: Ptr("example"), URL: Ptr("https://example.com")}} + if !cmp.Equal(accounts, want) { + t.Errorf("Users.ListSocialAccounts returned %#v, want %#v", accounts, want) + } + + const methodName = "ListSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListSocialAccounts(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_AddSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + input := socialAccountsRequest{ + AccountURLs: []string{"https://example.com"}, + } + + mux.HandleFunc("/user/social_accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `[{"provider":"example","url":"https://example.com"}]`) + }) + + ctx := t.Context() + accounts, _, err := client.Users.AddSocialAccounts(ctx, input.AccountURLs) + if err != nil { + t.Errorf("Users.AddSocialAccounts returned error: %v", err) + } + + want := []*SocialAccount{ + {Provider: Ptr("example"), URL: Ptr("https://example.com")}, + } + if !cmp.Equal(accounts, want) { + t.Errorf("Users.AddSocialAccounts returned %#v, want %#v", accounts, want) + } + + const methodName = "AddSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.AddSocialAccounts(ctx, input.AccountURLs) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_DeleteSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + input := socialAccountsRequest{ + AccountURLs: []string{"https://example.com"}, + } + + mux.HandleFunc("/user/social_accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + testJSONBody(t, r, input) + w.WriteHeader(http.StatusNoContent) + }) + + ctx := t.Context() + _, err := client.Users.DeleteSocialAccounts(ctx, input.AccountURLs) + if err != nil { + t.Errorf("Users.DeleteSocialAccounts returned error: %v", err) + } + + const methodName = "DeleteSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteSocialAccounts(ctx, input.AccountURLs) + }) +} + +func TestUsersService_ListUserSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/social_accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "provider": "example", + "url": "https://example.com" + }]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + accounts, _, err := client.Users.ListUserSocialAccounts(ctx, "u", opt) + if err != nil { + t.Errorf("Users.ListUserSocialAccounts returned error: %v", err) + } + + want := []*SocialAccount{{Provider: Ptr("example"), URL: Ptr("https://example.com")}} + if !cmp.Equal(accounts, want) { + t.Errorf("Users.ListUserSocialAccounts returned %#v, want %#v", accounts, want) + } + + const methodName = "ListUserSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListUserSocialAccounts(ctx, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} diff --git a/github/users_ssh_signing_keys.go b/github/users_ssh_signing_keys.go new file mode 100644 index 00000000000..a25deabdc21 --- /dev/null +++ b/github/users_ssh_signing_keys.go @@ -0,0 +1,118 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// SSHSigningKey represents a public SSH key used to sign git commits. +type SSHSigningKey struct { + ID *int64 `json:"id,omitempty"` + Key *string `json:"key,omitempty"` + Title *string `json:"title,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + +func (k SSHSigningKey) String() string { + return Stringify(k) +} + +// ListSSHSigningKeys lists the SSH signing keys for a user. Passing an empty +// username string will fetch SSH signing keys for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/ssh-signing-keys?apiVersion=2022-11-28#list-ssh-signing-keys-for-a-user +// +// GitHub API docs: https://docs.github.com/rest/users/ssh-signing-keys?apiVersion=2022-11-28#list-ssh-signing-keys-for-the-authenticated-user +// +//meta:operation GET /user/ssh_signing_keys +//meta:operation GET /users/{username}/ssh_signing_keys +func (s *UsersService) ListSSHSigningKeys(ctx context.Context, user string, opts *ListOptions) ([]*SSHSigningKey, *Response, error) { + var u string + if user != "" { + u = fmt.Sprintf("users/%v/ssh_signing_keys", user) + } else { + u = "user/ssh_signing_keys" + } + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var keys []*SSHSigningKey + resp, err := s.client.Do(req, &keys) + if err != nil { + return nil, resp, err + } + + return keys, resp, nil +} + +// GetSSHSigningKey fetches a single SSH signing key for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/ssh-signing-keys?apiVersion=2022-11-28#get-an-ssh-signing-key-for-the-authenticated-user +// +//meta:operation GET /user/ssh_signing_keys/{ssh_signing_key_id} +func (s *UsersService) GetSSHSigningKey(ctx context.Context, id int64) (*SSHSigningKey, *Response, error) { + u := fmt.Sprintf("user/ssh_signing_keys/%v", id) + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var key *SSHSigningKey + resp, err := s.client.Do(req, &key) + if err != nil { + return nil, resp, err + } + + return key, resp, nil +} + +// CreateSSHSigningKey adds a SSH signing key for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/ssh-signing-keys?apiVersion=2022-11-28#create-a-ssh-signing-key-for-the-authenticated-user +// +//meta:operation POST /user/ssh_signing_keys +func (s *UsersService) CreateSSHSigningKey(ctx context.Context, body *Key) (*SSHSigningKey, *Response, error) { + u := "user/ssh_signing_keys" + + req, err := s.client.NewRequest(ctx, "POST", u, body) + if err != nil { + return nil, nil, err + } + + var k *SSHSigningKey + resp, err := s.client.Do(req, &k) + if err != nil { + return nil, resp, err + } + + return k, resp, nil +} + +// DeleteSSHSigningKey deletes a SSH signing key for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/ssh-signing-keys?apiVersion=2022-11-28#delete-an-ssh-signing-key-for-the-authenticated-user +// +//meta:operation DELETE /user/ssh_signing_keys/{ssh_signing_key_id} +func (s *UsersService) DeleteSSHSigningKey(ctx context.Context, id int64) (*Response, error) { + u := fmt.Sprintf("user/ssh_signing_keys/%v", id) + + req, err := s.client.NewRequest(ctx, "DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(req, nil) +} diff --git a/github/users_ssh_signing_keys_test.go b/github/users_ssh_signing_keys_test.go new file mode 100644 index 00000000000..f4c9c7f7a52 --- /dev/null +++ b/github/users_ssh_signing_keys_test.go @@ -0,0 +1,174 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestUsersService_ListSSHSigningKeys_authenticatedUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/ssh_signing_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{"id":1}]`) + }) + + opt := &ListOptions{Page: 2} + ctx := t.Context() + keys, _, err := client.Users.ListSSHSigningKeys(ctx, "", opt) + if err != nil { + t.Errorf("Users.ListSSHSigningKeys returned error: %v", err) + } + + want := []*SSHSigningKey{{ID: Ptr(int64(1))}} + if !cmp.Equal(keys, want) { + t.Errorf("Users.ListSSHSigningKeys returned %+v, want %+v", keys, want) + } + + const methodName = "ListSSHSigningKeys" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.ListSSHSigningKeys(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListSSHSigningKeys(ctx, "", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_ListSSHSigningKeys_specifiedUser(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/ssh_signing_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{"id":1}]`) + }) + + ctx := t.Context() + keys, _, err := client.Users.ListSSHSigningKeys(ctx, "u", nil) + if err != nil { + t.Errorf("Users.ListSSHSigningKeys returned error: %v", err) + } + + want := []*SSHSigningKey{{ID: Ptr(int64(1))}} + if !cmp.Equal(keys, want) { + t.Errorf("Users.ListSSHSigningKeys returned %+v, want %+v", keys, want) + } +} + +func TestUsersService_ListSSHSigningKeys_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Users.ListSSHSigningKeys(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestUsersService_GetSSHSigningKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/ssh_signing_keys/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + key, _, err := client.Users.GetSSHSigningKey(ctx, 1) + if err != nil { + t.Errorf("Users.GetSSHSigningKey returned error: %v", err) + } + + want := &SSHSigningKey{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { + t.Errorf("Users.GetSSHSigningKey returned %+v, want %+v", key, want) + } + + const methodName = "GetSSHSigningKey" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetSSHSigningKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetSSHSigningKey(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_CreateSSHSigningKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + input := &Key{Key: Ptr("k"), Title: Ptr("t")} + + mux.HandleFunc("/user/ssh_signing_keys", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testJSONBody(t, r, input) + fmt.Fprint(w, `{"id":1}`) + }) + + ctx := t.Context() + key, _, err := client.Users.CreateSSHSigningKey(ctx, input) + if err != nil { + t.Errorf("Users.CreateSSHSigningKey returned error: %v", err) + } + + want := &SSHSigningKey{ID: Ptr(int64(1))} + if !cmp.Equal(key, want) { + t.Errorf("Users.CreateSSHSigningKey returned %+v, want %+v", key, want) + } + + const methodName = "CreateKey" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.CreateKey(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_DeleteSSHSigningKey(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + + mux.HandleFunc("/user/ssh_signing_keys/1", func(_ http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := t.Context() + _, err := client.Users.DeleteSSHSigningKey(ctx, 1) + if err != nil { + t.Errorf("Users.DeleteSSHSigningKey returned error: %v", err) + } + + const methodName = "DeleteSSHSigningKey" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeleteSSHSigningKey(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteSSHSigningKey(ctx, 1) + }) +} diff --git a/github/users_test.go b/github/users_test.go index 8fa95a227b5..7cf364c183c 100644 --- a/github/users_test.go +++ b/github/users_test.go @@ -6,259 +6,310 @@ package github import ( - "context" - "encoding/json" "fmt" "net/http" - "reflect" "testing" -) -func TestUser_marshall(t *testing.T) { - testJSONMarshal(t, &User{}, "{}") - - u := &User{ - Login: String("l"), - ID: Int64(1), - URL: String("u"), - AvatarURL: String("a"), - GravatarID: String("g"), - Name: String("n"), - Company: String("c"), - Blog: String("b"), - Location: String("l"), - Email: String("e"), - Hireable: Bool(true), - PublicRepos: Int(1), - Followers: Int(1), - Following: Int(1), - CreatedAt: &Timestamp{referenceTime}, - SuspendedAt: &Timestamp{referenceTime}, - } - want := `{ - "login": "l", - "id": 1, - "avatar_url": "a", - "gravatar_id": "g", - "name": "n", - "company": "c", - "blog": "b", - "location": "l", - "email": "e", - "hireable": true, - "public_repos": 1, - "followers": 1, - "following": 1, - "created_at": ` + referenceTimeStr + `, - "suspended_at": ` + referenceTimeStr + `, - "url": "u" - }` - testJSONMarshal(t, u, want) -} + "github.com/google/go-cmp/cmp" +) func TestUsersService_Get_authenticatedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - user, _, err := client.Users.Get(context.Background(), "") + ctx := t.Context() + user, _, err := client.Users.Get(ctx, "") if err != nil { t.Errorf("Users.Get returned error: %v", err) } - want := &User{ID: Int64(1)} - if !reflect.DeepEqual(user, want) { + want := &User{ID: Ptr(int64(1))} + if !cmp.Equal(user, want) { t.Errorf("Users.Get returned %+v, want %+v", user, want) } + + const methodName = "Get" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.Get(ctx, "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.Get(ctx, "") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_Get_specifiedUser(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - user, _, err := client.Users.Get(context.Background(), "u") + ctx := t.Context() + user, _, err := client.Users.Get(ctx, "u") if err != nil { t.Errorf("Users.Get returned error: %v", err) } - want := &User{ID: Int64(1)} - if !reflect.DeepEqual(user, want) { + want := &User{ID: Ptr(int64(1))} + if !cmp.Equal(user, want) { t.Errorf("Users.Get returned %+v, want %+v", user, want) } } func TestUsersService_Get_invalidUser(t *testing.T) { - client, _, _, teardown := setup() - defer teardown() + t.Parallel() + client, _, _ := setup(t) - _, _, err := client.Users.Get(context.Background(), "%") + ctx := t.Context() + _, _, err := client.Users.Get(ctx, "%") testURLParseError(t, err) } func TestUsersService_GetByID(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") fmt.Fprint(w, `{"id":1}`) }) - user, _, err := client.Users.GetByID(context.Background(), 1) + ctx := t.Context() + user, _, err := client.Users.GetByID(ctx, 1) if err != nil { t.Fatalf("Users.GetByID returned error: %v", err) } - want := &User{ID: Int64(1)} - if !reflect.DeepEqual(user, want) { + want := &User{ID: Ptr(int64(1))} + if !cmp.Equal(user, want) { t.Errorf("Users.GetByID returned %+v, want %+v", user, want) } + + const methodName = "GetByID" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetByID(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetByID(ctx, 1) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } -func TestUsersService_Edit(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() +func TestUsersService_Update(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) - input := &User{Name: String("n")} + input := UserUpdateRequest{Name: Ptr("n")} mux.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { - v := new(User) - json.NewDecoder(r.Body).Decode(v) - testMethod(t, r, "PATCH") - if !reflect.DeepEqual(v, input) { - t.Errorf("Request body = %+v, want %+v", v, input) - } - + testJSONBody(t, r, input) fmt.Fprint(w, `{"id":1}`) }) - user, _, err := client.Users.Edit(context.Background(), input) + ctx := t.Context() + user, _, err := client.Users.Update(ctx, input) if err != nil { - t.Errorf("Users.Edit returned error: %v", err) + t.Errorf("Users.Update returned error: %v", err) } - want := &User{ID: Int64(1)} - if !reflect.DeepEqual(user, want) { - t.Errorf("Users.Edit returned %+v, want %+v", user, want) + want := &User{ID: Ptr(int64(1))} + if !cmp.Equal(user, want) { + t.Errorf("Users.Update returned %+v, want %+v", user, want) } + + const methodName = "Update" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.Update(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_GetHovercard(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users/u/hovercard", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeHovercardPreview) testFormValues(t, r, values{"subject_type": "repository", "subject_id": "20180408"}) fmt.Fprint(w, `{"contexts": [{"message":"Owns this repository", "octicon": "repo"}]}`) }) opt := &HovercardOptions{SubjectType: "repository", SubjectID: "20180408"} - hovercard, _, err := client.Users.GetHovercard(context.Background(), "u", opt) + ctx := t.Context() + hovercard, _, err := client.Users.GetHovercard(ctx, "u", opt) if err != nil { t.Errorf("Users.GetHovercard returned error: %v", err) } - want := &Hovercard{Contexts: []*UserContext{{Message: String("Owns this repository"), Octicon: String("repo")}}} - if !reflect.DeepEqual(hovercard, want) { + want := &Hovercard{Contexts: []*UserContext{{Message: Ptr("Owns this repository"), Octicon: Ptr("repo")}}} + if !cmp.Equal(hovercard, want) { t.Errorf("Users.GetHovercard returned %+v, want %+v", hovercard, want) } + + const methodName = "GetHovercard" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Users.GetHovercard(ctx, "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.GetHovercard(ctx, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListAll(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testFormValues(t, r, values{"since": "1", "page": "2"}) + testFormValues(t, r, values{"since": "1", "per_page": "30"}) fmt.Fprint(w, `[{"id":2}]`) }) - opt := &UserListOptions{1, ListOptions{Page: 2}} - users, _, err := client.Users.ListAll(context.Background(), opt) + opt := &UserListOptions{Since: 1, PerPage: 30} + ctx := t.Context() + users, _, err := client.Users.ListAll(ctx, opt) if err != nil { - t.Errorf("Users.Get returned error: %v", err) + t.Errorf("Users.ListAll returned error: %v", err) } - want := []*User{{ID: Int64(2)}} - if !reflect.DeepEqual(users, want) { + want := []*User{{ID: Ptr(int64(2))}} + if !cmp.Equal(users, want) { t.Errorf("Users.ListAll returned %+v, want %+v", users, want) } + + const methodName = "ListAll" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListAll(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListInvitations(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/repository_invitations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) }) - got, _, err := client.Users.ListInvitations(context.Background(), nil) + ctx := t.Context() + got, _, err := client.Users.ListInvitations(ctx, nil) if err != nil { t.Errorf("Users.ListInvitations returned error: %v", err) } - want := []*RepositoryInvitation{{ID: Int64(1)}, {ID: Int64(2)}} - if !reflect.DeepEqual(got, want) { + want := []*RepositoryInvitation{{ID: Ptr(int64(1))}, {ID: Ptr(int64(2))}} + if !cmp.Equal(got, want) { t.Errorf("Users.ListInvitations = %+v, want %+v", got, want) } + + const methodName = "ListInvitations" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListInvitations(ctx, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) } func TestUsersService_ListInvitations_withOptions(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/repository_invitations", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testFormValues(t, r, values{ "page": "2", }) - fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + fmt.Fprint(w, `[{"id":1}, {"id":2}]`) }) - _, _, err := client.Users.ListInvitations(context.Background(), &ListOptions{Page: 2}) + ctx := t.Context() + _, _, err := client.Users.ListInvitations(ctx, &ListOptions{Page: 2}) if err != nil { t.Errorf("Users.ListInvitations returned error: %v", err) } } + func TestUsersService_AcceptInvitation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/repository_invitations/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Users.AcceptInvitation(context.Background(), 1); err != nil { + ctx := t.Context() + if _, err := client.Users.AcceptInvitation(ctx, 1); err != nil { t.Errorf("Users.AcceptInvitation returned error: %v", err) } + + const methodName = "AcceptInvitation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.AcceptInvitation(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.AcceptInvitation(ctx, 1) + }) } func TestUsersService_DeclineInvitation(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() + t.Parallel() + client, mux, _ := setup(t) mux.HandleFunc("/user/repository_invitations/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Users.DeclineInvitation(context.Background(), 1); err != nil { + ctx := t.Context() + if _, err := client.Users.DeclineInvitation(ctx, 1); err != nil { t.Errorf("Users.DeclineInvitation returned error: %v", err) } + + const methodName = "DeclineInvitation" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Users.DeclineInvitation(ctx, -1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeclineInvitation(ctx, 1) + }) } diff --git a/github/with_appengine.go b/github/with_appengine.go deleted file mode 100644 index 59ce26b2ea3..00000000000 --- a/github/with_appengine.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build appengine - -// This file provides glue for making github work on App Engine. - -package github - -import ( - "context" - "net/http" -) - -func withContext(ctx context.Context, req *http.Request) *http.Request { - // No-op because App Engine adds context to a request differently. - return req -} diff --git a/github/without_appengine.go b/github/without_appengine.go deleted file mode 100644 index 6f8fdac5603..00000000000 --- a/github/without_appengine.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine - -// This file provides glue for making github work without App Engine. - -package github - -import ( - "context" - "net/http" -) - -func withContext(ctx context.Context, req *http.Request) *http.Request { - return req.WithContext(ctx) -} diff --git a/go.mod b/go.mod index f6198d3c95c..229ed21bb29 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,8 @@ -module github.com/google/go-github/v28 +module github.com/google/go-github/v90 + +go 1.25.0 require ( - github.com/golang/protobuf v1.2.0 // indirect - github.com/google/go-querystring v1.0.0 - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 - golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect - golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be - golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect - google.golang.org/appengine v1.1.0 + github.com/google/go-cmp v0.7.0 + github.com/google/go-querystring v1.2.0 ) - -go 1.13 diff --git a/go.sum b/go.sum index 927256d3d86..68bb006efb3 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,5 @@ -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= diff --git a/openapi_operations.yaml b/openapi_operations.yaml new file mode 100644 index 00000000000..5e985b15ac9 --- /dev/null +++ b/openapi_operations.yaml @@ -0,0 +1,9168 @@ +operations: + - name: GET /enterprises/{enterprise}/copilot/metrics + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-an-enterprise + deprecated: true + - name: GET /enterprises/{enterprise}/team/{team_slug}/copilot/metrics + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-an-enterprise-team + deprecated: true + - name: POST /hub + documentation_url: https://docs.github.com/webhooks/about-webhooks-for-repositories#pubsubhubbub + - name: GET /organizations/{organization_id} + - name: DELETE /organizations/{organization_id}/team/{team_id} + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#delete-a-team + - name: GET /organizations/{organization_id}/team/{team_id} + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#get-a-team-by-name + - name: PATCH /organizations/{organization_id}/team/{team_id} + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#update-a-team + - name: GET /organizations/{organization_id}/team/{team_id}/invitations + documentation_url: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#list-pending-team-invitations + - name: GET /organizations/{organization_id}/team/{team_id}/members + documentation_url: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#list-team-members + - name: DELETE /organizations/{organization_id}/team/{team_id}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#remove-team-membership-for-a-user + - name: GET /organizations/{organization_id}/team/{team_id}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#get-team-membership-for-a-user + - name: PUT /organizations/{organization_id}/team/{team_id}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members?apiVersion=2022-11-28#add-or-update-team-membership-for-a-user + - name: GET /organizations/{organization_id}/team/{team_id}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#list-team-projects + deprecated: true + - name: DELETE /organizations/{organization_id}/team/{team_id}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#remove-a-project-from-a-team + deprecated: true + - name: GET /organizations/{organization_id}/team/{team_id}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#check-team-permissions-for-a-project + deprecated: true + - name: PUT /organizations/{organization_id}/team/{team_id}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#add-or-update-team-project-permissions + deprecated: true + - name: GET /organizations/{organization_id}/team/{team_id}/repos + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-team-repositories + - name: DELETE /organizations/{organization_id}/team/{team_id}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#remove-a-repository-from-a-team + - name: GET /organizations/{organization_id}/team/{team_id}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#check-team-permissions-for-a-repository + - name: PUT /organizations/{organization_id}/team/{team_id}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#add-or-update-team-repository-permissions + - name: GET /organizations/{organization_id}/team/{team_id}/team-sync/group-mappings + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#list-idp-groups-for-a-team + - name: PATCH /organizations/{organization_id}/team/{team_id}/team-sync/group-mappings + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync?apiVersion=2022-11-28#create-or-update-idp-group-connections + - name: GET /organizations/{organization_id}/team/{team_id}/teams + documentation_url: https://docs.github.com/rest/teams/teams?apiVersion=2022-11-28#list-child-teams + - name: GET /orgs/{org}/copilot/metrics + documentation_url: https://docs.github.com/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-an-organization + deprecated: true + - name: GET /orgs/{org}/settings/billing/packages + documentation_url: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-github-packages-billing-for-an-organization + deprecated: true + - name: GET /orgs/{org}/settings/billing/shared-storage + documentation_url: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-shared-storage-billing-for-an-organization + deprecated: true + - name: GET /orgs/{org}/team/{team_slug}/copilot/metrics + documentation_url: https://docs.github.com/rest/copilot/copilot-metrics?apiVersion=2022-11-28#get-copilot-metrics-for-a-team + deprecated: true + - name: GET /repos/{owner}/{repo}/import/issues + documentation_url: https://gist.github.com/jonmagic/5282384165e0f86ef105#check-status-of-multiple-issues + - name: POST /repos/{owner}/{repo}/import/issues + documentation_url: https://gist.github.com/jonmagic/5282384165e0f86ef105#start-an-issue-import + - name: GET /repos/{owner}/{repo}/import/issues/{issue_number} + documentation_url: https://gist.github.com/jonmagic/5282384165e0f86ef105#import-status-request + - name: GET /repositories/{repository_id} + - name: GET /repositories/{repository_id}/installation + - name: GET /users/{username}/settings/billing/packages + documentation_url: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-github-packages-billing-for-a-user + deprecated: true + - name: GET /users/{username}/settings/billing/shared-storage + documentation_url: https://docs.github.com/rest/billing/billing?apiVersion=2022-11-28#get-shared-storage-billing-for-a-user + deprecated: true +operation_overrides: + - name: GET /meta + documentation_url: https://docs.github.com/rest/meta/meta#get-github-meta-information + - name: GET /orgs/{org}/private-registries + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#list-private-registries-for-an-organization + - name: POST /orgs/{org}/private-registries + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#create-a-private-registry-for-an-organization + - name: GET /orgs/{org}/private-registries/public-key + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#get-private-registries-public-key-for-an-organization + - name: DELETE /orgs/{org}/private-registries/{secret_name} + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#delete-a-private-registry-for-an-organization + - name: GET /orgs/{org}/private-registries/{secret_name} + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#get-a-private-registry-for-an-organization + - name: PATCH /orgs/{org}/private-registries/{secret_name} + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#update-a-private-registry-for-an-organization + - name: DELETE /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#delete-a-github-pages-site + - name: GET /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#get-a-github-pages-site + - name: POST /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#create-a-github-pages-site + - name: PUT /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#update-information-about-a-github-pages-site + - name: GET /repos/{owner}/{repo}/pages/builds + documentation_url: https://docs.github.com/rest/pages/pages#list-github-pages-builds + - name: POST /repos/{owner}/{repo}/pages/builds + documentation_url: https://docs.github.com/rest/pages/pages#request-a-github-pages-build + - name: GET /repos/{owner}/{repo}/pages/builds/{build_id} + documentation_url: https://docs.github.com/rest/pages/pages#get-github-pages-build +openapi_commit: e50419c4bb8f2d1d34735044bb3b410863dc0a10 +openapi_operations: + - name: GET / + documentation_url: https://docs.github.com/rest/meta/meta#github-api-root + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/global-webhooks#list-global-webhooks + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/global-webhooks#create-a-global-webhook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/hooks/{hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/global-webhooks#delete-a-global-webhook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/hooks/{hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/global-webhooks#get-a-global-webhook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/hooks/{hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/global-webhooks#update-a-global-webhook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/hooks/{hook_id}/pings + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/global-webhooks#ping-a-global-webhook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/keys + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#list-public-keys + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/keys/{key_ids} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-a-public-key + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/ldap/teams/{team_id}/mapping + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/ldap#update-ldap-mapping-for-a-team + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/ldap/teams/{team_id}/sync + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/ldap#sync-ldap-mapping-for-a-team + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/ldap/users/{username}/mapping + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/ldap#update-ldap-mapping-for-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/ldap/users/{username}/sync + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/ldap#sync-ldap-mapping-for-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/organizations + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/orgs#create-an-organization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/organizations/{org} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/orgs#update-an-organization-name + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/pre-receive-environments + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#list-pre-receive-environments + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/pre-receive-environments + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#create-a-pre-receive-environment + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/pre-receive-environments/{pre_receive_environment_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#delete-a-pre-receive-environment + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/pre-receive-environments/{pre_receive_environment_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#get-a-pre-receive-environment + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/pre-receive-environments/{pre_receive_environment_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#update-a-pre-receive-environment + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/pre-receive-environments/{pre_receive_environment_id}/downloads + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#start-a-pre-receive-environment-download + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/pre-receive-environments/{pre_receive_environment_id}/downloads/latest + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-environments#get-the-download-status-for-a-pre-receive-environment + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/pre-receive-hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-hooks#list-pre-receive-hooks + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/pre-receive-hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-hooks#create-a-pre-receive-hook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-hooks#delete-a-pre-receive-hook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-hooks#get-a-pre-receive-hook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/pre-receive-hooks#update-a-pre-receive-hook + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /admin/tokens + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#list-personal-access-tokens + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/tokens/{token_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-a-personal-access-token + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/users + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#create-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/users/{username} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /admin/users/{username} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#update-the-username-for-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /admin/users/{username}/authorizations + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#delete-an-impersonation-oauth-token + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /admin/users/{username}/authorizations + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#create-an-impersonation-oauth-token + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /advisories + documentation_url: https://docs.github.com/rest/security-advisories/global-advisories#list-global-security-advisories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /advisories/{ghsa_id} + documentation_url: https://docs.github.com/rest/security-advisories/global-advisories#get-a-global-security-advisory + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /agents/repos/{owner}/{repo}/tasks + documentation_url: https://docs.github.com/rest/agent-tasks/agent-tasks#list-tasks-for-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /agents/repos/{owner}/{repo}/tasks + documentation_url: https://docs.github.com/rest/agent-tasks/agent-tasks#start-a-task + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /agents/repos/{owner}/{repo}/tasks/{task_id} + documentation_url: https://docs.github.com/rest/agent-tasks/agent-tasks#get-a-task-by-repo + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /agents/tasks + documentation_url: https://docs.github.com/rest/agent-tasks/agent-tasks#list-tasks + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /agents/tasks/{task_id} + documentation_url: https://docs.github.com/rest/agent-tasks/agent-tasks#get-a-task-by-id + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /app + documentation_url: https://docs.github.com/rest/apps/apps#get-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /app-manifests/{code}/conversions + documentation_url: https://docs.github.com/rest/apps/apps#create-a-github-app-from-a-manifest + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /app/hook/config + documentation_url: https://docs.github.com/rest/apps/webhooks#get-a-webhook-configuration-for-an-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /app/hook/config + documentation_url: https://docs.github.com/rest/apps/webhooks#update-a-webhook-configuration-for-an-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /app/hook/deliveries + documentation_url: https://docs.github.com/rest/apps/webhooks#list-deliveries-for-an-app-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /app/hook/deliveries/{delivery_id} + documentation_url: https://docs.github.com/rest/apps/webhooks#get-a-delivery-for-an-app-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /app/hook/deliveries/{delivery_id}/attempts + documentation_url: https://docs.github.com/rest/apps/webhooks#redeliver-a-delivery-for-an-app-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /app/installation-requests + documentation_url: https://docs.github.com/rest/apps/apps#list-installation-requests-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /app/installations + documentation_url: https://docs.github.com/rest/apps/apps#list-installations-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /app/installations/{installation_id} + documentation_url: https://docs.github.com/rest/apps/apps#delete-an-installation-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /app/installations/{installation_id} + documentation_url: https://docs.github.com/rest/apps/apps#get-an-installation-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /app/installations/{installation_id}/access_tokens + documentation_url: https://docs.github.com/rest/apps/apps#create-an-installation-access-token-for-an-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /app/installations/{installation_id}/suspended + documentation_url: https://docs.github.com/rest/apps/apps#unsuspend-an-app-installation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /app/installations/{installation_id}/suspended + documentation_url: https://docs.github.com/rest/apps/apps#suspend-an-app-installation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /applications/grants + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#list-your-grants + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /applications/grants/{grant_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#delete-a-grant + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /applications/grants/{grant_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#get-a-single-grant + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /applications/{client_id}/grant + documentation_url: https://docs.github.com/rest/apps/oauth-applications#delete-an-app-authorization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /applications/{client_id}/grants/{access_token} + documentation_url: https://docs.github.com/enterprise-server@3.3/rest/reference/apps#revoke-a-grant-for-an-application + openapi_files: + - descriptions/ghes-3.3/ghes-3.3.json + deprecated: true + - name: DELETE /applications/{client_id}/token + documentation_url: https://docs.github.com/rest/apps/oauth-applications#delete-an-app-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /applications/{client_id}/token + documentation_url: https://docs.github.com/rest/apps/oauth-applications#reset-a-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /applications/{client_id}/token + documentation_url: https://docs.github.com/rest/apps/oauth-applications#check-a-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /applications/{client_id}/token/scoped + documentation_url: https://docs.github.com/rest/apps/apps#create-a-scoped-access-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /applications/{client_id}/tokens/{access_token} + documentation_url: https://docs.github.com/enterprise-server@3.3/rest/reference/apps#revoke-an-authorization-for-an-application + openapi_files: + - descriptions/ghes-3.3/ghes-3.3.json + deprecated: true + - name: GET /applications/{client_id}/tokens/{access_token} + documentation_url: https://docs.github.com/enterprise-server@3.3/rest/reference/apps#check-an-authorization + openapi_files: + - descriptions/ghes-3.3/ghes-3.3.json + deprecated: true + - name: POST /applications/{client_id}/tokens/{access_token} + documentation_url: https://docs.github.com/enterprise-server@3.3/rest/reference/apps#reset-an-authorization + openapi_files: + - descriptions/ghes-3.3/ghes-3.3.json + deprecated: true + - name: GET /apps/{app_slug} + documentation_url: https://docs.github.com/rest/apps/apps#get-an-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /assignments/{assignment_id} + documentation_url: https://docs.github.com/rest/classroom/classroom#closing-down---get-an-assignment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /assignments/{assignment_id}/accepted_assignments + documentation_url: https://docs.github.com/rest/classroom/classroom#closing-down---list-accepted-assignments-for-an-assignment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /assignments/{assignment_id}/grades + documentation_url: https://docs.github.com/rest/classroom/classroom#closing-down---get-assignment-grades + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /authorizations + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#list-your-authorizations + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: POST /authorizations + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#create-a-new-authorization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PUT /authorizations/clients/{client_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#get-or-create-an-authorization-for-a-specific-app + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PUT /authorizations/clients/{client_id}/{fingerprint} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#get-or-create-an-authorization-for-a-specific-app-and-fingerprint + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /authorizations/{authorization_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#delete-an-authorization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /authorizations/{authorization_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#get-a-single-authorization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PATCH /authorizations/{authorization_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/oauth-authorizations/oauth-authorizations#update-an-existing-authorization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /classrooms + documentation_url: https://docs.github.com/rest/classroom/classroom#closing-down---list-classrooms + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /classrooms/{classroom_id} + documentation_url: https://docs.github.com/rest/classroom/classroom#closing-down---get-a-classroom + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /classrooms/{classroom_id}/assignments + documentation_url: https://docs.github.com/rest/classroom/classroom#closing-down---list-assignments-for-a-classroom + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /codes_of_conduct + documentation_url: https://docs.github.com/rest/codes-of-conduct/codes-of-conduct#get-all-codes-of-conduct + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /codes_of_conduct/{key} + documentation_url: https://docs.github.com/rest/codes-of-conduct/codes-of-conduct#get-a-code-of-conduct + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /credentials/revoke + documentation_url: https://docs.github.com/rest/credentials/revoke#revoke-a-list-of-credentials + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /emojis + documentation_url: https://docs.github.com/rest/emojis/emojis#get-emojis + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise-installation/{enterprise_or_org}/server-statistics + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/admin-stats#get-github-enterprise-server-statistics + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprise/announcement + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/announcement#remove-the-global-announcement-banner + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/announcement + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/announcement#get-the-global-announcement-banner + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprise/announcement + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/announcement#set-the-global-announcement-banner + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/settings/license + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/licensing#get-license-information + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/all + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-all-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/comments + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-comment-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/gists + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-gist-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-hooks-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/issues + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-issue-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/milestones + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-milestone-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/orgs + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-organization-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/pages + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-pages-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/pulls + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-pull-request-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/repos + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-repository-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/security-products + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-security-products-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprise/stats/users + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/admin-stats#get-users-statistics + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/access-restrictions/disable + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprises#disable-access-restrictions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/access-restrictions/enable + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprises#enable-access-restrictions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/cache/retention-limit + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-retention-limit-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/actions/cache/retention-limit + documentation_url: https://docs.github.com/rest/actions/cache#set-github-actions-cache-retention-limit-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/cache/storage-limit + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-storage-limit-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/actions/cache/storage-limit + documentation_url: https://docs.github.com/rest/actions/cache#set-github-actions-cache-storage-limit-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/cache/usage + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/cache#get-github-actions-cache-usage-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/cache/usage-policy + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/actions/cache#get-github-actions-cache-usage-policy-for-an-enterprise + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/actions/cache/usage-policy + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/actions/cache#set-github-actions-cache-usage-policy-for-an-enterprise + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#list-github-hosted-runners-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/actions/hosted-runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#create-a-github-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/images/custom + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#list-custom-images-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#delete-a-custom-image-from-the-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-an-enterprise-custom-image-definition-for-github-actions-hosted-runners + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id}/versions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#list-image-versions-of-a-custom-image-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#delete-an-image-version-of-custom-image-from-the-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-an-image-version-of-an-enterprise-custom-image-for-github-actions-hosted-runners + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/images/github-owned + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-github-owned-images-for-github-hosted-runners-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/images/partner + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-partner-images-for-github-hosted-runners-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/limits + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-limits-on-github-hosted-runners-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/machine-sizes + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-github-hosted-runners-machine-specs-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/platforms + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-platforms-for-github-hosted-runners-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#delete-a-github-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-a-github-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#update-a-github-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/actions/oidc/customization/issuer + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/oidc#set-the-github-actions-oidc-custom-issuer-policy-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/oidc/customization/properties/repo + documentation_url: https://docs.github.com/rest/actions/oidc#list-oidc-custom-property-inclusions-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/actions/oidc/customization/properties/repo + documentation_url: https://docs.github.com/rest/actions/oidc#create-an-oidc-custom-property-inclusion-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/actions/oidc/customization/properties/repo/{custom_property_name} + documentation_url: https://docs.github.com/rest/actions/oidc#delete-an-oidc-custom-property-inclusion-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/permissions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-github-actions-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-github-actions-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/artifact-and-log-retention + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-artifact-and-log-retention-settings-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/artifact-and-log-retention + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-artifact-and-log-retention-settings-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/fork-pr-contributor-approval + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-fork-pr-contributor-approval-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/fork-pr-contributor-approval + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-fork-pr-contributor-approval-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/fork-pr-workflows-private-repos + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-private-repo-fork-pr-workflow-settings-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/fork-pr-workflows-private-repos + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-private-repo-fork-pr-workflow-settings-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/organizations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#list-selected-organizations-enabled-for-github-actions-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/organizations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-selected-organizations-enabled-for-github-actions-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/permissions/organizations/{org_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#disable-a-selected-organization-for-github-actions-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/organizations/{org_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#enable-a-selected-organization-for-github-actions-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/selected-actions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-allowed-actions-and-reusable-workflows-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/selected-actions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-allowed-actions-and-reusable-workflows-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/self-hosted-runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-self-hosted-runners-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/self-hosted-runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-self-hosted-runners-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/permissions/workflow + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#get-default-workflow-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/permissions/workflow + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/permissions#set-default-workflow-permissions-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runner-groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#list-self-hosted-runner-groups-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/actions/runner-groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#create-a-self-hosted-runner-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/runner-groups/{runner_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#delete-a-self-hosted-runner-group-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#get-a-self-hosted-runner-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/actions/runner-groups/{runner_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#update-a-self-hosted-runner-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#list-organization-access-to-a-self-hosted-runner-group-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#set-organization-access-for-a-self-hosted-runner-group-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations/{org_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#remove-organization-access-to-a-self-hosted-runner-group-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations/{org_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#add-organization-access-to-a-self-hosted-runner-group-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#list-self-hosted-runners-in-a-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#set-self-hosted-runners-in-a-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners/{runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#remove-a-self-hosted-runner-from-a-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners/{runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runner-groups#add-a-self-hosted-runner-to-a-group-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runners + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#list-self-hosted-runners-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runners/deprecations/{version} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#get-runner-version-end-of-life-schedule-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/actions/runners/downloads + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#list-runner-applications-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/actions/runners/generate-jitconfig + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#create-configuration-for-a-just-in-time-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/actions/runners/registration-token + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#create-a-registration-token-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/actions/runners/remove-token + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#create-a-remove-token-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/runners/{runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#delete-a-self-hosted-runner-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runners/{runner_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#get-a-self-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#remove-all-custom-labels-from-a-self-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#list-labels-for-a-self-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#add-custom-labels-to-a-self-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#set-custom-labels-for-a-self-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/actions/runners/{runner_id}/labels/{name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#remove-a-custom-label-from-a-self-hosted-runner-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/announcement + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/announcement-banners/enterprises#remove-announcement-banner-from-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/announcement + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/announcement-banners/enterprises#get-announcement-banner-for-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/announcement + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/announcement-banners/enterprises#set-announcement-banner-for-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/apps/installable_organizations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#get-enterprise-owned-organizations-that-can-have-github-apps-installed + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/apps/installable_organizations/{org}/accessible_repositories + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#get-repositories-belonging-to-an-enterprise-owned-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/apps/organizations/{org}/installations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#list-github-apps-installed-on-an-enterprise-owned-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/apps/organizations/{org}/installations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#install-a-github-app-on-an-enterprise-owned-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#uninstall-a-github-app-from-an-enterprise-owned-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#get-the-repositories-accessible-to-a-given-github-app-installation + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#toggle-installation-repository-access-between-selected-and-all-repositories + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories/add + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#grant-repository-access-to-an-organization-installation + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/apps/organizations/{org}/installations/{installation_id}/repositories/remove + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/organization-installations#remove-repository-access-from-an-organization-installation + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/audit-log + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#get-the-audit-log-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/audit-log/stream-key + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#get-the-audit-log-stream-key-for-encrypting-secrets + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/audit-log/streams + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#list-audit-log-stream-configurations-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/audit-log/streams + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#create-an-audit-log-streaming-configuration-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/audit-log/streams/{stream_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#delete-an-audit-log-streaming-configuration-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/audit-log/streams/{stream_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#list-one-audit-log-streaming-configuration-via-a-stream-id + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/audit-log/streams/{stream_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/audit-log#update-an-existing-audit-log-stream-configuration + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/bypass-requests/push-rules + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/bypass-requests#list-push-rule-bypass-requests-within-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/bypass-requests/secret-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/delegated-bypass#list-bypass-requests-for-secret-scanning-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/code-scanning/alerts + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/code-scanning/code-scanning#list-code-scanning-alerts-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/code-security/configurations + documentation_url: https://docs.github.com/rest/code-security/configurations#get-code-security-configurations-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/code-security/configurations + documentation_url: https://docs.github.com/rest/code-security/configurations#create-a-code-security-configuration-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/code-security/configurations/defaults + documentation_url: https://docs.github.com/rest/code-security/configurations#get-default-code-security-configurations-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/code-security/configurations/{configuration_id} + documentation_url: https://docs.github.com/rest/code-security/configurations#delete-a-code-security-configuration-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/code-security/configurations/{configuration_id} + documentation_url: https://docs.github.com/rest/code-security/configurations#retrieve-a-code-security-configuration-of-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/code-security/configurations/{configuration_id} + documentation_url: https://docs.github.com/rest/code-security/configurations#update-a-custom-code-security-configuration-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/code-security/configurations/{configuration_id}/attach + documentation_url: https://docs.github.com/rest/code-security/configurations#attach-an-enterprise-configuration-to-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/code-security/configurations/{configuration_id}/defaults + documentation_url: https://docs.github.com/rest/code-security/configurations#set-a-code-security-configuration-as-a-default-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/code-security/configurations/{configuration_id}/repositories + documentation_url: https://docs.github.com/rest/code-security/configurations#get-repositories-associated-with-an-enterprise-code-security-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/code_security_and_analysis + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis#get-code-security-and-analysis-features-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PATCH /enterprises/{enterprise}/code_security_and_analysis + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis#update-code-security-and-analysis-features-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /enterprises/{enterprise}/consumed-licenses + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing#list-enterprise-consumed-licenses + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/billing/seats + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#list-all-copilot-seat-assignments-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/copilot/billing/selected_enterprise_teams + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#remove-enterprise-teams-from-the-copilot-subscription-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/copilot/billing/selected_enterprise_teams + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#add-enterprise-teams-to-the-copilot-subscription-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/copilot/billing/selected_users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#remove-users-from-the-copilot-subscription-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/copilot/billing/selected_users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#add-users-to-the-copilot-subscription-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/content_exclusion + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-content-exclusion-management#get-copilot-content-exclusion-rules-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/copilot/content_exclusion + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-content-exclusion-management#set-copilot-content-exclusion-rules-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/custom-agents + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-custom-agents#get-custom-agents-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/copilot/custom-agents/source + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-custom-agents#delete-the-custom-agents-source-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/custom-agents/source + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-custom-agents#get-the-source-organization-for-custom-agents-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/copilot/custom-agents/source + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-custom-agents#set-the-source-organization-for-custom-agents-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/metrics/reports/enterprise-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-enterprise-usage-metrics-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/metrics/reports/enterprise-28-day/latest + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-enterprise-usage-metrics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/metrics/reports/repos-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-enterprise-repository-report-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/metrics/reports/user-teams-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-enterprise-user-teams-report-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/metrics/reports/users-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-users-usage-metrics-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/metrics/reports/users-28-day/latest + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-users-usage-metrics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/copilot/policies/coding_agent + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#set-the-coding-agent-policy-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/copilot/policies/coding_agent/organizations + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#remove-organizations-from-the-enterprise-coding-agent-policy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/copilot/policies/coding_agent/organizations + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#add-organizations-to-the-enterprise-coding-agent-policy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/copilot/usage-records + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-usage-metrics#get-copilot-usage-records-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/credential-authorizations/revoke-all + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/credential-authorizations#revoke-all-credential-authorizations-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/credential-authorizations/revoke-credential-type + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/credential-authorizations#revoke-a-single-credential-type-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/credential-authorizations/{username}/revoke + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/credential-authorizations#revoke-credential-authorizations-for-a-user-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/credential-authorizations/{username}/revoke-credential-type + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/credential-authorizations#revoke-a-single-credential-type-for-a-user-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/dependabot/alerts + documentation_url: https://docs.github.com/rest/dependabot/alerts#list-dependabot-alerts-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/dependabot/repository-access + documentation_url: https://docs.github.com/rest/dependabot/repository-access#lists-the-repositories-dependabot-can-access-in-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/dependabot/repository-access + documentation_url: https://docs.github.com/rest/dependabot/repository-access#updates-dependabots-repository-access-list-for-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/dependabot/repository-access/default-level + documentation_url: https://docs.github.com/rest/dependabot/repository-access#set-the-default-repository-access-level-for-dependabot-in-an-enterprise + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/dismissal-requests/secret-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/alert-dismissal-requests#list-alert-dismissal-requests-for-secret-scanning-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/enterprise-roles + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#get-all-enterprise-roles-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/enterprise-roles/teams/{team_slug} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#remove-all-enterprise-roles-from-a-team + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/enterprise-roles/teams/{team_slug}/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#remove-an-enterprise-role-from-a-team + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/enterprise-roles/teams/{team_slug}/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#assign-an-enterprise-role-to-a-team + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/enterprise-roles/users/{username} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#remove-all-enterprise-roles-from-a-user + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/enterprise-roles/users/{username}/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#remove-enterprise-user-role-assignment + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/enterprise-roles/users/{username}/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#assign-an-enterprise-role-to-an-enterprise-user + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/enterprise-roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#get-an-enterprise-role + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/enterprise-roles/{role_id}/teams + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#list-teams-that-are-assigned-to-an-enterprise-role + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/enterprise-roles/{role_id}/users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/enterprise-roles#list-users-that-are-assigned-to-an-enterprise-role + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/innersource-vulnerabilities/sync + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/security-advisories#sync-innersource-vulnerabilities-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/innersource-vulnerabilities/sync/status/{job_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/security-advisories#get-innersource-vulnerability-sync-status-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/installation + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/apps/apps#get-an-enterprise-installation-for-the-authenticated-app + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/license-sync-status + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing#get-a-license-sync-status + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/members/{username}/copilot + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#get-copilot-seat-assignment-details-for-an-enterprise-user + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/network-configurations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#list-hosted-compute-network-configurations-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/network-configurations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#create-a-hosted-compute-network-configuration-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/network-configurations/{network_configuration_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#delete-a-hosted-compute-network-configuration-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/network-configurations/{network_configuration_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#get-a-hosted-compute-network-configuration-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/network-configurations/{network_configuration_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#update-a-hosted-compute-network-configuration-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/network-settings/{network_settings_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#get-a-hosted-compute-network-settings-resource-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/org-properties/schema + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#get-organization-custom-properties-schema-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/org-properties/schema + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#create-or-update-organization-custom-property-definitions-on-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/org-properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#remove-an-organization-custom-property-definition-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/org-properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#get-an-organization-custom-property-definition-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/org-properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#create-or-update-an-organization-custom-property-definition-on-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/org-properties/values + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#list-custom-property-values-for-organizations-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/org-properties/values + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties-for-orgs#create-or-update-custom-property-values-for-organizations-in-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/properties/schema + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties#get-custom-properties-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/properties/schema + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties#create-or-update-custom-properties-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/properties/schema/organizations/{org}/{custom_property_name}/promote + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties#promote-a-custom-property-to-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties#remove-a-custom-property-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties#get-a-custom-property-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/custom-properties#create-or-update-a-custom-property-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/rulesets + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#create-an-enterprise-repository-ruleset + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#delete-an-enterprise-repository-ruleset + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#get-an-enterprise-repository-ruleset + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#update-an-enterprise-repository-ruleset + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/rulesets/{ruleset_id}/history + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#get-enterprise-ruleset-history + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/rulesets/{ruleset_id}/history/{version_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#get-enterprise-ruleset-version + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/secret-scanning/alerts + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/secret-scanning#list-secret-scanning-alerts-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns#bulk-delete-enterprise-custom-patterns + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns#list-enterprise-custom-patterns + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns#bulk-create-enterprise-custom-patterns + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/secret-scanning/custom-patterns/{pattern_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/custom-patterns#update-an-enterprise-custom-pattern + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/secret-scanning/pattern-configurations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/push-protection#list-enterprise-pattern-configurations + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/secret-scanning/pattern-configurations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/push-protection#update-enterprise-pattern-configurations + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/settings/billing/advanced-security + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing#get-github-advanced-security-active-committers-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/settings/billing/ai_credit/usage + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage#get-billing-ai-credit-usage-report-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/budgets + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#get-all-budgets + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/settings/billing/budgets + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#create-a-budget + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/settings/billing/budgets/{budget_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#delete-a-budget + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/budgets/{budget_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#get-a-budget-by-id + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/settings/billing/budgets/{budget_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#update-a-budget + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/budgets/{budget_id}/user-states + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/budgets#get-user-states-for-a-multi-user-budget + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/cost-centers + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#get-all-cost-centers-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/settings/billing/cost-centers + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#create-a-new-cost-center + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#delete-a-cost-center + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#get-a-cost-center-by-id + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#update-a-cost-center + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id}/resource + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#remove-resources-from-a-cost-center + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/settings/billing/cost-centers/{cost_center_id}/resource + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/cost-centers#add-resources-to-a-cost-center + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/premium_request/usage + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage#get-billing-premium-request-usage-report-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/reports + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage-reports#list-usage-report-exports + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/settings/billing/reports + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage-reports#create-a-usage-report-export + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/reports/{report_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage-reports#get-a-usage-report-export + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/usage + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage#get-billing-usage-report-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/settings/billing/usage/summary + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/usage#get-billing-usage-summary-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /enterprises/{enterprise}/teams + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-teams#list-enterprise-teams + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/teams + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-teams#create-an-enterprise-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/teams/{enterprise-team}/memberships + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-members#list-members-in-an-enterprise-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/teams/{enterprise-team}/memberships/add + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-members#bulk-add-team-members + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/teams/{enterprise-team}/memberships/remove + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-members#bulk-remove-team-members + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/teams/{enterprise-team}/memberships/{username} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-members#remove-team-membership + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/teams/{enterprise-team}/memberships/{username} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-members#get-enterprise-team-membership + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/teams/{enterprise-team}/memberships/{username} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-members#add-team-member + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/teams/{enterprise-team}/organizations + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations#get-organization-assignments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/teams/{enterprise-team}/organizations/add + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations#add-organization-assignments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /enterprises/{enterprise}/teams/{enterprise-team}/organizations/remove + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations#remove-organization-assignments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/teams/{enterprise-team}/organizations/{org} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations#delete-an-organization-assignment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/teams/{enterprise-team}/organizations/{org} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations#get-organization-assignment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /enterprises/{enterprise}/teams/{enterprise-team}/organizations/{org} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-team-organizations#add-an-organization-assignment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /enterprises/{enterprise}/teams/{team_slug} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-teams#delete-an-enterprise-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/teams/{team_slug} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-teams#get-an-enterprise-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /enterprises/{enterprise}/teams/{team_slug} + documentation_url: https://docs.github.com/rest/enterprise-teams/enterprise-teams#update-an-enterprise-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /enterprises/{enterprise}/visual-studio-subscriptions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing#get-a-list-of-visual-studio-subscriptions-for-the-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /enterprises/{enterprise}/visual-studio-subscriptions/{visual_studio_subscription_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing#delete-a-visual-studio-subscription-user-match + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /enterprises/{enterprise}/visual-studio-subscriptions/{visual_studio_subscription_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/licensing#add-or-update-a-visual-studio-subscription-user-match + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /enterprises/{enterprise}/{security_product}/{enablement} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/code-security-and-analysis#enable-or-disable-a-security-feature + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /events + documentation_url: https://docs.github.com/rest/activity/events#list-public-events + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /feeds + documentation_url: https://docs.github.com/rest/activity/feeds#get-feeds + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists + documentation_url: https://docs.github.com/rest/gists/gists#list-gists-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /gists + documentation_url: https://docs.github.com/rest/gists/gists#create-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/public + documentation_url: https://docs.github.com/rest/gists/gists#list-public-gists + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/starred + documentation_url: https://docs.github.com/rest/gists/gists#list-starred-gists + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /gists/{gist_id} + documentation_url: https://docs.github.com/rest/gists/gists#delete-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id} + documentation_url: https://docs.github.com/rest/gists/gists#get-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /gists/{gist_id} + documentation_url: https://docs.github.com/rest/gists/gists#update-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id}/comments + documentation_url: https://docs.github.com/rest/gists/comments#list-gist-comments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /gists/{gist_id}/comments + documentation_url: https://docs.github.com/rest/gists/comments#create-a-gist-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /gists/{gist_id}/comments/{comment_id} + documentation_url: https://docs.github.com/rest/gists/comments#delete-a-gist-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id}/comments/{comment_id} + documentation_url: https://docs.github.com/rest/gists/comments#get-a-gist-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /gists/{gist_id}/comments/{comment_id} + documentation_url: https://docs.github.com/rest/gists/comments#update-a-gist-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id}/commits + documentation_url: https://docs.github.com/rest/gists/gists#list-gist-commits + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id}/forks + documentation_url: https://docs.github.com/rest/gists/gists#list-gist-forks + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /gists/{gist_id}/forks + documentation_url: https://docs.github.com/rest/gists/gists#fork-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /gists/{gist_id}/star + documentation_url: https://docs.github.com/rest/gists/gists#unstar-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id}/star + documentation_url: https://docs.github.com/rest/gists/gists#check-if-a-gist-is-starred + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /gists/{gist_id}/star + documentation_url: https://docs.github.com/rest/gists/gists#star-a-gist + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gists/{gist_id}/{sha} + documentation_url: https://docs.github.com/rest/gists/gists#get-a-gist-revision + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gitignore/templates + documentation_url: https://docs.github.com/rest/gitignore/gitignore#get-all-gitignore-templates + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /gitignore/templates/{name} + documentation_url: https://docs.github.com/rest/gitignore/gitignore#get-a-gitignore-template + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /installation/repositories + documentation_url: https://docs.github.com/rest/apps/installations#list-repositories-accessible-to-the-app-installation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /installation/token + documentation_url: https://docs.github.com/rest/apps/installations#revoke-an-installation-access-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /issues + documentation_url: https://docs.github.com/rest/issues/issues#list-issues-assigned-to-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /licenses + documentation_url: https://docs.github.com/rest/licenses/licenses#get-all-commonly-used-licenses + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /licenses/{license} + documentation_url: https://docs.github.com/rest/licenses/licenses#get-a-license + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /manage/v1/access/ssh + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#delete-a-ssh-key + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/access/ssh + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-configured-ssh-keys + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /manage/v1/access/ssh + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#set-a-new-ssh-key + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/checks/system-requirements + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-system-requirement-check-results-for-configured-cluster-nodes + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/cluster/status + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-services-running-on-all-cluster-nodes + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/config/apply + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-a-ghe-config-apply-run + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /manage/v1/config/apply + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#trigger-a-ghe-config-apply-run + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/config/apply/events + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#list-events-from-ghe-config-apply + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /manage/v1/config/init + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#initialize-instance-configuration-with-license-and-password + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/config/license + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-enterprise-license-information + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /manage/v1/config/license + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#upload-an-enterprise-license + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/config/license/check + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#check-a-license + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/config/nodes + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-ghes-node-metadata-for-all-nodes + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/config/settings + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-ghes-settings + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /manage/v1/config/settings + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#set-settings + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/maintenance + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-maintenance-mode + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /manage/v1/maintenance + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#set-the-status-of-maintenance-mode + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/replication/status + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-the-status-of-services-running-on-all-replica-nodes + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /manage/v1/version + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/manage-ghes#get-all-ghes-release-versions-for-all-nodes + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /markdown + documentation_url: https://docs.github.com/rest/markdown/markdown#render-a-markdown-document + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /markdown/raw + documentation_url: https://docs.github.com/rest/markdown/markdown#render-a-markdown-document-in-raw-mode + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /marketplace_listing/accounts/{account_id} + documentation_url: https://docs.github.com/rest/apps/marketplace#get-a-subscription-plan-for-an-account + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /marketplace_listing/plans + documentation_url: https://docs.github.com/rest/apps/marketplace#list-plans + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /marketplace_listing/plans/{plan_id}/accounts + documentation_url: https://docs.github.com/rest/apps/marketplace#list-accounts-for-a-plan + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /marketplace_listing/stubbed/accounts/{account_id} + documentation_url: https://docs.github.com/rest/apps/marketplace#get-a-subscription-plan-for-an-account-stubbed + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /marketplace_listing/stubbed/plans + documentation_url: https://docs.github.com/rest/apps/marketplace#list-plans-stubbed + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /marketplace_listing/stubbed/plans/{plan_id}/accounts + documentation_url: https://docs.github.com/rest/apps/marketplace#list-accounts-for-a-plan-stubbed + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /meta + documentation_url: https://docs.github.com/rest/meta/meta#get-apiname-meta-information + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /networks/{owner}/{repo}/events + documentation_url: https://docs.github.com/rest/activity/events#list-public-events-for-a-network-of-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /notifications + documentation_url: https://docs.github.com/rest/activity/notifications#list-notifications-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /notifications + documentation_url: https://docs.github.com/rest/activity/notifications#mark-notifications-as-read + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /notifications/threads/{thread_id} + documentation_url: https://docs.github.com/rest/activity/notifications#mark-a-thread-as-done + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /notifications/threads/{thread_id} + documentation_url: https://docs.github.com/rest/activity/notifications#get-a-thread + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /notifications/threads/{thread_id} + documentation_url: https://docs.github.com/rest/activity/notifications#mark-a-thread-as-read + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /notifications/threads/{thread_id}/subscription + documentation_url: https://docs.github.com/rest/activity/notifications#delete-a-thread-subscription + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /notifications/threads/{thread_id}/subscription + documentation_url: https://docs.github.com/rest/activity/notifications#get-a-thread-subscription-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /notifications/threads/{thread_id}/subscription + documentation_url: https://docs.github.com/rest/activity/notifications#set-a-thread-subscription + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /octocat + documentation_url: https://docs.github.com/rest/meta/meta#get-octocat + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /organizations + documentation_url: https://docs.github.com/rest/orgs/orgs#list-organizations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /organizations/{organization_id}/custom_roles + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#closing-down---list-custom-repository-roles-in-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /organizations/{org}/actions/cache/retention-limit + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-retention-limit-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /organizations/{org}/actions/cache/retention-limit + documentation_url: https://docs.github.com/rest/actions/cache#set-github-actions-cache-retention-limit-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /organizations/{org}/actions/cache/storage-limit + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-storage-limit-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /organizations/{org}/actions/cache/storage-limit + documentation_url: https://docs.github.com/rest/actions/cache#set-github-actions-cache-storage-limit-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /organizations/{org}/org-properties/values + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-properties-for-orgs#get-all-custom-property-values-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /organizations/{org}/org-properties/values + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-properties-for-orgs#create-or-update-custom-property-values-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /organizations/{org}/settings/billing/ai_credit/usage + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-ai-credit-usage-report-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /organizations/{org}/settings/billing/budgets + documentation_url: https://docs.github.com/rest/billing/budgets#get-all-budgets-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: POST /organizations/{org}/settings/billing/budgets + documentation_url: https://docs.github.com/rest/billing/budgets#create-a-budget-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: DELETE /organizations/{org}/settings/billing/budgets/{budget_id} + documentation_url: https://docs.github.com/rest/billing/budgets#delete-a-budget-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /organizations/{org}/settings/billing/budgets/{budget_id} + documentation_url: https://docs.github.com/rest/billing/budgets#get-a-budget-by-id-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: PATCH /organizations/{org}/settings/billing/budgets/{budget_id} + documentation_url: https://docs.github.com/rest/billing/budgets#update-a-budget-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /organizations/{org}/settings/billing/premium_request/usage + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-premium-request-usage-report-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /organizations/{org}/settings/billing/usage + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-usage-report-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /organizations/{org}/settings/billing/usage/summary + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-usage-summary-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: DELETE /orgs/{org} + documentation_url: https://docs.github.com/rest/orgs/orgs#delete-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org} + documentation_url: https://docs.github.com/rest/orgs/orgs#get-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org} + documentation_url: https://docs.github.com/rest/orgs/orgs#update-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/cache/usage + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-usage-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/cache/usage-by-repository + documentation_url: https://docs.github.com/rest/actions/cache#list-repositories-with-github-actions-cache-usage-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/hosted-runners + documentation_url: https://docs.github.com/rest/actions/hosted-runners#list-github-hosted-runners-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/actions/hosted-runners + documentation_url: https://docs.github.com/rest/actions/hosted-runners#create-a-github-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/images/custom + documentation_url: https://docs.github.com/rest/actions/hosted-runners#list-custom-images-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#delete-a-custom-image-from-the-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-a-custom-image-definition-for-github-actions-hosted-runners + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id}/versions + documentation_url: https://docs.github.com/rest/actions/hosted-runners#list-image-versions-of-a-custom-image-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#delete-an-image-version-of-custom-image-from-the-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/images/custom/{image_definition_id}/versions/{version} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-an-image-version-of-a-custom-image-for-github-actions-hosted-runners + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/images/github-owned + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-github-owned-images-for-github-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/images/partner + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-partner-images-for-github-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/limits + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-limits-on-github-hosted-runners-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/machine-sizes + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-github-hosted-runners-machine-specs-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/platforms + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-platforms-for-github-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/actions/hosted-runners/{hosted_runner_id} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#delete-a-github-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/hosted-runners/{hosted_runner_id} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#get-a-github-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/actions/hosted-runners/{hosted_runner_id} + documentation_url: https://docs.github.com/rest/actions/hosted-runners#update-a-github-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/oidc/customization/properties/repo + documentation_url: https://docs.github.com/rest/actions/oidc#list-oidc-custom-property-inclusions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/actions/oidc/customization/properties/repo + documentation_url: https://docs.github.com/rest/actions/oidc#create-an-oidc-custom-property-inclusion-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/actions/oidc/customization/properties/repo/{custom_property_name} + documentation_url: https://docs.github.com/rest/actions/oidc#delete-an-oidc-custom-property-inclusion-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/oidc/customization/sub + documentation_url: https://docs.github.com/rest/actions/oidc#get-the-customization-template-for-an-oidc-subject-claim-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/oidc/customization/sub + documentation_url: https://docs.github.com/rest/actions/oidc#set-the-customization-template-for-an-oidc-subject-claim-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions + documentation_url: https://docs.github.com/rest/actions/permissions#get-github-actions-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions + documentation_url: https://docs.github.com/rest/actions/permissions#set-github-actions-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/artifact-and-log-retention + documentation_url: https://docs.github.com/rest/actions/permissions#get-artifact-and-log-retention-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/artifact-and-log-retention + documentation_url: https://docs.github.com/rest/actions/permissions#set-artifact-and-log-retention-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/fork-pr-contributor-approval + documentation_url: https://docs.github.com/rest/actions/permissions#get-fork-pr-contributor-approval-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/fork-pr-contributor-approval + documentation_url: https://docs.github.com/rest/actions/permissions#set-fork-pr-contributor-approval-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/fork-pr-workflows-private-repos + documentation_url: https://docs.github.com/rest/actions/permissions#get-private-repo-fork-pr-workflow-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/fork-pr-workflows-private-repos + documentation_url: https://docs.github.com/rest/actions/permissions#set-private-repo-fork-pr-workflow-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/repositories + documentation_url: https://docs.github.com/rest/actions/permissions#list-selected-repositories-enabled-for-github-actions-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/repositories + documentation_url: https://docs.github.com/rest/actions/permissions#set-selected-repositories-enabled-for-github-actions-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/permissions/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/permissions#disable-a-selected-repository-for-github-actions-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/permissions#enable-a-selected-repository-for-github-actions-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/selected-actions + documentation_url: https://docs.github.com/rest/actions/permissions#get-allowed-actions-and-reusable-workflows-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/selected-actions + documentation_url: https://docs.github.com/rest/actions/permissions#set-allowed-actions-and-reusable-workflows-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/self-hosted-runners + documentation_url: https://docs.github.com/rest/actions/permissions#get-self-hosted-runners-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/self-hosted-runners + documentation_url: https://docs.github.com/rest/actions/permissions#set-self-hosted-runners-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/self-hosted-runners/repositories + documentation_url: https://docs.github.com/rest/actions/permissions#list-repositories-allowed-to-use-self-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/self-hosted-runners/repositories + documentation_url: https://docs.github.com/rest/actions/permissions#set-repositories-allowed-to-use-self-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/permissions/self-hosted-runners/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/permissions#remove-a-repository-from-the-list-of-repositories-allowed-to-use-self-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/self-hosted-runners/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/permissions#add-a-repository-to-the-list-of-repositories-allowed-to-use-self-hosted-runners-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/permissions/workflow + documentation_url: https://docs.github.com/rest/actions/permissions#get-default-workflow-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/permissions/workflow + documentation_url: https://docs.github.com/rest/actions/permissions#set-default-workflow-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runner-groups + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#list-self-hosted-runner-groups-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/actions/runner-groups + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#create-a-self-hosted-runner-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/runner-groups/{runner_group_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#delete-a-self-hosted-runner-group-from-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runner-groups/{runner_group_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#get-a-self-hosted-runner-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/actions/runner-groups/{runner_group_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#update-a-self-hosted-runner-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runner-groups/{runner_group_id}/hosted-runners + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#list-github-hosted-runners-in-a-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#list-repository-access-to-a-self-hosted-runner-group-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#set-repository-access-for-a-self-hosted-runner-group-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#remove-repository-access-to-a-self-hosted-runner-group-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#add-repository-access-to-a-self-hosted-runner-group-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#list-self-hosted-runners-in-a-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/runners + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#set-self-hosted-runners-in-a-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/runner-groups/{runner_group_id}/runners/{runner_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#remove-a-self-hosted-runner-from-a-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/runners/{runner_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runner-groups#add-a-self-hosted-runner-to-a-group-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runners + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#list-self-hosted-runners-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runners/deprecations/{version} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#get-runner-version-end-of-life-schedule-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/actions/runners/downloads + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#list-runner-applications-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/actions/runners/generate-jitconfig + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#create-configuration-for-a-just-in-time-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/actions/runners/registration-token + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#create-a-registration-token-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/actions/runners/remove-token + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#create-a-remove-token-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/runners/{runner_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#delete-a-self-hosted-runner-from-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runners/{runner_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#get-a-self-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#remove-all-custom-labels-from-a-self-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#list-labels-for-a-self-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#add-custom-labels-to-a-self-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#set-custom-labels-for-a-self-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/runners/{runner_id}/labels/{name} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#remove-a-custom-label-from-a-self-hosted-runner-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/secrets + documentation_url: https://docs.github.com/rest/actions/secrets#list-organization-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/secrets/public-key + documentation_url: https://docs.github.com/rest/actions/secrets#get-an-organization-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#delete-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#get-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#create-or-update-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/actions/secrets#list-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/actions/secrets#set-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/secrets#remove-selected-repository-from-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/secrets#add-selected-repository-to-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/variables + documentation_url: https://docs.github.com/rest/actions/variables#list-organization-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/actions/variables + documentation_url: https://docs.github.com/rest/actions/variables#create-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#delete-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#get-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/actions/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#update-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/actions/variables/{name}/repositories + documentation_url: https://docs.github.com/rest/actions/variables#list-selected-repositories-for-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/variables/{name}/repositories + documentation_url: https://docs.github.com/rest/actions/variables#set-selected-repositories-for-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/actions/variables/{name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/variables#remove-selected-repository-from-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/actions/variables#add-selected-repository-to-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/agents/secrets + documentation_url: https://docs.github.com/rest/agents/secrets#list-organization-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/agents/secrets/public-key + documentation_url: https://docs.github.com/rest/agents/secrets#get-an-organization-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets#delete-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets#get-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets#create-or-update-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/agents/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/agents/secrets#list-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/agents/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/agents/secrets#set-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/agents/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/agents/secrets#remove-selected-repository-from-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/agents/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/agents/secrets#add-selected-repository-to-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/agents/variables + documentation_url: https://docs.github.com/rest/agents/variables#list-organization-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/agents/variables + documentation_url: https://docs.github.com/rest/agents/variables#create-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/agents/variables/{name} + documentation_url: https://docs.github.com/rest/agents/variables#delete-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/agents/variables/{name} + documentation_url: https://docs.github.com/rest/agents/variables#get-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/agents/variables/{name} + documentation_url: https://docs.github.com/rest/agents/variables#update-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/agents/variables/{name}/repositories + documentation_url: https://docs.github.com/rest/agents/variables#list-selected-repositories-for-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/agents/variables/{name}/repositories + documentation_url: https://docs.github.com/rest/agents/variables#set-selected-repositories-for-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/agents/variables/{name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/agents/variables#remove-selected-repository-from-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/agents/variables/{name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/agents/variables#add-selected-repository-to-an-organization-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/announcement + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/announcement-banners/organizations#remove-announcement-banner-from-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/announcement + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/announcement-banners/organizations#get-announcement-banner-for-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/announcement + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/announcement-banners/organizations#set-announcement-banner-for-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/artifacts/metadata/deployment-record + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#create-an-artifact-deployment-record + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/artifacts/metadata/deployment-record/cluster/{cluster} + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#set-cluster-deployment-records + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/artifacts/metadata/deployment-record/cluster/{cluster}/jobs + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#create-a-cluster-deployment-records-job + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/artifacts/metadata/deployment-record/cluster/{cluster}/jobs/{job_id} + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#get-cluster-deployment-records-job-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/artifacts/metadata/storage-record + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#create-artifact-metadata-storage-record + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/artifacts/{subject_digest}/metadata/deployment-records + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#list-artifact-deployment-records + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/artifacts/{subject_digest}/metadata/storage-records + documentation_url: https://docs.github.com/rest/orgs/artifact-metadata#list-artifact-storage-records + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/attestations/bulk-list + documentation_url: https://docs.github.com/rest/orgs/attestations#list-attestations-by-bulk-subject-digests + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/attestations/delete-request + documentation_url: https://docs.github.com/rest/orgs/attestations#delete-attestations-in-bulk + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/attestations/digest/{subject_digest} + documentation_url: https://docs.github.com/rest/orgs/attestations#delete-attestations-by-subject-digest + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/attestations/repositories + documentation_url: https://docs.github.com/rest/orgs/attestations#list-attestation-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/attestations/{attestation_id} + documentation_url: https://docs.github.com/rest/orgs/attestations#delete-attestations-by-id + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/attestations/{subject_digest} + documentation_url: https://docs.github.com/rest/orgs/attestations#list-attestations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/audit-log + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs#get-the-audit-log-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/blocks + documentation_url: https://docs.github.com/rest/orgs/blocking#list-users-blocked-by-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/blocks/{username} + documentation_url: https://docs.github.com/rest/orgs/blocking#unblock-a-user-from-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/blocks/{username} + documentation_url: https://docs.github.com/rest/orgs/blocking#check-if-a-user-is-blocked-by-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/blocks/{username} + documentation_url: https://docs.github.com/rest/orgs/blocking#block-a-user-from-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/bypass-requests/push-rules + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/bypass-requests#list-push-rule-bypass-requests-within-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/bypass-requests/secret-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/delegated-bypass#list-bypass-requests-for-secret-scanning-for-an-org + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/campaigns + documentation_url: https://docs.github.com/rest/campaigns/campaigns#list-campaigns-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/campaigns + documentation_url: https://docs.github.com/rest/campaigns/campaigns#create-a-campaign-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/campaigns/{campaign_number} + documentation_url: https://docs.github.com/rest/campaigns/campaigns#delete-a-campaign-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/campaigns/{campaign_number} + documentation_url: https://docs.github.com/rest/campaigns/campaigns#get-a-campaign-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/campaigns/{campaign_number} + documentation_url: https://docs.github.com/rest/campaigns/campaigns#update-a-campaign + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/code-scanning/alerts + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#list-code-scanning-alerts-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/code-security/configurations + documentation_url: https://docs.github.com/rest/code-security/configurations#get-code-security-configurations-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/code-security/configurations + documentation_url: https://docs.github.com/rest/code-security/configurations#create-a-code-security-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/code-security/configurations/defaults + documentation_url: https://docs.github.com/rest/code-security/configurations#get-default-code-security-configurations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/code-security/configurations/detach + documentation_url: https://docs.github.com/rest/code-security/configurations#detach-configurations-from-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/code-security/configurations/{configuration_id} + documentation_url: https://docs.github.com/rest/code-security/configurations#delete-a-code-security-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/code-security/configurations/{configuration_id} + documentation_url: https://docs.github.com/rest/code-security/configurations#get-a-code-security-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/code-security/configurations/{configuration_id} + documentation_url: https://docs.github.com/rest/code-security/configurations#update-a-code-security-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/code-security/configurations/{configuration_id}/attach + documentation_url: https://docs.github.com/rest/code-security/configurations#attach-a-configuration-to-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/code-security/configurations/{configuration_id}/defaults + documentation_url: https://docs.github.com/rest/code-security/configurations#set-a-code-security-configuration-as-a-default-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/code-security/configurations/{configuration_id}/repositories + documentation_url: https://docs.github.com/rest/code-security/configurations#get-repositories-associated-with-a-code-security-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/codespaces + documentation_url: https://docs.github.com/rest/codespaces/organizations#list-codespaces-for-the-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/codespaces/access + documentation_url: https://docs.github.com/rest/codespaces/organizations#manage-access-control-for-organization-codespaces + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: DELETE /orgs/{org}/codespaces/access/selected_users + documentation_url: https://docs.github.com/rest/codespaces/organizations#remove-users-from-codespaces-access-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: POST /orgs/{org}/codespaces/access/selected_users + documentation_url: https://docs.github.com/rest/codespaces/organizations#add-users-to-codespaces-access-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /orgs/{org}/codespaces/secrets + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#list-organization-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/codespaces/secrets/public-key + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#get-an-organization-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#delete-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#get-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#create-or-update-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#list-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#set-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#remove-selected-repository-from-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/codespaces/organization-secrets#add-selected-repository-to-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot-spaces + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#list-organization-copilot-spaces + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/copilot-spaces + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#create-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/copilot-spaces/{space_number} + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#delete-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot-spaces/{space_number} + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#get-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot-spaces/{space_number} + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#set-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot-spaces/{space_number}/collaborators + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#list-collaborators-for-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/copilot-spaces/{space_number}/collaborators + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#add-a-collaborator-to-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/copilot-spaces/{space_number}/collaborators/{actor_type}/{actor_identifier} + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#remove-a-collaborator-from-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot-spaces/{space_number}/collaborators/{actor_type}/{actor_identifier} + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#set-a-collaborator-role-for-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot-spaces/{space_number}/resources + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#list-resources-for-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/copilot-spaces/{space_number}/resources + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#create-a-resource-for-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/copilot-spaces/{space_number}/resources/{space_resource_id} + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#delete-a-resource-from-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot-spaces/{space_number}/resources/{space_resource_id} + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#get-a-resource-for-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot-spaces/{space_number}/resources/{space_resource_id} + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#set-a-resource-for-an-organization-copilot-space + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/billing + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#get-copilot-seat-information-and-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/billing/seats + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#list-all-copilot-seat-assignments-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/copilot/billing/selected_teams + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#remove-teams-from-the-copilot-subscription-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/copilot/billing/selected_teams + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#add-teams-to-the-copilot-subscription-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/copilot/billing/selected_users + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#remove-users-from-the-copilot-subscription-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/copilot/billing/selected_users + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#add-users-to-the-copilot-subscription-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/coding-agent/permissions + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#get-copilot-cloud-agent-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot/coding-agent/permissions + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#set-copilot-cloud-agent-permissions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/coding-agent/permissions/repositories + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#list-repositories-enabled-for-copilot-cloud-agent-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot/coding-agent/permissions/repositories + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#set-selected-repositories-for-copilot-cloud-agent-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/copilot/coding-agent/permissions/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#disable-a-repository-for-copilot-cloud-agent-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot/coding-agent/permissions/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/copilot/copilot-coding-agent-management#enable-a-repository-for-copilot-cloud-agent-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/content_exclusion + documentation_url: https://docs.github.com/rest/copilot/copilot-content-exclusion-management#get-copilot-content-exclusion-rules-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/copilot/content_exclusion + documentation_url: https://docs.github.com/rest/copilot/copilot-content-exclusion-management#set-copilot-content-exclusion-rules-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/metrics/reports/organization-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-organization-usage-metrics-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/metrics/reports/organization-28-day/latest + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-organization-usage-metrics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/metrics/reports/repos-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-organization-repository-report-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/metrics/reports/user-teams-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-organization-user-teams-report-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/metrics/reports/users-1-day + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-organization-users-usage-metrics-for-a-specific-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/copilot/metrics/reports/users-28-day/latest + documentation_url: https://docs.github.com/rest/copilot/copilot-usage-metrics#get-copilot-organization-users-usage-metrics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/credential-authorizations + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs#list-saml-sso-authorizations-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/credential-authorizations/{credential_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/orgs#remove-a-saml-sso-authorization-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/custom-repository-roles + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#list-custom-repository-roles-in-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/custom-repository-roles + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#create-a-custom-repository-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/custom-repository-roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#delete-a-custom-repository-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/custom-repository-roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#get-a-custom-repository-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/custom-repository-roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#update-a-custom-repository-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/custom_roles + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#closing-down---create-a-custom-role + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: DELETE /orgs/{org}/custom_roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#closing-down---delete-a-custom-role + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /orgs/{org}/custom_roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#closing-down---get-a-custom-role + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: PATCH /orgs/{org}/custom_roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#closing-down---update-a-custom-role + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /orgs/{org}/dependabot/alerts + documentation_url: https://docs.github.com/rest/dependabot/alerts#list-dependabot-alerts-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dependabot/repository-access + documentation_url: https://docs.github.com/rest/dependabot/repository-access#lists-the-repositories-dependabot-can-access-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/dependabot/repository-access + documentation_url: https://docs.github.com/rest/dependabot/repository-access#updates-dependabots-repository-access-list-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/dependabot/repository-access/default-level + documentation_url: https://docs.github.com/rest/dependabot/repository-access#set-the-default-repository-access-level-for-dependabot + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dependabot/secrets + documentation_url: https://docs.github.com/rest/dependabot/secrets#list-organization-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dependabot/secrets/public-key + documentation_url: https://docs.github.com/rest/dependabot/secrets#get-an-organization-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/dependabot/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/dependabot/secrets#delete-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dependabot/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/dependabot/secrets#get-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/dependabot/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/dependabot/secrets#create-or-update-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/dependabot/secrets#list-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/dependabot/secrets#set-selected-repositories-for-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/dependabot/secrets#remove-selected-repository-from-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/dependabot/secrets#add-selected-repository-to-an-organization-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dismissal-requests/code-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/code-scanning/alert-dismissal-requests#list-dismissal-requests-for-code-scanning-alerts-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dismissal-requests/dependabot + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/dependabot/alert-dismissal-requests#list-dismissal-requests-for-dependabot-alerts-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/dismissal-requests/secret-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/alert-dismissal-requests#list-alert-dismissal-requests-for-secret-scanning-for-an-org + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/docker/conflicts + documentation_url: https://docs.github.com/rest/packages/packages#get-list-of-conflicting-packages-during-docker-migration-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/events + documentation_url: https://docs.github.com/rest/activity/events#list-public-organization-events + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/external-group/{group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups#get-an-external-group + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/external-groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups#list-external-groups-available-to-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/failed_invitations + documentation_url: https://docs.github.com/rest/orgs/members#list-failed-organization-invitations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/fine_grained_permissions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#closing-down---list-fine-grained-permissions-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /orgs/{org}/hooks + documentation_url: https://docs.github.com/rest/orgs/webhooks#list-organization-webhooks + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/hooks + documentation_url: https://docs.github.com/rest/orgs/webhooks#create-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/hooks/{hook_id} + documentation_url: https://docs.github.com/rest/orgs/webhooks#delete-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/hooks/{hook_id} + documentation_url: https://docs.github.com/rest/orgs/webhooks#get-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/hooks/{hook_id} + documentation_url: https://docs.github.com/rest/orgs/webhooks#update-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/hooks/{hook_id}/config + documentation_url: https://docs.github.com/rest/orgs/webhooks#get-a-webhook-configuration-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/hooks/{hook_id}/config + documentation_url: https://docs.github.com/rest/orgs/webhooks#update-a-webhook-configuration-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/hooks/{hook_id}/deliveries + documentation_url: https://docs.github.com/rest/orgs/webhooks#list-deliveries-for-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id} + documentation_url: https://docs.github.com/rest/orgs/webhooks#get-a-webhook-delivery-for-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts + documentation_url: https://docs.github.com/rest/orgs/webhooks#redeliver-a-delivery-for-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/hooks/{hook_id}/pings + documentation_url: https://docs.github.com/rest/orgs/webhooks#ping-an-organization-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/insights/api/route-stats/{actor_type}/{actor_id} + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-route-stats-by-actor + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/subject-stats + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-subject-stats + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/summary-stats + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-summary-stats + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/summary-stats/users/{user_id} + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-summary-stats-by-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/summary-stats/{actor_type}/{actor_id} + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-summary-stats-by-actor + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/time-stats + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-time-stats + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/time-stats/users/{user_id} + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-time-stats-by-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/time-stats/{actor_type}/{actor_id} + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-time-stats-by-actor + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/insights/api/user-stats/{user_id} + documentation_url: https://docs.github.com/rest/orgs/api-insights#get-user-stats + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/installation + documentation_url: https://docs.github.com/rest/apps/apps#get-an-organization-installation-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/installations + documentation_url: https://docs.github.com/rest/orgs/orgs#list-app-installations-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/orgs#remove-interaction-restrictions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/orgs#get-interaction-restrictions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/orgs#set-interaction-restrictions-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/interaction-limits/pulls/creation-cap + documentation_url: https://docs.github.com/rest/interactions/orgs#get-pull-request-creation-cap-for-an-org + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/interaction-limits/pulls/creation-cap + documentation_url: https://docs.github.com/rest/interactions/orgs#update-pull-request-creation-cap-for-an-org + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/invitations + documentation_url: https://docs.github.com/rest/orgs/members#list-pending-organization-invitations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/invitations + documentation_url: https://docs.github.com/rest/orgs/members#create-an-organization-invitation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/invitations/{invitation_id} + documentation_url: https://docs.github.com/rest/orgs/members#cancel-an-organization-invitation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/invitations/{invitation_id}/teams + documentation_url: https://docs.github.com/rest/orgs/members#list-organization-invitation-teams + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/issue-fields + documentation_url: https://docs.github.com/rest/orgs/issue-fields#list-issue-fields-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/issue-fields + documentation_url: https://docs.github.com/rest/orgs/issue-fields#create-issue-field-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/issue-fields/{issue_field_id} + documentation_url: https://docs.github.com/rest/orgs/issue-fields#delete-issue-field-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/issue-fields/{issue_field_id} + documentation_url: https://docs.github.com/rest/orgs/issue-fields#update-issue-field-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/issue-types + documentation_url: https://docs.github.com/rest/orgs/issue-types#list-issue-types-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/issue-types + documentation_url: https://docs.github.com/rest/orgs/issue-types#create-issue-type-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/issue-types/{issue_type_id} + documentation_url: https://docs.github.com/rest/orgs/issue-types#delete-issue-type-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /orgs/{org}/issue-types/{issue_type_id} + documentation_url: https://docs.github.com/rest/orgs/issue-types#update-issue-type-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/issues + documentation_url: https://docs.github.com/rest/issues/issues#list-organization-issues-assigned-to-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/members + documentation_url: https://docs.github.com/rest/orgs/members#list-organization-members + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/members/{username} + documentation_url: https://docs.github.com/rest/orgs/members#remove-an-organization-member + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/members/{username} + documentation_url: https://docs.github.com/rest/orgs/members#check-organization-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/members/{username}/codespaces + documentation_url: https://docs.github.com/rest/codespaces/organizations#list-codespaces-for-a-user-in-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/members/{username}/codespaces/{codespace_name} + documentation_url: https://docs.github.com/rest/codespaces/organizations#delete-a-codespace-from-the-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/members/{username}/codespaces/{codespace_name}/stop + documentation_url: https://docs.github.com/rest/codespaces/organizations#stop-a-codespace-for-an-organization-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/members/{username}/copilot + documentation_url: https://docs.github.com/rest/copilot/copilot-user-management#get-copilot-seat-assignment-details-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/memberships/{username} + documentation_url: https://docs.github.com/rest/orgs/members#remove-organization-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/memberships/{username} + documentation_url: https://docs.github.com/rest/orgs/members#get-organization-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/memberships/{username} + documentation_url: https://docs.github.com/rest/orgs/members#set-organization-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/migrations + documentation_url: https://docs.github.com/rest/migrations/orgs#list-organization-migrations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/migrations + documentation_url: https://docs.github.com/rest/migrations/orgs#start-an-organization-migration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/migrations/{migration_id} + documentation_url: https://docs.github.com/rest/migrations/orgs#get-an-organization-migration-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/migrations/{migration_id}/archive + documentation_url: https://docs.github.com/rest/migrations/orgs#delete-an-organization-migration-archive + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/migrations/{migration_id}/archive + documentation_url: https://docs.github.com/rest/migrations/orgs#download-an-organization-migration-archive + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/migrations/{migration_id}/repos/{repo_name}/lock + documentation_url: https://docs.github.com/rest/migrations/orgs#unlock-an-organization-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/migrations/{migration_id}/repositories + documentation_url: https://docs.github.com/rest/migrations/orgs#list-repositories-in-an-organization-migration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/organization-fine-grained-permissions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles#list-organization-fine-grained-permissions-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/organization-roles + documentation_url: https://docs.github.com/rest/orgs/organization-roles#get-all-organization-roles-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/organization-roles + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles#create-a-custom-organization-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/organization-roles/teams/{team_slug} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#remove-all-organization-roles-for-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/organization-roles/teams/{team_slug}/{role_id} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#remove-an-organization-role-from-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/organization-roles/teams/{team_slug}/{role_id} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#assign-an-organization-role-to-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/organization-roles/users/{username} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#remove-all-organization-roles-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/organization-roles/users/{username}/{role_id} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#remove-an-organization-role-from-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/organization-roles/users/{username}/{role_id} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#assign-an-organization-role-to-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/organization-roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles#delete-a-custom-organization-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/organization-roles/{role_id} + documentation_url: https://docs.github.com/rest/orgs/organization-roles#get-an-organization-role + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/organization-roles/{role_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/organization-roles#update-a-custom-organization-role + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/organization-roles/{role_id}/teams + documentation_url: https://docs.github.com/rest/orgs/organization-roles#list-teams-that-are-assigned-to-an-organization-role + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/organization-roles/{role_id}/users + documentation_url: https://docs.github.com/rest/orgs/organization-roles#list-users-that-are-assigned-to-an-organization-role + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/outside_collaborators + documentation_url: https://docs.github.com/rest/orgs/outside-collaborators#list-outside-collaborators-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/outside_collaborators/{username} + documentation_url: https://docs.github.com/rest/orgs/outside-collaborators#remove-outside-collaborator-from-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/outside_collaborators/{username} + documentation_url: https://docs.github.com/rest/orgs/outside-collaborators#convert-an-organization-member-to-outside-collaborator + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/packages + documentation_url: https://docs.github.com/rest/packages/packages#list-packages-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/packages/{package_type}/{package_name} + documentation_url: https://docs.github.com/rest/packages/packages#delete-a-package-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/packages/{package_type}/{package_name} + documentation_url: https://docs.github.com/rest/packages/packages#get-a-package-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/packages/{package_type}/{package_name}/restore + documentation_url: https://docs.github.com/rest/packages/packages#restore-a-package-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/packages/{package_type}/{package_name}/versions + documentation_url: https://docs.github.com/rest/packages/packages#list-package-versions-for-a-package-owned-by-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id} + documentation_url: https://docs.github.com/rest/packages/packages#delete-package-version-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id} + documentation_url: https://docs.github.com/rest/packages/packages#get-a-package-version-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore + documentation_url: https://docs.github.com/rest/packages/packages#restore-package-version-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/personal-access-token-requests + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#list-requests-to-access-organization-resources-with-fine-grained-personal-access-tokens + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/personal-access-token-requests + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#review-requests-to-access-organization-resources-with-fine-grained-personal-access-tokens + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/personal-access-token-requests/{pat_request_id} + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#review-a-request-to-access-organization-resources-with-a-fine-grained-personal-access-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#list-repositories-requested-to-be-accessed-by-a-fine-grained-personal-access-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/personal-access-tokens + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#list-fine-grained-personal-access-tokens-with-access-to-organization-resources + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/personal-access-tokens + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#update-the-access-to-organization-resources-via-fine-grained-personal-access-tokens + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/personal-access-tokens/{pat_id} + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#update-the-access-a-fine-grained-personal-access-token-has-to-organization-resources + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories + documentation_url: https://docs.github.com/rest/orgs/personal-access-tokens#list-repositories-a-fine-grained-personal-access-token-has-access-to + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/pre-receive-hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/org-pre-receive-hooks#list-pre-receive-hooks-for-an-organization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/org-pre-receive-hooks#remove-pre-receive-hook-enforcement-for-an-organization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/org-pre-receive-hooks#get-a-pre-receive-hook-for-an-organization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/org-pre-receive-hooks#update-pre-receive-hook-enforcement-for-an-organization + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/private-registries + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations#list-private-registries-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/private-registries + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations#create-a-private-registry-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/private-registries/public-key + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations#get-private-registries-public-key-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/private-registries/{secret_name} + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations#delete-a-private-registry-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/private-registries/{secret_name} + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations#get-a-private-registry-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/private-registries/{secret_name} + documentation_url: https://docs.github.com/rest/private-registries/organization-configurations#update-a-private-registry-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#list-organization-projects + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: POST /orgs/{org}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#create-an-organization-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /orgs/{org}/projectsV2 + documentation_url: https://docs.github.com/rest/projects/projects#list-projects-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projectsV2/{project_number} + documentation_url: https://docs.github.com/rest/projects/projects#get-project-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/projectsV2/{project_number}/drafts + documentation_url: https://docs.github.com/rest/projects/drafts#create-draft-item-for-organization-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projectsV2/{project_number}/fields + documentation_url: https://docs.github.com/rest/projects/fields#list-project-fields-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/projectsV2/{project_number}/fields + documentation_url: https://docs.github.com/rest/projects/fields#add-a-field-to-an-organization-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projectsV2/{project_number}/fields/{field_id} + documentation_url: https://docs.github.com/rest/projects/fields#get-project-field-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projectsV2/{project_number}/items + documentation_url: https://docs.github.com/rest/projects/items#list-items-for-an-organization-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/projectsV2/{project_number}/items + documentation_url: https://docs.github.com/rest/projects/items#add-item-to-organization-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/projectsV2/{project_number}/items/{item_id} + documentation_url: https://docs.github.com/rest/projects/items#delete-project-item-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projectsV2/{project_number}/items/{item_id} + documentation_url: https://docs.github.com/rest/projects/items#get-an-item-for-an-organization-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/projectsV2/{project_number}/items/{item_id} + documentation_url: https://docs.github.com/rest/projects/items#update-project-item-for-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/projectsV2/{project_number}/views + documentation_url: https://docs.github.com/rest/projects/views#create-a-view-for-an-organization-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/projectsV2/{project_number}/views/{view_number}/items + documentation_url: https://docs.github.com/rest/projects/items#list-items-for-an-organization-project-view + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/properties/schema + documentation_url: https://docs.github.com/rest/orgs/custom-properties#get-all-custom-properties-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/properties/schema + documentation_url: https://docs.github.com/rest/orgs/custom-properties#create-or-update-custom-properties-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/rest/orgs/custom-properties#remove-a-custom-property-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/rest/orgs/custom-properties#get-a-custom-property-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/properties/schema/{custom_property_name} + documentation_url: https://docs.github.com/rest/orgs/custom-properties#create-or-update-a-custom-property-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/properties/values + documentation_url: https://docs.github.com/rest/orgs/custom-properties#list-custom-property-values-for-organization-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/properties/values + documentation_url: https://docs.github.com/rest/orgs/custom-properties#create-or-update-custom-property-values-for-organization-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/public_members + documentation_url: https://docs.github.com/rest/orgs/members#list-public-organization-members + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/public_members/{username} + documentation_url: https://docs.github.com/rest/orgs/members#remove-public-organization-membership-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/public_members/{username} + documentation_url: https://docs.github.com/rest/orgs/members#check-public-organization-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/public_members/{username} + documentation_url: https://docs.github.com/rest/orgs/members#set-public-organization-membership-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/repos + documentation_url: https://docs.github.com/rest/repos/repos#list-organization-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/repos + documentation_url: https://docs.github.com/rest/repos/repos#create-an-organization-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/repository-fine-grained-permissions + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/orgs/custom-roles#list-repository-fine-grained-permissions-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/rulesets + documentation_url: https://docs.github.com/rest/orgs/rules#get-all-organization-repository-rulesets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/rulesets + documentation_url: https://docs.github.com/rest/orgs/rules#create-an-organization-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/rulesets/rule-suites + documentation_url: https://docs.github.com/rest/orgs/rule-suites#list-organization-rule-suites + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/rulesets/rule-suites/{rule_suite_id} + documentation_url: https://docs.github.com/rest/orgs/rule-suites#get-an-organization-rule-suite + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/rest/orgs/rules#delete-an-organization-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/rest/orgs/rules#get-an-organization-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/rest/orgs/rules#update-an-organization-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/rulesets/{ruleset_id}/history + documentation_url: https://docs.github.com/rest/orgs/rules#get-organization-ruleset-history + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/rulesets/{ruleset_id}/history/{version_id} + documentation_url: https://docs.github.com/rest/orgs/rules#get-organization-ruleset-version + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/secret-scanning/alerts + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#list-secret-scanning-alerts-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#bulk-delete-organization-custom-patterns + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#list-organization-custom-patterns + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#bulk-create-organization-custom-patterns + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/secret-scanning/custom-patterns/{pattern_id} + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#update-an-organization-custom-pattern + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/secret-scanning/pattern-configurations + documentation_url: https://docs.github.com/rest/secret-scanning/push-protection#list-organization-pattern-configurations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/secret-scanning/pattern-configurations + documentation_url: https://docs.github.com/rest/secret-scanning/push-protection#update-organization-pattern-configurations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/security-advisories + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#list-repository-security-advisories-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/security-managers + documentation_url: https://docs.github.com/rest/orgs/security-managers#list-security-manager-teams + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /orgs/{org}/security-managers/teams/{team_slug} + documentation_url: https://docs.github.com/rest/orgs/security-managers#remove-a-security-manager-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PUT /orgs/{org}/security-managers/teams/{team_slug} + documentation_url: https://docs.github.com/rest/orgs/security-managers#add-a-security-manager-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /orgs/{org}/settings/billing/advanced-security + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/billing/billing#get-github-advanced-security-active-committers-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/settings/immutable-releases + documentation_url: https://docs.github.com/rest/orgs/orgs#get-immutable-releases-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/settings/immutable-releases + documentation_url: https://docs.github.com/rest/orgs/orgs#set-immutable-releases-settings-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/settings/immutable-releases/repositories + documentation_url: https://docs.github.com/rest/orgs/orgs#list-selected-repositories-for-immutable-releases-enforcement + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/settings/immutable-releases/repositories + documentation_url: https://docs.github.com/rest/orgs/orgs#set-selected-repositories-for-immutable-releases-enforcement + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/settings/immutable-releases/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/orgs/orgs#disable-a-selected-repository-for-immutable-releases-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/settings/immutable-releases/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/orgs/orgs#enable-a-selected-repository-for-immutable-releases-in-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/settings/network-configurations + documentation_url: https://docs.github.com/rest/orgs/network-configurations#list-hosted-compute-network-configurations-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /orgs/{org}/settings/network-configurations + documentation_url: https://docs.github.com/rest/orgs/network-configurations#create-a-hosted-compute-network-configuration-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /orgs/{org}/settings/network-configurations/{network_configuration_id} + documentation_url: https://docs.github.com/rest/orgs/network-configurations#delete-a-hosted-compute-network-configuration-from-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/settings/network-configurations/{network_configuration_id} + documentation_url: https://docs.github.com/rest/orgs/network-configurations#get-a-hosted-compute-network-configuration-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/settings/network-configurations/{network_configuration_id} + documentation_url: https://docs.github.com/rest/orgs/network-configurations#update-a-hosted-compute-network-configuration-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/settings/network-settings/{network_settings_id} + documentation_url: https://docs.github.com/rest/orgs/network-configurations#get-a-hosted-compute-network-settings-resource-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/team-sync/groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync#list-idp-groups-for-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/teams + documentation_url: https://docs.github.com/rest/teams/teams#list-teams + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/teams + documentation_url: https://docs.github.com/rest/teams/teams#create-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/teams/{team_slug} + documentation_url: https://docs.github.com/rest/teams/teams#delete-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug} + documentation_url: https://docs.github.com/rest/teams/teams#get-a-team-by-name + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/teams/{team_slug} + documentation_url: https://docs.github.com/rest/teams/teams#update-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/discussions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#list-discussions + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /orgs/{org}/teams/{team_slug}/discussions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#create-a-discussion + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#delete-a-discussion + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#get-a-discussion + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#update-a-discussion + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#list-discussion-comments + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#create-a-discussion-comment + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#delete-a-discussion-comment + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#get-a-discussion-comment + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#update-a-discussion-comment + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#list-reactions-for-a-team-discussion-comment + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion-comment + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions/{reaction_id} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#delete-team-discussion-comment-reaction + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#list-reactions-for-a-team-discussion + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions/{reaction_id} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#delete-team-discussion-reaction + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /orgs/{org}/teams/{team_slug}/external-groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups#remove-the-connection-between-an-external-group-and-a-team + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/external-groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups#list-a-connection-between-an-external-group-and-a-team + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /orgs/{org}/teams/{team_slug}/external-groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/external-groups#update-the-connection-between-an-external-group-and-a-team + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/invitations + documentation_url: https://docs.github.com/rest/teams/members#list-pending-team-invitations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/teams/{team_slug}/members + documentation_url: https://docs.github.com/rest/teams/members#list-team-members + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/teams/{team_slug}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members#remove-team-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members#get-team-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/teams/{team_slug}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members#add-or-update-team-membership-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#list-team-projects + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#remove-a-project-from-a-team + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#check-team-permissions-for-a-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: PUT /orgs/{org}/teams/{team_slug}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#add-or-update-team-project-permissions + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /orgs/{org}/teams/{team_slug}/repos + documentation_url: https://docs.github.com/rest/teams/teams#list-team-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams#remove-a-repository-from-a-team + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams#check-team-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams#add-or-update-team-repository-permissions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /orgs/{org}/teams/{team_slug}/team-sync/group-mappings + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync#list-idp-groups-for-a-team + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /orgs/{org}/teams/{team_slug}/team-sync/group-mappings + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync#create-or-update-idp-group-connections + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /orgs/{org}/teams/{team_slug}/teams + documentation_url: https://docs.github.com/rest/teams/teams#list-child-teams + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /orgs/{org}/{security_product}/{enablement} + documentation_url: https://docs.github.com/rest/orgs/orgs#enable-or-disable-a-security-feature-for-an-organization + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /projects/columns/cards/{card_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/cards#delete-a-project-card + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/columns/cards/{card_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/cards#get-a-project-card + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: PATCH /projects/columns/cards/{card_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/cards#update-an-existing-project-card + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: POST /projects/columns/cards/{card_id}/moves + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/cards#move-a-project-card + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: DELETE /projects/columns/{column_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/columns#delete-a-project-column + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/columns/{column_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/columns#get-a-project-column + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: PATCH /projects/columns/{column_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/columns#update-an-existing-project-column + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/columns/{column_id}/cards + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/cards#list-project-cards + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: POST /projects/columns/{column_id}/cards + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/cards#create-a-project-card + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: POST /projects/columns/{column_id}/moves + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/columns#move-a-project-column + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: DELETE /projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#delete-a-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#get-a-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: PATCH /projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#update-a-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/{project_id}/collaborators + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/collaborators#list-project-collaborators + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: DELETE /projects/{project_id}/collaborators/{username} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/collaborators#remove-user-as-a-collaborator + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: PUT /projects/{project_id}/collaborators/{username} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/collaborators#add-project-collaborator + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/{project_id}/collaborators/{username}/permission + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/collaborators#get-project-permission-for-a-user + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /projects/{project_id}/columns + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/columns#list-project-columns + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: POST /projects/{project_id}/columns + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/columns#create-a-project-column + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /rate_limit + documentation_url: https://docs.github.com/rest/rate-limit/rate-limit#get-rate-limit-status-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /reactions/{reaction_id} + documentation_url: https://docs.github.com/enterprise-server@3.4/rest/reference/reactions/#delete-a-reaction-legacy + openapi_files: + - descriptions/ghes-3.4/ghes-3.4.json + deprecated: true + - name: DELETE /repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/repos/repos#delete-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/repos/repos#get-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/repos/repos#update-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/artifacts + documentation_url: https://docs.github.com/rest/actions/artifacts#list-artifacts-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id} + documentation_url: https://docs.github.com/rest/actions/artifacts#delete-an-artifact + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id} + documentation_url: https://docs.github.com/rest/actions/artifacts#get-an-artifact + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format} + documentation_url: https://docs.github.com/rest/actions/artifacts#download-an-artifact + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/cache/retention-limit + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-retention-limit-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/actions/cache/retention-limit + documentation_url: https://docs.github.com/rest/actions/cache#set-github-actions-cache-retention-limit-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/cache/storage-limit + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-storage-limit-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/actions/cache/storage-limit + documentation_url: https://docs.github.com/rest/actions/cache#set-github-actions-cache-storage-limit-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/cache/usage + documentation_url: https://docs.github.com/rest/actions/cache#get-github-actions-cache-usage-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/cache/usage-policy + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/actions/cache#get-github-actions-cache-usage-policy-for-a-repository + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/actions/cache/usage-policy + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/actions/cache#set-github-actions-cache-usage-policy-for-a-repository + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/caches + documentation_url: https://docs.github.com/rest/actions/cache#delete-github-actions-caches-for-a-repository-using-a-cache-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/caches + documentation_url: https://docs.github.com/rest/actions/cache#list-github-actions-caches-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/caches/{cache_id} + documentation_url: https://docs.github.com/rest/actions/cache#delete-a-github-actions-cache-for-a-repository-using-a-cache-id + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/concurrency_groups + documentation_url: https://docs.github.com/rest/actions/concurrency-groups#list-concurrency-groups-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/concurrency_groups/{concurrency_group_name} + documentation_url: https://docs.github.com/rest/actions/concurrency-groups#get-a-concurrency-group-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/jobs/{job_id} + documentation_url: https://docs.github.com/rest/actions/workflow-jobs#get-a-job-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs + documentation_url: https://docs.github.com/rest/actions/workflow-jobs#download-job-logs-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun + documentation_url: https://docs.github.com/rest/actions/workflow-runs#re-run-a-job-from-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/oidc/customization/sub + documentation_url: https://docs.github.com/rest/actions/oidc#get-the-customization-template-for-an-oidc-subject-claim-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/oidc/customization/sub + documentation_url: https://docs.github.com/rest/actions/oidc#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/organization-secrets + documentation_url: https://docs.github.com/rest/actions/secrets#list-repository-organization-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/organization-variables + documentation_url: https://docs.github.com/rest/actions/variables#list-repository-organization-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions + documentation_url: https://docs.github.com/rest/actions/permissions#get-github-actions-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions + documentation_url: https://docs.github.com/rest/actions/permissions#set-github-actions-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions/access + documentation_url: https://docs.github.com/rest/actions/permissions#get-the-level-of-access-for-workflows-outside-of-the-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions/access + documentation_url: https://docs.github.com/rest/actions/permissions#set-the-level-of-access-for-workflows-outside-of-the-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention + documentation_url: https://docs.github.com/rest/actions/permissions#get-artifact-and-log-retention-settings-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions/artifact-and-log-retention + documentation_url: https://docs.github.com/rest/actions/permissions#set-artifact-and-log-retention-settings-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions/fork-pr-contributor-approval + documentation_url: https://docs.github.com/rest/actions/permissions#get-fork-pr-contributor-approval-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions/fork-pr-contributor-approval + documentation_url: https://docs.github.com/rest/actions/permissions#set-fork-pr-contributor-approval-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions/fork-pr-workflows-private-repos + documentation_url: https://docs.github.com/rest/actions/permissions#get-private-repo-fork-pr-workflow-settings-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions/fork-pr-workflows-private-repos + documentation_url: https://docs.github.com/rest/actions/permissions#set-private-repo-fork-pr-workflow-settings-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions/selected-actions + documentation_url: https://docs.github.com/rest/actions/permissions#get-allowed-actions-and-reusable-workflows-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions/selected-actions + documentation_url: https://docs.github.com/rest/actions/permissions#set-allowed-actions-and-reusable-workflows-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/permissions/workflow + documentation_url: https://docs.github.com/rest/actions/permissions#get-default-workflow-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/permissions/workflow + documentation_url: https://docs.github.com/rest/actions/permissions#set-default-workflow-permissions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runners + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#list-self-hosted-runners-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runners/deprecations/{version} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/actions/self-hosted-runners#get-runner-version-end-of-life-schedule-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/runners/downloads + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#list-runner-applications-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runners/generate-jitconfig + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#create-configuration-for-a-just-in-time-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runners/registration-token + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#create-a-registration-token-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runners/remove-token + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#create-a-remove-token-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/runners/{runner_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#delete-a-self-hosted-runner-from-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runners/{runner_id} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#get-a-self-hosted-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#remove-all-custom-labels-from-a-self-hosted-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#list-labels-for-a-self-hosted-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#add-custom-labels-to-a-self-hosted-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/runners/{runner_id}/labels + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#set-custom-labels-for-a-self-hosted-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels/{name} + documentation_url: https://docs.github.com/rest/actions/self-hosted-runners#remove-a-custom-label-from-a-self-hosted-runner-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs + documentation_url: https://docs.github.com/rest/actions/workflow-runs#list-workflow-runs-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/runs/{run_id} + documentation_url: https://docs.github.com/rest/actions/workflow-runs#delete-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id} + documentation_url: https://docs.github.com/rest/actions/workflow-runs#get-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/approvals + documentation_url: https://docs.github.com/rest/actions/workflow-runs#get-the-review-history-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve + documentation_url: https://docs.github.com/rest/actions/workflow-runs#approve-a-workflow-run-for-a-fork-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts + documentation_url: https://docs.github.com/rest/actions/artifacts#list-workflow-run-artifacts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number} + documentation_url: https://docs.github.com/rest/actions/workflow-runs#get-a-workflow-run-attempt + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs + documentation_url: https://docs.github.com/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run-attempt + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/logs + documentation_url: https://docs.github.com/rest/actions/workflow-runs#download-workflow-run-attempt-logs + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel + documentation_url: https://docs.github.com/rest/actions/workflow-runs#cancel-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/concurrency_groups + documentation_url: https://docs.github.com/rest/actions/concurrency-groups#list-concurrency-groups-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/deployment_protection_rule + documentation_url: https://docs.github.com/rest/actions/workflow-runs#review-custom-deployment-protection-rules-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/force-cancel + documentation_url: https://docs.github.com/rest/actions/workflow-runs#force-cancel-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs + documentation_url: https://docs.github.com/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs + documentation_url: https://docs.github.com/rest/actions/workflow-runs#delete-workflow-run-logs + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs + documentation_url: https://docs.github.com/rest/actions/workflow-runs#download-workflow-run-logs + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments + documentation_url: https://docs.github.com/rest/actions/workflow-runs#get-pending-deployments-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments + documentation_url: https://docs.github.com/rest/actions/workflow-runs#review-pending-deployments-for-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun + documentation_url: https://docs.github.com/rest/actions/workflow-runs#re-run-a-workflow + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs + documentation_url: https://docs.github.com/rest/actions/workflow-runs#re-run-failed-jobs-from-a-workflow-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/runs/{run_id}/timing + documentation_url: https://docs.github.com/rest/actions/workflow-runs#get-workflow-run-usage + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/actions/secrets + documentation_url: https://docs.github.com/rest/actions/secrets#list-repository-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/secrets/public-key + documentation_url: https://docs.github.com/rest/actions/secrets#get-a-repository-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#delete-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#get-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#create-or-update-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/variables + documentation_url: https://docs.github.com/rest/actions/variables#list-repository-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/variables + documentation_url: https://docs.github.com/rest/actions/variables#create-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/actions/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#delete-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#get-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/actions/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#update-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/workflows + documentation_url: https://docs.github.com/rest/actions/workflows#list-repository-workflows + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} + documentation_url: https://docs.github.com/rest/actions/workflows#get-a-workflow + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable + documentation_url: https://docs.github.com/rest/actions/workflows#disable-a-workflow + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches + documentation_url: https://docs.github.com/rest/actions/workflows#create-a-workflow-dispatch-event + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable + documentation_url: https://docs.github.com/rest/actions/workflows#enable-a-workflow + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs + documentation_url: https://docs.github.com/rest/actions/workflow-runs#list-workflow-runs-for-a-workflow + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing + documentation_url: https://docs.github.com/rest/actions/workflows#get-workflow-usage + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/activity + documentation_url: https://docs.github.com/rest/repos/repos#list-repository-activities + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/agents/organization-secrets + documentation_url: https://docs.github.com/rest/agents/secrets#list-repository-organization-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/agents/organization-variables + documentation_url: https://docs.github.com/rest/agents/variables#list-repository-organization-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/agents/secrets + documentation_url: https://docs.github.com/rest/agents/secrets#list-repository-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/agents/secrets/public-key + documentation_url: https://docs.github.com/rest/agents/secrets#get-a-repository-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets#delete-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets#get-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/agents/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/agents/secrets#create-or-update-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/agents/variables + documentation_url: https://docs.github.com/rest/agents/variables#list-repository-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/agents/variables + documentation_url: https://docs.github.com/rest/agents/variables#create-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/agents/variables/{name} + documentation_url: https://docs.github.com/rest/agents/variables#delete-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/agents/variables/{name} + documentation_url: https://docs.github.com/rest/agents/variables#get-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /repos/{owner}/{repo}/agents/variables/{name} + documentation_url: https://docs.github.com/rest/agents/variables#update-a-repository-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/assignees + documentation_url: https://docs.github.com/rest/issues/assignees#list-assignees + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/assignees/{assignee} + documentation_url: https://docs.github.com/rest/issues/assignees#check-if-a-user-can-be-assigned + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/attestations + documentation_url: https://docs.github.com/rest/repos/attestations#create-an-attestation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/attestations/{subject_digest} + documentation_url: https://docs.github.com/rest/repos/attestations#list-attestations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/autolinks + documentation_url: https://docs.github.com/rest/repos/autolinks#get-all-autolinks-of-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/autolinks + documentation_url: https://docs.github.com/rest/repos/autolinks#create-an-autolink-reference-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/autolinks/{autolink_id} + documentation_url: https://docs.github.com/rest/repos/autolinks#delete-an-autolink-reference-from-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/autolinks/{autolink_id} + documentation_url: https://docs.github.com/rest/repos/autolinks#get-an-autolink-reference-of-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/automated-security-fixes + documentation_url: https://docs.github.com/rest/repos/repos#disable-dependabot-security-updates + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/automated-security-fixes + documentation_url: https://docs.github.com/rest/repos/repos#check-if-dependabot-security-updates-are-enabled-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/automated-security-fixes + documentation_url: https://docs.github.com/rest/repos/repos#enable-dependabot-security-updates + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/branches + documentation_url: https://docs.github.com/rest/branches/branches#list-branches + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch} + documentation_url: https://docs.github.com/rest/branches/branches#get-a-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection + documentation_url: https://docs.github.com/rest/branches/branch-protection#delete-branch-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-branch-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/branches/{branch}/protection + documentation_url: https://docs.github.com/rest/branches/branch-protection#update-branch-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins + documentation_url: https://docs.github.com/rest/branches/branch-protection#delete-admin-branch-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-admin-branch-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins + documentation_url: https://docs.github.com/rest/branches/branch-protection#set-admin-branch-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews + documentation_url: https://docs.github.com/rest/branches/branch-protection#delete-pull-request-review-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-pull-request-review-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews + documentation_url: https://docs.github.com/rest/branches/branch-protection#update-pull-request-review-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures + documentation_url: https://docs.github.com/rest/branches/branch-protection#delete-commit-signature-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-commit-signature-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures + documentation_url: https://docs.github.com/rest/branches/branch-protection#create-commit-signature-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks + documentation_url: https://docs.github.com/rest/branches/branch-protection#remove-status-check-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-status-checks-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks + documentation_url: https://docs.github.com/rest/branches/branch-protection#update-status-check-protection + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts + documentation_url: https://docs.github.com/rest/branches/branch-protection#remove-status-check-contexts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-all-status-check-contexts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts + documentation_url: https://docs.github.com/rest/branches/branch-protection#add-status-check-contexts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts + documentation_url: https://docs.github.com/rest/branches/branch-protection#set-status-check-contexts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions + documentation_url: https://docs.github.com/rest/branches/branch-protection#delete-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps + documentation_url: https://docs.github.com/rest/branches/branch-protection#remove-app-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-apps-with-access-to-the-protected-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps + documentation_url: https://docs.github.com/rest/branches/branch-protection#add-app-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps + documentation_url: https://docs.github.com/rest/branches/branch-protection#set-app-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams + documentation_url: https://docs.github.com/rest/branches/branch-protection#remove-team-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-teams-with-access-to-the-protected-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams + documentation_url: https://docs.github.com/rest/branches/branch-protection#add-team-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams + documentation_url: https://docs.github.com/rest/branches/branch-protection#set-team-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users + documentation_url: https://docs.github.com/rest/branches/branch-protection#remove-user-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users + documentation_url: https://docs.github.com/rest/branches/branch-protection#get-users-with-access-to-the-protected-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users + documentation_url: https://docs.github.com/rest/branches/branch-protection#add-user-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users + documentation_url: https://docs.github.com/rest/branches/branch-protection#set-user-access-restrictions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/branches/{branch}/rename + documentation_url: https://docs.github.com/rest/branches/branches#rename-a-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/bypass-requests/push-rules + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/repos/bypass-requests#list-repository-push-rule-bypass-requests + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/bypass-requests/push-rules/{bypass_request_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/repos/bypass-requests#get-a-repository-push-bypass-request + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/bypass-requests/secret-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/delegated-bypass#list-bypass-requests-for-secret-scanning-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/bypass-requests/secret-scanning/{bypass_request_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/delegated-bypass#get-a-bypass-request-for-secret-scanning + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/bypass-requests/secret-scanning/{bypass_request_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/delegated-bypass#review-a-bypass-request-for-secret-scanning + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/bypass-responses/secret-scanning/{bypass_response_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/delegated-bypass#dismiss-a-response-on-a-bypass-request-for-secret-scanning + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/check-runs + documentation_url: https://docs.github.com/rest/checks/runs#create-a-check-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/check-runs/{check_run_id} + documentation_url: https://docs.github.com/rest/checks/runs#get-a-check-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/check-runs/{check_run_id} + documentation_url: https://docs.github.com/rest/checks/runs#update-a-check-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations + documentation_url: https://docs.github.com/rest/checks/runs#list-check-run-annotations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/check-runs/{check_run_id}/rerequest + documentation_url: https://docs.github.com/rest/checks/runs#rerequest-a-check-run + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/check-suites + documentation_url: https://docs.github.com/rest/checks/suites#create-a-check-suite + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/check-suites/preferences + documentation_url: https://docs.github.com/rest/checks/suites#update-repository-preferences-for-check-suites + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/check-suites/{check_suite_id} + documentation_url: https://docs.github.com/rest/checks/suites#get-a-check-suite + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs + documentation_url: https://docs.github.com/rest/checks/runs#list-check-runs-in-a-check-suite + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/check-suites/{check_suite_id}/rerequest + documentation_url: https://docs.github.com/rest/checks/suites#rerequest-a-check-suite + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-quality/findings + documentation_url: https://docs.github.com/rest/code-quality/code-quality#list-code-quality-findings-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-quality/findings/{finding_number} + documentation_url: https://docs.github.com/rest/code-quality/code-quality#get-a-code-quality-finding + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-quality/setup + documentation_url: https://docs.github.com/rest/code-quality/code-quality#get-a-code-quality-setup-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /repos/{owner}/{repo}/code-quality/setup + documentation_url: https://docs.github.com/rest/code-quality/code-quality#update-a-code-quality-setup-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-scanning/alerts + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#list-code-scanning-alerts-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-a-code-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#update-a-code-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/autofix + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-the-status-of-an-autofix-for-a-code-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/autofix + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#create-an-autofix-for-a-code-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/autofix/commits + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#commit-an-autofix-for-a-code-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#list-instances-of-a-code-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-scanning/analyses + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#list-code-scanning-analyses-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#delete-a-code-scanning-analysis-from-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-a-code-scanning-analysis-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-scanning/codeql/databases + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#list-codeql-databases-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/code-scanning/codeql/databases/{language} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#delete-a-codeql-database + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-scanning/codeql/databases/{language} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-a-codeql-database-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/code-scanning/codeql/variant-analyses + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#create-a-codeql-variant-analysis + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-scanning/codeql/variant-analyses/{codeql_variant_analysis_id} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-the-summary-of-a-codeql-variant-analysis + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-scanning/codeql/variant-analyses/{codeql_variant_analysis_id}/repos/{repo_owner}/{repo_name} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-the-analysis-status-of-a-repository-in-a-codeql-variant-analysis + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/code-scanning/default-setup + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-a-code-scanning-default-setup-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/code-scanning/default-setup + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#update-a-code-scanning-default-setup-configuration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/code-scanning/sarifs + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#upload-an-analysis-as-sarif-data + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id} + documentation_url: https://docs.github.com/rest/code-scanning/code-scanning#get-information-about-a-sarif-upload + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/code-security-configuration + documentation_url: https://docs.github.com/rest/code-security/configurations#get-the-code-security-configuration-associated-with-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/codeowners/errors + documentation_url: https://docs.github.com/rest/repos/repos#list-codeowners-errors + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/codespaces + documentation_url: https://docs.github.com/rest/codespaces/codespaces#list-codespaces-in-a-repository-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/codespaces + documentation_url: https://docs.github.com/rest/codespaces/codespaces#create-a-codespace-in-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/devcontainers + documentation_url: https://docs.github.com/rest/codespaces/codespaces#list-devcontainer-configurations-in-a-repository-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/machines + documentation_url: https://docs.github.com/rest/codespaces/machines#list-available-machine-types-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/new + documentation_url: https://docs.github.com/rest/codespaces/codespaces#get-default-attributes-for-a-codespace + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/permissions_check + documentation_url: https://docs.github.com/rest/codespaces/codespaces#check-if-permissions-defined-by-a-devcontainer-have-been-accepted-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/secrets + documentation_url: https://docs.github.com/rest/codespaces/repository-secrets#list-repository-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/secrets/public-key + documentation_url: https://docs.github.com/rest/codespaces/repository-secrets#get-a-repository-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/repository-secrets#delete-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/repository-secrets#get-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/repository-secrets#create-or-update-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/collaborators + documentation_url: https://docs.github.com/rest/collaborators/collaborators#list-repository-collaborators + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/collaborators/{username} + documentation_url: https://docs.github.com/rest/collaborators/collaborators#remove-a-repository-collaborator + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/collaborators/{username} + documentation_url: https://docs.github.com/rest/collaborators/collaborators#check-if-a-user-is-a-repository-collaborator + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/collaborators/{username} + documentation_url: https://docs.github.com/rest/collaborators/collaborators#add-a-repository-collaborator + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/collaborators/{username}/permission + documentation_url: https://docs.github.com/rest/collaborators/collaborators#get-repository-permissions-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/comments + documentation_url: https://docs.github.com/rest/commits/comments#list-commit-comments-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/comments/{comment_id} + documentation_url: https://docs.github.com/rest/commits/comments#delete-a-commit-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/comments/{comment_id} + documentation_url: https://docs.github.com/rest/commits/comments#get-a-commit-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/comments/{comment_id} + documentation_url: https://docs.github.com/rest/commits/comments#update-a-commit-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/comments/{comment_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-commit-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/comments/{comment_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#create-reaction-for-a-commit-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id} + documentation_url: https://docs.github.com/rest/reactions/reactions#delete-a-commit-comment-reaction + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits + documentation_url: https://docs.github.com/rest/commits/commits#list-commits + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head + documentation_url: https://docs.github.com/rest/commits/commits#list-branches-for-head-commit + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{commit_sha}/comments + documentation_url: https://docs.github.com/rest/commits/comments#list-commit-comments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/commits/{commit_sha}/comments + documentation_url: https://docs.github.com/rest/commits/comments#create-a-commit-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls + documentation_url: https://docs.github.com/rest/commits/commits#list-pull-requests-associated-with-a-commit + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{ref} + documentation_url: https://docs.github.com/rest/commits/commits#get-a-commit + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{ref}/check-runs + documentation_url: https://docs.github.com/rest/checks/runs#list-check-runs-for-a-git-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{ref}/check-suites + documentation_url: https://docs.github.com/rest/checks/suites#list-check-suites-for-a-git-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{ref}/status + documentation_url: https://docs.github.com/rest/commits/statuses#get-the-combined-status-for-a-specific-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/commits/{ref}/statuses + documentation_url: https://docs.github.com/rest/commits/statuses#list-commit-statuses-for-a-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/community/profile + documentation_url: https://docs.github.com/rest/metrics/community#get-community-profile-metrics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/compare/{basehead} + documentation_url: https://docs.github.com/rest/commits/commits#compare-two-commits + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/content_references/{content_reference_id}/attachments + documentation_url: https://docs.github.com/enterprise-server@3.3/rest/reference/apps#create-a-content-attachment + openapi_files: + - descriptions/ghes-3.3/ghes-3.3.json + deprecated: true + - name: DELETE /repos/{owner}/{repo}/contents/{path} + documentation_url: https://docs.github.com/rest/repos/contents#delete-a-file + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/contents/{path} + documentation_url: https://docs.github.com/rest/repos/contents#get-repository-content + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/contents/{path} + documentation_url: https://docs.github.com/rest/repos/contents#create-or-update-file-contents + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/contributors + documentation_url: https://docs.github.com/rest/repos/repos#list-repository-contributors + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/copilot/cloud-agent/configuration + documentation_url: https://docs.github.com/rest/copilot/copilot-cloud-agent-management#get-copilot-cloud-agent-configuration-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/dependabot/alerts + documentation_url: https://docs.github.com/rest/dependabot/alerts#list-dependabot-alerts-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependabot/alerts/{alert_number} + documentation_url: https://docs.github.com/rest/dependabot/alerts#get-a-dependabot-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number} + documentation_url: https://docs.github.com/rest/dependabot/alerts#update-a-dependabot-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependabot/secrets + documentation_url: https://docs.github.com/rest/dependabot/secrets#list-repository-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependabot/secrets/public-key + documentation_url: https://docs.github.com/rest/dependabot/secrets#get-a-repository-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/dependabot/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/dependabot/secrets#delete-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependabot/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/dependabot/secrets#get-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/dependabot/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/dependabot/secrets#create-or-update-a-repository-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead} + documentation_url: https://docs.github.com/rest/dependency-graph/dependency-review#get-a-diff-of-the-dependencies-between-commits + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependency-graph/sbom + documentation_url: https://docs.github.com/rest/dependency-graph/sboms#export-a-software-bill-of-materials-sbom-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dependency-graph/sbom/fetch-report/{sbom_uuid} + documentation_url: https://docs.github.com/rest/dependency-graph/sboms#fetch-a-software-bill-of-materials-sbom-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/dependency-graph/sbom/generate-report + documentation_url: https://docs.github.com/rest/dependency-graph/sboms#request-generation-of-a-software-bill-of-materials-sbom-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/dependency-graph/snapshots + documentation_url: https://docs.github.com/rest/dependency-graph/dependency-submission#create-a-snapshot-of-dependencies-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/deployments + documentation_url: https://docs.github.com/rest/deployments/deployments#list-deployments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/deployments + documentation_url: https://docs.github.com/rest/deployments/deployments#create-a-deployment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/deployments/{deployment_id} + documentation_url: https://docs.github.com/rest/deployments/deployments#delete-a-deployment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/deployments/{deployment_id} + documentation_url: https://docs.github.com/rest/deployments/deployments#get-a-deployment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses + documentation_url: https://docs.github.com/rest/deployments/statuses#list-deployment-statuses + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses + documentation_url: https://docs.github.com/rest/deployments/statuses#create-a-deployment-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id} + documentation_url: https://docs.github.com/rest/deployments/statuses#get-a-deployment-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dismissal-requests/code-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/code-scanning/alert-dismissal-requests#list-dismissal-requests-for-code-scanning-alerts-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dismissal-requests/code-scanning/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/code-scanning/alert-dismissal-requests#get-a-dismissal-request-for-a-code-scanning-alert-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/dismissal-requests/code-scanning/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/code-scanning/alert-dismissal-requests#review-a-dismissal-request-for-a-code-scanning-alert-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dismissal-requests/dependabot + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/dependabot/alert-dismissal-requests#list-dismissal-requests-for-dependabot-alerts-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/dismissal-requests/dependabot/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/dependabot/alert-dismissal-requests#cancel-a-dismissal-request-for-a-dependabot-alert-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dismissal-requests/dependabot/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/dependabot/alert-dismissal-requests#get-a-dismissal-request-for-a-dependabot-alert-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/dismissal-requests/dependabot/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/dependabot/alert-dismissal-requests#review-a-dismissal-request-for-a-dependabot-alert-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/dismissal-requests/dependabot/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/dependabot/alert-dismissal-requests#create-a-dismissal-request-for-a-dependabot-alert-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dismissal-requests/secret-scanning + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/alert-dismissal-requests#list-alert-dismissal-requests-for-secret-scanning-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/dismissal-requests/secret-scanning/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/alert-dismissal-requests#get-an-alert-dismissal-request-for-secret-scanning + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/dismissal-requests/secret-scanning/{alert_number} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/secret-scanning/alert-dismissal-requests#review-an-alert-dismissal-request-for-secret-scanning + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/dispatches + documentation_url: https://docs.github.com/rest/repos/repos#create-a-repository-dispatch-event + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments + documentation_url: https://docs.github.com/rest/deployments/environments#list-environments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/environments/{environment_name} + documentation_url: https://docs.github.com/rest/deployments/environments#delete-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name} + documentation_url: https://docs.github.com/rest/deployments/environments#get-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/environments/{environment_name} + documentation_url: https://docs.github.com/rest/deployments/environments#create-or-update-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies + documentation_url: https://docs.github.com/rest/deployments/branch-policies#list-deployment-branch-policies + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies + documentation_url: https://docs.github.com/rest/deployments/branch-policies#create-a-deployment-branch-policy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id} + documentation_url: https://docs.github.com/rest/deployments/branch-policies#delete-a-deployment-branch-policy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id} + documentation_url: https://docs.github.com/rest/deployments/branch-policies#get-a-deployment-branch-policy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id} + documentation_url: https://docs.github.com/rest/deployments/branch-policies#update-a-deployment-branch-policy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules + documentation_url: https://docs.github.com/rest/deployments/protection-rules#get-all-deployment-protection-rules-for-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules + documentation_url: https://docs.github.com/rest/deployments/protection-rules#create-a-custom-deployment-protection-rule-on-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps + documentation_url: https://docs.github.com/rest/deployments/protection-rules#list-custom-deployment-rule-integrations-available-for-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id} + documentation_url: https://docs.github.com/rest/deployments/protection-rules#disable-a-custom-protection-rule-for-an-environment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id} + documentation_url: https://docs.github.com/rest/deployments/protection-rules#get-a-custom-deployment-protection-rule + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/secrets + documentation_url: https://docs.github.com/rest/actions/secrets#list-environment-secrets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/secrets/public-key + documentation_url: https://docs.github.com/rest/actions/secrets#get-an-environment-public-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#delete-an-environment-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#get-an-environment-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/actions/secrets#create-or-update-an-environment-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/variables + documentation_url: https://docs.github.com/rest/actions/variables#list-environment-variables + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/environments/{environment_name}/variables + documentation_url: https://docs.github.com/rest/actions/variables#create-an-environment-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/environments/{environment_name}/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#delete-an-environment-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/environments/{environment_name}/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#get-an-environment-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/environments/{environment_name}/variables/{name} + documentation_url: https://docs.github.com/rest/actions/variables#update-an-environment-variable + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/events + documentation_url: https://docs.github.com/rest/activity/events#list-repository-events + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/forks + documentation_url: https://docs.github.com/rest/repos/forks#list-forks + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/forks + documentation_url: https://docs.github.com/rest/repos/forks#create-a-fork + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/git/blobs + documentation_url: https://docs.github.com/rest/git/blobs#create-a-blob + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/git/blobs/{file_sha} + documentation_url: https://docs.github.com/rest/git/blobs#get-a-blob + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/git/commits + documentation_url: https://docs.github.com/rest/git/commits#create-a-commit + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/git/commits/{commit_sha} + documentation_url: https://docs.github.com/rest/git/commits#get-a-commit-object + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/git/matching-refs/{ref} + documentation_url: https://docs.github.com/rest/git/refs#list-matching-references + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/git/ref/{ref} + documentation_url: https://docs.github.com/rest/git/refs#get-a-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/git/refs + documentation_url: https://docs.github.com/rest/git/refs#create-a-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/git/refs/{ref} + documentation_url: https://docs.github.com/rest/git/refs#delete-a-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/git/refs/{ref} + documentation_url: https://docs.github.com/rest/git/refs#update-a-reference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/git/tags + documentation_url: https://docs.github.com/rest/git/tags#create-a-tag-object + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/git/tags/{tag_sha} + documentation_url: https://docs.github.com/rest/git/tags#get-a-tag + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/git/trees + documentation_url: https://docs.github.com/rest/git/trees#create-a-tree + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/git/trees/{tree_sha} + documentation_url: https://docs.github.com/rest/git/trees#get-a-tree + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/hash-algorithm + documentation_url: https://docs.github.com/rest/repos/repos#get-the-hash-algorithm-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/hooks + documentation_url: https://docs.github.com/rest/repos/webhooks#list-repository-webhooks + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/hooks + documentation_url: https://docs.github.com/rest/repos/webhooks#create-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/hooks/{hook_id} + documentation_url: https://docs.github.com/rest/repos/webhooks#delete-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/hooks/{hook_id} + documentation_url: https://docs.github.com/rest/repos/webhooks#get-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/hooks/{hook_id} + documentation_url: https://docs.github.com/rest/repos/webhooks#update-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/hooks/{hook_id}/config + documentation_url: https://docs.github.com/rest/repos/webhooks#get-a-webhook-configuration-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/hooks/{hook_id}/config + documentation_url: https://docs.github.com/rest/repos/webhooks#update-a-webhook-configuration-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries + documentation_url: https://docs.github.com/rest/repos/webhooks#list-deliveries-for-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id} + documentation_url: https://docs.github.com/rest/repos/webhooks#get-a-delivery-for-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts + documentation_url: https://docs.github.com/rest/repos/webhooks#redeliver-a-delivery-for-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/hooks/{hook_id}/pings + documentation_url: https://docs.github.com/rest/repos/webhooks#ping-a-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/hooks/{hook_id}/tests + documentation_url: https://docs.github.com/rest/repos/webhooks#test-the-push-repository-webhook + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/immutable-releases + documentation_url: https://docs.github.com/rest/repos/repos#disable-immutable-releases + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/immutable-releases + documentation_url: https://docs.github.com/rest/repos/repos#check-if-immutable-releases-are-enabled-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/immutable-releases + documentation_url: https://docs.github.com/rest/repos/repos#enable-immutable-releases + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/import + documentation_url: https://docs.github.com/rest/migrations/source-imports#cancel-an-import + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /repos/{owner}/{repo}/import + documentation_url: https://docs.github.com/rest/migrations/source-imports#get-an-import-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: PATCH /repos/{owner}/{repo}/import + documentation_url: https://docs.github.com/rest/migrations/source-imports#update-an-import + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: PUT /repos/{owner}/{repo}/import + documentation_url: https://docs.github.com/rest/migrations/source-imports#start-an-import + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /repos/{owner}/{repo}/import/authors + documentation_url: https://docs.github.com/rest/migrations/source-imports#get-commit-authors + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: PATCH /repos/{owner}/{repo}/import/authors/{author_id} + documentation_url: https://docs.github.com/rest/migrations/source-imports#map-a-commit-author + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /repos/{owner}/{repo}/import/large_files + documentation_url: https://docs.github.com/rest/migrations/source-imports#get-large-files + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: PATCH /repos/{owner}/{repo}/import/lfs + documentation_url: https://docs.github.com/rest/migrations/source-imports#update-git-lfs-preference + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /repos/{owner}/{repo}/installation + documentation_url: https://docs.github.com/rest/apps/apps#get-a-repository-installation-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/repos#remove-interaction-restrictions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/repos#get-interaction-restrictions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/repos#set-interaction-restrictions-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/interaction-limits/pulls/bypass-list + documentation_url: https://docs.github.com/rest/interactions/repos#remove-users-from-the-pull-request-creation-cap-bypass-list-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/interaction-limits/pulls/bypass-list + documentation_url: https://docs.github.com/rest/interactions/repos#get-pull-request-creation-cap-bypass-list-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/interaction-limits/pulls/bypass-list + documentation_url: https://docs.github.com/rest/interactions/repos#add-users-to-the-pull-request-creation-cap-bypass-list-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/interaction-limits/pulls/creation-cap + documentation_url: https://docs.github.com/rest/interactions/repos#get-pull-request-creation-cap-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /repos/{owner}/{repo}/interaction-limits/pulls/creation-cap + documentation_url: https://docs.github.com/rest/interactions/repos#update-pull-request-creation-cap-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/invitations + documentation_url: https://docs.github.com/rest/collaborators/invitations#list-repository-invitations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/invitations/{invitation_id} + documentation_url: https://docs.github.com/rest/collaborators/invitations#delete-a-repository-invitation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/invitations/{invitation_id} + documentation_url: https://docs.github.com/rest/collaborators/invitations#update-a-repository-invitation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issue-types + documentation_url: https://docs.github.com/rest/repos/issue-types#list-issue-types-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/issues + documentation_url: https://docs.github.com/rest/issues/issues#list-repository-issues + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues + documentation_url: https://docs.github.com/rest/issues/issues#create-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/comments + documentation_url: https://docs.github.com/rest/issues/comments#list-issue-comments-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/comments/{comment_id} + documentation_url: https://docs.github.com/rest/issues/comments#delete-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/comments/{comment_id} + documentation_url: https://docs.github.com/rest/issues/comments#get-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/issues/comments/{comment_id} + documentation_url: https://docs.github.com/rest/issues/comments#update-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/pin + documentation_url: https://docs.github.com/rest/issues/comments#unpin-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/issues/comments/{comment_id}/pin + documentation_url: https://docs.github.com/rest/issues/comments#pin-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#list-reactions-for-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#create-reaction-for-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id} + documentation_url: https://docs.github.com/rest/reactions/reactions#delete-an-issue-comment-reaction + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/events + documentation_url: https://docs.github.com/rest/issues/events#list-issue-events-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/events/{event_id} + documentation_url: https://docs.github.com/rest/issues/events#get-an-issue-event + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number} + documentation_url: https://docs.github.com/rest/issues/issues#get-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/issues/{issue_number} + documentation_url: https://docs.github.com/rest/issues/issues#update-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees + documentation_url: https://docs.github.com/rest/issues/assignees#remove-assignees-from-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/assignees + documentation_url: https://docs.github.com/rest/issues/assignees#add-assignees-to-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/assignees/{assignee} + documentation_url: https://docs.github.com/rest/issues/assignees#check-if-a-user-can-be-assigned-to-a-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/comments + documentation_url: https://docs.github.com/rest/issues/comments#list-issue-comments + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/comments + documentation_url: https://docs.github.com/rest/issues/comments#create-an-issue-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by + documentation_url: https://docs.github.com/rest/issues/issue-dependencies#list-dependencies-an-issue-is-blocked-by + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by + documentation_url: https://docs.github.com/rest/issues/issue-dependencies#add-a-dependency-an-issue-is-blocked-by + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by/{issue_id} + documentation_url: https://docs.github.com/rest/issues/issue-dependencies#remove-dependency-an-issue-is-blocked-by + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking + documentation_url: https://docs.github.com/rest/issues/issue-dependencies#list-dependencies-an-issue-is-blocking + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/events + documentation_url: https://docs.github.com/rest/issues/events#list-issue-events + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values + documentation_url: https://docs.github.com/rest/issues/issue-field-values#list-issue-field-values-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values + documentation_url: https://docs.github.com/rest/issues/issue-field-values#add-issue-field-values-to-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values + documentation_url: https://docs.github.com/rest/issues/issue-field-values#set-issue-field-values-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/issue-field-values/{issue_field_id} + documentation_url: https://docs.github.com/rest/issues/issue-field-values#delete-an-issue-field-value-from-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels + documentation_url: https://docs.github.com/rest/issues/labels#remove-all-labels-from-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/labels + documentation_url: https://docs.github.com/rest/issues/labels#list-labels-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/labels + documentation_url: https://docs.github.com/rest/issues/labels#add-labels-to-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/issues/{issue_number}/labels + documentation_url: https://docs.github.com/rest/issues/labels#set-labels-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name} + documentation_url: https://docs.github.com/rest/issues/labels#remove-a-label-from-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/lock + documentation_url: https://docs.github.com/rest/issues/issues#unlock-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/issues/{issue_number}/lock + documentation_url: https://docs.github.com/rest/issues/issues#lock-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/parent + documentation_url: https://docs.github.com/rest/issues/sub-issues#get-parent-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#list-reactions-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#create-reaction-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id} + documentation_url: https://docs.github.com/rest/reactions/reactions#delete-an-issue-reaction + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/issues/{issue_number}/sub_issue + documentation_url: https://docs.github.com/rest/issues/sub-issues#remove-sub-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/sub_issues + documentation_url: https://docs.github.com/rest/issues/sub-issues#list-sub-issues + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/sub_issues + documentation_url: https://docs.github.com/rest/issues/sub-issues#add-sub-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority + documentation_url: https://docs.github.com/rest/issues/sub-issues#reprioritize-sub-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/suggestions + documentation_url: https://docs.github.com/rest/issues/issues#list-issue-suggestions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/suggestions/{suggestion_id}/approve + documentation_url: https://docs.github.com/rest/issues/issues#approve-an-issue-suggestion + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/issues/{issue_number}/suggestions/{suggestion_id}/dismiss + documentation_url: https://docs.github.com/rest/issues/issues#dismiss-an-issue-suggestion + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/issues/{issue_number}/timeline + documentation_url: https://docs.github.com/rest/issues/timeline#list-timeline-events-for-an-issue + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/keys + documentation_url: https://docs.github.com/rest/deploy-keys/deploy-keys#list-deploy-keys + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/keys + documentation_url: https://docs.github.com/rest/deploy-keys/deploy-keys#create-a-deploy-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/keys/{key_id} + documentation_url: https://docs.github.com/rest/deploy-keys/deploy-keys#delete-a-deploy-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/keys/{key_id} + documentation_url: https://docs.github.com/rest/deploy-keys/deploy-keys#get-a-deploy-key + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/labels + documentation_url: https://docs.github.com/rest/issues/labels#list-labels-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/labels + documentation_url: https://docs.github.com/rest/issues/labels#create-a-label + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/labels/{name} + documentation_url: https://docs.github.com/rest/issues/labels#delete-a-label + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/labels/{name} + documentation_url: https://docs.github.com/rest/issues/labels#get-a-label + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/labels/{name} + documentation_url: https://docs.github.com/rest/issues/labels#update-a-label + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/languages + documentation_url: https://docs.github.com/rest/repos/repos#list-repository-languages + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/lfs + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/repos/lfs#disable-git-lfs-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/lfs + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/repos/lfs#enable-git-lfs-for-a-repository + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/license + documentation_url: https://docs.github.com/rest/licenses/licenses#get-the-license-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/merge-upstream + documentation_url: https://docs.github.com/rest/branches/branches#sync-a-fork-branch-with-the-upstream-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/merges + documentation_url: https://docs.github.com/rest/branches/branches#merge-a-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/milestones + documentation_url: https://docs.github.com/rest/issues/milestones#list-milestones + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/milestones + documentation_url: https://docs.github.com/rest/issues/milestones#create-a-milestone + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/milestones/{milestone_number} + documentation_url: https://docs.github.com/rest/issues/milestones#delete-a-milestone + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/milestones/{milestone_number} + documentation_url: https://docs.github.com/rest/issues/milestones#get-a-milestone + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/milestones/{milestone_number} + documentation_url: https://docs.github.com/rest/issues/milestones#update-a-milestone + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels + documentation_url: https://docs.github.com/rest/issues/labels#list-labels-for-issues-in-a-milestone + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/notifications + documentation_url: https://docs.github.com/rest/activity/notifications#list-repository-notifications-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/notifications + documentation_url: https://docs.github.com/rest/activity/notifications#mark-repository-notifications-as-read + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#delete-a-apiname-pages-site + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#get-a-apiname-pages-site + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#create-a-apiname-pages-site + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/pages + documentation_url: https://docs.github.com/rest/pages/pages#update-information-about-a-apiname-pages-site + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pages/builds + documentation_url: https://docs.github.com/rest/pages/pages#list-apiname-pages-builds + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pages/builds + documentation_url: https://docs.github.com/rest/pages/pages#request-a-apiname-pages-build + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pages/builds/latest + documentation_url: https://docs.github.com/rest/pages/pages#get-latest-pages-build + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pages/builds/{build_id} + documentation_url: https://docs.github.com/rest/pages/pages#get-apiname-pages-build + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pages/deployment + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/pages/pages#create-a-github-pages-deployment + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: POST /repos/{owner}/{repo}/pages/deployments + documentation_url: https://docs.github.com/rest/pages/pages#create-a-github-pages-deployment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pages/deployments/{pages_deployment_id} + documentation_url: https://docs.github.com/rest/pages/pages#get-the-status-of-a-github-pages-deployment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pages/deployments/{pages_deployment_id}/cancel + documentation_url: https://docs.github.com/rest/pages/pages#cancel-a-github-pages-deployment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pages/health + documentation_url: https://docs.github.com/rest/pages/pages#get-a-dns-health-check-for-github-pages + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/pre-receive-hooks + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#list-pre-receive-hooks-for-a-repository + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#remove-pre-receive-hook-enforcement-for-a-repository + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#get-a-pre-receive-hook-for-a-repository + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/pre-receive-hooks/{pre_receive_hook_id} + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/repo-pre-receive-hooks#update-pre-receive-hook-enforcement-for-a-repository + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/private-vulnerability-reporting + documentation_url: https://docs.github.com/rest/repos/repos#disable-private-vulnerability-reporting-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/private-vulnerability-reporting + documentation_url: https://docs.github.com/rest/repos/repos#check-if-private-vulnerability-reporting-is-enabled-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /repos/{owner}/{repo}/private-vulnerability-reporting + documentation_url: https://docs.github.com/rest/repos/repos#enable-private-vulnerability-reporting-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#list-repository-projects + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: POST /repos/{owner}/{repo}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#create-a-repository-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /repos/{owner}/{repo}/properties/values + documentation_url: https://docs.github.com/rest/repos/custom-properties#get-all-custom-property-values-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/properties/values + documentation_url: https://docs.github.com/rest/repos/custom-properties#create-or-update-custom-property-values-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls + documentation_url: https://docs.github.com/rest/pulls/pulls#list-pull-requests + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls + documentation_url: https://docs.github.com/rest/pulls/pulls#create-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/comments + documentation_url: https://docs.github.com/rest/pulls/comments#list-review-comments-in-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id} + documentation_url: https://docs.github.com/rest/pulls/comments#delete-a-review-comment-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/comments/{comment_id} + documentation_url: https://docs.github.com/rest/pulls/comments#get-a-review-comment-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id} + documentation_url: https://docs.github.com/rest/pulls/comments#update-a-review-comment-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-pull-request-review-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#create-reaction-for-a-pull-request-review-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id} + documentation_url: https://docs.github.com/rest/reactions/reactions#delete-a-pull-request-comment-reaction + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number} + documentation_url: https://docs.github.com/rest/pulls/pulls#get-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/pulls/{pull_number} + documentation_url: https://docs.github.com/rest/pulls/pulls#update-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces + documentation_url: https://docs.github.com/rest/codespaces/codespaces#create-a-codespace-from-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/comments + documentation_url: https://docs.github.com/rest/pulls/comments#list-review-comments-on-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/{pull_number}/comments + documentation_url: https://docs.github.com/rest/pulls/comments#create-a-review-comment-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies + documentation_url: https://docs.github.com/rest/pulls/comments#create-a-reply-for-a-review-comment + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/commits + documentation_url: https://docs.github.com/rest/pulls/pulls#list-commits-on-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/files + documentation_url: https://docs.github.com/rest/pulls/pulls#list-pull-requests-files + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/merge + documentation_url: https://docs.github.com/rest/pulls/pulls#check-if-a-pull-request-has-been-merged + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge + documentation_url: https://docs.github.com/rest/pulls/pulls#merge-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge-async + documentation_url: https://docs.github.com/rest/pulls/pulls#merge-a-pull-request-asynchronously + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/merge-async/{uuid} + documentation_url: https://docs.github.com/rest/pulls/pulls#get-the-result-of-an-asynchronous-merge + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers + documentation_url: https://docs.github.com/rest/pulls/review-requests#remove-requested-reviewers-from-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers + documentation_url: https://docs.github.com/rest/pulls/review-requests#get-all-requested-reviewers-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers + documentation_url: https://docs.github.com/rest/pulls/review-requests#request-reviewers-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews + documentation_url: https://docs.github.com/rest/pulls/reviews#list-reviews-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews + documentation_url: https://docs.github.com/rest/pulls/reviews#create-a-review-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id} + documentation_url: https://docs.github.com/rest/pulls/reviews#delete-a-pending-review-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id} + documentation_url: https://docs.github.com/rest/pulls/reviews#get-a-review-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id} + documentation_url: https://docs.github.com/rest/pulls/reviews#update-a-review-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments + documentation_url: https://docs.github.com/rest/pulls/reviews#list-comments-for-a-pull-request-review + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals + documentation_url: https://docs.github.com/rest/pulls/reviews#dismiss-a-review-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events + documentation_url: https://docs.github.com/rest/pulls/reviews#submit-a-review-for-a-pull-request + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch + documentation_url: https://docs.github.com/rest/pulls/pulls#update-a-pull-request-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/readme + documentation_url: https://docs.github.com/rest/repos/contents#get-a-repository-readme + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/readme/{dir} + documentation_url: https://docs.github.com/rest/repos/contents#get-a-repository-readme-for-a-directory + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases + documentation_url: https://docs.github.com/rest/releases/releases#list-releases + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/releases + documentation_url: https://docs.github.com/rest/releases/releases#create-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/releases/assets/{asset_id} + documentation_url: https://docs.github.com/rest/releases/assets#delete-a-release-asset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases/assets/{asset_id} + documentation_url: https://docs.github.com/rest/releases/assets#get-a-release-asset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/releases/assets/{asset_id} + documentation_url: https://docs.github.com/rest/releases/assets#update-a-release-asset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/releases/generate-notes + documentation_url: https://docs.github.com/rest/releases/releases#generate-release-notes-content-for-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases/latest + documentation_url: https://docs.github.com/rest/releases/releases#get-the-latest-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases/tags/{tag} + documentation_url: https://docs.github.com/rest/releases/releases#get-a-release-by-tag-name + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/releases/{release_id} + documentation_url: https://docs.github.com/rest/releases/releases#delete-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases/{release_id} + documentation_url: https://docs.github.com/rest/releases/releases#get-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/releases/{release_id} + documentation_url: https://docs.github.com/rest/releases/releases#update-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases/{release_id}/assets + documentation_url: https://docs.github.com/rest/releases/assets#list-release-assets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/releases/{release_id}/assets + documentation_url: https://docs.github.com/rest/releases/assets#upload-a-release-asset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/releases/{release_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#list-reactions-for-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/releases/{release_id}/reactions + documentation_url: https://docs.github.com/rest/reactions/reactions#create-reaction-for-a-release + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id} + documentation_url: https://docs.github.com/rest/reactions/reactions#delete-a-release-reaction + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/replicas/caches + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/repos/repos#list-repository-cache-replication-status + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rules/branches/{branch} + documentation_url: https://docs.github.com/rest/repos/rules#get-rules-for-a-branch + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rulesets + documentation_url: https://docs.github.com/rest/repos/rules#get-all-repository-rulesets + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/rulesets + documentation_url: https://docs.github.com/rest/repos/rules#create-a-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rulesets/rule-suites + documentation_url: https://docs.github.com/rest/repos/rule-suites#list-repository-rule-suites + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rulesets/rule-suites/{rule_suite_id} + documentation_url: https://docs.github.com/rest/repos/rule-suites#get-a-repository-rule-suite + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/rest/repos/rules#delete-a-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/rest/repos/rules#get-a-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} + documentation_url: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rulesets/{ruleset_id}/history + documentation_url: https://docs.github.com/rest/repos/rules#get-repository-ruleset-history + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/rulesets/{ruleset_id}/history/{version_id} + documentation_url: https://docs.github.com/rest/repos/rules#get-repository-ruleset-version + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/secret-scanning/alerts + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#list-secret-scanning-alerts-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number} + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#get-a-secret-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number} + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#update-a-secret-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#list-locations-for-a-secret-scanning-alert + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#bulk-delete-repository-custom-patterns + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#list-repository-custom-patterns + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/secret-scanning/custom-patterns + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#bulk-create-repository-custom-patterns + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /repos/{owner}/{repo}/secret-scanning/custom-patterns/{pattern_id} + documentation_url: https://docs.github.com/rest/secret-scanning/custom-patterns#update-a-repository-custom-pattern + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/secret-scanning/push-protection-bypasses + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#create-a-push-protection-bypass + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/secret-scanning/scan-history + documentation_url: https://docs.github.com/rest/secret-scanning/secret-scanning#get-secret-scanning-scan-history-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/security-advisories + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#list-repository-security-advisories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/security-advisories + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#create-a-repository-security-advisory + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/security-advisories/reports + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#privately-report-a-security-vulnerability + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/security-advisories/{ghsa_id} + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#get-a-repository-security-advisory + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /repos/{owner}/{repo}/security-advisories/{ghsa_id} + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#update-a-repository-security-advisory + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/cve + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#request-a-cve-for-a-repository-security-advisory + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/forks + documentation_url: https://docs.github.com/rest/security-advisories/repository-advisories#create-a-temporary-private-fork + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/stacks + documentation_url: https://docs.github.com/rest/pulls/stacks#list-pull-request-stacks + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/stacks + documentation_url: https://docs.github.com/rest/pulls/stacks#create-a-pull-request-stack + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/stacks/{stack_number} + documentation_url: https://docs.github.com/rest/pulls/stacks#get-a-pull-request-stack + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/stacks/{stack_number}/add + documentation_url: https://docs.github.com/rest/pulls/stacks#add-pull-requests-to-a-pull-request-stack + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/stacks/{stack_number}/unstack + documentation_url: https://docs.github.com/rest/pulls/stacks#remove-pull-requests-from-a-pull-request-stack + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/stargazers + documentation_url: https://docs.github.com/rest/activity/starring#list-stargazers + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/stats/code_frequency + documentation_url: https://docs.github.com/rest/metrics/statistics#get-the-weekly-commit-activity + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/stats/commit_activity + documentation_url: https://docs.github.com/rest/metrics/statistics#get-the-last-year-of-commit-activity + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/stats/contributors + documentation_url: https://docs.github.com/rest/metrics/statistics#get-all-contributor-commit-activity + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/stats/participation + documentation_url: https://docs.github.com/rest/metrics/statistics#get-the-weekly-commit-count + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/stats/punch_card + documentation_url: https://docs.github.com/rest/metrics/statistics#get-the-hourly-commit-count-for-each-day + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{owner}/{repo}/statuses/{sha} + documentation_url: https://docs.github.com/rest/commits/statuses#create-a-commit-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/subscribers + documentation_url: https://docs.github.com/rest/activity/watching#list-watchers + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/subscription + documentation_url: https://docs.github.com/rest/activity/watching#delete-a-repository-subscription + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/subscription + documentation_url: https://docs.github.com/rest/activity/watching#get-a-repository-subscription + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/subscription + documentation_url: https://docs.github.com/rest/activity/watching#set-a-repository-subscription + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/tags + documentation_url: https://docs.github.com/rest/repos/repos#list-repository-tags + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/tags/protection + documentation_url: https://docs.github.com/enterprise-server@3.20/rest/repos/tags#closing-down---list-tag-protection-states-for-a-repository + openapi_files: + - descriptions/ghes-3.20/ghes-3.20.json + deprecated: true + - name: POST /repos/{owner}/{repo}/tags/protection + documentation_url: https://docs.github.com/enterprise-server@3.20/rest/repos/tags#closing-down---create-a-tag-protection-state-for-a-repository + openapi_files: + - descriptions/ghes-3.20/ghes-3.20.json + deprecated: true + - name: DELETE /repos/{owner}/{repo}/tags/protection/{tag_protection_id} + documentation_url: https://docs.github.com/enterprise-server@3.20/rest/repos/tags#closing-down---delete-a-tag-protection-state-for-a-repository + openapi_files: + - descriptions/ghes-3.20/ghes-3.20.json + deprecated: true + - name: GET /repos/{owner}/{repo}/tarball/{ref} + documentation_url: https://docs.github.com/rest/repos/contents#download-a-repository-archive-tar + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/teams + documentation_url: https://docs.github.com/rest/repos/repos#list-repository-teams + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/topics + documentation_url: https://docs.github.com/rest/repos/repos#get-all-repository-topics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/topics + documentation_url: https://docs.github.com/rest/repos/repos#replace-all-repository-topics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/traffic/clones + documentation_url: https://docs.github.com/rest/metrics/traffic#get-repository-clones + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/traffic/popular/paths + documentation_url: https://docs.github.com/rest/metrics/traffic#get-top-referral-paths + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/traffic/popular/referrers + documentation_url: https://docs.github.com/rest/metrics/traffic#get-top-referral-sources + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /repos/{owner}/{repo}/traffic/views + documentation_url: https://docs.github.com/rest/metrics/traffic#get-page-views + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /repos/{owner}/{repo}/transfer + documentation_url: https://docs.github.com/rest/repos/repos#transfer-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /repos/{owner}/{repo}/vulnerability-alerts + documentation_url: https://docs.github.com/rest/repos/repos#disable-vulnerability-alerts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/vulnerability-alerts + documentation_url: https://docs.github.com/rest/repos/repos#check-if-vulnerability-alerts-are-enabled-for-a-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /repos/{owner}/{repo}/vulnerability-alerts + documentation_url: https://docs.github.com/rest/repos/repos#enable-vulnerability-alerts + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repos/{owner}/{repo}/zipball/{ref} + documentation_url: https://docs.github.com/rest/repos/contents#download-a-repository-archive-zip + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /repos/{template_owner}/{template_repo}/generate + documentation_url: https://docs.github.com/rest/repos/repos#create-a-repository-using-a-template + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repositories + documentation_url: https://docs.github.com/rest/repos/repos#list-public-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /repositories/{repository_id}/environments/{environment_name}/secrets + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/actions/secrets#list-environment-secrets + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/actions/secrets#get-an-environment-public-key + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/actions/secrets#delete-an-environment-secret + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/actions/secrets#get-an-environment-secret + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/actions/secrets#create-or-update-an-environment-secret + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /scim/v2/Groups + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#list-provisioned-scim-groups-for-an-enterprise + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: POST /scim/v2/Groups + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#provision-a-scim-enterprise-group + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: DELETE /scim/v2/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#delete-a-scim-group-from-an-enterprise + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /scim/v2/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#get-scim-provisioning-information-for-an-enterprise-group + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: PATCH /scim/v2/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#update-an-attribute-for-a-scim-enterprise-group + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: PUT /scim/v2/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#set-scim-information-for-a-provisioned-enterprise-group + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /scim/v2/Users + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#list-scim-provisioned-identities-for-an-enterprise + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: POST /scim/v2/Users + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#provision-a-scim-enterprise-user + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: DELETE /scim/v2/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#delete-a-scim-user-from-an-enterprise + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /scim/v2/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#get-scim-provisioning-information-for-an-enterprise-user + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: PATCH /scim/v2/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#update-an-attribute-for-a-scim-enterprise-user + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: PUT /scim/v2/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-server@3.7/rest/enterprise-admin/scim#set-scim-information-for-a-provisioned-enterprise-user + openapi_files: + - descriptions/ghes-3.7/ghes-3.7.json + deprecated: true + - name: GET /scim/v2/enterprises/{enterprise}/Groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#list-provisioned-scim-groups-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /scim/v2/enterprises/{enterprise}/Groups + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#provision-a-scim-enterprise-group + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#delete-a-scim-group-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#get-scim-provisioning-information-for-an-enterprise-group + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#update-an-attribute-for-a-scim-enterprise-group + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /scim/v2/enterprises/{enterprise}/Groups/{scim_group_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#set-scim-information-for-a-provisioned-enterprise-group + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /scim/v2/enterprises/{enterprise}/Users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#list-scim-provisioned-identities-for-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /scim/v2/enterprises/{enterprise}/Users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#provision-a-scim-enterprise-user + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#delete-a-scim-user-from-an-enterprise + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#get-scim-provisioning-information-for-an-enterprise-user + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#update-an-attribute-for-a-scim-enterprise-user + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /scim/v2/enterprises/{enterprise}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#set-scim-information-for-a-provisioned-enterprise-user + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /scim/v2/organizations/{org}/Users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim#list-scim-provisioned-identities + openapi_files: + - descriptions/ghec/ghec.json + - name: POST /scim/v2/organizations/{org}/Users + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim#provision-and-invite-a-scim-user + openapi_files: + - descriptions/ghec/ghec.json + - name: DELETE /scim/v2/organizations/{org}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim#delete-a-scim-user-from-an-organization + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /scim/v2/organizations/{org}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim#get-scim-provisioning-information-for-a-user + openapi_files: + - descriptions/ghec/ghec.json + - name: PATCH /scim/v2/organizations/{org}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim#update-an-attribute-for-a-scim-user + openapi_files: + - descriptions/ghec/ghec.json + - name: PUT /scim/v2/organizations/{org}/Users/{scim_user_id} + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/scim/scim#update-a-provisioned-organization-membership + openapi_files: + - descriptions/ghec/ghec.json + - name: GET /search/code + documentation_url: https://docs.github.com/rest/search/search#search-code + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /search/commits + documentation_url: https://docs.github.com/rest/search/search#search-commits + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /search/issues + documentation_url: https://docs.github.com/rest/search/search#search-issues-and-pull-requests + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /search/labels + documentation_url: https://docs.github.com/rest/search/search#search-labels + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /search/repositories + documentation_url: https://docs.github.com/rest/search/search#search-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /search/topics + documentation_url: https://docs.github.com/rest/search/search#search-topics + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /search/users + documentation_url: https://docs.github.com/rest/search/search#search-users + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /setup/api/configcheck + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#get-the-configuration-status + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: POST /setup/api/configure + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#start-a-configuration-process + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: GET /setup/api/maintenance + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#get-the-maintenance-status + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: POST /setup/api/maintenance + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#enable-or-disable-maintenance-mode + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: GET /setup/api/settings + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#get-settings + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: PUT /setup/api/settings + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#set-settings + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: DELETE /setup/api/settings/authorized-keys + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#remove-an-authorized-ssh-key + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: GET /setup/api/settings/authorized-keys + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#get-all-authorized-ssh-keys + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: POST /setup/api/settings/authorized-keys + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#add-an-authorized-ssh-key + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: POST /setup/api/start + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#create-a-github-license + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: POST /setup/api/upgrade + documentation_url: https://docs.github.com/enterprise-server@3.14/rest/enterprise-admin/management-console#upgrade-a-license + openapi_files: + - descriptions/ghes-3.14/ghes-3.14.json + deprecated: true + - name: DELETE /teams/{team_id} + documentation_url: https://docs.github.com/rest/teams/teams#delete-a-team-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id} + documentation_url: https://docs.github.com/rest/teams/teams#get-a-team-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PATCH /teams/{team_id} + documentation_url: https://docs.github.com/rest/teams/teams#update-a-team-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id}/discussions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#list-discussions-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /teams/{team_id}/discussions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#create-a-discussion-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /teams/{team_id}/discussions/{discussion_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#delete-a-discussion-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /teams/{team_id}/discussions/{discussion_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#get-a-discussion-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: PATCH /teams/{team_id}/discussions/{discussion_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussions#update-a-discussion-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /teams/{team_id}/discussions/{discussion_number}/comments + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#list-discussion-comments-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /teams/{team_id}/discussions/{discussion_number}/comments + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#create-a-discussion-comment-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: DELETE /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#delete-a-discussion-comment-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#get-a-discussion-comment-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: PATCH /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number} + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/teams/discussion-comments#update-a-discussion-comment-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#list-reactions-for-a-team-discussion-comment-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion-comment-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /teams/{team_id}/discussions/{discussion_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#list-reactions-for-a-team-discussion-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: POST /teams/{team_id}/discussions/{discussion_number}/reactions + documentation_url: https://docs.github.com/enterprise-server@3.13/rest/reactions/reactions#create-reaction-for-a-team-discussion-legacy + openapi_files: + - descriptions/ghes-3.13/ghes-3.13.json + deprecated: true + - name: GET /teams/{team_id}/invitations + documentation_url: https://docs.github.com/rest/teams/members#list-pending-team-invitations-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /teams/{team_id}/members + documentation_url: https://docs.github.com/rest/teams/members#list-team-members-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /teams/{team_id}/members/{username} + documentation_url: https://docs.github.com/rest/teams/members#remove-team-member-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id}/members/{username} + documentation_url: https://docs.github.com/rest/teams/members#get-team-member-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PUT /teams/{team_id}/members/{username} + documentation_url: https://docs.github.com/rest/teams/members#add-team-member-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /teams/{team_id}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members#remove-team-membership-for-a-user-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members#get-team-membership-for-a-user-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PUT /teams/{team_id}/memberships/{username} + documentation_url: https://docs.github.com/rest/teams/members#add-or-update-team-membership-for-a-user-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#list-team-projects-legacy + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: DELETE /teams/{team_id}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#remove-a-project-from-a-team-legacy + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /teams/{team_id}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#check-team-permissions-for-a-project-legacy + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: PUT /teams/{team_id}/projects/{project_id} + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/teams/teams#add-or-update-team-project-permissions-legacy + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /teams/{team_id}/repos + documentation_url: https://docs.github.com/rest/teams/teams#list-team-repositories-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: DELETE /teams/{team_id}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams#remove-a-repository-from-a-team-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams#check-team-permissions-for-a-repository-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: PUT /teams/{team_id}/repos/{owner}/{repo} + documentation_url: https://docs.github.com/rest/teams/teams#add-or-update-team-repository-permissions-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /teams/{team_id}/team-sync/group-mappings + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync#list-idp-groups-for-a-team-legacy + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: PATCH /teams/{team_id}/team-sync/group-mappings + documentation_url: https://docs.github.com/enterprise-cloud@latest/rest/teams/team-sync#create-or-update-idp-group-connections-legacy + openapi_files: + - descriptions/ghec/ghec.json + deprecated: true + - name: GET /teams/{team_id}/teams + documentation_url: https://docs.github.com/rest/teams/teams#list-child-teams-legacy + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + deprecated: true + - name: GET /user + documentation_url: https://docs.github.com/rest/users/users#get-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /user + documentation_url: https://docs.github.com/rest/users/users#update-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/blocks + documentation_url: https://docs.github.com/rest/users/blocking#list-users-blocked-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /user/blocks/{username} + documentation_url: https://docs.github.com/rest/users/blocking#unblock-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/blocks/{username} + documentation_url: https://docs.github.com/rest/users/blocking#check-if-a-user-is-blocked-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /user/blocks/{username} + documentation_url: https://docs.github.com/rest/users/blocking#block-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces + documentation_url: https://docs.github.com/rest/codespaces/codespaces#list-codespaces-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /user/codespaces + documentation_url: https://docs.github.com/rest/codespaces/codespaces#create-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/secrets + documentation_url: https://docs.github.com/rest/codespaces/secrets#list-secrets-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/secrets/public-key + documentation_url: https://docs.github.com/rest/codespaces/secrets#get-public-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /user/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/secrets#delete-a-secret-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/secrets#get-a-secret-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /user/codespaces/secrets/{secret_name} + documentation_url: https://docs.github.com/rest/codespaces/secrets#create-or-update-a-secret-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/codespaces/secrets#list-selected-repositories-for-a-user-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /user/codespaces/secrets/{secret_name}/repositories + documentation_url: https://docs.github.com/rest/codespaces/secrets#set-selected-repositories-for-a-user-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /user/codespaces/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/codespaces/secrets#remove-a-selected-repository-from-a-user-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /user/codespaces/secrets/{secret_name}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/codespaces/secrets#add-a-selected-repository-to-a-user-secret + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /user/codespaces/{codespace_name} + documentation_url: https://docs.github.com/rest/codespaces/codespaces#delete-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/{codespace_name} + documentation_url: https://docs.github.com/rest/codespaces/codespaces#get-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PATCH /user/codespaces/{codespace_name} + documentation_url: https://docs.github.com/rest/codespaces/codespaces#update-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /user/codespaces/{codespace_name}/exports + documentation_url: https://docs.github.com/rest/codespaces/codespaces#export-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/{codespace_name}/exports/{export_id} + documentation_url: https://docs.github.com/rest/codespaces/codespaces#get-details-about-a-codespace-export + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/codespaces/{codespace_name}/machines + documentation_url: https://docs.github.com/rest/codespaces/machines#list-machine-types-for-a-codespace + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /user/codespaces/{codespace_name}/publish + documentation_url: https://docs.github.com/rest/codespaces/codespaces#create-a-repository-from-an-unpublished-codespace + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /user/codespaces/{codespace_name}/start + documentation_url: https://docs.github.com/rest/codespaces/codespaces#start-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /user/codespaces/{codespace_name}/stop + documentation_url: https://docs.github.com/rest/codespaces/codespaces#stop-a-codespace-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/docker/conflicts + documentation_url: https://docs.github.com/rest/packages/packages#get-list-of-conflicting-packages-during-docker-migration-for-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /user/email/visibility + documentation_url: https://docs.github.com/rest/users/emails#set-primary-email-visibility-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /user/emails + documentation_url: https://docs.github.com/rest/users/emails#delete-an-email-address-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/emails + documentation_url: https://docs.github.com/rest/users/emails#list-email-addresses-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/emails + documentation_url: https://docs.github.com/rest/users/emails#add-an-email-address-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/followers + documentation_url: https://docs.github.com/rest/users/followers#list-followers-of-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/following + documentation_url: https://docs.github.com/rest/users/followers#list-the-people-the-authenticated-user-follows + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/following/{username} + documentation_url: https://docs.github.com/rest/users/followers#unfollow-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/following/{username} + documentation_url: https://docs.github.com/rest/users/followers#check-if-a-person-is-followed-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /user/following/{username} + documentation_url: https://docs.github.com/rest/users/followers#follow-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/gpg_keys + documentation_url: https://docs.github.com/rest/users/gpg-keys#list-gpg-keys-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/gpg_keys + documentation_url: https://docs.github.com/rest/users/gpg-keys#create-a-gpg-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/gpg_keys/{gpg_key_id} + documentation_url: https://docs.github.com/rest/users/gpg-keys#delete-a-gpg-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/gpg_keys/{gpg_key_id} + documentation_url: https://docs.github.com/rest/users/gpg-keys#get-a-gpg-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/installations + documentation_url: https://docs.github.com/rest/apps/installations#list-app-installations-accessible-to-the-user-access-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/installations/{installation_id}/repositories + documentation_url: https://docs.github.com/rest/apps/installations#list-repositories-accessible-to-the-user-access-token + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/installations/{installation_id}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/apps/installations#remove-a-repository-from-an-app-installation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /user/installations/{installation_id}/repositories/{repository_id} + documentation_url: https://docs.github.com/rest/apps/installations#add-a-repository-to-an-app-installation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/user#remove-interaction-restrictions-from-your-public-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/user#get-interaction-restrictions-for-your-public-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /user/interaction-limits + documentation_url: https://docs.github.com/rest/interactions/user#set-interaction-restrictions-for-your-public-repositories + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/issues + documentation_url: https://docs.github.com/rest/issues/issues#list-user-account-issues-assigned-to-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/keys + documentation_url: https://docs.github.com/rest/users/keys#list-public-ssh-keys-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/keys + documentation_url: https://docs.github.com/rest/users/keys#create-a-public-ssh-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/keys/{key_id} + documentation_url: https://docs.github.com/rest/users/keys#delete-a-public-ssh-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/keys/{key_id} + documentation_url: https://docs.github.com/rest/users/keys#get-a-public-ssh-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/marketplace_purchases + documentation_url: https://docs.github.com/rest/apps/marketplace#list-subscriptions-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/marketplace_purchases/stubbed + documentation_url: https://docs.github.com/rest/apps/marketplace#list-subscriptions-for-the-authenticated-user-stubbed + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/memberships/orgs + documentation_url: https://docs.github.com/rest/orgs/members#list-organization-memberships-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/memberships/orgs/{org} + documentation_url: https://docs.github.com/rest/orgs/members#get-an-organization-membership-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /user/memberships/orgs/{org} + documentation_url: https://docs.github.com/rest/orgs/members#update-an-organization-membership-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/migrations + documentation_url: https://docs.github.com/rest/migrations/users#list-user-migrations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/migrations + documentation_url: https://docs.github.com/rest/migrations/users#start-a-user-migration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/migrations/{migration_id} + documentation_url: https://docs.github.com/rest/migrations/users#get-a-user-migration-status + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /user/migrations/{migration_id}/archive + documentation_url: https://docs.github.com/rest/migrations/users#delete-a-user-migration-archive + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/migrations/{migration_id}/archive + documentation_url: https://docs.github.com/rest/migrations/users#download-a-user-migration-archive + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/migrations/{migration_id}/repos/{repo_name}/lock + documentation_url: https://docs.github.com/rest/migrations/users#unlock-a-user-repository + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /user/migrations/{migration_id}/repositories + documentation_url: https://docs.github.com/rest/migrations/users#list-repositories-for-a-user-migration + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/orgs + documentation_url: https://docs.github.com/rest/orgs/orgs#list-organizations-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/packages + documentation_url: https://docs.github.com/rest/packages/packages#list-packages-for-the-authenticated-users-namespace + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/packages/{package_type}/{package_name} + documentation_url: https://docs.github.com/rest/packages/packages#delete-a-package-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/packages/{package_type}/{package_name} + documentation_url: https://docs.github.com/rest/packages/packages#get-a-package-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/packages/{package_type}/{package_name}/restore + documentation_url: https://docs.github.com/rest/packages/packages#restore-a-package-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/packages/{package_type}/{package_name}/versions + documentation_url: https://docs.github.com/rest/packages/packages#list-package-versions-for-a-package-owned-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/packages/{package_type}/{package_name}/versions/{package_version_id} + documentation_url: https://docs.github.com/rest/packages/packages#delete-a-package-version-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/packages/{package_type}/{package_name}/versions/{package_version_id} + documentation_url: https://docs.github.com/rest/packages/packages#get-a-package-version-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/packages/{package_type}/{package_name}/versions/{package_version_id}/restore + documentation_url: https://docs.github.com/rest/packages/packages#restore-a-package-version-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#create-a-user-project + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /user/public_emails + documentation_url: https://docs.github.com/rest/users/emails#list-public-email-addresses-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/repos + documentation_url: https://docs.github.com/rest/repos/repos#list-repositories-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/repos + documentation_url: https://docs.github.com/rest/repos/repos#create-a-repository-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/repository_invitations + documentation_url: https://docs.github.com/rest/collaborators/invitations#list-repository-invitations-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/repository_invitations/{invitation_id} + documentation_url: https://docs.github.com/rest/collaborators/invitations#decline-a-repository-invitation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /user/repository_invitations/{invitation_id} + documentation_url: https://docs.github.com/rest/collaborators/invitations#accept-a-repository-invitation + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/social_accounts + documentation_url: https://docs.github.com/rest/users/social-accounts#delete-social-accounts-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/social_accounts + documentation_url: https://docs.github.com/rest/users/social-accounts#list-social-accounts-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/social_accounts + documentation_url: https://docs.github.com/rest/users/social-accounts#add-social-accounts-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/ssh_signing_keys + documentation_url: https://docs.github.com/rest/users/ssh-signing-keys#list-ssh-signing-keys-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/ssh_signing_keys + documentation_url: https://docs.github.com/rest/users/ssh-signing-keys#create-a-ssh-signing-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/ssh_signing_keys/{ssh_signing_key_id} + documentation_url: https://docs.github.com/rest/users/ssh-signing-keys#delete-an-ssh-signing-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/ssh_signing_keys/{ssh_signing_key_id} + documentation_url: https://docs.github.com/rest/users/ssh-signing-keys#get-an-ssh-signing-key-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/starred + documentation_url: https://docs.github.com/rest/activity/starring#list-repositories-starred-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /user/starred/{owner}/{repo} + documentation_url: https://docs.github.com/rest/activity/starring#unstar-a-repository-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/starred/{owner}/{repo} + documentation_url: https://docs.github.com/rest/activity/starring#check-if-a-repository-is-starred-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /user/starred/{owner}/{repo} + documentation_url: https://docs.github.com/rest/activity/starring#star-a-repository-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/subscriptions + documentation_url: https://docs.github.com/rest/activity/watching#list-repositories-watched-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/teams + documentation_url: https://docs.github.com/rest/teams/teams#list-teams-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /user/{account_id} + documentation_url: https://docs.github.com/rest/users/users#get-a-user-using-their-id + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /user/{user_id}/projectsV2/{project_number}/drafts + documentation_url: https://docs.github.com/rest/projects/drafts#create-draft-item-for-user-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users + documentation_url: https://docs.github.com/rest/users/users#list-users + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /users/{user_id}/projectsV2/{project_number}/views + documentation_url: https://docs.github.com/rest/projects/views#create-a-view-for-a-user-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username} + documentation_url: https://docs.github.com/rest/users/users#get-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /users/{username}/attestations/bulk-list + documentation_url: https://docs.github.com/rest/users/attestations#list-attestations-by-bulk-subject-digests + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /users/{username}/attestations/delete-request + documentation_url: https://docs.github.com/rest/users/attestations#delete-attestations-in-bulk + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /users/{username}/attestations/digest/{subject_digest} + documentation_url: https://docs.github.com/rest/users/attestations#delete-attestations-by-subject-digest + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /users/{username}/attestations/{attestation_id} + documentation_url: https://docs.github.com/rest/users/attestations#delete-attestations-by-id + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/attestations/{subject_digest} + documentation_url: https://docs.github.com/rest/users/attestations#list-attestations + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/copilot-spaces + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#list-copilot-spaces-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /users/{username}/copilot-spaces + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#create-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /users/{username}/copilot-spaces/{space_number} + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#delete-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/copilot-spaces/{space_number} + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#get-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /users/{username}/copilot-spaces/{space_number} + documentation_url: https://docs.github.com/rest/copilot-spaces/copilot-spaces#set-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/copilot-spaces/{space_number}/collaborators + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#list-collaborators-for-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /users/{username}/copilot-spaces/{space_number}/collaborators + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#add-a-collaborator-to-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /users/{username}/copilot-spaces/{space_number}/collaborators/{actor_type}/{actor_identifier} + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#remove-a-collaborator-from-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /users/{username}/copilot-spaces/{space_number}/collaborators/{actor_type}/{actor_identifier} + documentation_url: https://docs.github.com/rest/copilot-spaces/collaborators#set-a-collaborator-role-for-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/copilot-spaces/{space_number}/resources + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#list-resources-for-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: POST /users/{username}/copilot-spaces/{space_number}/resources + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#create-a-resource-for-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: DELETE /users/{username}/copilot-spaces/{space_number}/resources/{space_resource_id} + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#delete-a-resource-from-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/copilot-spaces/{space_number}/resources/{space_resource_id} + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#get-a-resource-for-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: PUT /users/{username}/copilot-spaces/{space_number}/resources/{space_resource_id} + documentation_url: https://docs.github.com/rest/copilot-spaces/resources#set-a-resource-for-a-copilot-space-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /users/{username}/docker/conflicts + documentation_url: https://docs.github.com/rest/packages/packages#get-list-of-conflicting-packages-during-docker-migration-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/events + documentation_url: https://docs.github.com/rest/activity/events#list-events-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/events/orgs/{org} + documentation_url: https://docs.github.com/rest/activity/events#list-organization-events-for-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/events/public + documentation_url: https://docs.github.com/rest/activity/events#list-public-events-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/followers + documentation_url: https://docs.github.com/rest/users/followers#list-followers-of-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/following + documentation_url: https://docs.github.com/rest/users/followers#list-the-people-a-user-follows + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/following/{target_user} + documentation_url: https://docs.github.com/rest/users/followers#check-if-a-user-follows-another-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/gists + documentation_url: https://docs.github.com/rest/gists/gists#list-gists-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/gpg_keys + documentation_url: https://docs.github.com/rest/users/gpg-keys#list-gpg-keys-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/hovercard + documentation_url: https://docs.github.com/rest/users/users#get-contextual-information-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/installation + documentation_url: https://docs.github.com/rest/apps/apps#get-a-user-installation-for-the-authenticated-app + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/keys + documentation_url: https://docs.github.com/rest/users/keys#list-public-keys-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/orgs + documentation_url: https://docs.github.com/rest/orgs/orgs#list-organizations-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/packages + documentation_url: https://docs.github.com/rest/packages/packages#list-packages-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /users/{username}/packages/{package_type}/{package_name} + documentation_url: https://docs.github.com/rest/packages/packages#delete-a-package-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/packages/{package_type}/{package_name} + documentation_url: https://docs.github.com/rest/packages/packages#get-a-package-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /users/{username}/packages/{package_type}/{package_name}/restore + documentation_url: https://docs.github.com/rest/packages/packages#restore-a-package-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/packages/{package_type}/{package_name}/versions + documentation_url: https://docs.github.com/rest/packages/packages#list-package-versions-for-a-package-owned-by-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id} + documentation_url: https://docs.github.com/rest/packages/packages#delete-package-version-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id} + documentation_url: https://docs.github.com/rest/packages/packages#get-a-package-version-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore + documentation_url: https://docs.github.com/rest/packages/packages#restore-package-version-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projects + documentation_url: https://docs.github.com/enterprise-server@3.16/rest/projects-classic/projects#list-user-projects + openapi_files: + - descriptions/ghes-3.16/ghes-3.16.json + deprecated: true + - name: GET /users/{username}/projectsV2 + documentation_url: https://docs.github.com/rest/projects/projects#list-projects-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projectsV2/{project_number} + documentation_url: https://docs.github.com/rest/projects/projects#get-project-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projectsV2/{project_number}/fields + documentation_url: https://docs.github.com/rest/projects/fields#list-project-fields-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /users/{username}/projectsV2/{project_number}/fields + documentation_url: https://docs.github.com/rest/projects/fields#add-field-to-user-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projectsV2/{project_number}/fields/{field_id} + documentation_url: https://docs.github.com/rest/projects/fields#get-project-field-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projectsV2/{project_number}/items + documentation_url: https://docs.github.com/rest/projects/items#list-items-for-a-user-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: POST /users/{username}/projectsV2/{project_number}/items + documentation_url: https://docs.github.com/rest/projects/items#add-item-to-user-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /users/{username}/projectsV2/{project_number}/items/{item_id} + documentation_url: https://docs.github.com/rest/projects/items#delete-project-item-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projectsV2/{project_number}/items/{item_id} + documentation_url: https://docs.github.com/rest/projects/items#get-an-item-for-a-user-owned-project + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: PATCH /users/{username}/projectsV2/{project_number}/items/{item_id} + documentation_url: https://docs.github.com/rest/projects/items#update-project-item-for-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/projectsV2/{project_number}/views/{view_number}/items + documentation_url: https://docs.github.com/rest/projects/items#list-items-for-a-user-project-view + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/received_events + documentation_url: https://docs.github.com/rest/activity/events#list-events-received-by-the-authenticated-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/received_events/public + documentation_url: https://docs.github.com/rest/activity/events#list-public-events-received-by-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/repos + documentation_url: https://docs.github.com/rest/repos/repos#list-repositories-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/settings/billing/ai_credit/usage + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-ai-credit-usage-report-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /users/{username}/settings/billing/premium_request/usage + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-premium-request-usage-report-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /users/{username}/settings/billing/usage + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-usage-report-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: GET /users/{username}/settings/billing/usage/summary + documentation_url: https://docs.github.com/rest/billing/usage#get-billing-usage-summary-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - name: DELETE /users/{username}/site_admin + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#demote-a-site-administrator + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /users/{username}/site_admin + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#promote-a-user-to-be-a-site-administrator + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/social_accounts + documentation_url: https://docs.github.com/rest/users/social-accounts#list-social-accounts-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/ssh_signing_keys + documentation_url: https://docs.github.com/rest/users/ssh-signing-keys#list-ssh-signing-keys-for-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/starred + documentation_url: https://docs.github.com/rest/activity/starring#list-repositories-starred-by-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /users/{username}/subscriptions + documentation_url: https://docs.github.com/rest/activity/watching#list-repositories-watched-by-a-user + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json + - name: DELETE /users/{username}/suspended + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#unsuspend-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: PUT /users/{username}/suspended + documentation_url: https://docs.github.com/enterprise-server@3.21/rest/enterprise-admin/users#suspend-a-user + openapi_files: + - descriptions/ghes-3.21/ghes-3.21.json + - name: GET /versions + documentation_url: https://docs.github.com/rest/meta/meta#get-all-api-versions + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - name: GET /zen + documentation_url: https://docs.github.com/rest/meta/meta#get-the-zen-of-github + openapi_files: + - descriptions/api.github.com/api.github.com.json + - descriptions/ghec/ghec.json + - descriptions/ghes-3.21/ghes-3.21.json diff --git a/otel/go.mod b/otel/go.mod new file mode 100644 index 00000000000..3dbd4297a24 --- /dev/null +++ b/otel/go.mod @@ -0,0 +1,23 @@ +module github.com/google/go-github/otel/v90 + +go 1.25.0 + +require ( + github.com/google/go-github/v90 v90.0.0 + go.opentelemetry.io/otel v1.45.0 + go.opentelemetry.io/otel/metric v1.45.0 + go.opentelemetry.io/otel/sdk v1.45.0 + go.opentelemetry.io/otel/trace v1.45.0 +) + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/go-logr/logr v1.4.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/go-querystring v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + golang.org/x/sys v0.47.0 // indirect +) + +replace github.com/google/go-github/v90 => ../ diff --git a/otel/go.sum b/otel/go.sum new file mode 100644 index 00000000000..0185bc2d9bf --- /dev/null +++ b/otel/go.sum @@ -0,0 +1,38 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= +go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= +go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= +go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/otel/transport.go b/otel/transport.go new file mode 100644 index 00000000000..35f26ab2136 --- /dev/null +++ b/otel/transport.go @@ -0,0 +1,126 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package otel provides OpenTelemetry instrumentation for the go-github client. +package otel + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/google/go-github/v90/github" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +const ( + // instrumentationName is the name of this instrumentation package. + instrumentationName = "github.com/google/go-github/v90/otel" +) + +// Transport is an http.RoundTripper that instrument requests with OpenTelemetry. +type Transport struct { + Base http.RoundTripper + Tracer trace.Tracer + Meter metric.Meter +} + +// NewTransport creates a new OpenTelemetry transport. +func NewTransport(base http.RoundTripper, opts ...Option) *Transport { + if base == nil { + base = http.DefaultTransport + } + t := &Transport{Base: base} + for _, opt := range opts { + opt(t) + } + if t.Tracer == nil { + t.Tracer = otel.Tracer(instrumentationName) + } + if t.Meter == nil { + t.Meter = otel.Meter(instrumentationName) + } + return t +} + +// RoundTrip implements http.RoundTripper. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + ctx := req.Context() + spanName := fmt.Sprintf("github/%v", req.Method) + // Start Span + ctx, span := t.Tracer.Start(ctx, spanName, trace.WithSpanKind(trace.SpanKindClient)) + defer span.End() + + // Inject Attributes + span.SetAttributes( + attribute.String("http.method", req.Method), + attribute.String("http.url", req.URL.String()), + attribute.String("http.host", req.URL.Host), + ) + + // Inject Propagation Headers + otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header)) + + // Execute Request + resp, err := t.Base.RoundTrip(req) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, err + } + + // Capture response attributes + span.SetAttributes(attribute.Int("http.status_code", resp.StatusCode)) + // Capture GitHub Specifics + if limit := resp.Header.Get(github.HeaderRateLimit); limit != "" { + if v, err := strconv.Atoi(limit); err == nil { + span.SetAttributes(attribute.Int("github.rate_limit.limit", v)) + } + } + if remaining := resp.Header.Get(github.HeaderRateRemaining); remaining != "" { + if v, err := strconv.Atoi(remaining); err == nil { + span.SetAttributes(attribute.Int("github.rate_limit.remaining", v)) + } + } + if reset := resp.Header.Get(github.HeaderRateReset); reset != "" { + span.SetAttributes(attribute.String("github.rate_limit.reset", reset)) + } + if reqID := resp.Header.Get(github.HeaderRequestID); reqID != "" { + span.SetAttributes(attribute.String("github.request_id", reqID)) + } + if resource := resp.Header.Get(github.HeaderRateResource); resource != "" { + span.SetAttributes(attribute.String("github.rate_limit.resource", resource)) + } + + if resp.StatusCode >= 400 { + span.SetStatus(codes.Error, fmt.Sprintf("HTTP %v", resp.StatusCode)) + } else { + span.SetStatus(codes.Ok, "OK") + } + + return resp, nil +} + +// Option applies configuration to Transport. +type Option func(*Transport) + +// WithTracerProvider configures the TracerProvider. +func WithTracerProvider(tp trace.TracerProvider) Option { + return func(t *Transport) { + t.Tracer = tp.Tracer(instrumentationName) + } +} + +// WithMeterProvider configures the MeterProvider. +func WithMeterProvider(mp metric.MeterProvider) Option { + return func(t *Transport) { + t.Meter = mp.Meter(instrumentationName) + } +} diff --git a/otel/transport_test.go b/otel/transport_test.go new file mode 100644 index 00000000000..0164e95f002 --- /dev/null +++ b/otel/transport_test.go @@ -0,0 +1,198 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package otel + +import ( + "errors" + "net/http" + "net/http/httptest" + "testing" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +type mockTransport struct { + Response *http.Response + Err error +} + +func (m *mockTransport) RoundTrip(_ *http.Request) (*http.Response, error) { + if m.Err != nil { + return nil, m.Err + } + // Return valid response with injected headers + if m.Response != nil { + return m.Response, nil + } + return &http.Response{StatusCode: 200, Header: make(http.Header)}, nil +} + +func TestNewTransport_Defaults(t *testing.T) { + t.Parallel() + transport := NewTransport(nil) + if transport.Base != http.DefaultTransport { + t.Error("NewTransport(nil) should result in http.DefaultTransport") + } + if transport.Tracer == nil { + t.Error("NewTransport(nil) should set default Tracer") + } + if transport.Meter == nil { + t.Error("NewTransport(nil) should set default Meter") + } +} + +func TestRoundTrip_Spans(t *testing.T) { + t.Parallel() + // Setup Trace Provider + exporter := tracetest.NewInMemoryExporter() + tp := trace.NewTracerProvider(trace.WithSyncer(exporter)) + + // Setup Headers + headers := http.Header{} + headers.Set("X-Ratelimit-Limit", "5000") + headers.Set("X-Ratelimit-Remaining", "4999") + headers.Set("X-Ratelimit-Reset", "1372700873") + headers.Set("X-Github-Request-Id", "1234-5678") + headers.Set("X-Ratelimit-Resource", "core") + + mockResp := &http.Response{ + StatusCode: 200, + Header: headers, + } + + transport := NewTransport( + &mockTransport{Response: mockResp}, + WithTracerProvider(tp), + ) + + req := httptest.NewRequest("GET", "https://api.github.com/users/google", nil) + resp, err := transport.RoundTrip(req) + if err != nil { + t.Fatalf("RoundTrip failed: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Errorf("Expected status code 200, got %v", resp.StatusCode) + } + spans := exporter.GetSpans() + if len(spans) != 1 { + t.Fatalf("Expected 1 span, got %d", len(spans)) + } + span := spans[0] + + // Verify Name + if span.Name != "github/GET" { + t.Errorf("Expected span name 'github/GET', got '%v'", span.Name) + } + + // Verify Attributes + attrs := make(map[attribute.Key]attribute.Value) + for _, a := range span.Attributes { + attrs[a.Key] = a.Value + } + + expectedStringAttrs := map[attribute.Key]string{ + "http.method": "GET", + "http.url": "https://api.github.com/users/google", + "http.host": "api.github.com", + "github.rate_limit.reset": "1372700873", + "github.request_id": "1234-5678", + "github.rate_limit.resource": "core", + } + + for k, v := range expectedStringAttrs { + if got, ok := attrs[k]; !ok || got.AsString() != v { + t.Errorf("Expected attr '%v' = '%v', got '%v'", k, v, got) + } + } + + expectedIntAttrs := map[attribute.Key]int64{ + "http.status_code": 200, + "github.rate_limit.limit": 5000, + "github.rate_limit.remaining": 4999, + } + + for k, v := range expectedIntAttrs { + if got, ok := attrs[k]; !ok || got.AsInt64() != v { + t.Errorf("Expected attr '%v' = '%v', got '%v'", k, v, got) + } + } +} + +func TestRoundTrip_Error(t *testing.T) { + t.Parallel() + exporter := tracetest.NewInMemoryExporter() + tp := trace.NewTracerProvider(trace.WithSyncer(exporter)) + + mockErr := errors.New("network failure") + transport := NewTransport( + &mockTransport{Err: mockErr}, + WithTracerProvider(tp), + ) + + req := httptest.NewRequest("POST", "https://api.github.com/repos/new", nil) + _, err := transport.RoundTrip(req) + + if !errors.Is(err, mockErr) { + t.Errorf("Expected error '%v', got '%v'", mockErr, err) + } + + spans := exporter.GetSpans() + if len(spans) != 1 { + t.Fatalf("Expected 1 span, got %d", len(spans)) + } + span := spans[0] + + if span.Status.Code != codes.Error { + t.Errorf("Expected span status Error, got %v", span.Status.Code) + } + if span.Status.Description != "network failure" { + t.Errorf("Expected span description 'network failure', got '%v'", span.Status.Description) + } +} + +func TestRoundTrip_HTTPError(t *testing.T) { + t.Parallel() + exporter := tracetest.NewInMemoryExporter() + tp := trace.NewTracerProvider(trace.WithSyncer(exporter)) + + mockResp := &http.Response{ + StatusCode: 404, + Header: make(http.Header), + } + transport := NewTransport( + &mockTransport{Response: mockResp}, + WithTracerProvider(tp), + ) + + req := httptest.NewRequest("DELETE", "https://api.github.com/user", nil) + _, err := transport.RoundTrip(req) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + spans := exporter.GetSpans() + span := spans[0] + + if span.Status.Code != codes.Error { + t.Errorf("Expected span status Error, got %v", span.Status.Code) + } + if span.Status.Description != "HTTP 404" { + t.Errorf("Expected span description 'HTTP 404', got '%v'", span.Status.Description) + } +} + +func TestWithMeterProvider(t *testing.T) { + t.Parallel() + meter := otel.GetMeterProvider() + transport := NewTransport(nil, WithMeterProvider(meter)) + if transport.Meter == nil { + t.Error("WithMeterProvider failed to set Meter") + } +} diff --git a/scrape/README.md b/scrape/README.md new file mode 100644 index 00000000000..5396c940f49 --- /dev/null +++ b/scrape/README.md @@ -0,0 +1,26 @@ +[![Go Reference](https://pkg.go.dev/badge/github.com/google/go-github/scrape.svg)](https://pkg.go.dev/github.com/google/go-github/scrape) + +The scrape package provides an experimental client for accessing additional +GitHub data via screen scraping. It is designed to be a client of last resort +for data that cannot be retrieved via the REST or GraphQL APIs. + +# What should be added here + +**Add only what you need.** Whereas the main go-github library attempts to +implement the entire GitHub REST API, there is little point in trying to do that +here. Certainly, feel free to contribute patches to get data you actually +need, but I'd rather not try and provide exhaustive coverage of all GitHub data +here. + +**Add only what can't be accessed elsewhere.** If the data can be retrieved +through the REST or GraphQL API, use the appropriate libraries for that. + +**Prefer read-only access.** For now, I'm only focusing on reading data. It +might be that writing works fine as well, but it is of course much riskier. + +# How to add methods + +See [apps.go](apps.go) for examples of methods that access data. Basically, +fetch the contents of the page using `client.get`, and then use goquery to dig +into the markup on the page. Prefer selectors that grab semantic ID or class +names, as they are more likely to be stable. diff --git a/scrape/apps.go b/scrape/apps.go new file mode 100644 index 00000000000..6f8b8f260c9 --- /dev/null +++ b/scrape/apps.go @@ -0,0 +1,157 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// apps.go contains functions for accessing data about applications installed +// on a GitHub organization. + +package scrape + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/google/go-github/v90/github" +) + +// AppRestrictionsEnabled returns whether the specified organization has +// restricted third-party application access. +func (c *Client) AppRestrictionsEnabled(org string) (bool, error) { + doc, err := c.get("/organizations/%v/settings/oauth_application_policy", org) + if err != nil { + return false, err + } + + s := doc.Find(".oauth-application-allowlist svg").First() + if s.Length() == 0 { + return false, errors.New("unable to find expected markup") + } + + if s.HasClass("octicon-check") { + return true, nil + } + if s.HasClass("octicon-alert") { + return false, nil + } + + return false, errors.New("unable to find expected markup") +} + +// ListOAuthApps lists the reviewed OAuth Applications for the +// specified organization (whether approved or denied). +func (c *Client) ListOAuthApps(org string) ([]*OAuthApp, error) { + doc, err := c.get("/organizations/%v/settings/oauth_application_policy", org) + if err != nil { + return nil, err + } + + var apps []*OAuthApp + doc.Find(".oauth-application-allowlist ul > li").Each(func(_ int, s *goquery.Selection) { + var app OAuthApp + app.Name = s.Find(".request-info strong").First().Text() + app.Description = s.Find(".request-info .application-description").Text() + + if editURL, ok := s.Find(".request-indicator a.edit-link").Attr("href"); ok { + app.ID = intFromLastPathSegment(editURL) + } + + if r := s.Find(".request-indicator .requestor"); r.Length() > 0 { + app.State = OAuthAppRequested + app.RequestedBy = r.Text() + if editURL, ok := s.Find(".request-indicator a").Last().Attr("href"); ok { + app.ID = intFromLastPathSegment(editURL) + } + } else if r := s.Find(".request-indicator .approved-request"); r.Length() > 0 { + app.State = OAuthAppApproved + } else if r := s.Find(".request-indicator .denied-request"); r.Length() > 0 { + app.State = OAuthAppDenied + } + apps = append(apps, &app) + }) + + return apps, nil +} + +func intFromLastPathSegment(s string) int { + seg := strings.Split(s, "/") + if len(seg) > 0 { + i, _ := strconv.Atoi(seg[len(seg)-1]) + return i + } + return 0 +} + +// OAuthAppReviewState indicates the current state of a requested OAuth Application. +type OAuthAppReviewState int + +const ( + // OAuthAppRequested indicates access has been requested, but not reviewed. + OAuthAppRequested OAuthAppReviewState = iota + 1 + // OAuthAppApproved indicates access has been approved. + OAuthAppApproved + // OAuthAppDenied indicates access has been denied. + OAuthAppDenied +) + +// OAuthApp represents an OAuth application that has been reviewed for access to organization data. +type OAuthApp struct { + ID int + Name string + Description string + State OAuthAppReviewState + RequestedBy string +} + +// AppManifest represents a GitHub App manifest, used for preconfiguring +// GitHub App configuration. +// c.f. https://docs.github.com/en/apps/sharing-github-apps/registering-a-github-app-from-a-manifest +type AppManifest struct { + // The name of the GitHub App. + Name *string `json:"name,omitempty"` + // Required. The homepage of your GitHub App. + URL *string `json:"url,omitempty"` + // The full URL(s) of the endpoint(s) to authenticate users via the GitHub App (Max: 10). + CallbackURLs []string `json:"callback_urls,omitempty"` + // Required. The configuration of the GitHub App's webhook. + HookAttributes map[string]string `json:"hook_attributes,omitempty"` + // The full URL to redirect to after the person installs the GitHub App. + RedirectURL *string `json:"redirect_url,omitempty"` + // A description of the GitHub App. + Description *string `json:"description,omitempty"` + // Set to true when your GitHub App is available to the public or false when + // it is only accessible to the owner of the app. + Public *bool `json:"public,omitempty"` + // The list of events the GitHub App subscribes to. + DefaultEvents []string `json:"default_events,omitempty"` + // The set of permissions needed by the GitHub App. + DefaultPermissions *github.InstallationPermissions `json:"default_permissions,omitempty"` +} + +// CreateApp creates a new GitHub App with the given manifest configuration. +// orgName is optional, and if provided, the App will be created within the specified organization. +func (c *Client) CreateApp(m *AppManifest, orgName string) (*http.Response, error) { + url := "/settings/apps/new" + + if orgName != "" { + url = fmt.Sprintf("/organizations/%v/settings/apps/new", orgName) + } + + u, err := c.baseURL.Parse(url) + if err != nil { + return nil, err + } + + body, err := json.Marshal(map[string]*AppManifest{"manifest": m}) + if err != nil { + return nil, err + } + + return c.Client.Post(u.String(), "json", bytes.NewReader(body)) +} diff --git a/scrape/apps_test.go b/scrape/apps_test.go new file mode 100644 index 00000000000..9e557ab83b4 --- /dev/null +++ b/scrape/apps_test.go @@ -0,0 +1,131 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scrape + +import ( + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-github/v90/github" +) + +func Test_AppRestrictionsEnabled(t *testing.T) { + t.Parallel() + tests := []struct { + description string + testFile string + org string + want bool + }{ + { + description: "return true for enabled orgs", + testFile: "access-restrictions-enabled.html", + want: true, + }, + { + description: "return false for disabled orgs", + testFile: "access-restrictions-disabled.html", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + t.Parallel() + client, mux := setup(t) + + mux.HandleFunc("/organizations/o/settings/oauth_application_policy", func(w http.ResponseWriter, _ *http.Request) { + copyTestFile(t, w, tt.testFile) + }) + + got, err := client.AppRestrictionsEnabled("o") + if err != nil { + t.Fatalf("AppRestrictionsEnabled returned err: %v", err) + } + if want := tt.want; got != want { + t.Errorf("AppRestrictionsEnabled returned %t, want %t", got, want) + } + }) + } +} + +func Test_ListOAuthApps(t *testing.T) { + t.Parallel() + client, mux := setup(t) + + mux.HandleFunc("/organizations/e/settings/oauth_application_policy", func(w http.ResponseWriter, _ *http.Request) { + copyTestFile(t, w, "access-restrictions-enabled.html") + }) + + got, err := client.ListOAuthApps("e") + if err != nil { + t.Fatalf("ListOAuthApps(e) returned err: %v", err) + } + want := []*OAuthApp{ + { + ID: 22222, + Name: "Coveralls", + Description: "Test coverage history and statistics.", + State: OAuthAppRequested, + RequestedBy: "willnorris", + }, + { + ID: 530107, + Name: "Google Cloud Platform", + State: OAuthAppApproved, + }, + { + ID: 231424, + Name: "GitKraken", + Description: "An intuitive, cross-platform Git client that doesn't suck, built by @axosoft and made with @nodegit & @ElectronJS.", + State: OAuthAppDenied, + }, + } + if !cmp.Equal(got, want) { + t.Errorf("ListOAuthApps(o) returned %v, want %v", got, want) + } +} + +func Test_CreateApp(t *testing.T) { + t.Parallel() + client, mux := setup(t) + + mux.HandleFunc("/settings/apps/new", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusCreated) + }) + + resp, err := client.CreateApp(&AppManifest{ + URL: github.Ptr("https://example.com"), + HookAttributes: map[string]string{ + "url": "https://example.com/hook", + }, + }, "") + if err != nil { + t.Fatalf("CreateApp: %v", err) + } + if got, want := resp.StatusCode, http.StatusCreated; got != want { + t.Errorf("CreateApp returned status code %v, want %v", got, want) + } +} + +func Test_CreateAppWithOrg(t *testing.T) { + t.Parallel() + client, mux := setup(t) + + mux.HandleFunc("/organizations/example/apps/settings/new", func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusCreated) + }) + + if _, err := client.CreateApp(&AppManifest{ + URL: github.Ptr("https://example.com"), + HookAttributes: map[string]string{ + "url": "https://example.com/hook", + }, + }, "example"); err != nil { + t.Fatalf("CreateAppWithOrg: %v", err) + } +} diff --git a/scrape/example/scrape/main.go b/scrape/example/scrape/main.go new file mode 100644 index 00000000000..febf07cd854 --- /dev/null +++ b/scrape/example/scrape/main.go @@ -0,0 +1,66 @@ +// Copyright 2019 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The scrape tool demonstrates use of the github.com/google/go-github/scrape +// package to fetch data from GitHub. The tool lists whether third-party app +// restrictions are enabled for an organization, and lists information about +// OAuth apps requested for the org. +package main + +import ( + "bufio" + "flag" + "fmt" + "log" + "os" + "strings" + + "github.com/google/go-github/scrape" +) + +var ( + username = flag.String("username", "", "github auth: username") + password = flag.String("password", "", "github auth: password") + otpseed = flag.String("otpseed", "", "github auth: otp seed") + org = flag.String("org", "", "github org to get data for") +) + +func main() { + flag.Parse() + + // prompt for password and otpseed in case the user didn't want to specify as flags + reader := bufio.NewReader(os.Stdin) + if *password == "" { + fmt.Print("password: ") + *password, _ = reader.ReadString('\n') + *password = strings.TrimSpace(*password) + } + if *otpseed == "" { + fmt.Print("OTP seed: ") + *otpseed, _ = reader.ReadString('\n') + *otpseed = strings.TrimSpace(*otpseed) + } + + client := scrape.NewClient(nil) + + if err := client.Authenticate(*username, *password, *otpseed); err != nil { + log.Fatal(err) + } + + enabled, err := client.AppRestrictionsEnabled(*org) + if err != nil { + log.Fatal(err) + } + fmt.Printf("App restrictions enabled for %q: %t\n", *org, enabled) + + apps, err := client.ListOAuthApps(*org) + if err != nil { + log.Fatal(err) + } + fmt.Printf("OAuth apps for %q: \n", *org) + for _, app := range apps { + fmt.Printf("\t%+v\n", app) + } +} diff --git a/scrape/forms.go b/scrape/forms.go new file mode 100644 index 00000000000..7beb46cf6e1 --- /dev/null +++ b/scrape/forms.go @@ -0,0 +1,119 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// forms.go contains logic for parsing and submitting HTML forms. None of this +// is specific to go-github in any way, and could easily be pulled out into a +// general purpose scraping library in the future. + +package scrape + +import ( + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/PuerkitoBio/goquery" + "golang.org/x/net/html" +) + +// htmlForm represents the basic elements of an HTML Form. +type htmlForm struct { + // Action is the URL where the form will be submitted + Action string + // Method is the HTTP method to use when submitting the form + Method string + // Values contains form values to be submitted + Values url.Values +} + +// parseForms parses and returns all form elements beneath node. Form values +// include all input and textarea elements within the form. The values of radio +// and checkbox inputs are included only if they are checked. +// +// In the future, we might want to allow a custom selector to be passed in to +// further restrict what forms will be returned. +func parseForms(node *html.Node) (forms []*htmlForm) { + if node == nil { + return nil + } + + doc := goquery.NewDocumentFromNode(node) + doc.Find("form").Each(func(_ int, s *goquery.Selection) { + form := htmlForm{Values: url.Values{}} + form.Action, _ = s.Attr("action") + form.Method, _ = s.Attr("method") + + s.Find("input").Each(func(_ int, s *goquery.Selection) { + name, _ := s.Attr("name") + if name == "" { + return + } + + typ, _ := s.Attr("type") + typ = strings.ToLower(typ) + _, checked := s.Attr("checked") + if (typ == "radio" || typ == "checkbox") && !checked { + return + } + + value, _ := s.Attr("value") + form.Values.Add(name, value) + }) + s.Find("textarea").Each(func(_ int, s *goquery.Selection) { + name, _ := s.Attr("name") + if name == "" { + return + } + + value := s.Text() + form.Values.Add(name, value) + }) + forms = append(forms, &form) + }) + + return forms +} + +// fetchAndSubmitForm will fetch the page at urlStr, then parse and submit the first form found. +// setValues will be called with the parsed form values, allowing the caller to set any custom +// form values. Form submission will always use the POST method, regardless of the value of the +// method attribute in the form. The response from submitting the parsed form is returned. +func fetchAndSubmitForm(client *http.Client, urlStr string, setValues func(url.Values)) (*http.Response, error) { + resp, err := client.Get(urlStr) + if err != nil { + return nil, fmt.Errorf("error fetching url %q: %v", urlStr, err) + } + + defer resp.Body.Close() + root, err := html.Parse(resp.Body) + if err != nil { + return nil, fmt.Errorf("error parsing response: %v", err) + } + + forms := parseForms(root) + if len(forms) == 0 { + return nil, fmt.Errorf("no forms found at %q", urlStr) + } + form := forms[0] + + actionURL, err := url.Parse(form.Action) + if err != nil { + return nil, fmt.Errorf("error parsing form action URL %q: %v", form.Action, err) + } + actionURL = resp.Request.URL.ResolveReference(actionURL) + + // allow caller to fill out the form + if setValues != nil { + setValues(form.Values) + } + + resp, err = client.PostForm(actionURL.String(), form.Values) + if err != nil { + return nil, fmt.Errorf("error posting form: %v", err) + } + + return resp, nil +} diff --git a/scrape/forms_test.go b/scrape/forms_test.go new file mode 100644 index 00000000000..c1f4152c384 --- /dev/null +++ b/scrape/forms_test.go @@ -0,0 +1,123 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scrape + +import ( + "fmt" + "net/http" + "net/url" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "golang.org/x/net/html" +) + +func Test_ParseForms(t *testing.T) { + t.Parallel() + tests := []struct { + description string + html string + forms []*htmlForm + }{ + {"no forms", ``, nil}, + {"empty form", `
`, []*htmlForm{{Values: url.Values{}}}}, + { + "single form with one value", + `
`, + []*htmlForm{{Action: "a", Method: "m", Values: url.Values{"n1": {"v1"}}}}, + }, + { + "two forms", + ` +
+
+ `, + []*htmlForm{ + {Action: "a1", Method: "m1", Values: url.Values{"n1": {"v1"}}}, + {Action: "a2", Method: "m2", Values: url.Values{"n2": {"v2"}}}, + }, + }, + { + "form with radio buttons (none checked)", + `
+ + + +
`, + []*htmlForm{{Values: url.Values{}}}, + }, + { + "form with radio buttons", + `
+ + + +
`, + []*htmlForm{{Values: url.Values{"n1": {"v3"}}}}, + }, + { + "form with checkboxes", + `
+ + + +
`, + []*htmlForm{{Values: url.Values{"n1": {"v1"}, "n3": {"v3"}}}}, + }, + { + "single form with textarea", + `
`, + []*htmlForm{{Values: url.Values{"n1": {"v1"}}}}, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + t.Parallel() + node, err := html.Parse(strings.NewReader(tt.html)) + if err != nil { + t.Fatalf("error parsing html: %v", err) + } + if got, want := parseForms(node), tt.forms; !cmp.Equal(got, want) { + t.Errorf("parseForms(%q) returned %+v, want %+v", tt.html, got, want) + } + }) + } +} + +func Test_FetchAndSubmitForm(t *testing.T) { + t.Parallel() + client, mux := setup(t) + var submitted bool + + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, `
+ + +
`) + }) + mux.HandleFunc("/submit", func(_ http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + t.Fatalf("error parsing form: %v", err) + } + want := url.Values{"hidden": {"h"}, "name": {"n"}} + if got := r.Form; !cmp.Equal(got, want) { + t.Errorf("submitted form contained values %v, want %v", got, want) + } + submitted = true + }) + + setValues := func(values url.Values) { values.Set("name", "n") } + _, err := fetchAndSubmitForm(client.Client, client.baseURL.String()+"/", setValues) + if err != nil { + t.Fatalf("fetchAndSubmitForm returned err: %v", err) + } + if !submitted { + t.Error("form was never submitted") + } +} diff --git a/scrape/go.mod b/scrape/go.mod new file mode 100644 index 00000000000..6acb8fe3ae2 --- /dev/null +++ b/scrape/go.mod @@ -0,0 +1,16 @@ +module github.com/google/go-github/scrape + +go 1.25.0 + +require ( + github.com/PuerkitoBio/goquery v1.12.0 + github.com/google/go-cmp v0.7.0 + github.com/google/go-github/v90 v90.0.0 + github.com/xlzd/gotp v0.1.0 + golang.org/x/net v0.57.0 +) + +require ( + github.com/andybalholm/cascadia v1.3.3 // indirect + github.com/google/go-querystring v1.2.0 // indirect +) diff --git a/scrape/go.sum b/scrape/go.sum new file mode 100644 index 00000000000..3d37ffdf277 --- /dev/null +++ b/scrape/go.sum @@ -0,0 +1,79 @@ +github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo= +github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-github/v90 v90.0.0 h1:EnX9HvTfqvuJbUSWu1/jLrYH6JJLMz0w0qfQVbTxPzE= +github.com/google/go-github/v90 v90.0.0/go.mod h1:pLzt1FZURZyoTHT5/Z1UQY3b9fYyrbXH6aj7X+qgID4= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= +github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po= +github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/scrape/payment.go b/scrape/payment.go new file mode 100644 index 00000000000..6fbb3757386 --- /dev/null +++ b/scrape/payment.go @@ -0,0 +1,51 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// apps.go contains functions for accessing data about applications installed +// on a GitHub organization. + +package scrape + +import ( + "strings" + + "github.com/PuerkitoBio/goquery" +) + +// OrgPaymentInformation returns payment information for the specified org. +func (c *Client) OrgPaymentInformation(org string) (PaymentInformation, error) { + var info PaymentInformation + + doc, err := c.get("/organizations/%v/settings/billing/payment_information", org) + if err != nil { + return info, err + } + + doc.Find("main h4.mb-1").Each(func(_ int, s *goquery.Selection) { + name := strings.TrimSpace(strings.ToLower(s.Text())) + value := strings.Join(strings.Fields(strings.TrimSpace(s.NextFiltered("p").Text())), " ") + + switch name { + case "payment method": + info.PaymentMethod = value + case "last payment": + info.LastPayment = value + case "coupon": + info.Coupon = value + case "extra information": + info.ExtraInformation = value + } + }) + + return info, nil +} + +// PaymentInformation for an organization on a paid plan. +type PaymentInformation struct { + PaymentMethod string + LastPayment string + Coupon string + ExtraInformation string +} diff --git a/scrape/scrape.go b/scrape/scrape.go new file mode 100644 index 00000000000..9f218b7f172 --- /dev/null +++ b/scrape/scrape.go @@ -0,0 +1,155 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package scrape provides a client for interacting with GitHub using screen +// scraping. +// +// This is intended to be used as a supplement to the standard go-github +// library to access data that is not currently exposed by either the official +// REST or GraphQL APIs. +// +// Because of the nature of screen scraping, this package should be treated as +// HIGHLY EXPERIMENTAL, and potentially unstable. We make no promises relating +// to compatibility or stability of the exported API. Even though this package +// is distributed as part of the go-github library, it is explicitly exempt +// from any stability promises that my be implied by the library version +// number. +package scrape + +import ( + "bytes" + "encoding/gob" + "fmt" + "log" + "net/http" + "net/http/cookiejar" + "net/url" + "strings" + + "github.com/PuerkitoBio/goquery" + "github.com/xlzd/gotp" + "golang.org/x/net/publicsuffix" +) + +var defaultBaseURL = "https://github.com/" + +// Client is a GitHub scraping client. +type Client struct { + *http.Client + + // base URL for github.com pages. Exposed primarily for testing. Also + // used for saving and restoring cookies on the Client. + baseURL *url.URL +} + +// NewClient constructs a new Client. If transport is nil, a default transport is used. +func NewClient(transport http.RoundTripper) *Client { + jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) + if err != nil { + log.Fatalf("error creating cookiejar: %v", err) + } + baseURL, _ := url.Parse(defaultBaseURL) + + return &Client{ + Client: &http.Client{ + Transport: transport, + Jar: jar, + }, + baseURL: baseURL, + } +} + +// SaveCookies returns an encoded form of the github.com cookies set on this +// client. If Authenticate() has been called, this should include the +// github.com session cookie. These cookies can be loaded onto a new client by +// calling LoadCookies. +// +// GitHub session cookies are bearer tokens that are not tied to any particular +// client, so should be treated with the same sensitivity as the account +// credentials. +func (c *Client) SaveCookies() ([]byte, error) { + cookies := c.Client.Jar.Cookies(c.baseURL) + + var b bytes.Buffer + err := gob.NewEncoder(&b).Encode(cookies) + return b.Bytes(), err +} + +// LoadCookies loads the provided cookies for github.com. +func (c *Client) LoadCookies(v []byte) error { + var cookies []*http.Cookie + r := bytes.NewReader(v) + err := gob.NewDecoder(r).Decode(&cookies) + if err != nil { + return err + } + + c.Client.Jar.SetCookies(c.baseURL, cookies) + return nil +} + +// get fetches a urlStr (a GitHub URL relative to the client's baseURL), and +// returns the parsed response document. +func (c *Client) get(urlStr string, a ...any) (*goquery.Document, error) { + u, err := c.baseURL.Parse(fmt.Sprintf(urlStr, a...)) + if err != nil { + return nil, fmt.Errorf("error parsing URL: %q: %v", urlStr, err) + } + + resp, err := c.Client.Get(u.String()) + if err != nil { + return nil, fmt.Errorf("error fetching url %q: %v", u, err) + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNotFound { + return nil, fmt.Errorf("received %v response fetching URL %q", resp.StatusCode, u) + } + + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + return nil, fmt.Errorf("error parsing response: %v", err) + } + + return doc, nil +} + +// Authenticate client to GitHub with the provided username, password, and if +// two-factor auth is enabled for the account, otpseed. +// +// otpseed is the OTP Secret provided from GitHub as part of two-factor +// application enrollment. When registering the application, click the "enter +// this text code" link on the QR Code page to see the raw OTP Secret. +func (c *Client) Authenticate(username, password, otpseed string) error { + setPassword := func(values url.Values) { + values.Set("login", username) + values.Set("password", password) + } + resp, err := fetchAndSubmitForm(c.Client, "https://github.com/login", setPassword) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("received %v response submitting login form", resp.StatusCode) + } + + if otpseed == "" { + return nil + } + + setOTP := func(values url.Values) { + otp := gotp.NewDefaultTOTP(strings.ToUpper(otpseed)).Now() + values.Set("otp", otp) + } + resp, err = fetchAndSubmitForm(c.Client, "https://github.com/sessions/two-factor", setOTP) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("received %v response submitting otp form", resp.StatusCode) + } + + return nil +} diff --git a/scrape/scrape_test.go b/scrape/scrape_test.go new file mode 100644 index 00000000000..d77857da478 --- /dev/null +++ b/scrape/scrape_test.go @@ -0,0 +1,48 @@ +// Copyright 2013 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package scrape + +import ( + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "testing" +) + +// set up a test HTTP server along with a scrape.Client that is configured to +// talk to that test server. Tests should register handlers on the mux which +// provide mock responses for the GitHub pages being tested. +func setup(t *testing.T) (client *Client, mux *http.ServeMux) { + t.Helper() + mux = http.NewServeMux() + server := httptest.NewServer(mux) + + client = NewClient(nil) + client.baseURL, _ = url.Parse(server.URL + "/") + + t.Cleanup(server.Close) + + return client, mux +} + +func copyTestFile(t *testing.T, w io.Writer, filename string) { + t.Helper() + f, err := os.Open(filepath.Join("testdata", filename)) + if err != nil { + t.Fatalf("unable to open test file: %v", err) + } + _, err = io.Copy(w, f) + if err != nil { + t.Errorf("failure copying test file: %v", err) + } + err = f.Close() + if err != nil { + t.Errorf("failure closing test file: %v", err) + } +} diff --git a/scrape/testdata/access-restrictions-disabled.html b/scrape/testdata/access-restrictions-disabled.html new file mode 100644 index 00000000000..e5431e14c60 --- /dev/null +++ b/scrape/testdata/access-restrictions-disabled.html @@ -0,0 +1,39 @@ + + + + +
+
+ +
+ +
+ +
+ +
+

Third-party application access policy

+
+

+ Policy: No restrictions +

+

+ All applications authorized by organization members have access to google-test’s data. +

+ Setup application access restrictions +

+ + When authorized, applications can act on behalf of organization members. Your access policy determines which applications can access data in your organization. Read more about third-party access and organizations. +

+
+
+ +
+
+
+ +
+
+ + diff --git a/scrape/testdata/access-restrictions-enabled.html b/scrape/testdata/access-restrictions-enabled.html new file mode 100644 index 00000000000..d5b869c79d1 --- /dev/null +++ b/scrape/testdata/access-restrictions-enabled.html @@ -0,0 +1,117 @@ + + + + +
+
+ + + +
+ +
+ +
+ +
+

Third-party application access policy

+
+

+ Policy: Access restricted +

+

+ Only approved applications can access data in this organization. Applications owned by google-test always have access. +

+ +
+ + + Remove restrictions + + +
+ +

Are you sure?

+
+ +
+ You’re about to remove all third-party application restrictions. Please read this carefully. +
+
+

Removing third-party application restrictions will immediately give member authorized applications access to private data in the google-test organization.

+

Please be sure you want to do this.

+
+ + +
+
+
    +
  • +
    + Approval requested by willnorris — + Review +
    + + + Coveralls Test coverage history and statistics. + +
  • +
  • + + + Approved + + + + + Google Cloud Platform + +
  • +
  • + + + Denied + + + + + GitKraken An intuitive, cross-platform Git client that doesn't suck, built by @axosoft and made with @nodegit & @ElectronJS. + +
  • +
+

+ + When authorized, applications can act on behalf of organization members. Your access policy determines which applications can access data in your organization. Read more about third-party access and organizations. +

+
+
+ +
+
+
+ +
+
+ + diff --git a/script/fmt.sh b/script/fmt.sh new file mode 100755 index 00000000000..13400c08569 --- /dev/null +++ b/script/fmt.sh @@ -0,0 +1,18 @@ +#!/bin/sh +#/ script/fmt.sh runs formatting on all Go files in the project. +#/ It uses custom golangci-lint to format the code. + +set -e + +CUSTOM_GCL="$(script/setup-custom-gcl.sh)" + +CDPATH="" cd -- "$(dirname -- "$0")/.." + +MOD_DIRS="$(git ls-files '*go.mod' | xargs dirname | sort)" + +for dir in $MOD_DIRS; do + ( + cd "$dir" + "$CUSTOM_GCL" fmt + ) +done diff --git a/script/generate.sh b/script/generate.sh new file mode 100755 index 00000000000..9408ac052b9 --- /dev/null +++ b/script/generate.sh @@ -0,0 +1,35 @@ +#!/bin/sh +#/ `script/generate.sh` runs `go generate` on repo. +#/ It also runs `script/run-check-structfield-settings.sh -fix` to keep linter +#/ exceptions in `.golangci.yml` up to date. +#/ `script/generate.sh --check` checks that the generated files are up to date. + +set -e + +CDPATH="" cd -- "$(dirname -- "$0")/.." + +CHECK_MODE=0 +if [ "$1" = "--check" ]; then + export CHECK=1 + CHECK_MODE=1 +fi + +go generate ./... + +MOD_DIRS="$(git ls-files '*go.mod' | xargs dirname | sort)" + +for dir in $MOD_DIRS; do + ( + cd "$dir" + if [ "$CHECK_MODE" = "1" ]; then + if ! go mod tidy -diff; then + echo "go.mod/go.sum are out of date in $dir" + exit 1 + fi + else + go mod tidy + fi + ) +done + +script/run-check-structfield-settings.sh -fix diff --git a/script/lint.sh b/script/lint.sh new file mode 100755 index 00000000000..ca4000647a8 --- /dev/null +++ b/script/lint.sh @@ -0,0 +1,119 @@ +#!/bin/sh +#/ [ CHECK_GITHUB_OPENAPI=1 ] script/lint.sh runs linters and validates generated files. +#/ When CHECK_GITHUB is set, it validates that openapi_operations.yaml is consistent with the +#/ descriptions from github.com/github/rest-api-description. + +set -e + +CUSTOM_GCL="$(script/setup-custom-gcl.sh)" + +CDPATH="" cd -- "$(dirname -- "$0")/.." + +EXIT_CODE=0 + +# Colors & Formatting +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[0;33m' +BOLD='\033[1m' +NC='\033[0m' + +fail() { + EXIT_CODE=1 +} + +MOD_DIRS="$(git ls-files '*go.mod' | xargs dirname | sort -u)" + +# Number of module lint jobs to run concurrently. +# Override with LINT_JOBS, otherwise use detected CPU count. +: "${LINT_JOBS:=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}" + +LINT_DIRS="$(printf '%s\n' "$MOD_DIRS")" + +FAILED_COUNT=0 +LINT_FAILED=0 +RUNNING=0 +PIDS="" +DIRS_IN_FLIGHT="" + +LOG_DIR="$(mktemp -d)" +trap 'rm -rf "$LOG_DIR"' EXIT + +# --- Helper Functions --- +print_header() { + printf "${BOLD}%s${NC}\n\n" "$1" +} + + +wait_pids() { + i=1 + for pid in $PIDS; do + # Identify the directory for this PID + dir=$(echo "$DIRS_IN_FLIGHT" | awk -v i="$i" '{print $i}') + log_file="$LOG_DIR/$(echo "$dir" | tr '/' '_').log" + + if wait "$pid"; then + printf "${GREEN}✔ %-40s [ PASS ]${NC}\n" "$dir" + else + printf "${RED}✘ %-40s [ FAIL ]${NC}\n" "$dir" + if [ -f "$log_file" ]; then + sed 's/^/ /' "$log_file" + fi + FAILED_COUNT=$((FAILED_COUNT + 1)) + fail + fi + i=$((i + 1)) + done + PIDS="" + DIRS_IN_FLIGHT="" + RUNNING=0 +} + +print_header "Linting modules" + +for dir in $LINT_DIRS; do + log_file="$LOG_DIR/$(echo "$dir" | tr '/' '_').log" + + # Run the linter in the background and redirect output to a log file + (cd "$dir" && "$CUSTOM_GCL" run --color always > "$log_file" 2>&1) & + + PIDS="$PIDS $!" + DIRS_IN_FLIGHT="$DIRS_IN_FLIGHT $dir" + RUNNING=$((RUNNING + 1)) + + if [ "$RUNNING" -ge "$LINT_JOBS" ]; then + wait_pids + fi +done + +wait_pids + +if [ -n "$CHECK_GITHUB_OPENAPI" ]; then + print_header "Validating openapi_operations.yaml" + if script/metadata.sh update-openapi --validate; then + printf "${GREEN}✔ openapi_operations.yaml is valid${NC}\n" + else + printf "${RED}✘ openapi_operations.yaml validation failed${NC}\n" + fail + fi +fi + +print_header "Validating generated files" +if script/generate.sh --check; then + printf "${GREEN}✔ Generated files are up to date${NC}\n" +else + printf "${RED}✘ Generated files out of sync${NC}\n" + fail +fi + +# --- Final Summary --- +printf -- "----------------------------\n" +SUMMARY_COLOR="$GREEN" +if [ "$FAILED_COUNT" -gt 0 ]; then + SUMMARY_COLOR="$RED" +fi + +printf "%bLinting: issues found in %d module directories.%b\n" "$SUMMARY_COLOR" "$FAILED_COUNT" "$NC" +printf -- "--------------------------------------------\n" + +exit "$EXIT_CODE" diff --git a/script/metadata.sh b/script/metadata.sh new file mode 100755 index 00000000000..3b7c7fbeb8b --- /dev/null +++ b/script/metadata.sh @@ -0,0 +1,14 @@ +#!/bin/sh +#/ script/metadata.sh runs ./tools/metadata in the repository root with the given arguments + +set -e + +CDPATH="" cd -- "$(dirname -- "$0")/.." +REPO_DIR="$(pwd)" + +( + cd tools/metadata + go build -o "$REPO_DIR"/bin/metadata +) + +exec bin/metadata "$@" diff --git a/script/run-check-structfield-settings.sh b/script/run-check-structfield-settings.sh new file mode 100755 index 00000000000..f10bfbc6bb8 --- /dev/null +++ b/script/run-check-structfield-settings.sh @@ -0,0 +1,2 @@ +#!/bin/bash -e +go run -C tools/check-structfield-settings . "$@" diff --git a/script/setup-custom-gcl.sh b/script/setup-custom-gcl.sh new file mode 100755 index 00000000000..36936e62a3b --- /dev/null +++ b/script/setup-custom-gcl.sh @@ -0,0 +1,29 @@ +#!/bin/sh +#/ script/setup-custom-gcl.sh ensures custom golangci-lint is installed. +#/ It returns the path to the custom-gcl binary. + +set -e + +ROOT_DIR="$(cd -- "$(dirname -- "$0")/.." > /dev/null 2>&1 && pwd -P)" +CUSTOM_GCL_CONFIG="$ROOT_DIR/.custom-gcl.yml" +GOLANGCI_LINT_VERSION="$( + sed -n 's/^[[:space:]]*version:[[:space:]]*//p' "$CUSTOM_GCL_CONFIG" \ + | sed '1{s/[[:space:]]*#.*$//;s/^"//;s/"$//;s/[[:space:]]*$//;p;};d' +)" + +if [ -z "$GOLANGCI_LINT_VERSION" ]; then + echo "Error: could not determine golangci-lint version from $CUSTOM_GCL_CONFIG" >&2 + exit 1 +fi + +BIN="$ROOT_DIR/bin" + +mkdir -p "$BIN" + +# install golangci-lint and custom-gcl in ./bin if they don't exist with the correct version +if ! "$BIN"/custom-gcl version --short 2> /dev/null | grep -q "$GOLANGCI_LINT_VERSION"; then + curl -sSfL https://golangci-lint.run/install.sh | sh -s -- -b "$BIN" "$GOLANGCI_LINT_VERSION" + "$BIN"/golangci-lint custom --name custom-gcl --destination "$BIN" +fi + +echo "$BIN/custom-gcl" diff --git a/script/test.sh b/script/test.sh new file mode 100755 index 00000000000..5d42097b90b --- /dev/null +++ b/script/test.sh @@ -0,0 +1,94 @@ +#!/bin/sh +#/ script/test.sh runs tests on each go module in go-github in parallel. +#/ Arguments are passed to each go test invocation. +#/ "-race -covermode atomic ./..." is used when no arguments are given. +#/ +#/ When UPDATE_GOLDEN is set, all directories named "golden" are removed before running tests. +#/ +#/ TEST_JOBS can be set to control parallelism (defaults to detected CPU count). + +set -e + +CDPATH="" cd -- "$(dirname -- "$0")/.." + +if [ "$#" = "0" ]; then + set -- -race -covermode atomic ./... +fi + +if [ -n "$UPDATE_GOLDEN" ]; then + find . -name golden -type d -exec rm -rf {} + +fi + +MOD_DIRS="$(git ls-files '*go.mod' | xargs dirname | sort -u)" + +GREEN='\033[0;32m' +RED='\033[0;31m' +BOLD='\033[1m' +NC='\033[0m' + +EXIT_CODE=0 +FAILED_COUNT=0 +RUNNING=0 +PIDS="" +DIRS_IN_FLIGHT="" + +LOG_DIR="$(mktemp -d)" +trap 'rm -rf "$LOG_DIR"' EXIT + +: "${TEST_JOBS:=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}" + +print_header() { + printf "${BOLD}%s${NC}\n\n" "$1" +} + +wait_pids() { + i=1 + for pid in $PIDS; do + dir=$(echo "$DIRS_IN_FLIGHT" | awk -v i="$i" '{print $i}') + log_file="$LOG_DIR/$(echo "$dir" | tr '/' '_').log" + + if wait "$pid"; then + printf "${GREEN}✔ %-40s [ PASS ]${NC}\n" "$dir" + else + printf "${RED}✘ %-40s [ FAIL ]${NC}\n" "$dir" + if [ -f "$log_file" ]; then + sed 's/^/ /' "$log_file" + fi + FAILED_COUNT=$((FAILED_COUNT + 1)) + EXIT_CODE=1 + fi + i=$((i + 1)) + done + PIDS="" + DIRS_IN_FLIGHT="" + RUNNING=0 +} + +print_header "Testing modules" + +for dir in $MOD_DIRS; do + log_file="$LOG_DIR/$(echo "$dir" | tr '/' '_').log" + + (cd "$dir" && go test "$@" > "$log_file" 2>&1) & + + PIDS="$PIDS $!" + DIRS_IN_FLIGHT="$DIRS_IN_FLIGHT $dir" + RUNNING=$((RUNNING + 1)) + + if [ "$RUNNING" -ge "$TEST_JOBS" ]; then + wait_pids + fi +done + +wait_pids + +printf -- "----------------------------\n" +SUMMARY_COLOR="$GREEN" +if [ "$FAILED_COUNT" -gt 0 ]; then + SUMMARY_COLOR="$RED" +fi + +printf "%bTesting: issues found in %d module directories.%b\n" "$SUMMARY_COLOR" "$FAILED_COUNT" "$NC" +printf -- "--------------------------------------------\n" + +exit "$EXIT_CODE" diff --git a/test/README.md b/test/README.md index 455795a7b80..74b65d0b84f 100644 --- a/test/README.md +++ b/test/README.md @@ -31,6 +31,10 @@ Run tests using: GITHUB_AUTH_TOKEN=XXX go test -v -tags=integration ./integration +Some tests create repositories. By default, the new repositories will be owned +by the user identified by the OAuth token. Set the `GITHUB_OWNER=''` +environment variable to specify a different owner, such as an organization. + Additionally there are a set of integration tests for the Authorizations API. These tests require a GitHub user (username and password), and also that a [GitHub Application](https://github.com/settings/applications/new) (with @@ -41,22 +45,3 @@ Authorization tests: If some or all of these environment variables are not available, certain of the Authorization integration tests will be skipped. - -fields ------- - -This will identify the fields being returned by the live GitHub API that are -not currently being mapped into the relevant Go data type. Sometimes fields -are deliberately not mapped, so the results of this tool should just be taken -as a hint. - -This test sends real network traffic to the GitHub API and will exhaust the -default unregistered rate limit (60 requests per hour) very quickly. -Additionally, some data is only returned for authenticated API calls. Unlike -the integration tests above, these tests only read data, so it's less -imperative that these be run using a dedicated test account (though you still -really should). - -Run the fields tool using: - - GITHUB_AUTH_TOKEN=XXX go run ./fields/fields.go diff --git a/test/fields/fields.go b/test/fields/fields.go deleted file mode 100644 index 6c7eac6b886..00000000000 --- a/test/fields/fields.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2014 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This tool tests for the JSON mappings in the go-github data types. It will -// identify fields that are returned by the live GitHub API, but that are not -// currently mapped into a struct field of the relevant go-github type. This -// helps to ensure that all relevant data returned by the API is being made -// accessible, particularly new fields that are periodically (and sometimes -// quietly) added to the API over time. -// -// These tests simply aid in identifying which fields aren't being mapped; it -// is not necessarily true that every one of them should always be mapped. -// Some fields may be undocumented for a reason, either because they aren't -// actually used yet or should not be relied upon. -package main - -import ( - "context" - "encoding/json" - "flag" - "fmt" - "os" - "reflect" - "strings" - - "github.com/google/go-github/v28/github" - "golang.org/x/oauth2" -) - -var ( - client *github.Client - - // auth indicates whether tests are being run with an OAuth token. - // Tests can use this flag to skip certain tests when run without auth. - auth bool - - skipURLs = flag.Bool("skip_urls", false, "skip url fields") -) - -func main() { - flag.Parse() - - token := os.Getenv("GITHUB_AUTH_TOKEN") - if token == "" { - print("!!! No OAuth token. Some tests won't run. !!!\n\n") - client = github.NewClient(nil) - } else { - tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - )) - client = github.NewClient(tc) - auth = true - } - - for _, tt := range []struct { - url string - typ interface{} - }{ - //{"rate_limit", &github.RateLimits{}}, - {"users/octocat", &github.User{}}, - {"user", &github.User{}}, - {"users/willnorris/keys", &[]github.Key{}}, - {"orgs/google-test", &github.Organization{}}, - {"repos/google/go-github", &github.Repository{}}, - {"repos/google/go-github/issues/1", &github.Issue{}}, - {"/gists/9257657", &github.Gist{}}, - } { - err := testType(tt.url, tt.typ) - if err != nil { - fmt.Printf("error: %v\n", err) - } - } -} - -// testType fetches the JSON resource at urlStr and compares its keys to the -// struct fields of typ. -func testType(urlStr string, typ interface{}) error { - slice := reflect.Indirect(reflect.ValueOf(typ)).Kind() == reflect.Slice - - req, err := client.NewRequest("GET", urlStr, nil) - if err != nil { - return err - } - - // start with a json.RawMessage so we can decode multiple ways below - raw := new(json.RawMessage) - _, err = client.Do(context.Background(), req, raw) - if err != nil { - return err - } - - // unmarshal directly to a map - var m1 map[string]interface{} - if slice { - var s []map[string]interface{} - err = json.Unmarshal(*raw, &s) - if err != nil { - return err - } - m1 = s[0] - } else { - err = json.Unmarshal(*raw, &m1) - if err != nil { - return err - } - } - - // unmarshal to typ first, then re-marshal and unmarshal to a map - err = json.Unmarshal(*raw, typ) - if err != nil { - return err - } - - var byt []byte - if slice { - // use first item in slice - v := reflect.Indirect(reflect.ValueOf(typ)) - byt, err = json.Marshal(v.Index(0).Interface()) - if err != nil { - return err - } - } else { - byt, err = json.Marshal(typ) - if err != nil { - return err - } - } - - var m2 map[string]interface{} - err = json.Unmarshal(byt, &m2) - if err != nil { - return err - } - - // now compare the two maps - for k, v := range m1 { - if *skipURLs && strings.HasSuffix(k, "_url") { - continue - } - if _, ok := m2[k]; !ok { - fmt.Printf("%v missing field for key: %v (example value: %v)\n", reflect.TypeOf(typ), k, v) - } - } - - return nil -} diff --git a/test/integration/activity_test.go b/test/integration/activity_test.go index 0ec78056e5a..8b8e09c12e1 100644 --- a/test/integration/activity_test.go +++ b/test/integration/activity_test.go @@ -3,15 +3,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" "testing" - "github.com/google/go-github/v28/github" + "github.com/google/go-github/v90/github" ) const ( @@ -20,7 +19,7 @@ const ( ) func TestActivity_Starring(t *testing.T) { - stargazers, _, err := client.Activity.ListStargazers(context.Background(), owner, repo, nil) + stargazers, _, err := client.Activity.ListStargazers(t.Context(), owner, repo, nil) if err != nil { t.Fatalf("Activity.ListStargazers returned error: %v", err) } @@ -30,12 +29,10 @@ func TestActivity_Starring(t *testing.T) { } // the rest of the tests requires auth - if !checkAuth("TestActivity_Starring") { - return - } + skipIfMissingAuth(t) // first, check if already starred the target repository - star, _, err := client.Activity.IsStarred(context.Background(), owner, repo) + star, _, err := client.Activity.IsStarred(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.IsStarred returned error: %v", err) } @@ -44,13 +41,13 @@ func TestActivity_Starring(t *testing.T) { } // star the target repository - _, err = client.Activity.Star(context.Background(), owner, repo) + _, err = client.Activity.Star(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.Star returned error: %v", err) } // check again and verify starred - star, _, err = client.Activity.IsStarred(context.Background(), owner, repo) + star, _, err = client.Activity.IsStarred(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.IsStarred returned error: %v", err) } @@ -59,13 +56,13 @@ func TestActivity_Starring(t *testing.T) { } // unstar - _, err = client.Activity.Unstar(context.Background(), owner, repo) + _, err = client.Activity.Unstar(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.Unstar returned error: %v", err) } // check again and verify not watching - star, _, err = client.Activity.IsStarred(context.Background(), owner, repo) + star, _, err = client.Activity.IsStarred(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.IsStarred returned error: %v", err) } @@ -76,13 +73,13 @@ func TestActivity_Starring(t *testing.T) { func deleteSubscription(t *testing.T) { // delete subscription - _, err := client.Activity.DeleteRepositorySubscription(context.Background(), owner, repo) + _, err := client.Activity.DeleteRepositorySubscription(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.DeleteRepositorySubscription returned error: %v", err) } // check again and verify not watching - sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), owner, repo) + sub, _, err := client.Activity.GetRepositorySubscription(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.GetRepositorySubscription returned error: %v", err) } @@ -93,14 +90,14 @@ func deleteSubscription(t *testing.T) { func createSubscription(t *testing.T) { // watch the target repository - sub := &github.Subscription{Subscribed: github.Bool(true)} - _, _, err := client.Activity.SetRepositorySubscription(context.Background(), owner, repo, sub) + sub := &github.Subscription{Subscribed: github.Ptr(true)} + _, _, err := client.Activity.SetRepositorySubscription(t.Context(), owner, repo, sub) if err != nil { t.Fatalf("Activity.SetRepositorySubscription returned error: %v", err) } // check again and verify watching - sub, _, err = client.Activity.GetRepositorySubscription(context.Background(), owner, repo) + sub, _, err = client.Activity.GetRepositorySubscription(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.GetRepositorySubscription returned error: %v", err) } @@ -110,7 +107,7 @@ func createSubscription(t *testing.T) { } func TestActivity_Watching(t *testing.T) { - watchers, _, err := client.Activity.ListWatchers(context.Background(), owner, repo, nil) + watchers, _, err := client.Activity.ListWatchers(t.Context(), owner, repo, nil) if err != nil { t.Fatalf("Activity.ListWatchers returned error: %v", err) } @@ -120,21 +117,18 @@ func TestActivity_Watching(t *testing.T) { } // the rest of the tests requires auth - if !checkAuth("TestActivity_Watching") { - return - } + skipIfMissingAuth(t) // first, check if already watching the target repository - sub, _, err := client.Activity.GetRepositorySubscription(context.Background(), owner, repo) + sub, _, err := client.Activity.GetRepositorySubscription(t.Context(), owner, repo) if err != nil { t.Fatalf("Activity.GetRepositorySubscription returned error: %v", err) } - switch { - case sub != nil: // If already subscribing, delete then recreate subscription. + if sub != nil { // If already subscribing, delete then recreate subscription. deleteSubscription(t) createSubscription(t) - case sub == nil: // Otherwise, create subscription and then delete it. + } else { // Otherwise, create subscription and then delete it. createSubscription(t) deleteSubscription(t) } diff --git a/test/integration/audit_log_test.go b/test/integration/audit_log_test.go new file mode 100644 index 00000000000..3ac53e26220 --- /dev/null +++ b/test/integration/audit_log_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build integration + +package integration + +import ( + "testing" +) + +// TestOrganizationAuditLog test that the client can read an org's audit log. +// Note: Org must be part of an enterprise. +// Test requires auth - set env var GITHUB_AUTH_TOKEN. +func TestOrganizationAuditLog(t *testing.T) { + skipIfMissingAuth(t) + + org := "example_org" + entries, _, err := client.Organizations.GetAuditLog(t.Context(), org, nil) + if err != nil { + t.Fatalf("Organizations.GetAuditLog returned error: %v", err) + } + + if len(entries) == 0 { + t.Error("No AuditLog events returned for org") + } + + for _, e := range entries { + t.Log(e.GetAction(), e.GetActor(), e.GetTimestamp(), e.GetUser()) + } +} diff --git a/test/integration/authorizations_test.go b/test/integration/authorizations_test.go index f6f065d3692..560da0e4036 100644 --- a/test/integration/authorizations_test.go +++ b/test/integration/authorizations_test.go @@ -3,169 +3,68 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" - "math/rand" "os" - "strconv" "strings" "testing" "time" - "github.com/google/go-github/v28/github" + "github.com/google/go-github/v90/github" ) -const msgEnvMissing = "Skipping test because the required environment variable (%v) is not present." -const envKeyGitHubUsername = "GITHUB_USERNAME" -const envKeyGitHubPassword = "GITHUB_PASSWORD" -const envKeyClientID = "GITHUB_CLIENT_ID" -const envKeyClientSecret = "GITHUB_CLIENT_SECRET" -const InvalidTokenValue = "iamnotacroken" - -// TestAuthorizationsBasicOperations tests the basic CRUD operations of the API (mostly for -// the Personal Access Token scenario). -func TestAuthorizationsBasicOperations(t *testing.T) { - - client := getUserPassClient(t) - - auths, resp, err := client.Authorizations.List(context.Background(), nil) - failOnError(t, err) - failIfNotStatusCode(t, resp, 200) - - initialAuthCount := len(auths) - - authReq := generatePersonalAuthTokenRequest() - - createdAuth, resp, err := client.Authorizations.Create(context.Background(), authReq) - failOnError(t, err) - failIfNotStatusCode(t, resp, 201) - - if *authReq.Note != *createdAuth.Note { - t.Fatal("Returned Authorization does not match the requested Authorization.") - } - - auths, resp, err = client.Authorizations.List(context.Background(), nil) - failOnError(t, err) - failIfNotStatusCode(t, resp, 200) - - if len(auths) != initialAuthCount+1 { - t.Fatalf("The number of Authorizations should have increased. Expected [%v], was [%v]", initialAuthCount+1, len(auths)) - } - - // Test updating the authorization - authUpdate := new(github.AuthorizationUpdateRequest) - authUpdate.Note = github.String("Updated note: " + randString()) - - updatedAuth, resp, err := client.Authorizations.Edit(context.Background(), *createdAuth.ID, authUpdate) - failOnError(t, err) - failIfNotStatusCode(t, resp, 200) - - if *updatedAuth.Note != *authUpdate.Note { - t.Fatal("The returned Authorization does not match the requested updated value.") - } - - // Verify that the Get operation also reflects the update - retrievedAuth, resp, err := client.Authorizations.Get(context.Background(), *createdAuth.ID) - failOnError(t, err) - failIfNotStatusCode(t, resp, 200) - - if *retrievedAuth.Note != *updatedAuth.Note { - t.Fatal("The retrieved Authorization does not match the expected (updated) value.") - } - - // Now, let's delete... - resp, err = client.Authorizations.Delete(context.Background(), *createdAuth.ID) - failOnError(t, err) - failIfNotStatusCode(t, resp, 204) - - // Verify that we can no longer retrieve the auth - retrievedAuth, resp, err = client.Authorizations.Get(context.Background(), *createdAuth.ID) - if err == nil { - t.Fatal("Should have failed due to 404") - } - failIfNotStatusCode(t, resp, 404) - - // Verify that our count reset back to the initial value - auths, resp, err = client.Authorizations.List(context.Background(), nil) - failOnError(t, err) - failIfNotStatusCode(t, resp, 200) - - if len(auths) != initialAuthCount { - t.Fatalf("The number of Authorizations should match the initial count Expected [%v], got [%v]", initialAuthCount, len(auths)) - } - -} +const ( + msgEnvMissing = "Skipping test because the required environment variable (%v) is not present." + envKeyClientID = "GITHUB_CLIENT_ID" + envKeyClientSecret = "GITHUB_CLIENT_SECRET" + envKeyAccessToken = "GITHUB_ACCESS_TOKEN" + InvalidTokenValue = "iamnotacroken" +) // TestAuthorizationsAppOperations tests the application/token related operations, such // as creating, testing, resetting and revoking application OAuth tokens. func TestAuthorizationsAppOperations(t *testing.T) { - - userAuthenticatedClient := getUserPassClient(t) - appAuthenticatedClient := getOAuthAppClient(t) // We know these vars are set because getOAuthAppClient would have // skipped the test by now clientID := os.Getenv(envKeyClientID) - clientSecret := os.Getenv(envKeyClientSecret) - - authRequest := generateAppAuthTokenRequest(clientID, clientSecret) - - createdAuth, resp, err := userAuthenticatedClient.Authorizations.GetOrCreateForApp(context.Background(), clientID, authRequest) - failOnError(t, err) - failIfNotStatusCode(t, resp, 201) - - // Quick sanity check: - if *createdAuth.Note != *authRequest.Note { - t.Fatal("The returned auth does not match expected value.") - } - - // Let's try the same request again, this time it should return the same - // auth instead of creating a new one - secondAuth, resp, err := userAuthenticatedClient.Authorizations.GetOrCreateForApp(context.Background(), clientID, authRequest) - failOnError(t, err) - failIfNotStatusCode(t, resp, 200) - - // Verify that the IDs are the same - if *createdAuth.ID != *secondAuth.ID { - t.Fatalf("The ID of the second returned auth should be the same as the first. Expected [%v], got [%v]", createdAuth.ID, secondAuth.ID) - } + accessToken := os.Getenv(envKeyAccessToken) // Verify the token - appAuth, resp, err := appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *createdAuth.Token) + appAuth, resp, err := appAuthenticatedClient.Authorizations.Check(t.Context(), clientID, accessToken) failOnError(t, err) failIfNotStatusCode(t, resp, 200) // Quick sanity check - if *appAuth.ID != *createdAuth.ID || *appAuth.Token != *createdAuth.Token { + if *appAuth.Token != accessToken { t.Fatal("The returned auth/token does not match.") } - // Let's verify that we get a 404 for a non-existent token - _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, InvalidTokenValue) + // Let's verify that we get a 404 for a nonexistent token + _, resp, err = appAuthenticatedClient.Authorizations.Check(t.Context(), clientID, InvalidTokenValue) if err == nil { t.Fatal("An error should have been returned because of the invalid token.") } failIfNotStatusCode(t, resp, 404) // Let's reset the token - resetAuth, resp, err := appAuthenticatedClient.Authorizations.Reset(context.Background(), clientID, *createdAuth.Token) + resetAuth, resp, err := appAuthenticatedClient.Authorizations.Reset(t.Context(), clientID, accessToken) failOnError(t, err) failIfNotStatusCode(t, resp, 200) - // Let's verify that we get a 404 for a non-existent token - _, resp, err = appAuthenticatedClient.Authorizations.Reset(context.Background(), clientID, InvalidTokenValue) + // Let's verify that we get a 404 for a nonexistent token + _, resp, err = appAuthenticatedClient.Authorizations.Reset(t.Context(), clientID, InvalidTokenValue) if err == nil { t.Fatal("An error should have been returned because of the invalid token.") } failIfNotStatusCode(t, resp, 404) // Verify that the token has changed - if resetAuth.Token == createdAuth.Token { + if *resetAuth.Token == accessToken { t.Fatal("The reset token should be different from the original.") } @@ -175,19 +74,19 @@ func TestAuthorizationsAppOperations(t *testing.T) { } // Verify that the original token is now invalid - _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *createdAuth.Token) + _, resp, err = appAuthenticatedClient.Authorizations.Check(t.Context(), clientID, accessToken) if err == nil { t.Fatal("The original token should be invalid.") } failIfNotStatusCode(t, resp, 404) // Check that the reset token is valid - _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *resetAuth.Token) + _, resp, err = appAuthenticatedClient.Authorizations.Check(t.Context(), clientID, *resetAuth.Token) failOnError(t, err) failIfNotStatusCode(t, resp, 200) // Let's revoke the token - resp, err = appAuthenticatedClient.Authorizations.Revoke(context.Background(), clientID, *resetAuth.Token) + resp, err = appAuthenticatedClient.Authorizations.Revoke(t.Context(), clientID, *resetAuth.Token) failOnError(t, err) failIfNotStatusCode(t, resp, 204) @@ -196,51 +95,15 @@ func TestAuthorizationsAppOperations(t *testing.T) { time.Sleep(time.Second * 2) // Now, the reset token should also be invalid - _, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *resetAuth.Token) + _, resp, err = appAuthenticatedClient.Authorizations.Check(t.Context(), clientID, *resetAuth.Token) if err == nil { t.Fatal("The reset token should be invalid.") } failIfNotStatusCode(t, resp, 404) } -// generatePersonalAuthTokenRequest is a helper function that generates an -// AuthorizationRequest for a Personal Access Token (no client id). -func generatePersonalAuthTokenRequest() *github.AuthorizationRequest { - - rand := randString() - auth := github.AuthorizationRequest{ - Note: github.String("Personal token: Note generated by test: " + rand), - Scopes: []github.Scope{github.ScopePublicRepo}, - Fingerprint: github.String("Personal token: Fingerprint generated by test: " + rand), - } - - return &auth -} - -// generatePersonalAuthTokenRequest is a helper function that generates an -// AuthorizationRequest for an OAuth application Token (uses client id). -func generateAppAuthTokenRequest(clientID string, clientSecret string) *github.AuthorizationRequest { - - rand := randString() - auth := github.AuthorizationRequest{ - Note: github.String("App token: Note generated by test: " + rand), - Scopes: []github.Scope{github.ScopePublicRepo}, - Fingerprint: github.String("App token: Fingerprint generated by test: " + rand), - ClientID: github.String(clientID), - ClientSecret: github.String(clientSecret), - } - - return &auth -} - -// randString returns a (kinda) random string for uniqueness purposes. -func randString() string { - return strconv.FormatInt(rand.NewSource(time.Now().UnixNano()).Int63(), 10) -} - // failOnError invokes t.Fatal() if err is present. func failOnError(t *testing.T, err error) { - if err != nil { t.Fatal(err) } @@ -248,33 +111,9 @@ func failOnError(t *testing.T, err error) { // failIfNotStatusCode invokes t.Fatal() if the response's status code doesn't match the expected code. func failIfNotStatusCode(t *testing.T, resp *github.Response, expectedCode int) { - if resp.StatusCode != expectedCode { t.Fatalf("Expected HTTP status code [%v] but received [%v]", expectedCode, resp.StatusCode) } - -} - -// getUserPassClient returns a GitHub client for authorization testing. The client -// uses BasicAuth via GH username and password passed in environment variables -// (and will skip the calling test if those vars are not present). -func getUserPassClient(t *testing.T) *github.Client { - username, ok := os.LookupEnv(envKeyGitHubUsername) - if !ok { - t.Skipf(msgEnvMissing, envKeyGitHubUsername) - } - - password, ok := os.LookupEnv(envKeyGitHubPassword) - if !ok { - t.Skipf(msgEnvMissing, envKeyGitHubPassword) - } - - tp := github.BasicAuthTransport{ - Username: strings.TrimSpace(username), - Password: strings.TrimSpace(password), - } - - return github.NewClient(tp.Client()) } // getOAuthAppClient returns a GitHub client for authorization testing. The client @@ -284,8 +123,9 @@ func getUserPassClient(t *testing.T) *github.Client { // an authorization; reset an authorization; revoke an authorization for an app) // require this authentication mechanism. // -// See GitHub API docs: https://developer.com/v3/oauth_authorizations/#check-an-authorization +// See GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization func getOAuthAppClient(t *testing.T) *github.Client { + t.Helper() username, ok := os.LookupEnv(envKeyClientID) if !ok { @@ -302,5 +142,9 @@ func getOAuthAppClient(t *testing.T) *github.Client { Password: strings.TrimSpace(password), } - return github.NewClient(tp.Client()) + c, err := github.NewClient(github.WithHTTPClient(tp.Client())) + if err != nil { + t.Fatal(err) + } + return c } diff --git a/test/integration/github_test.go b/test/integration/github_test.go index e68a56c44cf..b18223be052 100644 --- a/test/integration/github_test.go +++ b/test/integration/github_test.go @@ -3,82 +3,90 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" "fmt" + "log" "math/rand" "net/http" "os" + "sync" + "testing" - "github.com/google/go-github/v28/github" - "golang.org/x/oauth2" + "github.com/google/go-github/v90/github" ) -var ( - client *github.Client - - // auth indicates whether tests are being run with an OAuth token. - // Tests can use this flag to skip certain tests when run without auth. - auth bool -) - -func init() { +// client is a github.Client with the default http.Client. It is authorized if auth is true. +// auth indicates whether tests are being run with an OAuth token +// that is defined in the GITHUB_AUTH_TOKEN environment variable. +var client, auth = sync.OnceValues(func() (*github.Client, bool) { token := os.Getenv("GITHUB_AUTH_TOKEN") if token == "" { - print("!!! No OAuth token. Some tests won't run. !!!\n\n") - client = github.NewClient(nil) - } else { - tc := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - )) - client = github.NewClient(tc) - auth = true - } - - // Environment variables required for Authorization integration tests - vars := []string{envKeyGitHubUsername, envKeyGitHubPassword, envKeyClientID, envKeyClientSecret} - - for _, v := range vars { - value := os.Getenv(v) - if value == "" { - print("!!! " + fmt.Sprintf(msgEnvMissing, v) + " !!!\n\n") + c, err := github.NewClient() + if err != nil { + log.Fatalf("Error creating GitHub client: %v", err) } + return c, false } -} + c, err := github.NewClient(github.WithAuthToken(token)) + if err != nil { + log.Fatalf("Error creating GitHub client with token: %v", err) + } + return c, true +})() -func checkAuth(name string) bool { +func skipIfMissingAuth(t *testing.T) { if !auth { - fmt.Printf("No auth - skipping portions of %v\n", name) + t.Skipf("No OAuth token - skipping portions of %v\n", t.Name()) } - return auth } -func createRandomTestRepository(owner string, autoinit bool) (*github.Repository, error) { +func createRandomTestRepository(t *testing.T, owner string, autoinit bool) *github.Repository { + t.Helper() + + // determine the owner to use if one wasn't specified + if owner == "" { + owner = os.Getenv("GITHUB_OWNER") + if owner == "" { + me, _, err := client.Users.Get(t.Context(), "") + if err != nil { + t.Fatalf("Users.Get returned error: %v", err) + } + owner = *me.Login + } + } + // create random repo name that does not currently exist var repoName string for { - repoName = fmt.Sprintf("test-%d", rand.Int()) - _, resp, err := client.Repositories.Get(context.Background(), owner, repoName) + repoName = fmt.Sprintf("test-%v", rand.Int()) + _, resp, err := client.Repositories.Get(t.Context(), owner, repoName) if err != nil { if resp.StatusCode == http.StatusNotFound { - // found a non-existent repo, perfect + // found a nonexistent repo, perfect break } - return nil, err + t.Fatalf("Repositories.Get returned error: %v", err) } } // create the repository - repo, _, err := client.Repositories.Create(context.Background(), "", &github.Repository{Name: github.String(repoName), AutoInit: github.Bool(autoinit)}) + repo, _, err := client.Repositories.Create( + t.Context(), + owner, + &github.Repository{ + Name: &repoName, + AutoInit: &autoinit, + }, + ) if err != nil { - return nil, err + t.Fatalf("Repositories.Create returned error: %v", err) } - return repo, nil + return repo } diff --git a/test/integration/issues_test.go b/test/integration/issues_test.go index a5e489006bc..26a536b1acc 100644 --- a/test/integration/issues_test.go +++ b/test/integration/issues_test.go @@ -3,35 +3,34 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" "testing" ) func TestIssueEvents(t *testing.T) { - events, _, err := client.Issues.ListRepositoryEvents(context.Background(), "google", "go-github", nil) + events, _, err := client.Issues.ListRepositoryEvents(t.Context(), "google", "go-github", nil) if err != nil { t.Fatalf("Issues.ListRepositoryEvents returned error: %v", err) } if len(events) == 0 { - t.Errorf("ListRepositoryEvents returned no events") + t.Error("ListRepositoryEvents returned no events") } - events, _, err = client.Issues.ListIssueEvents(context.Background(), "google", "go-github", 1, nil) + events, _, err = client.Issues.ListIssueEvents(t.Context(), "google", "go-github", 1, nil) if err != nil { t.Fatalf("Issues.ListIssueEvents returned error: %v", err) } if len(events) == 0 { - t.Errorf("ListIssueEvents returned no events") + t.Error("ListIssueEvents returned no events") } - event, _, err := client.Issues.GetEvent(context.Background(), "google", "go-github", *events[0].ID) + event, _, err := client.Issues.GetEvent(t.Context(), "google", "go-github", *events[0].ID) if err != nil { t.Fatalf("Issues.GetEvent returned error: %v", err) } diff --git a/test/integration/licences_test.go b/test/integration/licences_test.go new file mode 100644 index 00000000000..1e3822f15fb --- /dev/null +++ b/test/integration/licences_test.go @@ -0,0 +1,36 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build integration + +package integration + +import ( + "testing" + + "github.com/google/go-github/v90/github" +) + +func TestLicenses_ListIter(t *testing.T) { + opts := &github.ListLicensesOptions{ + Featured: github.Ptr(true), + ListOptions: github.ListOptions{ + Page: 1, + PerPage: 1, + }, + } + + var featuredLicensesCount int + for _, err := range client.Licenses.ListIter(t.Context(), opts) { + if err != nil { + t.Fatalf("Licenses.ListIter returned error during iteration: %v", err) + } + featuredLicensesCount++ + } + + if featuredLicensesCount < 2 { + t.Errorf("Licenses.ListIter returned fewer than 2 featured licenses: %v", featuredLicensesCount) + } +} diff --git a/test/integration/misc_test.go b/test/integration/misc_test.go index 1595cf07b9b..a6937d63944 100644 --- a/test/integration/misc_test.go +++ b/test/integration/misc_test.go @@ -3,77 +3,65 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" "testing" "time" ) func TestEmojis(t *testing.T) { - emoji, _, err := client.ListEmojis(context.Background()) + emoji, _, err := client.Emojis.List(t.Context()) if err != nil { - t.Fatalf("ListEmojis returned error: %v", err) + t.Fatalf("List returned error: %v", err) } if len(emoji) == 0 { - t.Errorf("ListEmojis returned no emojis") + t.Error("List returned no emojis") } if _, ok := emoji["+1"]; !ok { - t.Errorf("ListEmojis missing '+1' emoji") + t.Error("List missing '+1' emoji") } } func TestAPIMeta(t *testing.T) { - meta, _, err := client.APIMeta(context.Background()) + meta, _, err := client.Meta.Get(t.Context()) if err != nil { - t.Fatalf("APIMeta returned error: %v", err) + t.Fatalf("Get returned error: %v", err) } if len(meta.Hooks) == 0 { - t.Errorf("APIMeta returned no hook addresses") + t.Error("Get returned no hook addresses") } if len(meta.Git) == 0 { - t.Errorf("APIMeta returned no git addresses") + t.Error("Get returned no git addresses") } - if !*meta.VerifiablePasswordAuthentication { - t.Errorf("APIMeta VerifiablePasswordAuthentication is false") + if *meta.VerifiablePasswordAuthentication { + t.Error("APIMeta VerifiablePasswordAuthentication is true") } } func TestRateLimits(t *testing.T) { - limits, _, err := client.RateLimits(context.Background()) + limits, _, err := client.RateLimit.Get(t.Context()) if err != nil { t.Fatalf("RateLimits returned error: %v", err) } // do some sanity checks if limits.Core.Limit == 0 { - t.Errorf("RateLimits returned 0 core limit") + t.Error("RateLimits returned 0 core limit") } if limits.Core.Limit < limits.Core.Remaining { - t.Errorf("Core.Limits is less than Core.Remaining.") + t.Error("Core.Limits is less than Core.Remaining.") } if limits.Core.Reset.Time.Before(time.Now().Add(-1 * time.Minute)) { - t.Errorf("Core.Reset is more than 1 minute in the past; that doesn't seem right.") - } -} - -func TestListServiceHooks(t *testing.T) { - hooks, _, err := client.ListServiceHooks(context.Background()) - if err != nil { - t.Fatalf("ListServiceHooks returned error: %v", err) - } - - if len(hooks) == 0 { - t.Fatalf("ListServiceHooks returned no hooks") + t.Error("Core.Reset is more than 1 minute in the past; that doesn't seem right.") } } diff --git a/test/integration/pagination_test.go b/test/integration/pagination_test.go new file mode 100644 index 00000000000..75a2b9de373 --- /dev/null +++ b/test/integration/pagination_test.go @@ -0,0 +1,38 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build integration + +package integration + +import ( + "testing" + + "github.com/google/go-github/v90/github" +) + +func TestSecurityAdvisories_ListGlobalSecurityAdvisories(t *testing.T) { + opt := &github.ListGlobalSecurityAdvisoriesOptions{ + ListCursorOptions: github.ListCursorOptions{ + PerPage: 2, + }, + } + advisories, resp, err := client.SecurityAdvisories.ListGlobalSecurityAdvisories(t.Context(), opt) + if err != nil { + t.Fatalf("ListGlobalSecurityAdvisories returned error: %v", err) + } + + if got, want := len(advisories), 2; got != want { + t.Errorf("ListGlobalSecurityAdvisories returned %v advisories, want %v", got, want) + } + + if resp.After == "" { + t.Error("ListGlobalSecurityAdvisories returned an empty 'after' cursor") + } + + if resp.Cursor != "" { + t.Error("ListGlobalSecurityAdvisories returned a non-empty 'cursor' value") + } +} diff --git a/test/integration/projects_test.go b/test/integration/projects_test.go new file mode 100644 index 00000000000..63d5640cb27 --- /dev/null +++ b/test/integration/projects_test.go @@ -0,0 +1,104 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build integration + +package integration + +import ( + "os" + "testing" + + "github.com/google/go-github/v90/github" +) + +// Integration tests for Projects V2 endpoints defined in github/projects.go. +// +// These tests are intentionally defensive. They only require minimal +// environment variables identifying a target org and user. Project numbers are +// discovered dynamically by first listing projects and selecting one. For item +// CRUD operations, the test creates a temporary repository & issue (where +// possible) and adds/removes that issue as a project item. If prerequisites +// (auth, env vars, permissions, presence of at least one project) are missing, +// the relevant sub-test is skipped so other integration tests can still run. +// +// Required / optional environment variables: +// GITHUB_AUTH_TOKEN (required for any of these tests to run) +// GITHUB_TEST_ORG (org login; required for org project tests) +// GITHUB_TEST_USER (user login; required for user project tests) +// GITHUB_TEST_REPO (repo name) + +func TestProjectsV2_Org(t *testing.T) { + skipIfMissingAuth(t) + org := os.Getenv("GITHUB_TEST_ORG") + if org == "" { + t.Skip("GITHUB_TEST_ORG not set") + } + + ctx := t.Context() + + opts := &github.ListProjectsOptions{} + // List projects for org; pick the first available project we can read. + projects, _, err := client.Projects.ListOrganizationProjects(ctx, org, opts) + if err != nil { + // If listing itself fails, abort this test. + t.Fatalf("Projects.ListOrganizationProjects returned error: %v", err) + } + if len(projects) == 0 { + t.Skipf("no Projects V2 found for org %s", org) + } + project := projects[0] + if project.Number == nil { + t.Skip("selected org project has nil Number field") + } + projectNumber := *project.Number + + // Re-fetch via Get to exercise endpoint explicitly. + proj, _, err := client.Projects.GetOrganizationProject(ctx, org, projectNumber) + if err != nil { + // Permission mismatch? Skip CRUD while still reporting failure would make the test fail; + // we want correctness so treat as fatal here. + t.Fatalf("Projects.GetOrganizationProject returned error: %v", err) + } + if proj.Number == nil || *proj.Number != projectNumber { + t.Fatalf("GetOrganizationProject returned unexpected project number: got %+v want %d", proj.Number, projectNumber) + } + + _, _, err = client.Projects.ListOrganizationProjectFields(ctx, org, projectNumber, nil) + if err != nil { + t.Fatalf("Projects.ListOrganizationProjectFields returned error: %v. Fields listing might require extra permissions", err) + } +} + +func TestProjectsV2_User(t *testing.T) { + skipIfMissingAuth(t) + user := os.Getenv("GITHUB_TEST_USER") + if user == "" { + t.Skip("GITHUB_TEST_USER not set") + } + + ctx := t.Context() + opts := &github.ListProjectsOptions{} + projects, _, err := client.Projects.ListUserProjects(ctx, user, opts) + if err != nil { + t.Fatalf("Projects.ListUserProjects returned error: %v. This indicates API or permission issue", err) + } + if len(projects) == 0 { + t.Skipf("no Projects V2 found for user %s", user) + } + project := projects[0] + if project.Number == nil { + t.Skip("selected user project has nil Number field") + } + + proj, _, err := client.Projects.GetUserProject(ctx, user, *project.Number) + if err != nil { + // can't fetch specific project; treat as fatal + t.Fatalf("Projects.GetUserProject returned error: %v", err) + } + if proj.Number == nil || *proj.Number != *project.Number { + t.Fatalf("GetUserProject returned unexpected project number: got %+v want %d", proj.Number, *project.Number) + } +} diff --git a/test/integration/pulls_test.go b/test/integration/pulls_test.go index 4aadfbd7117..2a923b770cc 100644 --- a/test/integration/pulls_test.go +++ b/test/integration/pulls_test.go @@ -3,23 +3,22 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" "testing" ) func TestPullRequests_ListCommits(t *testing.T) { - commits, _, err := client.PullRequests.ListCommits(context.Background(), "google", "go-github", 2, nil) + commits, _, err := client.PullRequests.ListCommits(t.Context(), "google", "go-github", 2, nil) if err != nil { t.Fatalf("PullRequests.ListCommits() returned error: %v", err) } if got, want := len(commits), 3; got != want { - t.Fatalf("PullRequests.ListCommits() returned %d commits, want %d", got, want) + t.Fatalf("PullRequests.ListCommits() returned %v commits, want %v", got, want) } if got, want := *commits[0].Author.Login, "sqs"; got != want { diff --git a/test/integration/repos_test.go b/test/integration/repos_test.go index c722fabdf3e..eb8294c3b8a 100644 --- a/test/integration/repos_test.go +++ b/test/integration/repos_test.go @@ -3,103 +3,81 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" + "io" "net/http" - "reflect" "testing" - "github.com/google/go-github/v28/github" + "github.com/google/go-cmp/cmp" + "github.com/google/go-github/v90/github" ) func TestRepositories_CRUD(t *testing.T) { - if !checkAuth("TestRepositories_CRUD") { - return - } + skipIfMissingAuth(t) - // get authenticated user - me, _, err := client.Users.Get(context.Background(), "") - if err != nil { - t.Fatalf("Users.Get('') returned error: %v", err) - } - - repo, err := createRandomTestRepository(*me.Login, false) - if err != nil { - t.Fatalf("createRandomTestRepository returned error: %v", err) - } + repo := createRandomTestRepository(t, "", true) // update the repository description - repo.Description = github.String("description") + repo.Description = github.Ptr("description") repo.DefaultBranch = nil // FIXME: this shouldn't be necessary - _, _, err = client.Repositories.Edit(context.Background(), *repo.Owner.Login, *repo.Name, repo) + _, _, err := client.Repositories.Edit(t.Context(), *repo.Owner.Login, *repo.Name, repo) if err != nil { t.Fatalf("Repositories.Edit() returned error: %v", err) } // delete the repository - _, err = client.Repositories.Delete(context.Background(), *repo.Owner.Login, *repo.Name) + _, err = client.Repositories.Delete(t.Context(), *repo.Owner.Login, *repo.Name) if err != nil { t.Fatalf("Repositories.Delete() returned error: %v", err) } // verify that the repository was deleted - _, resp, err := client.Repositories.Get(context.Background(), *repo.Owner.Login, *repo.Name) + _, resp, err := client.Repositories.Get(t.Context(), *repo.Owner.Login, *repo.Name) if err == nil { - t.Fatalf("Test repository still exists after deleting it.") + t.Fatal("Test repository still exists after deleting it.") } - if err != nil && resp.StatusCode != http.StatusNotFound { + if resp.StatusCode != http.StatusNotFound { t.Fatalf("Repositories.Get() returned error: %v", err) } } func TestRepositories_BranchesTags(t *testing.T) { // branches - branches, _, err := client.Repositories.ListBranches(context.Background(), "git", "git", nil) + branches, _, err := client.Repositories.ListBranches(t.Context(), "git", "git", nil) if err != nil { t.Fatalf("Repositories.ListBranches() returned error: %v", err) } if len(branches) == 0 { - t.Fatalf("Repositories.ListBranches('git', 'git') returned no branches") + t.Fatal("Repositories.ListBranches('git', 'git') returned no branches") } - _, _, err = client.Repositories.GetBranch(context.Background(), "git", "git", *branches[0].Name) + _, _, err = client.Repositories.GetBranch(t.Context(), "git", "git", *branches[0].Name, 0) if err != nil { t.Fatalf("Repositories.GetBranch() returned error: %v", err) } // tags - tags, _, err := client.Repositories.ListTags(context.Background(), "git", "git", nil) + tags, _, err := client.Repositories.ListTags(t.Context(), "git", "git", nil) if err != nil { t.Fatalf("Repositories.ListTags() returned error: %v", err) } if len(tags) == 0 { - t.Fatalf("Repositories.ListTags('git', 'git') returned no tags") + t.Fatal("Repositories.ListTags('git', 'git') returned no tags") } } func TestRepositories_EditBranches(t *testing.T) { - if !checkAuth("TestRepositories_EditBranches") { - return - } - - // get authenticated user - me, _, err := client.Users.Get(context.Background(), "") - if err != nil { - t.Fatalf("Users.Get('') returned error: %v", err) - } + skipIfMissingAuth(t) - repo, err := createRandomTestRepository(*me.Login, true) - if err != nil { - t.Fatalf("createRandomTestRepository returned error: %v", err) - } + repo := createRandomTestRepository(t, "", true) - branch, _, err := client.Repositories.GetBranch(context.Background(), *repo.Owner.Login, *repo.Name, "master") + branch, _, err := client.Repositories.GetBranch(t.Context(), *repo.Owner.Login, *repo.Name, "master", 0) if err != nil { t.Fatalf("Repositories.GetBranch() returned error: %v", err) } @@ -111,7 +89,7 @@ func TestRepositories_EditBranches(t *testing.T) { protectionRequest := &github.ProtectionRequest{ RequiredStatusChecks: &github.RequiredStatusChecks{ Strict: true, - Contexts: []string{"continuous-integration"}, + Contexts: &[]string{"continuous-integration"}, }, RequiredPullRequestReviews: &github.PullRequestReviewsEnforcementRequest{ DismissStaleReviews: true, @@ -120,10 +98,13 @@ func TestRepositories_EditBranches(t *testing.T) { // TODO: Only organization repositories can have users and team restrictions. // In order to be able to test these Restrictions, need to add support // for creating temporary organization repositories. - Restrictions: nil, + Restrictions: nil, + BlockCreations: github.Ptr(false), + LockBranch: github.Ptr(false), + AllowForkSyncing: github.Ptr(false), } - protection, _, err := client.Repositories.UpdateBranchProtection(context.Background(), *repo.Owner.Login, *repo.Name, "master", protectionRequest) + protection, _, err := client.Repositories.UpdateBranchProtection(t.Context(), *repo.Owner.Login, *repo.Name, "master", protectionRequest) if err != nil { t.Fatalf("Repositories.UpdateBranchProtection() returned error: %v", err) } @@ -131,51 +112,102 @@ func TestRepositories_EditBranches(t *testing.T) { want := &github.Protection{ RequiredStatusChecks: &github.RequiredStatusChecks{ Strict: true, - Contexts: []string{"continuous-integration"}, + Contexts: &[]string{"continuous-integration"}, }, RequiredPullRequestReviews: &github.PullRequestReviewsEnforcement{ DismissStaleReviews: true, RequiredApprovingReviewCount: 0, }, EnforceAdmins: &github.AdminEnforcement{ - URL: github.String("https://api.github.com/repos/" + *repo.Owner.Login + "/" + *repo.Name + "/branches/master/protection/enforce_admins"), + URL: github.Ptr("https://api.github.com/repos/" + *repo.Owner.Login + "/" + *repo.Name + "/branches/master/protection/enforce_admins"), Enabled: true, }, Restrictions: nil, + BlockCreations: &github.BlockCreations{ + Enabled: github.Ptr(false), + }, + LockBranch: &github.LockBranch{ + Enabled: github.Ptr(false), + }, + AllowForkSyncing: &github.AllowForkSyncing{ + Enabled: github.Ptr(false), + }, } - if !reflect.DeepEqual(protection, want) { + if !cmp.Equal(protection, want) { t.Errorf("Repositories.UpdateBranchProtection() returned %+v, want %+v", protection, want) } - _, err = client.Repositories.Delete(context.Background(), *repo.Owner.Login, *repo.Name) + _, err = client.Repositories.Delete(t.Context(), *repo.Owner.Login, *repo.Name) if err != nil { t.Fatalf("Repositories.Delete() returned error: %v", err) } } -func TestRepositories_List(t *testing.T) { - if !checkAuth("TestRepositories_List") { - return - } +func TestRepositories_ListByAuthenticatedUser(t *testing.T) { + skipIfMissingAuth(t) - _, _, err := client.Repositories.List(context.Background(), "", nil) + _, _, err := client.Repositories.ListByAuthenticatedUser(t.Context(), nil) if err != nil { - t.Fatalf("Repositories.List('') returned error: %v", err) + t.Fatalf("Repositories.ListByAuthenticatedUser() returned error: %v", err) } +} - _, _, err = client.Repositories.List(context.Background(), "google", nil) +func TestRepositories_ListByUser(t *testing.T) { + _, _, err := client.Repositories.ListByUser(t.Context(), "google", nil) if err != nil { - t.Fatalf("Repositories.List('google') returned error: %v", err) + t.Fatalf("Repositories.ListByUser('google') returned error: %v", err) } - opt := github.RepositoryListOptions{Sort: "created"} - repos, _, err := client.Repositories.List(context.Background(), "google", &opt) + opt := github.RepositoryListByUserOptions{Sort: "created"} + repos, _, err := client.Repositories.ListByUser(t.Context(), "google", &opt) if err != nil { t.Fatalf("Repositories.List('google') with Sort opt returned error: %v", err) } for i, repo := range repos { if i > 0 && (*repos[i-1].CreatedAt).Time.Before((*repo.CreatedAt).Time) { - t.Fatalf("Repositories.List('google') with default descending Sort returned incorrect order") + t.Fatal("Repositories.ListByUser('google') with default descending Sort returned incorrect order") } } } + +func TestRepositories_DownloadReleaseAsset(t *testing.T) { + skipIfMissingAuth(t) + + rc, _, err := client.Repositories.DownloadReleaseAsset(t.Context(), "andersjanmyr", "goose", 484892, http.DefaultClient) + if err != nil { + t.Fatalf("Repositories.DownloadReleaseAsset(andersjanmyr, goose, 484892, true) returned error: %v", err) + } + defer func() { _ = rc.Close() }() + _, err = io.Copy(io.Discard, rc) + if err != nil { + t.Fatalf("Repositories.DownloadReleaseAsset(andersjanmyr, goose, 484892, true) returned error: %v", err) + } +} + +func TestRepositories_Autolinks(t *testing.T) { + skipIfMissingAuth(t) + + repo := createRandomTestRepository(t, "", true) + + body := github.CreateAutolinkRequest{ + KeyPrefix: "TICKET-", + URLTemplate: "https://example.com/TICKET?query=", + IsAlphanumeric: github.Ptr(false), + } + + actionlink, _, err := client.Repositories.CreateAutolink(t.Context(), *repo.Owner.Login, *repo.Name, body) + if err != nil { + t.Fatalf("Repositories.CreateAutolink() returned error: %v", err) + } + + if !cmp.Equal(actionlink.GetKeyPrefix(), body.KeyPrefix) || + !cmp.Equal(actionlink.GetURLTemplate(), body.URLTemplate) || + !cmp.Equal(actionlink.IsAlphanumeric, body.IsAlphanumeric) { + t.Errorf("Repositories.CreateAutolink() returned %+v, want %+v", actionlink, body) + } + + _, err = client.Repositories.Delete(t.Context(), *repo.Owner.Login, *repo.Name) + if err != nil { + t.Fatalf("Repositories.Delete() returned error: %v", err) + } +} diff --git a/test/integration/users_test.go b/test/integration/users_test.go index 3d587b694a6..f490f4ac1c1 100644 --- a/test/integration/users_test.go +++ b/test/integration/users_test.go @@ -3,28 +3,27 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build integration +//go:build integration package integration import ( - "context" "fmt" "math/rand" "testing" - "github.com/google/go-github/v28/github" + "github.com/google/go-github/v90/github" ) func TestUsers_Get(t *testing.T) { // list all users - users, _, err := client.Users.ListAll(context.Background(), nil) + users, _, err := client.Users.ListAll(t.Context(), nil) if err != nil { t.Fatalf("Users.ListAll returned error: %v", err) } if len(users) == 0 { - t.Errorf("Users.ListAll returned no users") + t.Error("Users.ListAll returned no users") } // mojombo is user #1 @@ -33,7 +32,7 @@ func TestUsers_Get(t *testing.T) { } // get individual user - u, _, err := client.Users.Get(context.Background(), "octocat") + u, _, err := client.Users.Get(t.Context(), "octocat") if err != nil { t.Fatalf("Users.Get('octocat') returned error: %v", err) } @@ -47,17 +46,15 @@ func TestUsers_Get(t *testing.T) { } func TestUsers_Update(t *testing.T) { - if !checkAuth("TestUsers_Get") { - return - } + skipIfMissingAuth(t) - u, _, err := client.Users.Get(context.Background(), "") + u, _, err := client.Users.Get(t.Context(), "") if err != nil { t.Fatalf("Users.Get('') returned error: %v", err) } if *u.Login == "" { - t.Errorf("wanted non-empty values for user.Login") + t.Error("wanted non-empty values for user.Login") } // save original location @@ -67,16 +64,15 @@ func TestUsers_Update(t *testing.T) { } // update location to test value - testLoc := fmt.Sprintf("test-%d", rand.Int()) - u.Location = &testLoc + testLoc := fmt.Sprintf("test-%v", rand.Int()) - _, _, err = client.Users.Edit(context.Background(), u) + _, _, err = client.Users.Update(t.Context(), github.UserUpdateRequest{Location: &testLoc}) if err != nil { t.Fatalf("Users.Update returned error: %v", err) } // refetch user and check location value - u, _, err = client.Users.Get(context.Background(), "") + u, _, err = client.Users.Get(t.Context(), "") if err != nil { t.Fatalf("Users.Get('') returned error: %v", err) } @@ -86,19 +82,16 @@ func TestUsers_Update(t *testing.T) { } // set location back to the original value - u.Location = &location - _, _, err = client.Users.Edit(context.Background(), u) + _, _, err = client.Users.Update(t.Context(), github.UserUpdateRequest{Location: &location}) if err != nil { - t.Fatalf("Users.Edit returned error: %v", err) + t.Fatalf("Users.Update returned error: %v", err) } } func TestUsers_Emails(t *testing.T) { - if !checkAuth("TestUsers_Emails") { - return - } + skipIfMissingAuth(t) - emails, _, err := client.Users.ListEmails(context.Background(), nil) + emails, _, err := client.Users.ListEmails(t.Context(), nil) if err != nil { t.Fatalf("Users.ListEmails() returned error: %v", err) } @@ -107,7 +100,7 @@ func TestUsers_Emails(t *testing.T) { var email string EmailLoop: for { - email = fmt.Sprintf("test-%d@example.com", rand.Int()) + email = fmt.Sprintf("test-%v@example.com", rand.Int()) for _, e := range emails { if e.Email != nil && *e.Email == email { continue EmailLoop @@ -117,13 +110,13 @@ EmailLoop: } // Add new address - _, _, err = client.Users.AddEmails(context.Background(), []string{email}) + _, _, err = client.Users.AddEmails(t.Context(), []string{email}) if err != nil { t.Fatalf("Users.AddEmails() returned error: %v", err) } // List emails again and verify new email is present - emails, _, err = client.Users.ListEmails(context.Background(), nil) + emails, _, err = client.Users.ListEmails(t.Context(), nil) if err != nil { t.Fatalf("Users.ListEmails() returned error: %v", err) } @@ -141,13 +134,13 @@ EmailLoop: } // Remove new address - _, err = client.Users.DeleteEmails(context.Background(), []string{email}) + _, err = client.Users.DeleteEmails(t.Context(), []string{email}) if err != nil { t.Fatalf("Users.DeleteEmails() returned error: %v", err) } // List emails again and verify new email was removed - emails, _, err = client.Users.ListEmails(context.Background(), nil) + emails, _, err = client.Users.ListEmails(t.Context(), nil) if err != nil { t.Fatalf("Users.ListEmails() returned error: %v", err) } @@ -160,22 +153,20 @@ EmailLoop: } func TestUsers_Keys(t *testing.T) { - keys, _, err := client.Users.ListKeys(context.Background(), "willnorris", nil) + keys, _, err := client.Users.ListKeys(t.Context(), "willnorris", nil) if err != nil { t.Fatalf("Users.ListKeys('willnorris') returned error: %v", err) } if len(keys) == 0 { - t.Errorf("Users.ListKeys('willnorris') returned no keys") + t.Error("Users.ListKeys('willnorris') returned no keys") } // the rest of the tests requires auth - if !checkAuth("TestUsers_Keys") { - return - } + skipIfMissingAuth(t) // TODO: make this integration test work for any authenticated user. - keys, _, err = client.Users.ListKeys(context.Background(), "", nil) + keys, _, err = client.Users.ListKeys(t.Context(), "", nil) if err != nil { t.Fatalf("Users.ListKeys('') returned error: %v", err) } @@ -184,21 +175,21 @@ func TestUsers_Keys(t *testing.T) { key := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCy/RIqaMFj2wjkOEjx9EAU0ReLAIhodga82/feo5nnT9UUkHLbL9xrIavfdLHx28lD3xYgPfAoSicUMaAeNwuQhmuerr2c2LFGxzrdXP8pVsQ+Ol7y7OdmFPfe0KrzoZaLJs9aSiZ4VKyY4z5Se/k2UgcJTdgQVlLfw/P96aqCx8yUu94BiWqkDqYEvgWKRNHrTiIo1EXeVBCCcfgNZe1suFfNJUJSUU2T3EG2bpwBbSOCjE3FyH8+Lz3K3BOGzm3df8E7Regj9j4YIcD8cWJYO86jLJoGgQ0L5MSOq+ishNaHQXech22Ix03D1lVMjCvDT7S/C94Z1LzhI2lhvyff" for _, k := range keys { if k.Key != nil && *k.Key == key { - t.Fatalf("Test key already exists for user. Please manually remove it first.") + t.Fatal("Test key already exists for user. Please manually remove it first.") } } // Add new key - _, _, err = client.Users.CreateKey(context.Background(), &github.Key{ - Title: github.String("go-github test key"), - Key: github.String(key), + _, _, err = client.Users.CreateKey(t.Context(), &github.Key{ + Title: github.Ptr("go-github test key"), + Key: &key, }) if err != nil { t.Fatalf("Users.CreateKey() returned error: %v", err) } // List keys again and verify new key is present - keys, _, err = client.Users.ListKeys(context.Background(), "", nil) + keys, _, err = client.Users.ListKeys(t.Context(), "", nil) if err != nil { t.Fatalf("Users.ListKeys('') returned error: %v", err) } @@ -212,11 +203,11 @@ func TestUsers_Keys(t *testing.T) { } if id == 0 { - t.Fatalf("Users.ListKeys('') does not contain added test key") + t.Fatal("Users.ListKeys('') does not contain added test key") } // Verify that fetching individual key works - k, _, err := client.Users.GetKey(context.Background(), id) + k, _, err := client.Users.GetKey(t.Context(), id) if err != nil { t.Fatalf("Users.GetKey(%q) returned error: %v", id, err) } @@ -225,20 +216,20 @@ func TestUsers_Keys(t *testing.T) { } // Remove test key - _, err = client.Users.DeleteKey(context.Background(), id) + _, err = client.Users.DeleteKey(t.Context(), id) if err != nil { - t.Fatalf("Users.DeleteKey(%d) returned error: %v", id, err) + t.Fatalf("Users.DeleteKey(%v) returned error: %v", id, err) } // List keys again and verify test key was removed - keys, _, err = client.Users.ListKeys(context.Background(), "", nil) + keys, _, err = client.Users.ListKeys(t.Context(), "", nil) if err != nil { t.Fatalf("Users.ListKeys('') returned error: %v", err) } for _, k := range keys { if k.Key != nil && *k.Key == key { - t.Fatalf("Users.ListKeys('') still contains test key after removing it") + t.Fatal("Users.ListKeys('') still contains test key after removing it") } } } diff --git a/tools/check-structfield-settings/go.mod b/tools/check-structfield-settings/go.mod new file mode 100644 index 00000000000..68ae0bd17d6 --- /dev/null +++ b/tools/check-structfield-settings/go.mod @@ -0,0 +1,18 @@ +module github.com/google/go-github/v90/tools/check-structfield-settings + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + github.com/google/go-github/v90/tools/structfield v0.0.0 + go.yaml.in/yaml/v3 v3.0.5 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) + +// Use version at HEAD, not the latest published. +replace github.com/google/go-github/v90/tools/structfield v0.0.0 => ../structfield diff --git a/tools/check-structfield-settings/go.sum b/tools/check-structfield-settings/go.sum new file mode 100644 index 00000000000..ce6322a16a1 --- /dev/null +++ b/tools/check-structfield-settings/go.sum @@ -0,0 +1,12 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/check-structfield-settings/main.go b/tools/check-structfield-settings/main.go new file mode 100644 index 00000000000..35149dda325 --- /dev/null +++ b/tools/check-structfield-settings/main.go @@ -0,0 +1,409 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// check-structfield-settings reads the settings for +// the custom `structfield` linter in ".golangci.yml" - +// specifically, the "allowed-tag-names" and "allowed-tag-types" +// exceptions, then scans the code repo to find all exceptions +// that are no longer needed and reports a list. +package main + +import ( + "cmp" + "errors" + "flag" + "fmt" + "io/fs" + "log" + "maps" + "os" + "path/filepath" + "regexp" + "slices" + "strings" + + "github.com/golangci/plugin-module-register/register" + "github.com/google/go-github/v90/tools/structfield" + "go.yaml.in/yaml/v3" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/checker" + "golang.org/x/tools/go/packages" +) + +func init() { + register.Plugin("structfield", structfield.New) +} + +func main() { + log.SetFlags(0) + configPath := flag.String("config", "", "path to .golangci.yml (defaults to searching up from cwd)") + packagesFlag := flag.String("packages", "./...", "comma-separated list of package patterns to analyze") + includeTests := flag.Bool("tests", false, "include test files in analysis") + fix := flag.Bool("fix", false, "remove obsolete exceptions and sort/dedupe lists in .golangci.yml") + flag.Parse() + + resolvedConfig, repoRoot, err := resolveConfig(*configPath) + if err != nil { + log.Fatalf("config: %v", err) + } + + allowedNamesList, allowedTypesList, allowedNames, allowedTypes, err := readStructfieldSettings(resolvedConfig) + if err != nil { + log.Fatalf("parse config: %v", err) + } + if len(allowedNames) == 0 && len(allowedTypes) == 0 { + log.Fatalf("no structfield settings found in %s", resolvedConfig) + } + + duplicateNames := findDuplicates(allowedNamesList) + duplicateTypes := findDuplicates(allowedTypesList) + + patterns := strings.Split(*packagesFlag, ",") + for i, pattern := range patterns { + patterns[i] = strings.TrimSpace(pattern) + } + + usedNames, usedTypes, err := analyzeRepo(repoRoot, patterns, *includeTests, allowedNames, allowedTypes) + if err != nil { + log.Fatalf("analyze: %v", err) + } + + obsoleteNames := diffKeys(allowedNames, usedNames) + obsoleteTypes := diffKeys(allowedTypes, usedTypes) + + if len(obsoleteNames) == 0 && len(obsoleteTypes) == 0 && len(duplicateNames) == 0 && len(duplicateTypes) == 0 { + return + } + + if *fix { + if err := removeObsoleteExceptions(resolvedConfig, obsoleteNames, obsoleteTypes); err != nil { + log.Fatalf("fix: %v", err) + } + return + } + + if len(obsoleteNames) > 0 { + fmt.Println("Obsolete allowed-tag-names:") + for _, name := range obsoleteNames { + fmt.Printf(" - %v\n", name) + } + } + if len(obsoleteTypes) > 0 { + if len(obsoleteNames) > 0 { + fmt.Println() + } + fmt.Println("Obsolete allowed-tag-types:") + for _, name := range obsoleteTypes { + fmt.Printf(" - %v\n", name) + } + } + if len(duplicateNames) > 0 { + if len(obsoleteNames) > 0 || len(obsoleteTypes) > 0 { + fmt.Println() + } + fmt.Println("Duplicate allowed-tag-names:") + for _, name := range sortedKeys(duplicateNames) { + fmt.Printf(" - %v (%v)\n", name, duplicateNames[name]) + } + } + if len(duplicateTypes) > 0 { + if len(obsoleteNames) > 0 || len(obsoleteTypes) > 0 || len(duplicateNames) > 0 { + fmt.Println() + } + fmt.Println("Duplicate allowed-tag-types:") + for _, name := range sortedKeys(duplicateTypes) { + fmt.Printf(" - %v (%v)\n", name, duplicateTypes[name]) + } + } +} + +type golangciConfig struct { + Linters struct { + Settings struct { + Custom struct { + Structfield struct { + Settings struct { + structfield.Settings `yaml:",inline"` + } `yaml:"settings"` + } `yaml:"structfield"` + } `yaml:"custom"` + } `yaml:"settings"` + } `yaml:"linters"` +} + +func resolveConfig(configPath string) (string, string, error) { + if configPath != "" { + resolved, err := filepath.Abs(configPath) + if err != nil { + return "", "", err + } + repoRoot := filepath.Dir(resolved) + return resolved, repoRoot, nil + } + + cwd, err := os.Getwd() + if err != nil { + return "", "", err + } + + for dir := cwd; ; dir = filepath.Dir(dir) { + candidate := filepath.Join(dir, ".golangci.yml") + if _, err := os.Stat(candidate); err == nil { + return candidate, dir, nil + } else if !errors.Is(err, fs.ErrNotExist) { + return "", "", err + } + + parent := filepath.Dir(dir) + if parent == dir { + break + } + } + + return "", "", errors.New("unable to locate .golangci.yml") +} + +func readStructfieldSettings(configPath string) ([]string, []string, map[string]bool, map[string]bool, error) { + data, err := os.ReadFile(configPath) + if err != nil { + return nil, nil, nil, nil, err + } + + var cfg golangciConfig + if err := yaml.Unmarshal(data, &cfg); err != nil { + return nil, nil, nil, nil, err + } + + allowedNamesList := cfg.Linters.Settings.Custom.Structfield.Settings.AllowedTagNames + allowedTypesList := cfg.Linters.Settings.Custom.Structfield.Settings.AllowedTagTypes + + allowedNames := make(map[string]bool) + for _, name := range allowedNamesList { + allowedNames[name] = true + } + + allowedTypes := make(map[string]bool) + for _, name := range allowedTypesList { + allowedTypes[name] = true + } + + return allowedNamesList, allowedTypesList, allowedNames, allowedTypes, nil +} + +func analyzeRepo(repoRoot string, patterns []string, includeTests bool, allowedNames, allowedTypes map[string]bool) (map[string]bool, map[string]bool, error) { + plugin, err := structfield.New(nil) + if err != nil { + return nil, nil, err + } + created, err := plugin.BuildAnalyzers() + if err != nil { + return nil, nil, err + } + if len(created) == 0 { + return nil, nil, errors.New("no analyzers returned by structfield") + } + analyzer := created[0] + + cfg := &packages.Config{ + Mode: packages.LoadAllSyntax, + Dir: repoRoot, + Tests: includeTests, + } + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + return nil, nil, err + } + if packages.PrintErrors(pkgs) > 0 { + return nil, nil, errors.New("package load errors") + } + + graph, err := checker.Analyze([]*analysis.Analyzer{analyzer}, pkgs, &checker.Options{Sequential: true}) + if err != nil { + return nil, nil, err + } + + usedNames := make(map[string]bool) + usedTypes := make(map[string]bool) + + for act := range graph.All() { + if !act.IsRoot || act.Analyzer != analyzer { + continue + } + for _, diag := range act.Diagnostics { + markUsedException(diag.Message, allowedNames, allowedTypes, usedNames, usedTypes) + } + } + + return usedNames, usedTypes, nil +} + +var ( + nameMismatchRE = regexp.MustCompile(`^change Go field name "([^"]+)" to ".*" for .* tag ".*" in struct "([^"]+)"$`) + typeChangeRE = regexp.MustCompile(`^change the "([^"]+)" field type to ".*" in the struct "([^"]+)"`) + fieldInStructRE = regexp.MustCompile(`^the "([^"]+)" field in struct "([^"]+)" .*`) +) + +func markUsedException(msg string, allowedNames, allowedTypes, usedNames, usedTypes map[string]bool) { + if match := nameMismatchRE.FindStringSubmatch(msg); match != nil { + key := match[2] + "." + match[1] + if allowedNames[key] { + usedNames[key] = true + } + return + } + + if match := typeChangeRE.FindStringSubmatch(msg); match != nil { + key := match[2] + "." + match[1] + if allowedTypes[key] { + usedTypes[key] = true + } + return + } + + if match := fieldInStructRE.FindStringSubmatch(msg); match != nil { + key := match[2] + "." + match[1] + if allowedTypes[key] { + usedTypes[key] = true + } + } +} + +func diffKeys(all, used map[string]bool) []string { + var obsolete []string + for key := range all { + if !used[key] { + obsolete = append(obsolete, key) + } + } + return slices.Sorted(slices.Values(obsolete)) +} + +func findDuplicates(values []string) map[string]int { + counts := make(map[string]int) + duplicates := make(map[string]int) + for _, value := range values { + counts[value]++ + if counts[value] > 1 { + duplicates[value] = counts[value] + } + } + return duplicates +} + +func sortedKeys(values map[string]int) []string { + return slices.Sorted(maps.Keys(values)) +} + +func removeObsoleteExceptions(configPath string, obsoleteNames, obsoleteTypes []string) error { + data, err := os.ReadFile(configPath) + if err != nil { + return err + } + + fileInfo, statErr := os.Stat(configPath) + fileMode := os.FileMode(0o644) + if statErr == nil { + fileMode = fileInfo.Mode() + } + + hasTrailingNewline := strings.HasSuffix(string(data), "\n") + lines := strings.Split(string(data), "\n") + + obsoleteNamesSet := make(map[string]bool, len(obsoleteNames)) + for _, name := range obsoleteNames { + obsoleteNamesSet[name] = true + } + obsoleteTypesSet := make(map[string]bool, len(obsoleteTypes)) + for _, name := range obsoleteTypes { + obsoleteTypesSet[name] = true + } + seenNames := make(map[string]bool) + seenTypes := make(map[string]bool) + var items []*listItem + + updated := make([]string, 0, len(lines)) + section := "" + + for i := 0; i < len(lines); { + line := lines[i] + trimmed := strings.TrimSpace(line) + + if section == "" { + switch { + case strings.HasPrefix(trimmed, "allowed-tag-names:"): + section = "names" + items = items[:0] + updated = append(updated, line) + i++ + continue + case strings.HasPrefix(trimmed, "allowed-tag-types:"): + section = "types" + items = items[:0] + updated = append(updated, line) + i++ + continue + default: + updated = append(updated, line) + i++ + continue + } + } + + if strings.HasPrefix(trimmed, "- ") { + value := strings.TrimSpace(trimmed[2:]) + if hash := strings.Index(value, "#"); hash >= 0 { + value = strings.TrimSpace(value[:hash]) + } + if section == "names" { + if obsoleteNamesSet[value] || seenNames[value] { + i++ + continue + } + seenNames[value] = true + } + if section == "types" { + if obsoleteTypesSet[value] || seenTypes[value] { + i++ + continue + } + seenTypes[value] = true + } + items = append(items, &listItem{value: value, line: line}) + i++ + continue + } + + updated = appendSortedItems(updated, items) + section = "" + items = items[:0] + continue + } + + if section != "" { + updated = appendSortedItems(updated, items) + } + + content := strings.Join(updated, "\n") + if hasTrailingNewline && !strings.HasSuffix(content, "\n") { + content += "\n" + } + + return os.WriteFile(configPath, []byte(content), fileMode) +} + +type listItem struct { + value string + line string +} + +func appendSortedItems(lines []string, items []*listItem) []string { + slices.SortFunc(items, func(a, b *listItem) int { + return cmp.Compare(a.value, b.value) + }) + for _, item := range items { + lines = append(lines, item.line) + } + return lines +} diff --git a/tools/extraneousnew/extraneousnew.go b/tools/extraneousnew/extraneousnew.go new file mode 100644 index 00000000000..c48ae0cf38e --- /dev/null +++ b/tools/extraneousnew/extraneousnew.go @@ -0,0 +1,434 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package extraneousnew is a custom linter to be used by +// golangci-lint to find instances where the Go code could +// replace extraneous and problematic usage of `new` or `&SomeStruct{}` +// with an initialized pointer in very specific instances. +// It promotes the idiomatic Go concept that the zero-value is useful. +package extraneousnew + +import ( + "go/ast" + "go/token" + + "github.com/golangci/plugin-module-register/register" + "golang.org/x/tools/go/analysis" +) + +func init() { + register.Plugin("extraneousnew", New) +} + +// ExtraneousNewPlugin is a custom linter plugin for golangci-lint. +type ExtraneousNewPlugin struct { + ignoredMethods map[string]bool +} + +// New returns an analysis.Analyzer to use with golangci-lint. +func New(cfg any) (register.LinterPlugin, error) { + ignoredMethods := map[string]bool{} + if cfg != nil { + if data, ok := cfg.(map[string]any); ok { + if ignored, ok := data["ignored-methods"].([]any); ok { + for _, m := range ignored { + if s, ok := m.(string); ok { + ignoredMethods[s] = true + } + } + } + } + } + + return &ExtraneousNewPlugin{ + ignoredMethods: ignoredMethods, + }, nil +} + +// BuildAnalyzers builds the analyzers for the ExtraneousNewPlugin. +func (f *ExtraneousNewPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "extraneousnew", + Doc: `Reports problematic usage of 'new' or '&SomeStruct{}' when a +more idiomatic 'var' pointer would be more appropriate. +It encourages use of the idiomatic Go concept that the zero-value is useful.`, + Run: func(pass *analysis.Pass) (any, error) { + return run(pass, f.ignoredMethods) + }, + }, + }, nil +} + +// GetLoadMode returns the load mode for the ExtraneousNewPlugin. +func (f *ExtraneousNewPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} + +func run(pass *analysis.Pass, ignoredMethods map[string]bool) (any, error) { + for _, file := range pass.Files { + ast.Inspect(file, func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok { + return true + } + + if !fn.Name.IsExported() { + return false + } + + // Check if method should be ignored. + if fn.Recv != nil && len(fn.Recv.List) > 0 { + var recvName string + recvType := fn.Recv.List[0].Type + if star, ok := recvType.(*ast.StarExpr); ok { + recvType = star.X + } + if ident, ok := recvType.(*ast.Ident); ok { + recvName = ident.Name + } + + if recvName != "" { + fullName := recvName + "." + fn.Name.Name + if ignoredMethods[fullName] { + return false + } + } + } + + if fn.Body != nil { + inspectAllBlocks(pass, fn.Body) + } + return true + }) + } + return nil, nil +} + +func inspectAllBlocks(pass *analysis.Pass, root ast.Node) { + ast.Inspect(root, func(n ast.Node) bool { + block, ok := n.(*ast.BlockStmt) + if !ok { + return true + } + inspectBlock(pass, block) + return true + }) +} + +func inspectBlock(pass *analysis.Pass, block *ast.BlockStmt) { + nilPointers := make(map[string]*ast.Ident) + valueVars := make(map[string]*valueVarInfo) + + for i, stmt := range block.List { + // 1. Check for `var v *T` or `var v *struct{...}` (nil pointer declarations) + // and `var v T` (value-type declarations that should use a pointer instead). + if decl, ok := stmt.(*ast.DeclStmt); ok { + if gen, ok := decl.Decl.(*ast.GenDecl); ok && gen.Tok == token.VAR { + for _, spec := range gen.Specs { + if vSpec, ok := spec.(*ast.ValueSpec); ok { + if _, ok := vSpec.Type.(*ast.StarExpr); ok && len(vSpec.Values) == 0 { + for _, name := range vSpec.Names { + nilPointers[name.Name] = name + } + } else if typeIdent, ok := vSpec.Type.(*ast.Ident); ok && len(vSpec.Values) == 0 && token.IsExported(typeIdent.Name) { + for _, name := range vSpec.Names { + valueVars[name.Name] = &valueVarInfo{ + ident: name, + typeName: typeIdent.Name, + stmtIndex: i, + } + } + } + } + } + } + } + + // 2. Check for `v = new(T)` or `v := new(T)` + var assignLHS *ast.Ident + var isNewT bool + var typeName string + + if assign, ok := stmt.(*ast.AssignStmt); ok && len(assign.Lhs) == 1 && len(assign.Rhs) == 1 { + if lhs, ok := assign.Lhs[0].(*ast.Ident); ok { + assignLHS = lhs + // Any assignment to v means it's no longer tracked as a nil pointer or value var. + delete(nilPointers, lhs.Name) + delete(valueVars, lhs.Name) + + // Check for v := new(T) or v := &T{} + if call, ok := assign.Rhs[0].(*ast.CallExpr); ok { + if fun, ok := call.Fun.(*ast.Ident); ok && fun.Name == "new" && len(call.Args) == 1 { + isNewT = true + if typeIdent, ok := call.Args[0].(*ast.Ident); ok { + typeName = typeIdent.Name + } + } + } + if unary, ok := assign.Rhs[0].(*ast.UnaryExpr); ok && unary.Op == token.AND { + if composite, ok := unary.X.(*ast.CompositeLit); ok && len(composite.Elts) == 0 { + isNewT = true + if typeIdent, ok := composite.Type.(*ast.Ident); ok { + typeName = typeIdent.Name + } + } + } + } + } + + if isNewT && assignLHS != nil { + lookAhead(pass, block, i, assignLHS, typeName) + continue + } + + // If it's a regular assignment (possibly with multiple variables), remove tracked variables + // since they are no longer nil pointers or uninitialized value vars. + if assign, ok := stmt.(*ast.AssignStmt); ok { + for _, lhs := range assign.Lhs { + if ident, ok := lhs.(*ast.Ident); ok { + delete(nilPointers, ident.Name) + delete(valueVars, ident.Name) + } + } + } + + // 3. Check if a nil pointer is passed to Do/Decode. + ast.Inspect(stmt, func(n ast.Node) bool { + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + fnName := getFunctionName(call.Fun) + if fnName == "" { + return true + } + + var targetArg ast.Expr + if fnName == "Do" && len(call.Args) == 2 { + targetArg = call.Args[1] + } else if fnName == "Decode" && len(call.Args) == 1 { + targetArg = call.Args[0] + } + + if targetArg != nil { + if ident, ok := targetArg.(*ast.Ident); ok { + if _, isNil := nilPointers[ident.Name]; isNil { + pass.Reportf(ident.Pos(), "pass '&%v' instead", ident.Name) + } + } + } + return true + }) + + // 4. Check if a value-type var is passed to Do/Decode and could use a pointer instead. + ast.Inspect(stmt, func(n ast.Node) bool { + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + fnName := getFunctionName(call.Fun) + if fnName == "" { + return true + } + + var targetArg ast.Expr + if fnName == "Do" && len(call.Args) == 2 { + targetArg = call.Args[1] + } else if fnName == "Decode" && len(call.Args) == 1 { + targetArg = call.Args[0] + } + + if targetArg == nil { + return true + } + + if ident, ok := targetArg.(*ast.Ident); ok { + if info, isValue := valueVars[ident.Name]; isValue { + delete(valueVars, ident.Name) + if !isUsedElsewhere(block, info.stmtIndex, i, info.ident.Name) && !isReadAfterCall(block, i, info.ident.Name) { + pass.Reportf(ident.Pos(), "use 'var %v *%v' and pass '&%v' instead", ident.Name, info.typeName, ident.Name) + } + } + } + if unary, ok := targetArg.(*ast.UnaryExpr); ok && unary.Op == token.AND { + if ident, ok := unary.X.(*ast.Ident); ok { + if info, isValue := valueVars[ident.Name]; isValue { + delete(valueVars, ident.Name) + if !isUsedElsewhere(block, info.stmtIndex, i, info.ident.Name) && !isReadAfterCall(block, i, info.ident.Name) { + pass.Reportf(ident.Pos(), "use 'var %v *%v' instead", ident.Name, info.typeName) + } + } + } + } + return true + }) + } +} + +func getFunctionName(expr ast.Expr) string { + switch f := expr.(type) { + case *ast.SelectorExpr: + return f.Sel.Name + case *ast.Ident: + return f.Name + } + return "" +} + +func lookAhead(pass *analysis.Pass, block *ast.BlockStmt, startIndex int, lhsIdent *ast.Ident, typeName string) { + var foundProperUse bool + var foundOtherUse bool + + for j := startIndex + 1; j < len(block.List); j++ { + nextStmt := block.List[j] + ast.Inspect(nextStmt, func(un ast.Node) bool { + if foundProperUse || foundOtherUse { + return false + } + + // Check if lhsIdent is used here. + ident, ok := un.(*ast.Ident) + if !ok || ident.Name != lhsIdent.Name { + return true + } + + // Found a use of lhsIdent. Is it the target argument in a call to Do or Decode? + isSafe := false + ast.Inspect(nextStmt, func(n ast.Node) bool { + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + fnName := getFunctionName(call.Fun) + var targetArg ast.Expr + if fnName == "Do" && len(call.Args) == 2 { + targetArg = call.Args[1] + } else if fnName == "Decode" && len(call.Args) == 1 { + targetArg = call.Args[0] + } + + if targetArg != nil { + if isIdentOrAddressOfIdent(targetArg, lhsIdent.Name) { + isSafe = true + return false + } + } + return true + }) + + if isSafe { + if typeName != "" { + pass.Reportf(ident.Pos(), "use 'var %v *%v' and pass '&%v' instead", lhsIdent.Name, typeName, lhsIdent.Name) + } else { + pass.Reportf(ident.Pos(), "use 'var %v *T' and pass '&%v' instead", lhsIdent.Name, lhsIdent.Name) + } + foundProperUse = true + } else { + foundOtherUse = true + } + return false + }) + if foundProperUse || foundOtherUse { + break + } + } +} + +type valueVarInfo struct { + ident *ast.Ident + typeName string + stmtIndex int +} + +// isUsedElsewhere checks if the variable named name is used for anything other than +// being a target argument in a Do/Decode call, between declIndex and useIndex (exclusive). +func isUsedElsewhere(block *ast.BlockStmt, declIndex, useIndex int, name string) bool { + for j := declIndex + 1; j < useIndex; j++ { + stmt := block.List[j] + var foundOtherUse bool + ast.Inspect(stmt, func(un ast.Node) bool { + if foundOtherUse { + return false + } + ident, ok := un.(*ast.Ident) + if !ok || ident.Name != name { + return true + } + + isTargetArg := false + ast.Inspect(stmt, func(n ast.Node) bool { + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + fnName := getFunctionName(call.Fun) + var targetArg ast.Expr + if fnName == "Do" && len(call.Args) == 2 { + targetArg = call.Args[1] + } else if fnName == "Decode" && len(call.Args) == 1 { + targetArg = call.Args[0] + } + if targetArg != nil && isIdentOrAddressOfIdent(targetArg, name) { + isTargetArg = true + return false + } + return true + }) + + if !isTargetArg { + foundOtherUse = true + } + return false + }) + if foundOtherUse { + return true + } + } + return false +} + +// isReadAfterCall checks if the variable is read (accessed via selector like v.Field) +// in any statement after callIndex. This indicates correct zero-value usage where +// Do/Decode fills the value and the caller reads its fields afterward. +func isReadAfterCall(block *ast.BlockStmt, callIndex int, name string) bool { + for j := callIndex + 1; j < len(block.List); j++ { + stmt := block.List[j] + var found bool + ast.Inspect(stmt, func(un ast.Node) bool { + if found { + return false + } + sel, ok := un.(*ast.SelectorExpr) + if !ok { + return true + } + if ident, ok := sel.X.(*ast.Ident); ok && ident.Name == name { + found = true + return false + } + return true + }) + if found { + return true + } + } + return false +} + +func isIdentOrAddressOfIdent(expr ast.Expr, name string) bool { + if ident, ok := expr.(*ast.Ident); ok { + return ident.Name == name + } + if unary, ok := expr.(*ast.UnaryExpr); ok && unary.Op == token.AND { + if ident, ok := unary.X.(*ast.Ident); ok { + return ident.Name == name + } + } + return false +} diff --git a/tools/extraneousnew/extraneousnew_test.go b/tools/extraneousnew/extraneousnew_test.go new file mode 100644 index 00000000000..b0058781f63 --- /dev/null +++ b/tools/extraneousnew/extraneousnew_test.go @@ -0,0 +1,24 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package extraneousnew + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + t.Parallel() + testdata := analysistest.TestData() + plugin, _ := New(map[string]any{ + "ignored-methods": []any{ + "Receiver.MethodNameToIgnore", + }, + }) + analyzers, _ := plugin.BuildAnalyzers() + analysistest.Run(t, testdata, analyzers[0], "has-warnings", "no-warnings") +} diff --git a/tools/extraneousnew/go.mod b/tools/extraneousnew/go.mod new file mode 100644 index 00000000000..887b8130834 --- /dev/null +++ b/tools/extraneousnew/go.mod @@ -0,0 +1,13 @@ +module github.com/google/go-github/v90/tools/extraneousnew + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/tools/extraneousnew/go.sum b/tools/extraneousnew/go.sum new file mode 100644 index 00000000000..d120eba8160 --- /dev/null +++ b/tools/extraneousnew/go.sum @@ -0,0 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/extraneousnew/testdata/src/has-warnings/main.go b/tools/extraneousnew/testdata/src/has-warnings/main.go new file mode 100644 index 00000000000..53354c33b3b --- /dev/null +++ b/tools/extraneousnew/testdata/src/has-warnings/main.go @@ -0,0 +1,90 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "net/http" + "testing" +) + +type T struct { + Field string +} + +type Client struct{} + +func (c *Client) Do(req any, v any) (any, error) { + return nil, nil +} + +type Service struct { + client *Client +} + +func assertNilError(t *testing.T, err error) {} + +func (s *Service) TestMethod(req any, r *http.Request, t *testing.T) { + v1 := new(T) + s.client.Do(req, v1) // want "use 'var v1 [*]T' and pass '&v1' instead" + + v2 := &T{} + s.client.Do(req, v2) // want "use 'var v2 [*]T' and pass '&v2' instead" + + v3 := new(T) + json.NewDecoder(r.Body).Decode(v3) // want "use 'var v3 [*]T' and pass '&v3' instead" + + v4 := &T{} + json.NewDecoder(r.Body).Decode(v4) // want "use 'var v4 [*]T' and pass '&v4' instead" + + v5 := &T{} + s.client.Do(req, &v5) // want "use 'var v5 [*]T' and pass '&v5' instead" + + v6 := new(T) + assertNilError(t, json.NewDecoder(r.Body).Decode(v6)) // want "use 'var v6 [*]T' and pass '&v6' instead" + + v7 := &T{Field: "something"} + s.client.Do(req, v7) // No warning + + var v8 *T + v8 = new(T) + s.client.Do(req, v8) // want "use 'var v8 [*]T' and pass '&v8' instead" + + // Multiple assignments in same block + v9 := new(T) + v10 := new(T) + s.client.Do(req, v9) // want "use 'var v9 [*]T' and pass '&v9' instead" + s.client.Do(req, v10) // want "use 'var v10 [*]T' and pass '&v10' instead" + + // Anonymous struct + v11 := new(struct { + F string + }) + s.client.Do(req, v11) // want "use 'var v11 [*]T' and pass '&v11' instead" + + // Anonymous struct + var v12 *struct { + F string + } + s.client.Do(req, v12) // want "pass '&v12' instead" + + var v13 *T + s.client.Do(req, v13) // want "pass '&v13' instead" + + // Unnecessary use of value + var v14 T + s.client.Do(req, &v14) // want "use 'var v14 [*]T' instead" + + var v15 T + s.client.Do(req, v15) // want "use 'var v15 [*]T' and pass '&v15' instead" + + // Value-type var with Decode + var v16 T + json.NewDecoder(r.Body).Decode(&v16) // want "use 'var v16 [*]T' instead" + + var v17 T + json.NewDecoder(r.Body).Decode(v17) // want "use 'var v17 [*]T' and pass '&v17' instead" +} diff --git a/tools/extraneousnew/testdata/src/no-warnings/main.go b/tools/extraneousnew/testdata/src/no-warnings/main.go new file mode 100644 index 00000000000..e1adcb84ac9 --- /dev/null +++ b/tools/extraneousnew/testdata/src/no-warnings/main.go @@ -0,0 +1,71 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type T struct { + Field string +} + +type CheckPrivateReporting struct { + Enabled bool +} + +type Client struct{} + +func (c *Client) Do(req any, v any) (any, error) { + return nil, nil +} + +type Receiver struct { + client *Client +} + +func (s *Receiver) TestMethod(req any) { + // Proper usage: var pointer and pass &v + var v1 *T + s.client.Do(req, &v1) + + // Literal with fields + v2 := &T{Field: "something"} + s.client.Do(req, v2) + + // new(T) but used for something else first + v3 := new(T) + v3.Field = "set" + s.client.Do(req, v3) + + // Anonymous struct + var v11 *struct { + F string + } + s.client.Do(req, &v11) +} + +func (s *Receiver) MethodNameToIgnore(req any) { + v := new(T) + s.client.Do(req, v) +} + +func (s *Receiver) unexportedMethod(req any) { + v := new(T) + s.client.Do(req, v) // Should be ignored because unexported. +} + +func (s *Receiver) ValueVarMethod(req any) { + // Value-type var used elsewhere before Do — no warning + var v T + v.Field = "set" + s.client.Do(req, &v) + + // Value-type var with non-struct type — no warning + var data any + s.client.Do(req, &data) + + // Value-type var read after Do via selector — correct zero-value usage, no warning + var reporting CheckPrivateReporting + s.client.Do(req, &reporting) + _ = reporting.Enabled +} diff --git a/tools/fmtpercentv/fmtpercentv.go b/tools/fmtpercentv/fmtpercentv.go new file mode 100644 index 00000000000..bb707d7d7ab --- /dev/null +++ b/tools/fmtpercentv/fmtpercentv.go @@ -0,0 +1,99 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fmtpercentv is a custom linter to be used by +// golangci-lint to find instances of `%d` or `%s` in +// format strings when `%v` would be more consistent. +package fmtpercentv + +import ( + "go/ast" + "go/token" + "strings" + + "github.com/golangci/plugin-module-register/register" + "golang.org/x/tools/go/analysis" +) + +func init() { + register.Plugin("fmtpercentv", New) +} + +// FmtPercentVPlugin is a custom linter plugin for golangci-lint. +type FmtPercentVPlugin struct{} + +// New returns an analysis.Analyzer to use with golangci-lint. +func New(_ any) (register.LinterPlugin, error) { + return &FmtPercentVPlugin{}, nil +} + +// BuildAnalyzers builds the analyzers for the FmtPercentVPlugin. +func (f *FmtPercentVPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "fmtpercentv", + Doc: "Reports usage of %d or %s in format strings.", + Run: run, + }, + }, nil +} + +// GetLoadMode returns the load mode for the FmtPercentVPlugin. +func (f *FmtPercentVPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} + +func run(pass *analysis.Pass) (any, error) { + for _, file := range pass.Files { + ast.Inspect(file, func(n ast.Node) bool { + if n == nil { + return false + } + + if t, ok := n.(*ast.CallExpr); ok { + checkCallExpr(t, t.Pos(), pass) + } + + return true + }) + } + return nil, nil +} + +func checkCallExpr(expr *ast.CallExpr, tokenPos token.Pos, pass *analysis.Pass) { + fun, ok := expr.Fun.(*ast.SelectorExpr) + if !ok { + return + } + funX, ok := fun.X.(*ast.Ident) + if !ok { + return + } + if funX.Name != "fmt" && funX.Name != "t" { + return + } + if fun.Sel.Name != "Sprintf" && fun.Sel.Name != "Printf" && fun.Sel.Name != "Fprintf" && fun.Sel.Name != "Errorf" { + return + } + argIdx := 0 + if fun.Sel.Name == "Fprintf" { + argIdx = 1 + } + fmtStrBasicLit, ok := expr.Args[argIdx].(*ast.BasicLit) + if !ok { + return + } + fmtStr := fmtStrBasicLit.Value + hasD := strings.Contains(fmtStr, "%d") + hasS := strings.Contains(fmtStr, "%s") + switch { + case hasD && hasS: + pass.Reportf(tokenPos, "use %%v instead of %%s and %%d") + case hasD: + pass.Reportf(tokenPos, "use %%v instead of %%d") + case hasS: + pass.Reportf(tokenPos, "use %%v instead of %%s") + } +} diff --git a/tools/fmtpercentv/fmtpercentv_test.go b/tools/fmtpercentv/fmtpercentv_test.go new file mode 100644 index 00000000000..876b1270405 --- /dev/null +++ b/tools/fmtpercentv/fmtpercentv_test.go @@ -0,0 +1,20 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fmtpercentv + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + t.Parallel() + testdata := analysistest.TestData() + plugin, _ := New(nil) + analyzers, _ := plugin.BuildAnalyzers() + analysistest.Run(t, testdata, analyzers[0], "has-warnings", "no-warnings") +} diff --git a/tools/fmtpercentv/go.mod b/tools/fmtpercentv/go.mod new file mode 100644 index 00000000000..f1707d7ffff --- /dev/null +++ b/tools/fmtpercentv/go.mod @@ -0,0 +1,13 @@ +module tools/fmtpercentv + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/tools/fmtpercentv/go.sum b/tools/fmtpercentv/go.sum new file mode 100644 index 00000000000..d120eba8160 --- /dev/null +++ b/tools/fmtpercentv/go.sum @@ -0,0 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/fmtpercentv/testdata/src/has-warnings/main.go b/tools/fmtpercentv/testdata/src/has-warnings/main.go new file mode 100644 index 00000000000..9ab25f12515 --- /dev/null +++ b/tools/fmtpercentv/testdata/src/has-warnings/main.go @@ -0,0 +1,18 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func main() { + _ = fmt.Sprintf("some/%d/url", 1) // want `use %v instead of %d` + _ = fmt.Sprintf("some/%s/url", "yo") // want `use %v instead of %s` + _ = fmt.Sprintf("some/%s/%d/url", "yo", 1) // want `use %v instead of %s and %d` + _ = fmt.Sprintf("some/%d/%s/url", 1, "yo") // want `use %v instead of %s and %d` + fmt.Printf("some %d", 1) // want `use %v instead of %d` + fmt.Printf("some %s", "thing") // want `use %v instead of %s` + fmt.Fprintf(nil, "some %s", "thing") // want `use %v instead of %s` +} diff --git a/tools/fmtpercentv/testdata/src/no-warnings/main.go b/tools/fmtpercentv/testdata/src/no-warnings/main.go new file mode 100644 index 00000000000..270105f4304 --- /dev/null +++ b/tools/fmtpercentv/testdata/src/no-warnings/main.go @@ -0,0 +1,15 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func main() { + _ = fmt.Sprintf("some/%v/url", 1) // Should not be flagged + fmt.Printf("some %v", 1) // Should not be flagged + fmt.Printf("some %v", "thing") // Should not be flagged + fmt.Fprintf(nil, "some %v", 1) // Should not be flagged +} diff --git a/tools/gen-release-notes/main.go b/tools/gen-release-notes/main.go new file mode 100644 index 00000000000..915f5d36c4a --- /dev/null +++ b/tools/gen-release-notes/main.go @@ -0,0 +1,167 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// gen-release-notes calls `git` to determine what the prior release was, (e.g. "v76.0.0") +// then calls `git` again to find out what changes were made since then. +// +// Finally, it writes the release notes to stdout, summarizing the +// breaking and non-breaking changes since that release. +// +// Usage: +// +// go run tools/gen-release-notes/main.go [--tag v76.0.0] +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "strings" +) + +var ( + sinceTag = flag.String("tag", "", "List all changes since this tag (e.g. 'v76.0.0')") + + descriptionRE = regexp.MustCompile(`^\* (.*?\((#[^\)]+)\))`) + releaseTagRE = regexp.MustCompile(`[^a-zA-Z0-9.\-_]+`) +) + +func main() { + log.SetFlags(0) + flag.Parse() + + priorRelease := *sinceTag + if priorRelease == "" { + priorRelease = getPriorRelease() + log.Printf("Prior release: %v", priorRelease) + } + + newChanges := newChangesSinceRelease(priorRelease) + + releaseNotes := genReleaseNotes(newChanges) + fmt.Printf("%v%v", releaseNotes, "\n") + + log.Print("Done.") +} + +func genReleaseNotes(text string) string { + allLines := splitIntoPRs(text) + fullBreakingLines, fullNonBreakingLines := splitBreakingLines(allLines) + refBreakingLines, refNonBreakingLines := genRefLines(fullBreakingLines, fullNonBreakingLines) + + return fmt.Sprintf(releaseNotesFmt, + strings.Join(fullBreakingLines, "\n"), + strings.Join(fullNonBreakingLines, "\n"), + strings.Join(refBreakingLines, "\n"), + strings.Join(refNonBreakingLines, "\n")) +} + +func splitIntoPRs(text string) []string { + parts := strings.Split("\n"+text, "\ncommit ") + if len(parts) < 2 { + log.Fatal("unable to find PRs") + } + prs := make([]string, 0, len(parts)-1) + for _, part := range parts { + if part == "" { + continue + } + lines := strings.Split(part, "\n") + if len(lines) < 5 { // commit, Author:, Date:, blank, msg + continue + } + var newPR []string + for _, line := range lines[1:] { + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, "Author: ") || strings.HasPrefix(line, "Date: ") { + continue + } + if len(newPR) == 0 { + newPR = append(newPR, "* "+line) + } else { + newPR = append(newPR, " "+line) + } + } + prs = append(prs, strings.Join(newPR, "\n")) + } + return prs +} + +func splitBreakingLines(allLines []string) (breaking, nonBreaking []string) { + for _, pr := range allLines { + if strings.Contains(pr, "!: ") { + breaking = append(breaking, pr) + } else { + nonBreaking = append(nonBreaking, pr) + } + } + return breaking, nonBreaking +} + +func genRefLines(breaking, nonBreaking []string) (ref, refNon []string) { + for _, pr := range breaking { + m := descriptionRE.FindStringSubmatch(pr) + if len(m) == 3 { + ref = append(ref, strings.Replace(pr, m[1], m[2], 1)) + } + } + for _, pr := range nonBreaking { + m := descriptionRE.FindStringSubmatch(pr) + if len(m) == 3 { + refNon = append(refNon, strings.Replace(pr, m[1], m[2], 1)) + } + } + return ref, refNon +} + +func runCommand(cmdArgs []string) string { + cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...) //nolint:gosec + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = os.Stderr + + log.Printf("Running command: %v", strings.Join(cmdArgs, " ")) + if err := cmd.Run(); err != nil { + log.Fatalf("command failed: %v", err) + } + + return strings.TrimSpace(out.String()) +} + +func newChangesSinceRelease(priorRelease string) string { + priorRelease = releaseTagRE.ReplaceAllString(priorRelease, "") + cmdArgs := []string{"git", "log", priorRelease + "..", "--no-color"} + return runCommand(cmdArgs) +} + +func getPriorRelease() string { + cmdArgs := []string{"git", "describe", "--tags", "--abbrev=0"} + release := runCommand(cmdArgs) + return strings.ReplaceAll(release, "otel/", "") +} + +const releaseNotesFmt = ` +This release contains the following breaking API changes: + +%v + +...and the following additional changes: + +%v + +&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& + +This release contains the following breaking API changes: + +%v + +...and the following additional changes: + +%v +` diff --git a/tools/go.mod b/tools/go.mod new file mode 100644 index 00000000000..ccb88193c24 --- /dev/null +++ b/tools/go.mod @@ -0,0 +1,26 @@ +module tools + +go 1.25.0 + +require ( + github.com/alecthomas/kong v1.16.0 + github.com/getkin/kin-openapi v0.146.0 + github.com/google/go-cmp v0.7.0 + github.com/google/go-github/v90 v90.0.0 + go.yaml.in/yaml/v3 v3.0.5 + golang.org/x/sync v0.22.0 +) + +require ( + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect + github.com/google/go-querystring v1.2.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/oasdiff/yaml v0.1.1 // indirect + github.com/oasdiff/yaml3 v0.0.14 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + golang.org/x/text v0.14.0 // indirect +) + +// Use version at HEAD, not the latest published. +replace github.com/google/go-github/v90 => ../ diff --git a/tools/go.sum b/tools/go.sum new file mode 100644 index 00000000000..18654586604 --- /dev/null +++ b/tools/go.sum @@ -0,0 +1,53 @@ +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/kong v1.16.0 h1:g92/kUxBcdcTPOM79yE63viJgtcp5dNyrB3/O2cjYT4= +github.com/alecthomas/kong v1.16.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I= +github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= +github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/getkin/kin-openapi v0.146.0 h1:RA/1RdxrSJW4oc1+6IfnYB6AO9CaGy8GTKPh0k4Ordo= +github.com/getkin/kin-openapi v0.146.0/go.mod h1:3BH9M9XDe/y9M5DSvEocVYAYq1w0qrhJHjC/vZi0AaY= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= +github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= +github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= +github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tools/metadata/main.go b/tools/metadata/main.go new file mode 100644 index 00000000000..09de07cd9d5 --- /dev/null +++ b/tools/metadata/main.go @@ -0,0 +1,204 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// metadata is a command-line tool used to check and update this repo. +// See CONTRIBUTING.md for details. +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "slices" + + "github.com/alecthomas/kong" + "github.com/google/go-github/v90/github" +) + +var helpVars = kong.Vars{ + "update_openapi_help": ` +Update openapi_operations.yaml from OpenAPI descriptions in github.com/github/rest-api-description at the given git ref. +`, + + "update_go_help": ` +Update go source code to be consistent with openapi_operations.yaml. + - Adds and updates "// GitHub API docs:" comments for service methods. + - Updates "//meta:operation" comments to use canonical operation names. + - Updates formatting of "//meta:operation" comments to make sure there isn't a space between the "//" and the "meta". + - Formats modified files with the equivalent of "go fmt". +`, + + "format_help": `Format white space in openapi_operations.yaml and sort its operations.`, + "unused_help": `List operations in openapi_operations.yaml that aren't used by any service methods.`, + + "working_dir_help": `Working directory. Should be the root of the go-github repository.`, + "openapi_ref_help": `Git ref to pull OpenAPI descriptions from.`, + + "openapi_validate_help": ` +Instead of updating, make sure that the operations in openapi_operations.yaml's "openapi_operations" field are +consistent with the SHA listed in "openapi_commit". This is run in CI as a convenience so that reviewers can trust +changes to openapi_operations.yaml. +`, + + "unused_deprecated_help": `Only list deprecated operations in openapi_operations.yaml that aren't used by any service methods.`, + "output_json_help": `Output JSON.`, +} + +type rootCmd struct { + UpdateOpenAPI updateOpenAPICmd `kong:"cmd,name=update-openapi,help=${update_openapi_help}"` + UpdateGo updateGoCmd `kong:"cmd,help=${update_go_help}"` + Format formatCmd `kong:"cmd,help=${format_help}"` + Unused unusedCmd `kong:"cmd,help=${unused_help}"` + + WorkingDir string `kong:"short=C,default=.,help=${working_dir_help}"` + + // for testing + GithubURL string `kong:"hidden,default='https://api.github.com'"` + UploadURL string `kong:"hidden,default='https://uploads.github.com'"` +} + +func (c *rootCmd) opsFile() (string, *operationsFile, error) { + filename := filepath.Join(c.WorkingDir, "openapi_operations.yaml") + opsFile, err := loadOperationsFile(filename) + if err != nil { + return "", nil, err + } + return filename, opsFile, nil +} + +func githubClient(apiURL, uploadURL string) (*github.Client, error) { + token := os.Getenv("GITHUB_TOKEN") + if token == "" { + return nil, errors.New("GITHUB_TOKEN environment variable must be set to a GitHub personal access token with the public_repo scope") + } + return github.NewClient(github.WithAuthToken(token), github.WithEnterpriseURLs(apiURL, uploadURL)) +} + +type updateOpenAPICmd struct { + Ref string `kong:"default=main,help=${openapi_ref_help}"` + ValidateGithub bool `kong:"name=validate,help=${openapi_validate_help}"` +} + +func (c *updateOpenAPICmd) Run(root *rootCmd) error { + ctx := context.Background() + if c.ValidateGithub && c.Ref != "main" { + return errors.New("--validate and --ref are mutually exclusive") + } + filename, opsFile, err := root.opsFile() + if err != nil { + return err + } + origOps := make([]*operation, len(opsFile.OpenapiOps)) + copy(origOps, opsFile.OpenapiOps) + for i := range origOps { + origOps[i] = origOps[i].clone() + } + client, err := githubClient(root.GithubURL, root.UploadURL) + if err != nil { + return err + } + ref := c.Ref + if c.ValidateGithub { + ref = opsFile.GitCommit + if ref == "" { + return errors.New("openapi_operations.yaml does not have an openapi_commit field") + } + } + err = opsFile.updateFromGithub(ctx, client, ref) + if err != nil { + return err + } + if !c.ValidateGithub { + return opsFile.saveFile(filename) + } + if !operationsEqual(origOps, opsFile.OpenapiOps) { + return errors.New("openapi_operations.yaml does not match the OpenAPI descriptions in github.com/github/rest-api-description") + } + return nil +} + +type formatCmd struct{} + +func (c *formatCmd) Run(root *rootCmd) error { + filename, opsFile, err := root.opsFile() + if err != nil { + return err + } + return opsFile.saveFile(filename) +} + +type updateGoCmd struct{} + +func (c *updateGoCmd) Run(root *rootCmd) error { + _, opsFile, err := root.opsFile() + if err != nil { + return err + } + err = updateDocs(opsFile, filepath.Join(root.WorkingDir, "github")) + return err +} + +type unusedCmd struct { + Deprecated bool `kong:"help=${unused_deprecated_help}"` + JSON bool `kong:"help=${output_json_help}"` +} + +func (c *unusedCmd) Run(root *rootCmd, k *kong.Context) error { + _, opsFile, err := root.opsFile() + if err != nil { + return err + } + unused, err := unusedOps(opsFile, filepath.Join(root.WorkingDir, "github")) + if err != nil { + return err + } + if c.Deprecated { + unused = slices.DeleteFunc(unused, func(op *operation) bool { + return !op.Deprecated + }) + } + if c.JSON { + enc := json.NewEncoder(k.Stdout) + enc.SetIndent("", " ") + return enc.Encode(unused) + } + fmt.Fprintf(k.Stdout, "Found %v unused operations\n", len(unused)) + if len(unused) == 0 { + return nil + } + fmt.Fprintln(k.Stdout, "") + for _, op := range unused { + fmt.Fprintln(k.Stdout, op.Name) + if op.DocumentationURL != "" { + fmt.Fprintf(k.Stdout, "doc: %v\n", op.DocumentationURL) + } + fmt.Fprintln(k.Stdout, "") + } + return nil +} + +func main() { + err := run(os.Args[1:], nil) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(args []string, opts []kong.Option) error { + var cmd rootCmd + parser, err := kong.New(&cmd, append(opts, helpVars)...) + if err != nil { + return err + } + k, err := parser.Parse(args) + if err != nil { + return err + } + return k.Run() +} diff --git a/tools/metadata/main_test.go b/tools/metadata/main_test.go new file mode 100644 index 00000000000..a62786dd752 --- /dev/null +++ b/tools/metadata/main_test.go @@ -0,0 +1,421 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/fs" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "testing" + + "github.com/alecthomas/kong" + "github.com/getkin/kin-openapi/openapi3" + "github.com/google/go-cmp/cmp" + "github.com/google/go-github/v90/github" +) + +func TestUpdateGo(t *testing.T) { + t.Parallel() + t.Run("valid", func(t *testing.T) { + t.Parallel() + res := runTest(t, "testdata/update-go/valid", "update-go") + res.assertOutput("", "") + res.assertNoErr() + res.checkGolden() + }) + + t.Run("invalid", func(t *testing.T) { + t.Parallel() + res := runTest(t, "testdata/update-go/invalid", "update-go") + res.assertOutput("", "") + res.assertErr(` +no operations defined for AService.NoOperation +no operations defined for AService.NoComment +ambiguous operation "GET /ambiguous/{}" could match any of: [GET /ambiguous/{id} GET /ambiguous/{name}] +could not find operation "GET /missing/{id}" in openapi_operations.yaml +duplicate operation: GET /a/{a_id} +`) + res.checkGolden() + }) +} + +func TestUnused(t *testing.T) { + t.Parallel() + res := runTest(t, "testdata/unused", "unused") + res.assertOutput(` +Found 3 unused operations + +GET /a/{a_id} +doc: https://docs.github.com/rest/a/a#overridden-get-a + +POST /a/{a_id} +doc: https://docs.github.com/rest/a/a#update-a + +GET /undocumented/{undocumented_id} +`, "") +} + +//nolint:paralleltest // cannot use t.Parallel() when helper calls t.Setenv +func TestUpdateOpenAPI(t *testing.T) { + testServer := newTestServer(t, "main", map[string]any{ + "api.github.com/api.github.com.json": openapi3.T{ + Paths: openapi3.NewPaths( + openapi3.WithPath("/a/{a_id}", &openapi3.PathItem{ + Get: &openapi3.Operation{ + ExternalDocs: &openapi3.ExternalDocs{ + URL: "https://docs.github.com/rest/reference/a", + }, + }, + })), + }, + "ghec/ghec.json": openapi3.T{ + Paths: openapi3.NewPaths( + openapi3.WithPath("/a/b/{a_id}", &openapi3.PathItem{ + Get: &openapi3.Operation{ + ExternalDocs: &openapi3.ExternalDocs{ + URL: "https://docs.github.com/rest/reference/a", + }, + }, + })), + }, + "ghes-3.9/ghes-3.9.json": openapi3.T{ + Paths: openapi3.NewPaths( + openapi3.WithPath("/a/b/{a_id}", &openapi3.PathItem{ + Get: &openapi3.Operation{ + ExternalDocs: &openapi3.ExternalDocs{ + URL: "https://docs.github.com/rest/reference/a", + }, + }, + })), + }, + "ghes-3.10/ghes-3.10.json": openapi3.T{ + Paths: openapi3.NewPaths( + openapi3.WithPath("/a/b/{a_id}", &openapi3.PathItem{ + Get: &openapi3.Operation{ + ExternalDocs: &openapi3.ExternalDocs{ + URL: "https://docs.github.com/rest/reference/a", + }, + }, + })), + }, + "ghes-2.22/ghes-2.22.json": openapi3.T{ + Paths: openapi3.NewPaths( + openapi3.WithPath("/a/b/{a_id}", &openapi3.PathItem{ + Get: &openapi3.Operation{ + ExternalDocs: &openapi3.ExternalDocs{ + URL: "https://docs.github.com/rest/reference/a", + }, + }, + })), + }, + }) + + res := runTest(t, "testdata/update-openapi", "update-openapi", "--github-url", testServer.URL) + res.assertOutput("", "") + res.assertNoErr() + res.checkGolden() +} + +func TestFormat(t *testing.T) { + t.Parallel() + res := runTest(t, "testdata/format", "format") + res.assertOutput("", "") + res.assertNoErr() + res.checkGolden() +} + +func updateGoldenDir(t *testing.T, origDir, resultDir, goldenDir string) { + t.Helper() + if os.Getenv("UPDATE_GOLDEN") == "" { + return + } + assertNilError(t, os.RemoveAll(goldenDir)) + assertNilError(t, filepath.WalkDir(resultDir, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return err + } + relName := mustRel(t, resultDir, path) + origName := filepath.Join(origDir, relName) + _, err = os.Stat(origName) + if err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(filepath.Dir(filepath.Join(goldenDir, relName)), d.Type()) + if err != nil { + return err + } + return copyFile(path, filepath.Join(goldenDir, relName)) + } + return err + } + resContent, err := os.ReadFile(path) + if err != nil { + return err + } + origContent, err := os.ReadFile(origName) + if err != nil { + return err + } + if bytes.Equal(resContent, origContent) { + return nil + } + return copyFile(path, filepath.Join(goldenDir, relName)) + })) +} + +func checkGoldenDir(t *testing.T, origDir, resultDir, goldenDir string) { + t.Helper() + golden := true + t.Cleanup(func() { + t.Helper() + if !golden { + t.Log("To regenerate golden files run `UPDATE_GOLDEN=1 script/test.sh`") + } + }) + updateGoldenDir(t, origDir, resultDir, goldenDir) + checked := map[string]bool{} + _, err := os.Stat(goldenDir) + if err == nil { + assertNilError(t, filepath.Walk(goldenDir, func(wantPath string, info fs.FileInfo, err error) error { + relPath := mustRel(t, goldenDir, wantPath) + if err != nil || info.IsDir() { + return err + } + if !assertEqualFiles(t, wantPath, filepath.Join(resultDir, relPath)) { + golden = false + } + checked[relPath] = true + return nil + })) + } + assertNilError(t, filepath.Walk(origDir, func(wantPath string, info fs.FileInfo, err error) error { + relPath := mustRel(t, origDir, wantPath) + if err != nil || info.IsDir() || checked[relPath] { + return err + } + if !assertEqualFiles(t, wantPath, filepath.Join(resultDir, relPath)) { + golden = false + } + checked[relPath] = true + return nil + })) + assertNilError(t, filepath.Walk(resultDir, func(resultPath string, info fs.FileInfo, err error) error { + relPath := mustRel(t, resultDir, resultPath) + if err != nil || info.IsDir() || checked[relPath] { + return err + } + golden = false + return fmt.Errorf("found unexpected file:\n%v", relPath) + })) +} + +func mustRel(t *testing.T, base, target string) string { + t.Helper() + rel, err := filepath.Rel(base, target) + assertNilError(t, err) + return rel +} + +func copyDir(t *testing.T, dst, src string) error { + fmt.Println("dst", dst) + dst, err := filepath.Abs(dst) + if err != nil { + return err + } + return filepath.Walk(src, func(srcPath string, info fs.FileInfo, err error) error { + if err != nil || info.IsDir() { + return err + } + dstPath := filepath.Join(dst, mustRel(t, src, srcPath)) + err = copyFile(srcPath, dstPath) + return err + }) +} + +func copyFile(src, dst string) (errOut error) { + srcDirStat, err := os.Stat(filepath.Dir(src)) + if err != nil { + return err + } + err = os.MkdirAll(filepath.Dir(dst), srcDirStat.Mode()) + if err != nil { + return err + } + dstFile, err := os.Create(dst) + if err != nil { + return err + } + defer func() { + e := dstFile.Close() + if errOut == nil { + errOut = e + } + }() + srcFile, err := os.Open(src) + if err != nil { + return err + } + defer func() { + e := srcFile.Close() + if errOut == nil { + errOut = e + } + }() + _, err = io.Copy(dstFile, srcFile) + return err +} + +type testRun struct { + t *testing.T + workDir string + srcDir string + stdOut bytes.Buffer + stdErr bytes.Buffer + err error +} + +func (r testRun) checkGolden() { + r.t.Helper() + checkGoldenDir(r.t, r.srcDir, r.workDir, filepath.Join("testdata", "golden", r.t.Name())) +} + +func (r testRun) assertOutput(stdout, stderr string) { + r.t.Helper() + assertEqualStrings(r.t, strings.TrimSpace(stdout), strings.TrimSpace(r.stdOut.String())) + assertEqualStrings(r.t, strings.TrimSpace(stderr), strings.TrimSpace(r.stdErr.String())) +} + +func (r testRun) assertNoErr() { + r.t.Helper() + assertNilError(r.t, r.err) +} + +func (r testRun) assertErr(want string) { + r.t.Helper() + if r.err == nil { + r.t.Error("expected error") + return + } + if strings.TrimSpace(r.err.Error()) != strings.TrimSpace(want) { + r.t.Errorf("unexpected error:\nwant:\n%v\ngot:\n%v", want, r.err.Error()) + } +} + +func runTest(t *testing.T, srcDir string, args ...string) testRun { + t.Helper() + srcDir = filepath.FromSlash(srcDir) + res := testRun{ + t: t, + workDir: t.TempDir(), + srcDir: srcDir, + } + err := copyDir(t, res.workDir, srcDir) + if err != nil { + t.Error(err) + return res + } + res.err = run( + append(args, "-C", res.workDir), + []kong.Option{kong.Writers(&res.stdOut, &res.stdErr)}, + ) + return res +} + +func newTestServer(t *testing.T, ref string, files map[string]any) *httptest.Server { + t.Helper() + jsonHandler := func(wantQuery url.Values, val any) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + gotQuery := r.URL.Query() + queryDiff := cmp.Diff(wantQuery, gotQuery) + if queryDiff != "" { + t.Errorf("query mismatch for %v (-want +got):\n%v", r.URL.Path, queryDiff) + } + w.WriteHeader(200) + err := json.NewEncoder(w).Encode(val) + if err != nil { + panic(err) + } + } + } + repoPath := "/api/v3/repos/github/rest-api-description" + emptyQuery := url.Values{} + refQuery := url.Values{"ref": []string{ref}} + mux := http.NewServeMux() + server := httptest.NewServer(mux) + mux.HandleFunc( + path.Join(repoPath, "commits", ref), + jsonHandler(emptyQuery, &github.RepositoryCommit{SHA: github.Ptr("s")}), + ) + var descriptionsContent []*github.RepositoryContent + for name, content := range files { + descriptionsContent = append(descriptionsContent, &github.RepositoryContent{ + Name: github.Ptr(path.Base(path.Dir(name))), + }) + mux.HandleFunc( + path.Join(repoPath, "contents/descriptions", name), + jsonHandler(refQuery, &github.RepositoryContent{ + Name: github.Ptr(path.Base(name)), + DownloadURL: github.Ptr(server.URL + "/dl/" + name), + }), + ) + mux.HandleFunc( + path.Join("/dl", name), + jsonHandler(emptyQuery, content), + ) + } + mux.HandleFunc( + path.Join(repoPath, "contents/descriptions"), + jsonHandler(refQuery, descriptionsContent), + ) + t.Cleanup(server.Close) + t.Setenv("GITHUB_TOKEN", "fake token") + return server +} + +func assertEqualStrings(t *testing.T, want, got string) { + t.Helper() + diff := cmp.Diff(want, got) + if diff != "" { + t.Error(diff) + } +} + +func assertEqualFiles(t *testing.T, want, got string) bool { + t.Helper() + wantBytes, err := os.ReadFile(want) + if !assertNilError(t, err) { + return false + } + wantBytes = bytes.ReplaceAll(wantBytes, []byte("\r\n"), []byte("\n")) + gotBytes, err := os.ReadFile(got) + if !assertNilError(t, err) { + return false + } + gotBytes = bytes.ReplaceAll(gotBytes, []byte("\r\n"), []byte("\n")) + if !bytes.Equal(wantBytes, gotBytes) { + diff := cmp.Diff(string(wantBytes), string(gotBytes)) + t.Errorf("files %q and %q differ: %v", want, got, diff) + return false + } + return true +} + +func assertNilError(t *testing.T, err error) bool { + t.Helper() + if err != nil { + t.Error(err) + return false + } + return true +} diff --git a/tools/metadata/metadata.go b/tools/metadata/metadata.go new file mode 100644 index 00000000000..d6306c2e8ad --- /dev/null +++ b/tools/metadata/metadata.go @@ -0,0 +1,574 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "cmp" + "context" + "errors" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/printer" + "go/token" + "maps" + "net/url" + "os" + "path" + "path/filepath" + "regexp" + "slices" + "strings" + "sync" + + "github.com/google/go-github/v90/github" + "go.yaml.in/yaml/v3" +) + +type operation struct { + Name string `yaml:"name,omitempty" json:"name,omitempty"` + DocumentationURL string `yaml:"documentation_url,omitempty" json:"documentation_url,omitempty"` + OpenAPIFiles []string `yaml:"openapi_files,omitempty" json:"openapi_files,omitempty"` + Deprecated bool `yaml:"deprecated,omitempty" json:"deprecated,omitempty"` +} + +func (o *operation) equal(other *operation) bool { + if o.Name != other.Name || o.DocumentationURL != other.DocumentationURL || o.Deprecated != other.Deprecated { + return false + } + if len(o.OpenAPIFiles) != len(other.OpenAPIFiles) { + return false + } + for i := range o.OpenAPIFiles { + if o.OpenAPIFiles[i] != other.OpenAPIFiles[i] { + return false + } + } + return true +} + +func (o *operation) clone() *operation { + return &operation{ + Name: o.Name, + DocumentationURL: o.DocumentationURL, + OpenAPIFiles: append([]string{}, o.OpenAPIFiles...), + Deprecated: o.Deprecated, + } +} + +func operationsEqual(a, b []*operation) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if !a[i].equal(b[i]) { + return false + } + } + return true +} + +func sortOperations(ops []*operation) { + slices.SortFunc(ops, func(a, b *operation) int { + leftVerb, leftURL := parseOpName(a.Name) + rightVerb, rightURL := parseOpName(b.Name) + return cmp.Or(cmp.Compare(leftURL, rightURL), cmp.Compare(leftVerb, rightVerb)) + }) +} + +// normalizeOpPath returns an endpoint with all templated path parameters replaced with *. +func normalizeOpPath(opPath string) string { + if !strings.ContainsAny(opPath, "{%") { + return opPath + } + segments := strings.Split(opPath, "/") + for i, segment := range segments { + if len(segment) == 0 { + continue + } + if segment[0] == '{' || segment[0] == '%' { + segments[i] = "*" + } + } + return strings.Join(segments, "/") +} + +func normalizedOpName(name string) string { + verb, u := parseOpName(name) + return strings.TrimSpace(verb + " " + normalizeOpPath(u)) +} + +// matches something like "GET /some/path". +var opNameRe = regexp.MustCompile(`(?i)(\S+)(?:\s+(\S.*))?`) + +func parseOpName(id string) (verb, url string) { + match := opNameRe.FindStringSubmatch(id) + if match == nil { + return "", "" + } + u := strings.TrimSpace(match[2]) + if !strings.HasPrefix(u, "/") { + u = "/" + u + } + return strings.ToUpper(match[1]), u +} + +type operationsFile struct { + ManualOps []*operation `yaml:"operations,omitempty"` + OverrideOps []*operation `yaml:"operation_overrides,omitempty"` + GitCommit string `yaml:"openapi_commit,omitempty"` + OpenapiOps []*operation `yaml:"openapi_operations,omitempty"` + + mu sync.Mutex + resolvedOps map[string]*operation +} + +func (m *operationsFile) resolve() { + m.mu.Lock() + defer m.mu.Unlock() + if m.resolvedOps != nil { + return + } + m.resolvedOps = map[string]*operation{} + for _, op := range m.OpenapiOps { + m.resolvedOps[op.Name] = op.clone() + } + for _, op := range m.ManualOps { + m.resolvedOps[op.Name] = op.clone() + } + for _, override := range m.OverrideOps { + _, ok := m.resolvedOps[override.Name] + if !ok { + continue + } + override = override.clone() + if override.DocumentationURL != "" { + m.resolvedOps[override.Name].DocumentationURL = override.DocumentationURL + } + if len(override.OpenAPIFiles) > 0 { + m.resolvedOps[override.Name].OpenAPIFiles = override.OpenAPIFiles + } + } +} + +func (m *operationsFile) saveFile(filename string) (errOut error) { + sortOperations(m.ManualOps) + sortOperations(m.OverrideOps) + sortOperations(m.OpenapiOps) + f, err := os.Create(filename) + if err != nil { + return err + } + defer func() { + e := f.Close() + if errOut == nil { + errOut = e + } + }() + enc := yaml.NewEncoder(f) + enc.SetIndent(2) + defer func() { + e := enc.Close() + if errOut == nil { + errOut = e + } + }() + return enc.Encode(m) +} + +func (m *operationsFile) updateFromGithub(ctx context.Context, client *github.Client, ref string) error { + commit, resp, err := client.Repositories.GetCommit(ctx, descriptionsOwnerName, descriptionsRepoName, ref, nil) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return fmt.Errorf("unexpected status code: %v", resp.Status) + } + ops, err := getOpsFromGithub(ctx, client, ref) + if err != nil { + return err + } + if !operationsEqual(m.OpenapiOps, ops) { + m.OpenapiOps = ops + m.GitCommit = commit.GetSHA() + } + return nil +} + +func loadOperationsFile(filename string) (*operationsFile, error) { + b, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + var opsFile operationsFile + err = yaml.Unmarshal(b, &opsFile) + if err != nil { + return nil, err + } + return &opsFile, nil +} + +func addOperation(ops []*operation, filename, opName, docURL string, deprecated bool) []*operation { + for _, op := range ops { + if opName != op.Name { + continue + } + if len(op.OpenAPIFiles) == 0 { + op.OpenAPIFiles = append(op.OpenAPIFiles, filename) + op.DocumentationURL = docURL + op.Deprecated = deprecated + return ops + } + // just append to files, but only add the first ghes file + if !strings.Contains(filename, "/ghes") { + op.OpenAPIFiles = append(op.OpenAPIFiles, filename) + op.Deprecated = op.Deprecated || deprecated + return ops + } + for _, f := range op.OpenAPIFiles { + if strings.Contains(f, "/ghes") { + return ops + } + } + op.OpenAPIFiles = append(op.OpenAPIFiles, filename) + op.Deprecated = op.Deprecated || deprecated + return ops + } + return append(ops, &operation{ + Name: opName, + OpenAPIFiles: []string{filename}, + DocumentationURL: docURL, + Deprecated: deprecated, + }) +} + +func unusedOps(opsFile *operationsFile, dir string) ([]*operation, error) { + var usedOps map[string]bool + err := visitServiceMethods(dir, false, func(_ string, fn *ast.FuncDecl, cmap ast.CommentMap) error { + ops, err := methodOps(opsFile, cmap, fn) + if err != nil { + return err + } + for _, op := range ops { + if usedOps == nil { + usedOps = map[string]bool{} + } + usedOps[op.Name] = true + } + return nil + }) + if err != nil { + return nil, err + } + var result []*operation + opsFile.resolve() + for opName, op := range opsFile.resolvedOps { + if !usedOps[opName] { + result = append(result, op) + } + } + sortOperations(result) + return result, nil +} + +func updateDocsVisitor(opsFile *operationsFile) nodeVisitor { + return func(serviceMethod string, fn *ast.FuncDecl, cmap ast.CommentMap) error { + linksMap := map[string]struct{}{} + undocMap := map[string]bool{} + + ops, err := methodOps(opsFile, cmap, fn) + if err != nil { + return err + } + if len(ops) == 0 { + return fmt.Errorf("no operations defined for %v", serviceMethod) + } + + for _, op := range ops { + if op.DocumentationURL == "" { + undocMap[op.Name] = true + continue + } + linksMap[op.DocumentationURL] = struct{}{} + } + undocumentedOps := slices.Sorted(maps.Keys(undocMap)) + + // Find the group that comes before the function + var group *ast.CommentGroup + for _, g := range cmap[fn] { + if g.End() == fn.Pos()-1 { + group = g + } + } + + // If there is no group, create one + if group == nil { + group = &ast.CommentGroup{ + List: []*ast.Comment{{Text: "//", Slash: fn.Pos() - 1}}, + } + cmap[fn] = append(cmap[fn], group) + } + + hasCustomDeprecated := slices.ContainsFunc(group.List, func(comment *ast.Comment) bool { + return anyDeprecatedRE.MatchString(comment.Text) && !deprecatedRE.MatchString(comment.Text) + }) + + origList := group.List + group.List = nil + for _, comment := range origList { + if metaOpRe.MatchString(comment.Text) || + docLineRE.MatchString(comment.Text) || + undocRE.MatchString(comment.Text) || + deprecatedRE.MatchString(comment.Text) { + continue + } + group.List = append(group.List, comment) + } + + isDeprecated := slices.ContainsFunc(ops, func(op *operation) bool { + return op.Deprecated + }) + if isDeprecated && !hasCustomDeprecated { + group.List = append(group.List, &ast.Comment{Text: "// Deprecated: This endpoint has been deprecated by GitHub."}, &ast.Comment{Text: "//"}) + } + + // add an empty line before adding doc links + group.List = append(group.List, &ast.Comment{Text: "//"}) + + docLinks := slices.Sorted(maps.Keys(linksMap)) + for i, dl := range docLinks { + group.List = append( + group.List, + &ast.Comment{ + Text: "// GitHub API docs: " + normalizeDocURL(dl), + }, + ) + if i < len(docLinks)-1 { + // add empty line between doc links + group.List = append(group.List, &ast.Comment{Text: "//"}) + } + } + _, methodName, _ := strings.Cut(serviceMethod, ".") + for _, opName := range undocumentedOps { + line := fmt.Sprintf("// Note: %v uses the undocumented GitHub API endpoint %q.", methodName, opName) + group.List = append(group.List, &ast.Comment{Text: line}) + } + for _, op := range ops { + group.List = append(group.List, &ast.Comment{ + Text: fmt.Sprintf("//meta:operation %v", op.Name), + }) + } + group.List[0].Slash = fn.Pos() - 1 + for i := 1; i < len(group.List); i++ { + group.List[i].Slash = token.NoPos + } + return nil + } +} + +// updateDocs updates the code comments in dir with doc urls from metadata. +func updateDocs(opsFile *operationsFile, dir string) error { + return visitServiceMethods(dir, true, updateDocsVisitor(opsFile)) +} + +type nodeVisitor func(serviceMethod string, fn *ast.FuncDecl, cmap ast.CommentMap) error + +// visitServiceMethods runs visit on the ast.Node of every service method in dir. When writeFiles is true it will +// save any changes back to the original file. +func visitServiceMethods(dir string, writeFiles bool, visit nodeVisitor) error { + dirEntries, err := os.ReadDir(dir) + if err != nil { + return err + } + for _, dirEntry := range dirEntries { + filename := filepath.Join(dir, dirEntry.Name()) + if dirEntry.IsDir() || + !strings.HasSuffix(filename, ".go") || + strings.HasSuffix(filename, "_test.go") { + continue + } + err = errors.Join(err, visitFileMethods(writeFiles, filename, visit)) + } + return err +} + +func visitFileMethods(updateFile bool, filename string, visit nodeVisitor) error { + content, err := os.ReadFile(filename) + if err != nil { + return err + } + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) + + fset := token.NewFileSet() + fileNode, err := parser.ParseFile(fset, "", content, parser.ParseComments) + if err != nil { + return err + } + cmap := ast.NewCommentMap(fset, fileNode, fileNode.Comments) + + ast.Inspect(fileNode, func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok { + return true + } + serviceMethod := nodeServiceMethod(fn) + if serviceMethod == "" { + return true + } + e := visit(serviceMethod, fn, cmap) + err = errors.Join(err, e) + return true + }) + if err != nil { + return err + } + if !updateFile { + return nil + } + fileNode.Comments = cmap.Filter(fileNode).Comments() + var buf bytes.Buffer + err = printer.Fprint(&buf, fset, fileNode) + if err != nil { + return err + } + updatedContent, err := format.Source(buf.Bytes()) + if err != nil { + return err + } + if bytes.Equal(content, updatedContent) { + return nil + } + return os.WriteFile(filename, updatedContent, 0o600) +} + +var ( + metaOpRe = regexp.MustCompile(`(?i)\s*//\s*meta:operation\s+(\S.+)`) + undocRE = regexp.MustCompile(`(?i)\s*//\s*Note:\s+\S.+ uses the undocumented GitHub API endpoint`) + docLineRE = regexp.MustCompile(`(?i)\s*//\s*GitHub\s+API\s+docs:`) + deprecatedRE = regexp.MustCompile(`(?i)\s*//\s*Deprecated: This endpoint has been deprecated by GitHub\.`) + anyDeprecatedRE = regexp.MustCompile(`(?i)\s*//\s*Deprecated:`) // Avoid previously tagged Deprecated endpoints +) + +// methodOps parses a method's comments for //meta:operation lines and returns the corresponding operations. +func methodOps(opsFile *operationsFile, cmap ast.CommentMap, fn *ast.FuncDecl) ([]*operation, error) { + var ops []*operation + var err error + seen := map[string]bool{} + for _, g := range cmap[fn] { + for _, c := range g.List { + match := metaOpRe.FindStringSubmatch(c.Text) + if match == nil { + continue + } + opName := strings.TrimSpace(match[1]) + opsFile.resolve() + var found []*operation + norm := normalizedOpName(opName) + for n := range opsFile.resolvedOps { + if normalizedOpName(n) == norm { + found = append(found, opsFile.resolvedOps[n]) + } + } + switch len(found) { + case 0: + err = errors.Join(err, fmt.Errorf("could not find operation %q in openapi_operations.yaml", opName)) + case 1: + name := found[0].Name + if seen[name] { + err = errors.Join(err, fmt.Errorf("duplicate operation: %v", name)) + } + seen[name] = true + ops = append(ops, found[0]) + default: + var foundNames []string + for _, op := range found { + foundNames = append(foundNames, op.Name) + } + slices.Sort(foundNames) + err = errors.Join(err, fmt.Errorf("ambiguous operation %q could match any of: %v", opName, foundNames)) + } + } + } + sortOperations(ops) + return ops, err +} + +// metadataDocsAPIVersion is appended to generated docs links. +// Keep this in sync with defaultAPIVersion in github/github.go. +const metadataDocsAPIVersion = "2022-11-28" + +// normalizeDocURL cleans docURL's path and enforces metadataDocsAPIVersion for +// https://docs.github.com/rest and +// https://docs.github.com/enterprise-cloud@latest/rest URLs. +func normalizeDocURL(docURL string) string { + u, err := url.Parse(docURL) + if err != nil { + return docURL + } + cleanPath := path.Clean(u.Path) + isRESTDocsURL := u.Host == "docs.github.com" && + (cleanPath == "/rest" || + strings.HasPrefix(cleanPath, "/rest/") || + cleanPath == "/enterprise-cloud@latest/rest" || + strings.HasPrefix(cleanPath, "/enterprise-cloud@latest/rest/")) + if !isRESTDocsURL { + return docURL + } + + u.Path = cleanPath + q := u.Query() + if q.Get("apiVersion") == "" { + q.Set("apiVersion", metadataDocsAPIVersion) + } + u.RawQuery = q.Encode() + return u.String() +} + +// nodeServiceMethod returns the name of the service method represented by fn, or "" if fn is not a service method. +// Name is in the form of "Receiver.Function", for example "IssuesService.Create". +func nodeServiceMethod(fn *ast.FuncDecl) string { + if fn.Recv == nil || len(fn.Recv.List) != 1 { + return "" + } + recv := fn.Recv.List[0] + se, ok := recv.Type.(*ast.StarExpr) + if !ok { + return "" + } + id, ok := se.X.(*ast.Ident) + if !ok { + return "" + } + + // We only want exported methods on exported types where the type name ends in "Service". + if !id.IsExported() || !fn.Name.IsExported() || !strings.HasSuffix(id.Name, "Service") { + return "" + } + + // Skip generated Iterator methods. + if strings.HasSuffix(fn.Name.Name, "Iter") { + return "" + } + + serviceMethod := id.Name + "." + fn.Name.Name + if skipServiceMethod[serviceMethod] { + return "" + } + + return serviceMethod +} + +// skipServiceMethod lists helper methods that download from URLs returned by +// other endpoints and therefore have no REST API operation of their own. +var skipServiceMethod = map[string]bool{ + "CopilotService.DownloadCopilotMetrics": true, + "CopilotService.DownloadDailyMetrics": true, + "CopilotService.DownloadPeriodicMetrics": true, + "CopilotService.DownloadUserDailyMetrics": true, + "CopilotService.DownloadUserPeriodicMetrics": true, +} diff --git a/tools/metadata/metadata_test.go b/tools/metadata/metadata_test.go new file mode 100644 index 00000000000..59e8f514799 --- /dev/null +++ b/tools/metadata/metadata_test.go @@ -0,0 +1,89 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "testing" +) + +func Test_normalizedOpName(t *testing.T) { + t.Parallel() + for _, td := range []struct { + name string + want string + }{ + {name: "", want: ""}, + {name: "get /foo/{id}", want: "GET /foo/*"}, + {name: "get foo", want: "GET /foo"}, + } { + t.Run(td.name, func(t *testing.T) { + t.Parallel() + got := normalizedOpName(td.name) + if got != td.want { + t.Errorf("normalizedOpName() = %v, want %v", got, td.want) + } + }) + } +} + +func Test_normalizeDocURL(t *testing.T) { + t.Parallel() + + for _, td := range []struct { + name string + docURL string + want string + }{ + { + name: "invalid URL", + docURL: "://bad", + want: "://bad", + }, + { + name: "clean path and add api version", + docURL: "https://docs.github.com//rest/repos/repos#get-a-repository", + want: "https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28#get-a-repository", + }, + { + name: "preserve query and add api version", + docURL: "https://docs.github.com/rest/repos/repos?foo=bar", + want: "https://docs.github.com/rest/repos/repos?apiVersion=2022-11-28&foo=bar", + }, + { + name: "preserve existing api version", + docURL: "https://docs.github.com/rest/repos/repos?apiVersion=2021-01-01&foo=bar", + want: "https://docs.github.com/rest/repos/repos?apiVersion=2021-01-01&foo=bar", + }, + { + name: "preserve non-default api version", + docURL: "https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#list-private-registries-for-an-organization", + want: "https://docs.github.com/rest/private-registries/organization-configurations?apiVersion=2026-03-10#list-private-registries-for-an-organization", + }, + { + name: "enterprise cloud latest rest is normalized", + docURL: "https://docs.github.com/enterprise-cloud@latest/rest/repos/repos#get-a-repository", + want: "https://docs.github.com/enterprise-cloud@latest/rest/repos/repos?apiVersion=2022-11-28#get-a-repository", + }, + { + name: "non rest docs path unchanged", + docURL: "https://gist.github.com/jonmagic/5282384165e0f86ef105", + want: "https://gist.github.com/jonmagic/5282384165e0f86ef105", + }, + { + name: "non docs host unchanged", + docURL: "https://example.com/rest/repos/repos", + want: "https://example.com/rest/repos/repos", + }, + } { + t.Run(td.name, func(t *testing.T) { + t.Parallel() + got := normalizeDocURL(td.docURL) + if got != td.want { + t.Errorf("normalizeDocURL() = %v, want %v", got, td.want) + } + }) + } +} diff --git a/tools/metadata/openapi.go b/tools/metadata/openapi.go new file mode 100644 index 00000000000..a74a9870b88 --- /dev/null +++ b/tools/metadata/openapi.go @@ -0,0 +1,210 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "cmp" + "context" + "fmt" + "io" + "regexp" + "slices" + "strconv" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/google/go-github/v90/github" + "golang.org/x/sync/errgroup" +) + +const ( + descriptionsOwnerName = "github" + descriptionsRepoName = "rest-api-description" + descriptionsPath = "descriptions" +) + +type openapiFile struct { + description *openapi3.T + filename string + plan string + planIdx int + releaseMajor int + releaseMinor int +} + +func getOpsFromGithub(ctx context.Context, client *github.Client, gitRef string) ([]*operation, error) { + descs, err := getDescriptions(ctx, client, gitRef) + if err != nil { + return nil, err + } + var ops []*operation + for _, desc := range descs { + for p, pathItem := range desc.description.Paths.Map() { + for method, op := range pathItem.Operations() { + docURL := "" + if op.ExternalDocs != nil { + docURL = op.ExternalDocs.URL + } + ops = addOperation(ops, desc.filename, method+" "+p, docURL, op.Deprecated) + } + } + } + deprecateOperations(descs, ops) + sortOperations(ops) + return ops, nil +} + +func deprecateOperations(descs []*openapiFile, ops []*operation) { + var ghesLatestMajor, ghesLatestMinor int + + for _, desc := range descs { + if strings.Contains(desc.filename, "/ghes") { + if desc.releaseMajor > ghesLatestMajor || (desc.releaseMajor == ghesLatestMajor && desc.releaseMinor > ghesLatestMinor) { + ghesLatestMajor = desc.releaseMajor + ghesLatestMinor = desc.releaseMinor + } + } + } + + ghesLatest := fmt.Sprintf("/ghes-%v.%v", ghesLatestMajor, ghesLatestMinor) + + for _, op := range ops { + if op.Deprecated { + continue + } + + var latest bool + for _, f := range op.OpenAPIFiles { + if strings.Contains(f, "/api.github.com") { + latest = true + break + } + if strings.Contains(f, "/ghec") { + latest = true + break + } + if strings.Contains(f, ghesLatest) { + latest = true + break + } + } + + if !latest { + op.Deprecated = true + } + } +} + +func (o *openapiFile) loadDescription(ctx context.Context, client *github.Client, gitRef string) error { + contents, resp, err := client.Repositories.DownloadContents( + ctx, + descriptionsOwnerName, + descriptionsRepoName, + o.filename, + &github.RepositoryContentGetOptions{Ref: gitRef}, + ) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return fmt.Errorf("unexpected status code: %v", resp.Status) + } + b, err := io.ReadAll(contents) + if err != nil { + return err + } + err = contents.Close() + if err != nil { + return err + } + o.description, err = openapi3.NewLoader().LoadFromData(b) + return err +} + +var dirPatterns = []*regexp.Regexp{ + regexp.MustCompile(`^(?Papi\.github\.com)(-(?P\d+)\.(?P\d+))?$`), + regexp.MustCompile(`^(?Pghec)(-(?P\d+)\.(?P\d+))?$`), + regexp.MustCompile(`^(?Pghes)(-(?P\d+)\.(?P\d+))?$`), +} + +// getDescriptions loads OpenapiFiles for all the OpenAPI 3.0 description files in github/rest-api-description. +// This assumes that all directories in "descriptions/" contain OpenAPI 3.0 description files with the same +// name as the directory (plus the ".json" extension). For example, "descriptions/api.github.com/api.github.com.json". +// Results are sorted by these rules: +// - Directories that don't match any of the patterns in dirPatterns are removed. +// - Directories are sorted by the pattern that matched in the same order they appear in dirPatterns. +// - Directories are then sorted by major and minor version in descending order. +func getDescriptions(ctx context.Context, client *github.Client, gitRef string) ([]*openapiFile, error) { + _, dir, resp, err := client.Repositories.GetContents( + ctx, + descriptionsOwnerName, + descriptionsRepoName, + descriptionsPath, + &github.RepositoryContentGetOptions{Ref: gitRef}, + ) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("unexpected status code: %v", resp.Status) + } + files := make([]*openapiFile, 0, len(dir)) + for _, d := range dir { + for i, pattern := range dirPatterns { + m := pattern.FindStringSubmatch(d.GetName()) + if m == nil { + continue + } + file := openapiFile{ + filename: fmt.Sprintf("descriptions/%v/%v.json", d.GetName(), d.GetName()), + plan: m[pattern.SubexpIndex("plan")], + planIdx: i, + } + rawMajor := m[pattern.SubexpIndex("major")] + if rawMajor != "" { + file.releaseMajor, err = strconv.Atoi(rawMajor) + if err != nil { + return nil, err + } + } + rawMinor := m[pattern.SubexpIndex("minor")] + if rawMinor != "" { + file.releaseMinor, err = strconv.Atoi(rawMinor) + if err != nil { + return nil, err + } + } + if file.plan == "ghes" && file.releaseMajor < 3 { + continue + } + files = append(files, &file) + break + } + } + slices.SortFunc(files, func(a, b *openapiFile) int { + // sort by the following rules: + // - planIdx ascending + // - releaseMajor descending + // - releaseMinor descending + return cmp.Or( + cmp.Compare(a.planIdx, b.planIdx), + cmp.Compare(b.releaseMajor, a.releaseMajor), + cmp.Compare(b.releaseMinor, a.releaseMinor), + ) + }) + g, ctx := errgroup.WithContext(ctx) + for _, file := range files { + f := file + g.Go(func() error { + return f.loadDescription(ctx, client, gitRef) + }) + } + err = g.Wait() + if err != nil { + return nil, err + } + return files, nil +} diff --git a/tools/metadata/testdata/format/openapi_operations.yaml b/tools/metadata/testdata/format/openapi_operations.yaml new file mode 100644 index 00000000000..abaafeb9aa8 --- /dev/null +++ b/tools/metadata/testdata/format/openapi_operations.yaml @@ -0,0 +1,16 @@ + +operations: + - name: POST /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#update-a +openapi_operations: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a + - name: GET /undocumented/{undocumented_id} + +operation_overrides: + - name: GET /a/{a_id_noncanonical2} # this comment will disappear + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a + - name: GET /fake/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a + +openapi_commit: b8dafbe912a3be421d21346faa2b29bf15e6f84d diff --git a/tools/metadata/testdata/golden/TestFormat/openapi_operations.yaml b/tools/metadata/testdata/golden/TestFormat/openapi_operations.yaml new file mode 100644 index 00000000000..524d56817fb --- /dev/null +++ b/tools/metadata/testdata/golden/TestFormat/openapi_operations.yaml @@ -0,0 +1,13 @@ +operations: + - name: POST /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#update-a +operation_overrides: + - name: GET /a/{a_id_noncanonical2} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a + - name: GET /fake/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a +openapi_commit: b8dafbe912a3be421d21346faa2b29bf15e6f84d +openapi_operations: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a + - name: GET /undocumented/{undocumented_id} diff --git a/tools/metadata/testdata/golden/TestUpdateGo/valid/github/a.go b/tools/metadata/testdata/golden/TestUpdateGo/valid/github/a.go new file mode 100644 index 00000000000..4c9c686f027 --- /dev/null +++ b/tools/metadata/testdata/golden/TestUpdateGo/valid/github/a.go @@ -0,0 +1,47 @@ +package github + +type AService struct{} + +// Get gets an A +// +// GitHub API docs: https://docs.github.com/rest/a/a?apiVersion=2022-11-28#overridden-get-a +// +//meta:operation GET /a/{a_id} +func (s *AService) Get() {} + +// Undocumented uses an undocumented operation +// +// Note: Undocumented uses the undocumented GitHub API endpoint "GET /undocumented/{undocumented_id}". +// +//meta:operation GET /undocumented/{undocumented_id} +func (s *AService) Undocumented() {} + +// OutdatedLinks has links that are outdated or wrong +// +// GitHub API docs: https://docs.github.com/rest/a/a?apiVersion=2022-11-28#update-a +// +//meta:operation POST /a/{a_id} +func (s *AService) OutdatedLinks() {} + +// GitHub API docs: https://docs.github.com/rest/a/a?apiVersion=2022-11-28#overridden-get-a +// +//meta:operation GET /a/{a_id} +func (s *AService) Uncommented() {} + +// Get gets a user. +// +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#get-a-user +// +// GitHub API docs: https://docs.github.com/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user +// +//meta:operation GET /user +//meta:operation GET /users/{username} +func (s *AService) Get(user string) {} + +func (s *AService) unexported() {} + +func NotAMethod() {} + +type internalService struct{} + +func (i *internalService) Get() {} diff --git a/tools/metadata/testdata/golden/TestUpdateOpenAPI/openapi_operations.yaml b/tools/metadata/testdata/golden/TestUpdateOpenAPI/openapi_operations.yaml new file mode 100644 index 00000000000..c1e3cf4cbd6 --- /dev/null +++ b/tools/metadata/testdata/golden/TestUpdateOpenAPI/openapi_operations.yaml @@ -0,0 +1,14 @@ +operations: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a +openapi_commit: s +openapi_operations: + - name: GET /a/b/{a_id} + documentation_url: https://docs.github.com/rest/reference/a + openapi_files: + - descriptions/ghec/ghec.json + - descriptions/ghes-3.10/ghes-3.10.json + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/reference/a + openapi_files: + - descriptions/api.github.com/api.github.com.json diff --git a/tools/metadata/testdata/unused/github/a.go b/tools/metadata/testdata/unused/github/a.go new file mode 100644 index 00000000000..0169531b2b6 --- /dev/null +++ b/tools/metadata/testdata/unused/github/a.go @@ -0,0 +1,8 @@ +package github + +type AService struct{} + +// PostABC +// +//meta:operation POST /a/b/c/{a_id} +func (a *AService) PostABC() {} diff --git a/tools/metadata/testdata/unused/openapi_operations.yaml b/tools/metadata/testdata/unused/openapi_operations.yaml new file mode 100644 index 00000000000..c5c3772b1f3 --- /dev/null +++ b/tools/metadata/testdata/unused/openapi_operations.yaml @@ -0,0 +1,15 @@ +openapi_commit: b8dafbe912a3be421d21346faa2b29bf15e6f84d +operations: + - name: POST /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#update-a +openapi_operations: + - name: POST /a/b/c/{a_id} + documentation_url: https://docs.github.com/rest/a/b/c#update-a + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a + - name: GET /undocumented/{undocumented_id} +operation_overrides: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a + - name: GET /fake/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a diff --git a/tools/metadata/testdata/update-go/invalid/github/a.go b/tools/metadata/testdata/update-go/invalid/github/a.go new file mode 100644 index 00000000000..3f58dd2b846 --- /dev/null +++ b/tools/metadata/testdata/update-go/invalid/github/a.go @@ -0,0 +1,25 @@ +package github + +type AService struct{} + +// NoOperation has no operation +func (*AService) NoOperation() {} + +func (*AService) NoComment() {} + +// Ambiguous has an operation that could resolve to multiple operations +// +//meta:operation GET /ambiguous/{} +func (*AService) Ambiguous() {} + +// MissingOperation has an operation that is missing from the OpenAPI spec +// +//meta:operation GET /missing/{id} +func (*AService) MissingOperation() {} + +// DuplicateOperations has duplicate operations +// +//meta:operation GET /a/{a_id} +//meta:operation POST /a/{a_id} +//meta:operation GET /a/{a_id} +func (*AService) DuplicateOperations() {} diff --git a/tools/metadata/testdata/update-go/invalid/openapi_operations.yaml b/tools/metadata/testdata/update-go/invalid/openapi_operations.yaml new file mode 100644 index 00000000000..07b0c3e5013 --- /dev/null +++ b/tools/metadata/testdata/update-go/invalid/openapi_operations.yaml @@ -0,0 +1,16 @@ +operations: + - name: POST /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#update-a +openapi_operations: + - name: GET /ambiguous/{id} + documentation_url: https://docs.github.com/rest/ambiguous/ + - name: GET /ambiguous/{name} + documentation_url: https://docs.github.com/rest/ambiguous/ + - name: GET /undocumented/{undocumented_id} + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a +operation_overrides: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a + - name: GET /fake/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a diff --git a/tools/metadata/testdata/update-go/valid/github/a.go b/tools/metadata/testdata/update-go/valid/github/a.go new file mode 100644 index 00000000000..63af01975b0 --- /dev/null +++ b/tools/metadata/testdata/update-go/valid/github/a.go @@ -0,0 +1,39 @@ +package github + +type AService struct{} + +// Get gets an A +// +//meta:operation GET /a/{non-canonical-id} +func (s *AService) Get() {} + +// Undocumented uses an undocumented operation +// +//meta:operation GET /undocumented/{undocumented_id} +func (s *AService) Undocumented() {} + +// OutdatedLinks has links that are outdated or wrong +// +// GitHub API docs: https://docs.github.com/rest/a/a#get-a +// GitHub API docs: https://example.com +// Note: Undocumented uses the undocumented GitHub API endpoint "GET /undocumented/{undocumented_id}". +// +//meta:operation post a/{a_id} +func (s *AService) OutdatedLinks() {} + +//meta:operation GET /a/{a_id} +func (s *AService) Uncommented() {} + +// Get gets a user. +// +//meta:operation GET /user +//meta:operation GET /users/{username} +func (s *AService) Get(user string) {} + +func (s *AService) unexported() {} + +func NotAMethod() {} + +type internalService struct{} + +func (i *internalService) Get() {} diff --git a/tools/metadata/testdata/update-go/valid/github/ignoreme.txt b/tools/metadata/testdata/update-go/valid/github/ignoreme.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/metadata/testdata/update-go/valid/openapi_operations.yaml b/tools/metadata/testdata/update-go/valid/openapi_operations.yaml new file mode 100644 index 00000000000..dfa8af18a47 --- /dev/null +++ b/tools/metadata/testdata/update-go/valid/openapi_operations.yaml @@ -0,0 +1,17 @@ +openapi_commit: b8dafbe912a3be421d21346faa2b29bf15e6f84d +operations: + - name: POST /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#update-a +openapi_operations: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a + - name: GET /undocumented/{undocumented_id} + - name: GET /user + documentation_url: https://docs.github.com/rest/users/users#get-a-user + - name: GET /users/{username} + documentation_url: https://docs.github.com/rest/users/users#get-the-authenticated-user +operation_overrides: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a + - name: GET /fake/{a_id} + documentation_url: https://docs.github.com/rest/a/a#overridden-get-a diff --git a/tools/metadata/testdata/update-openapi/openapi_operations.yaml b/tools/metadata/testdata/update-openapi/openapi_operations.yaml new file mode 100644 index 00000000000..1577e739ab6 --- /dev/null +++ b/tools/metadata/testdata/update-openapi/openapi_operations.yaml @@ -0,0 +1,4 @@ +operations: + - name: GET /a/{a_id} + documentation_url: https://docs.github.com/rest/a/a#get-a +openapi_commit: s diff --git a/tools/paramcheck/go.mod b/tools/paramcheck/go.mod new file mode 100644 index 00000000000..a89c0e930e9 --- /dev/null +++ b/tools/paramcheck/go.mod @@ -0,0 +1,13 @@ +module tools/paramcheck + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/tools/paramcheck/go.sum b/tools/paramcheck/go.sum new file mode 100644 index 00000000000..d120eba8160 --- /dev/null +++ b/tools/paramcheck/go.sum @@ -0,0 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/paramcheck/paramcheck.go b/tools/paramcheck/paramcheck.go new file mode 100644 index 00000000000..4e97028dd5e --- /dev/null +++ b/tools/paramcheck/paramcheck.go @@ -0,0 +1,347 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package paramcheck is a custom linter for parameter naming and type conventions. +// +// For body parameters it: +// - suggests renaming a body parameter to "body" +// - reports body parameters passed by pointer, which should be passed by value +// - reports body parameter types with an "Options" suffix, which should use a "Request" suffix instead. +// +// For query parameters it: +// - suggests renaming the options parameter to "opts" +// - reports options parameters passed by value, which should be passed by pointer +// - reports options parameter types without an "Options" suffix. +package paramcheck + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "github.com/golangci/plugin-module-register/register" + "golang.org/x/tools/go/analysis" +) + +func init() { + register.Plugin("paramcheck", New) +} + +// ParamCheckPlugin is a custom linter plugin for golangci-lint. +type ParamCheckPlugin struct { + // bodyAllowedPointerTypes are body type names exempt from the by-value rule. + bodyAllowedPointerTypes map[string]bool + // bodyAllowedWrongNames are body type names exempt from the "Options" suffix rule. + bodyAllowedWrongNames map[string]bool +} + +// New returns an analysis.Analyzer to use with golangci-lint. +func New(cfg any) (register.LinterPlugin, error) { + bodyAllowedPointerTypes := map[string]bool{} + bodyAllowedWrongNames := map[string]bool{} + + if cfg != nil { + if settingsMap, ok := cfg.(map[string]any); ok { + if exceptionsRaw, ok := settingsMap["body-allowed-pointer-types"]; ok { + if exceptionsList, ok := exceptionsRaw.([]any); ok { + for _, item := range exceptionsList { + if exception, ok := item.(string); ok { + bodyAllowedPointerTypes[exception] = true + } + } + } + } + + if exceptionsRaw, ok := settingsMap["body-allowed-wrong-names"]; ok { + if exceptionsList, ok := exceptionsRaw.([]any); ok { + for _, item := range exceptionsList { + if exception, ok := item.(string); ok { + bodyAllowedWrongNames[exception] = true + } + } + } + } + } + } + return &ParamCheckPlugin{ + bodyAllowedPointerTypes: bodyAllowedPointerTypes, + bodyAllowedWrongNames: bodyAllowedWrongNames, + }, nil +} + +// BuildAnalyzers builds the analyzers for the ParamCheckPlugin. +func (p *ParamCheckPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "paramcheck", + Doc: "Reports parameter naming and type convention issues in endpoint method calls.", + Run: p.run, + }, + }, nil +} + +// GetLoadMode returns the load mode for the ParamCheckPlugin. +func (p *ParamCheckPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} + +func (p *ParamCheckPlugin) run(pass *analysis.Pass) (any, error) { + for _, file := range pass.Files { + for _, decl := range file.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok || fn.Body == nil { + continue + } + p.analyzeFunc(pass, fn) + } + } + return nil, nil +} + +func (p *ParamCheckPlugin) analyzeFunc(pass *analysis.Pass, fn *ast.FuncDecl) { + ast.Inspect(fn.Body, func(n ast.Node) bool { + call, ok := n.(*ast.CallExpr) + if !ok { + return true + } + + if isClientNewRequest(call) && isMutatingMethod(call) { + bodyIdent, ok := call.Args[3].(*ast.Ident) + if !ok { + return true + } + field, name := findParam(fn, bodyIdent.Name) + if field == nil { + return true + } + bodyReportName(pass, fn, name) + p.bodyReportPass(pass, field, name) + p.bodyReportSuffix(pass, field) + } + + if isAddOptions(call) { + optsIdent, ok := call.Args[1].(*ast.Ident) + if !ok { + return true + } + field, name := findParam(fn, optsIdent.Name) + if field == nil { + return true + } + queryReportName(pass, fn, name) + p.queryReportPass(pass, field, name) + p.queryReportSuffix(pass, field) + } + + return true + }) +} + +func bodyReportName(pass *analysis.Pass, fn *ast.FuncDecl, name *ast.Ident) { + if name.Name == "body" { + return + } + + diag := analysis.Diagnostic{ + Pos: name.Pos(), + End: name.End(), + Message: fmt.Sprintf("rename request body parameter %q to \"body\"", name.Name), + } + if edits := renameEdits(fn, name.Name, "body"); edits != nil { + diag.SuggestedFixes = []analysis.SuggestedFix{ + { + Message: `Rename to "body"`, + TextEdits: edits, + }, + } + } + pass.Report(diag) +} + +func queryReportName(pass *analysis.Pass, fn *ast.FuncDecl, name *ast.Ident) { + if name.Name == "opts" { + return + } + + diag := analysis.Diagnostic{ + Pos: name.Pos(), + End: name.End(), + Message: fmt.Sprintf("rename addOptions parameter %q to \"opts\"", name.Name), + } + if edits := renameEdits(fn, name.Name, "opts"); edits != nil { + diag.SuggestedFixes = []analysis.SuggestedFix{ + { + Message: `Rename to "opts"`, + TextEdits: edits, + }, + } + } + pass.Report(diag) +} + +func (p *ParamCheckPlugin) bodyReportPass(pass *analysis.Pass, field *ast.Field, name *ast.Ident) { + if _, ok := field.Type.(*ast.StarExpr); !ok { + return + } + if ident := typeNameIdent(field.Type); ident != nil && p.bodyAllowedPointerTypes[ident.Name] { + return + } + pass.Report(analysis.Diagnostic{ + Pos: name.Pos(), + End: name.End(), + Message: fmt.Sprintf("pass request body %q by value, not by pointer", name.Name), + }) +} + +func (p *ParamCheckPlugin) queryReportPass(pass *analysis.Pass, field *ast.Field, name *ast.Ident) { + if _, ok := field.Type.(*ast.StarExpr); ok { + return + } + pass.Report(analysis.Diagnostic{ + Pos: name.Pos(), + End: name.End(), + Message: fmt.Sprintf("pass query parameter %q by pointer, not by value", name.Name), + }) +} + +func (p *ParamCheckPlugin) queryReportSuffix(pass *analysis.Pass, field *ast.Field) { + ident := typeNameIdent(field.Type) + if ident == nil || strings.HasSuffix(ident.Name, "Options") { + return + } + pass.Report(analysis.Diagnostic{ + Pos: ident.Pos(), + End: ident.End(), + Message: fmt.Sprintf("query parameter type %q should use an \"Options\" suffix", ident.Name), + }) +} + +// bodyReportSuffix reports body parameter types whose name ends with "Options", which should use a "Request" suffix instead +// (e.g. UserSuspendOptions -> UserSuspendRequest). +// It is report-only because renaming a type affects its declaration and every use across the codebase. +func (p *ParamCheckPlugin) bodyReportSuffix(pass *analysis.Pass, field *ast.Field) { + ident := typeNameIdent(field.Type) + if ident == nil || !strings.HasSuffix(ident.Name, "Options") { + return + } + if p.bodyAllowedWrongNames[ident.Name] { + return + } + pass.Report(analysis.Diagnostic{ + Pos: ident.Pos(), + End: ident.End(), + Message: fmt.Sprintf("request body type %q should use a \"Request\" suffix, not \"Options\"", ident.Name), + }) +} + +// isAddOptions reports whether call is of the form addOptions(...) with at least two arguments. +// addOptions is used in endpoint methods to add query parameters, so the second argument is expected to be the options struct. +func isAddOptions(call *ast.CallExpr) bool { + if len(call.Args) < 2 { + return false + } + ident, ok := call.Fun.(*ast.Ident) + return ok && ident.Name == "addOptions" +} + +// isClientNewRequest reports whether call is of the form x.client.NewRequest(...) or client.NewRequest(...). +func isClientNewRequest(call *ast.CallExpr) bool { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || sel.Sel.Name != "NewRequest" { + return false + } + switch x := sel.X.(type) { + case *ast.SelectorExpr: + return x.Sel.Name == "client" + case *ast.Ident: + return x.Name == "client" + default: + return false + } +} + +// isMutatingMethod reports whether the call's method argument is "PATCH", "POST", or "PUT" and a body argument is present. +func isMutatingMethod(call *ast.CallExpr) bool { + if len(call.Args) < 4 { + return false + } + lit, ok := call.Args[1].(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return false + } + switch lit.Value { + case `"PATCH"`, `"POST"`, `"PUT"`: + return true + default: + return false + } +} + +func findParam(fn *ast.FuncDecl, name string) (*ast.Field, *ast.Ident) { + if fn.Type.Params == nil { + return nil, nil + } + for _, field := range fn.Type.Params.List { + for _, ident := range field.Names { + if ident.Name == name { + return field, ident + } + } + } + return nil, nil +} + +// typeNameIdent returns the base type name identifier of expr, unwrapping a pointer and resolving a qualified (pkg.Type) selector. +func typeNameIdent(expr ast.Expr) *ast.Ident { + switch t := expr.(type) { + case *ast.StarExpr: + return typeNameIdent(t.X) + case *ast.Ident: + return t + case *ast.SelectorExpr: + return t.Sel + default: + return nil + } +} + +// renameEdits builds the text edits that rename every reference to the old parameter within fn to newName. +// It returns nil (no auto-fix) when newName is already used as an identifier in fn or when old is shadowed by a local declaration, +// since either case could make the rename incorrect. +func renameEdits(fn *ast.FuncDecl, old, newName string) []analysis.TextEdit { + // Idents that are the selector field (x.old) must not be renamed. + skip := map[*ast.Ident]bool{} + ast.Inspect(fn, func(n ast.Node) bool { + if sel, ok := n.(*ast.SelectorExpr); ok { + skip[sel.Sel] = true + } + return true + }) + + var edits []analysis.TextEdit + conflict := false + ast.Inspect(fn, func(n ast.Node) bool { + ident, ok := n.(*ast.Ident) + if !ok || skip[ident] { + return true + } + switch ident.Name { + case newName: + conflict = true + case old: + edits = append(edits, analysis.TextEdit{ + Pos: ident.Pos(), + End: ident.End(), + NewText: []byte(newName), + }) + } + return true + }) + if conflict { + return nil + } + return edits +} diff --git a/tools/paramcheck/paramcheck_test.go b/tools/paramcheck/paramcheck_test.go new file mode 100644 index 00000000000..7831169ab84 --- /dev/null +++ b/tools/paramcheck/paramcheck_test.go @@ -0,0 +1,23 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package paramcheck + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + t.Parallel() + testdata := analysistest.TestData() + plugin, _ := New(map[string]any{ + "body-allowed-pointer-types": []any{"AllowedPtr"}, + "body-allowed-wrong-names": []any{"AllowedOptions"}, + }) + analyzers, _ := plugin.BuildAnalyzers() + analysistest.Run(t, testdata, analyzers[0], "has-warnings", "no-warnings") +} diff --git a/tools/paramcheck/testdata/src/has-warnings/github.go b/tools/paramcheck/testdata/src/has-warnings/github.go new file mode 100644 index 00000000000..1da7237a8e6 --- /dev/null +++ b/tools/paramcheck/testdata/src/has-warnings/github.go @@ -0,0 +1,94 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import "context" + +type Client struct{} + +func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body any) error { + return nil +} + +type service struct { + client *Client +} + +type CreateRequest struct { + Name string +} + +type UpdateRequest struct { + Name string +} + +type SuspendOptions struct { + Reason string +} + +type ServerOptions struct { + Host string +} + +type ListOptions struct { + Page int +} + +type ListRequest struct { + Filter string +} + +func addOptions(s string, opt any) (string, error) { return s, nil } + +// opts named, value, good type: rename only. +func (s *service) Create(ctx context.Context, opts CreateRequest) error { // want `rename request body parameter "opts" to "body"` + return s.client.NewRequest(ctx, "POST", "u", opts) +} + +// request named, pointer, good type: rename and by-value. +func (s *service) Update(ctx context.Context, request *UpdateRequest) error { // want `rename request body parameter "request" to "body"` `pass request body "request" by value, not by pointer` + return s.client.NewRequest(ctx, "PATCH", "u", request) +} + +// opts named, pointer, Options-suffixed type: rename, by-value, and type suffix. +func (s *service) Suspend(ctx context.Context, opts *SuspendOptions) error { // want `rename request body parameter "opts" to "body"` `pass request body "opts" by value, not by pointer` `request body type "SuspendOptions" should use a "Request" suffix, not "Options"` + return s.client.NewRequest(ctx, "PUT", "u", opts) +} + +// Domain-specific name, value, Options-suffixed type: rename and type suffix. +func (s *service) Save(ctx context.Context, settings ServerOptions) error { // want `rename request body parameter "settings" to "body"` `request body type "ServerOptions" should use a "Request" suffix, not "Options"` + return s.client.NewRequest(ctx, "POST", "u", settings) +} + +// Wrong name for addOptions parameter: rename only. +func (s *service) List(ctx context.Context, options *ListOptions) ([]string, error) { // want `rename addOptions parameter "options" to "opts"` + u, err := addOptions("list", options) + if err != nil { + return nil, err + } + _ = u + return nil, nil +} + +// addOptions parameter passed by value: by-pointer warning. +func (s *service) Search(ctx context.Context, opts ListOptions) ([]string, error) { // want `pass query parameter "opts" by pointer, not by value` + u, err := addOptions("search", opts) + if err != nil { + return nil, err + } + _ = u + return nil, nil +} + +// addOptions parameter type with Request suffix: type suffix warning. +func (s *service) Browse(ctx context.Context, opts *ListRequest) ([]string, error) { // want `query parameter type "ListRequest" should use an "Options" suffix` + u, err := addOptions("browse", opts) + if err != nil { + return nil, err + } + _ = u + return nil, nil +} diff --git a/tools/paramcheck/testdata/src/no-warnings/github.go b/tools/paramcheck/testdata/src/no-warnings/github.go new file mode 100644 index 00000000000..d1eed5ef6a8 --- /dev/null +++ b/tools/paramcheck/testdata/src/no-warnings/github.go @@ -0,0 +1,92 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import "context" + +type Client struct{} + +func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body any) error { + return nil +} + +type service struct { + client *Client +} + +type CreateRequest struct { + Name string +} + +type ListOptions struct { + Page int +} + +type SearchOptions struct { + Query string +} + +type AllowedPtr struct { + Name string +} + +type AllowedOptions struct { + Name string +} + +// Already named body, value, good type: no warning. +func (s *service) Create(ctx context.Context, body CreateRequest) error { + return s.client.NewRequest(ctx, "POST", "u", body) +} + +// No body: no warning. +func (s *service) Delete(ctx context.Context, id string) error { + return s.client.NewRequest(ctx, "DELETE", "u", nil) +} + +// GET with an Options-suffixed pointer must not fire any rule. +func (s *service) List(ctx context.Context, opts *ListOptions) error { + return s.client.NewRequest(ctx, "GET", "u", opts) +} + +// Named body, value, good type: no warning. +func (s *service) Save(ctx context.Context, body CreateRequest) error { + return s.client.NewRequest(ctx, "PUT", "u", body) +} + +// Not a client.NewRequest receiver: no warning. +type other struct{} + +func (o *other) NewRequest(ctx context.Context, method, urlStr string, body any) error { + return nil +} + +func (s *service) Other(ctx context.Context, opts *ListOptions) error { + var o other + return o.NewRequest(ctx, "POST", "u", opts) +} + +func addOptions(s string, opt any) (string, error) { return s, nil } + +// opts named, pointer, Options-suffixed type: no warning. +func (s *service) Search(ctx context.Context, opts *SearchOptions) ([]string, error) { + u, err := addOptions("search", opts) + if err != nil { + return nil, err + } + _ = u + return nil, nil +} + +// Pointer body whose type is in allowed-pointer-types: by-value rule suppressed. +func (s *service) IgnoredPointer(ctx context.Context, body *AllowedPtr) error { + return s.client.NewRequest(ctx, "PUT", "u", body) +} + +// Options-suffixed body whose type is in allowed-wrong-names: suffix rule suppressed. +func (s *service) IgnoredOptions(ctx context.Context, body AllowedOptions) error { + return s.client.NewRequest(ctx, "POST", "u", body) +} diff --git a/tools/redundantptr/go.mod b/tools/redundantptr/go.mod new file mode 100644 index 00000000000..32e4feb5888 --- /dev/null +++ b/tools/redundantptr/go.mod @@ -0,0 +1,13 @@ +module tools/redundantptr + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/tools/redundantptr/go.sum b/tools/redundantptr/go.sum new file mode 100644 index 00000000000..d120eba8160 --- /dev/null +++ b/tools/redundantptr/go.sum @@ -0,0 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/redundantptr/redundantptr.go b/tools/redundantptr/redundantptr.go new file mode 100644 index 00000000000..951e06bfed3 --- /dev/null +++ b/tools/redundantptr/redundantptr.go @@ -0,0 +1,239 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package redundantptr is a custom linter to find redundant github.Ptr calls. +package redundantptr + +import ( + "bytes" + "go/ast" + "go/format" + "go/token" + + "github.com/golangci/plugin-module-register/register" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/astutil" +) + +func init() { + register.Plugin("redundantptr", New) +} + +// RedundantPtrPlugin is a custom linter plugin for golangci-lint. +type RedundantPtrPlugin struct{} + +// New returns an analysis.Analyzer to use with golangci-lint. +func New(_ any) (register.LinterPlugin, error) { + return &RedundantPtrPlugin{}, nil +} + +// BuildAnalyzers builds the analyzers for the RedundantPtrPlugin. +func (p *RedundantPtrPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "redundantptr", + Doc: "Reports github.Ptr(x) calls that can be replaced with &x.", + Run: run, + }, + }, nil +} + +// GetLoadMode returns the load mode for the RedundantPtrPlugin. +func (p *RedundantPtrPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} + +func run(pass *analysis.Pass) (any, error) { + for _, file := range pass.Files { + ast.Inspect(file, func(n ast.Node) bool { + switch fn := n.(type) { + case *ast.FuncDecl: + if fn.Body != nil { + analyzeFunction(pass, fn, fn.Body) + } + case *ast.FuncLit: + analyzeFunction(pass, fn, fn.Body) + } + return true + }) + } + + return nil, nil +} + +func analyzeFunction(pass *analysis.Pass, fn ast.Node, body *ast.BlockStmt) { + if shouldIgnoreDeprecatedPtrWrapper(fn) { + return + } + + locals := collectLocals(fn, body) + + astutil.Apply(body, func(cursor *astutil.Cursor) bool { + call, ok := cursor.Node().(*ast.CallExpr) + if !ok { + return true + } + + rootName, argText := redundantPtrCall(pass, call) + if rootName == "" || argText == "" { + return true + } + + if !locals[rootName] { + return true + } + + replacement := "&" + argText + pass.Report(analysis.Diagnostic{ + Pos: call.Pos(), + End: call.End(), + Message: "replace github.Ptr(" + argText + ") with " + replacement, + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: "Use address-of operator", + TextEdits: []analysis.TextEdit{ + {Pos: call.Pos(), End: call.End(), NewText: []byte(replacement)}, + }, + }, + }, + }) + return true + }, nil) +} + +func redundantPtrCall(pass *analysis.Pass, call *ast.CallExpr) (rootName, argText string) { + if len(call.Args) != 1 { + return "", "" + } + + switch fun := call.Fun.(type) { + case *ast.SelectorExpr: + if fun.Sel == nil || fun.Sel.Name != "Ptr" { + return "", "" + } + qual, ok := fun.X.(*ast.Ident) + if !ok || qual.Name != "github" { + return "", "" + } + case *ast.Ident: + if fun.Name != "Ptr" { + return "", "" + } + default: + return "", "" + } + + arg := call.Args[0] + root := rootIdentName(arg) + if root == "" { + return "", "" + } + return root, exprString(pass, arg) +} + +func rootIdentName(expr ast.Expr) string { + switch e := expr.(type) { + case *ast.Ident: + return e.Name + case *ast.SelectorExpr: + return rootIdentName(e.X) + default: + return "" + } +} + +func exprString(pass *analysis.Pass, expr ast.Expr) string { + var b bytes.Buffer + if err := format.Node(&b, pass.Fset, expr); err != nil { + return "" + } + return b.String() +} + +func collectLocals(fn ast.Node, body *ast.BlockStmt) map[string]bool { + locals := map[string]bool{} + + switch node := fn.(type) { + case *ast.FuncDecl: + addFieldListNames(locals, node.Recv) + addFieldListNames(locals, node.Type.Params) + addFieldListNames(locals, node.Type.Results) + case *ast.FuncLit: + addFieldListNames(locals, node.Type.Params) + addFieldListNames(locals, node.Type.Results) + } + + ast.Inspect(body, func(n ast.Node) bool { + switch stmt := n.(type) { + case *ast.DeclStmt: + gen, ok := stmt.Decl.(*ast.GenDecl) + if !ok || gen.Tok != token.VAR { + return true + } + for _, spec := range gen.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + for _, name := range valueSpec.Names { + locals[name.Name] = true + } + } + case *ast.AssignStmt: + if stmt.Tok == token.DEFINE { + for _, lhs := range stmt.Lhs { + ident, ok := lhs.(*ast.Ident) + if !ok || ident.Name == "_" { + continue + } + locals[ident.Name] = true + } + } + case *ast.RangeStmt: + if stmt.Tok == token.DEFINE { + if ident, ok := stmt.Key.(*ast.Ident); ok && ident.Name != "_" { + locals[ident.Name] = true + } + if ident, ok := stmt.Value.(*ast.Ident); ok && ident.Name != "_" { + locals[ident.Name] = true + } + } + } + return true + }) + + return locals +} + +func addFieldListNames(locals map[string]bool, fields *ast.FieldList) { + if fields == nil { + return + } + for _, field := range fields.List { + for _, name := range field.Names { + if name.Name != "_" { + locals[name.Name] = true + } + } + } +} + +func shouldIgnoreDeprecatedPtrWrapper(fn ast.Node) bool { + decl, ok := fn.(*ast.FuncDecl) + if !ok { + return false + } + + if decl.Recv != nil || decl.Name == nil { + return false + } + + switch decl.Name.Name { + case "Bool", "Int", "Int64", "String": + return true + default: + return false + } +} diff --git a/tools/redundantptr/redundantptr_test.go b/tools/redundantptr/redundantptr_test.go new file mode 100644 index 00000000000..2b8465db0dd --- /dev/null +++ b/tools/redundantptr/redundantptr_test.go @@ -0,0 +1,20 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package redundantptr + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + t.Parallel() + testdata := analysistest.TestData() + plugin, _ := New(nil) + analyzers, _ := plugin.BuildAnalyzers() + analysistest.Run(t, testdata, analyzers[0], "has-warnings", "no-warnings") +} diff --git a/tools/redundantptr/testdata/src/has-warnings/github.go b/tools/redundantptr/testdata/src/has-warnings/github.go new file mode 100644 index 00000000000..e06225792d8 --- /dev/null +++ b/tools/redundantptr/testdata/src/has-warnings/github.go @@ -0,0 +1,42 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +func main() { + file, content := getFileAndContent() + opts := struct { + Mode string + }{Mode: "gfm"} + + _ = Ptr(string(content)) + + _ = Ptr(file) // want `replace github.Ptr\(file\) with &file` + + other := "b.txt" + _ = Ptr(other) // want `replace github.Ptr\(other\) with &other` + _ = Ptr(opts.Mode) // want `replace github.Ptr\(opts.Mode\) with &opts.Mode` + + for _, loopFile := range []string{"x", "y"} { + _ = Ptr(loopFile) // want `replace github.Ptr\(loopFile\) with &loopFile` + } + + name := "before" + _ = Ptr(name) // want `replace github.Ptr\(name\) with &name` + name = "after" + _ = Ptr(name) // want `replace github.Ptr\(name\) with &name` + + i := 1 + _ = Ptr(i) // want `replace github.Ptr\(i\) with &i` + _ = Ptr(opts.Mode) // want `replace github.Ptr\(opts.Mode\) with &opts.Mode` +} + +func getFileAndContent() (string, []byte) { + return "", nil +} + +func Ptr[T any](v T) *T { + return &v +} diff --git a/tools/redundantptr/testdata/src/no-warnings/github.go b/tools/redundantptr/testdata/src/no-warnings/github.go new file mode 100644 index 00000000000..c1721610e8b --- /dev/null +++ b/tools/redundantptr/testdata/src/no-warnings/github.go @@ -0,0 +1,44 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +func main() { + // Literal argument cannot be addressed. + _ = Ptr("a.txt") + + const file = "a.txt" + _ = Ptr(file) + + for range []int{1, 2} { + _ = Ptr("a") + } + + _ = Ptr(getOptions().Mode) +} + +func getOptions() struct { + Mode string +} { + return struct { + Mode string + }{Mode: "gfm"} +} + +func unqualifiedIdentifierArgumentDoesNotWarn() { + _ = Ptr("x") +} + +func Bool(v bool) *bool { return Ptr(v) } + +func Int(v int) *int { return Ptr(v) } + +func Int64(v int64) *int64 { return Ptr(v) } + +func String(v string) *string { return Ptr(v) } + +func Ptr[T any](v T) *T { + return &v +} diff --git a/tools/sliceofpointers/go.mod b/tools/sliceofpointers/go.mod new file mode 100644 index 00000000000..939f838a004 --- /dev/null +++ b/tools/sliceofpointers/go.mod @@ -0,0 +1,13 @@ +module tools/sliceofpointers + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/tools/sliceofpointers/go.sum b/tools/sliceofpointers/go.sum new file mode 100644 index 00000000000..d120eba8160 --- /dev/null +++ b/tools/sliceofpointers/go.sum @@ -0,0 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/sliceofpointers/sliceofpointers.go b/tools/sliceofpointers/sliceofpointers.go new file mode 100644 index 00000000000..50a5bdf0c7f --- /dev/null +++ b/tools/sliceofpointers/sliceofpointers.go @@ -0,0 +1,76 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sliceofpointers is a custom linter to be used by +// golangci-lint to find instances of `[]*string` and +// slices of structs without pointers and report them. +// See: https://github.com/google/go-github/issues/180 +package sliceofpointers + +import ( + "go/ast" + "go/token" + + "github.com/golangci/plugin-module-register/register" + "golang.org/x/tools/go/analysis" +) + +func init() { + register.Plugin("sliceofpointers", New) +} + +// SliceOfPointersPlugin is a custom linter plugin for golangci-lint. +type SliceOfPointersPlugin struct{} + +// New returns an analysis.Analyzer to use with golangci-lint. +func New(_ any) (register.LinterPlugin, error) { + return &SliceOfPointersPlugin{}, nil +} + +// BuildAnalyzers builds the analyzers for the SliceOfPointersPlugin. +func (f *SliceOfPointersPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "sliceofpointers", + Doc: "Reports usage of []*string and slices of structs without pointers.", + Run: run, + }, + }, nil +} + +// GetLoadMode returns the load mode for the SliceOfPointersPlugin. +func (f *SliceOfPointersPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} + +func run(pass *analysis.Pass) (any, error) { + for _, file := range pass.Files { + ast.Inspect(file, func(n ast.Node) bool { + if n == nil { + return false + } + + if t, ok := n.(*ast.ArrayType); ok { + checkArrayType(t, t.Pos(), pass) + } + + return true + }) + } + return nil, nil +} + +func checkArrayType(arrType *ast.ArrayType, tokenPos token.Pos, pass *analysis.Pass) { + if starExpr, ok := arrType.Elt.(*ast.StarExpr); ok { + if ident, ok := starExpr.X.(*ast.Ident); ok && ident.Name == "string" { + const msg = "use []string instead of []*string" + pass.Reportf(tokenPos, msg) + } + } else if ident, ok := arrType.Elt.(*ast.Ident); ok && ident.Obj != nil { + if _, ok := ident.Obj.Decl.(*ast.TypeSpec).Type.(*ast.StructType); ok { + pass.Reportf(tokenPos, "use []*%v instead of []%[1]v", ident.Name) + } + } +} diff --git a/tools/sliceofpointers/sliceofpointers_test.go b/tools/sliceofpointers/sliceofpointers_test.go new file mode 100644 index 00000000000..8fb0787f022 --- /dev/null +++ b/tools/sliceofpointers/sliceofpointers_test.go @@ -0,0 +1,20 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sliceofpointers + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + t.Parallel() + testdata := analysistest.TestData() + plugin, _ := New(nil) + analyzers, _ := plugin.BuildAnalyzers() + analysistest.Run(t, testdata, analyzers[0], "has-warnings", "no-warnings") +} diff --git a/tools/sliceofpointers/testdata/src/has-warnings/main.go b/tools/sliceofpointers/testdata/src/has-warnings/main.go new file mode 100644 index 00000000000..84e0d024e4b --- /dev/null +++ b/tools/sliceofpointers/testdata/src/has-warnings/main.go @@ -0,0 +1,21 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type Example struct { + Strings []*string `json:"strings,omitempty"` // want `use \[\]string instead of \[\]\*string` + Things []Thing `json:"things,omitempty"` // want `use \[\]\*Thing instead of \[\]Thing` +} + +type Thing struct { +} + +func main() { + slice1 := []*string{} // want `use \[\]string instead of \[\]\*string` + _ = slice1 + slice2 := []Thing{} // want `use \[\]\*Thing instead of \[\]Thing` + _ = slice2 +} diff --git a/tools/sliceofpointers/testdata/src/no-warnings/main.go b/tools/sliceofpointers/testdata/src/no-warnings/main.go new file mode 100644 index 00000000000..529d1d2fdad --- /dev/null +++ b/tools/sliceofpointers/testdata/src/no-warnings/main.go @@ -0,0 +1,21 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type Example struct { + Strings []string `json:"strings,omitempty"` // Should not be flagged + Things []*Thing `json:"things,omitempty"` // Should not be flagged +} + +type Thing struct { +} + +func main() { + slice1 := []string{} // Should not be flagged + _ = slice1 + slice2 := []*Thing{} // Should not be flagged + _ = slice2 +} diff --git a/tools/structfield/go.mod b/tools/structfield/go.mod new file mode 100644 index 00000000000..e2ca2546e7b --- /dev/null +++ b/tools/structfield/go.mod @@ -0,0 +1,13 @@ +module github.com/google/go-github/v90/tools/structfield + +go 1.25.0 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + golang.org/x/tools v0.48.0 +) + +require ( + golang.org/x/mod v0.38.0 // indirect + golang.org/x/sync v0.22.0 // indirect +) diff --git a/tools/structfield/go.sum b/tools/structfield/go.sum new file mode 100644 index 00000000000..d120eba8160 --- /dev/null +++ b/tools/structfield/go.sum @@ -0,0 +1,10 @@ +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= diff --git a/tools/structfield/structfield.go b/tools/structfield/structfield.go new file mode 100644 index 00000000000..7b43a6d5c69 --- /dev/null +++ b/tools/structfield/structfield.go @@ -0,0 +1,459 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package structfield is a custom linter to be used by +// golangci-lint to find instances where the Go field name +// of a struct does not match the JSON or URL tag name. +// It honors idiomatic Go initialisms and handles the +// special case of `Github` vs `GitHub` as agreed upon +// by the original author of the repo. +// It also checks that fields with "omitempty" tags are reference types +// for `json` struct tags and value types for `url` struct tags. +package structfield + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "log" + "reflect" + "regexp" + "strings" + + "github.com/golangci/plugin-module-register/register" + "golang.org/x/tools/go/analysis" +) + +func init() { + register.Plugin("structfield", New) +} + +// StructFieldPlugin is a custom linter plugin for golangci-lint. +type StructFieldPlugin struct { + allowedTagNames map[string]bool + allowedTagTypes map[string]bool +} + +// Settings is the configuration for the structfield linter. +type Settings struct { + AllowedTagNames []string `json:"allowed-tag-names" yaml:"allowed-tag-names"` + AllowedTagTypes []string `json:"allowed-tag-types" yaml:"allowed-tag-types"` +} + +// New returns an analysis.Analyzer to use with golangci-lint. +func New(cfg any) (register.LinterPlugin, error) { + allowedTagNames := map[string]bool{} + allowedTagTypes := map[string]bool{} + + if cfg != nil { + if settingsMap, ok := cfg.(map[string]any); ok { + if exceptionsRaw, ok := settingsMap["allowed-tag-names"]; ok { + if exceptionsList, ok := exceptionsRaw.([]any); ok { + for _, item := range exceptionsList { + if exception, ok := item.(string); ok { + allowedTagNames[exception] = true + } + } + } + } + + if exceptionsRaw, ok := settingsMap["allowed-tag-types"]; ok { + if exceptionsList, ok := exceptionsRaw.([]any); ok { + for _, item := range exceptionsList { + if exception, ok := item.(string); ok { + allowedTagTypes[exception] = true + } + } + } + } + } + } + + return &StructFieldPlugin{ + allowedTagNames: allowedTagNames, + allowedTagTypes: allowedTagTypes, + }, nil +} + +// BuildAnalyzers builds the analyzers for the StructFieldPlugin. +func (f *StructFieldPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "structfield", + Doc: `Reports mismatches between Go field and JSON or URL tag names and types. +Note that the JSON or URL tag name is the source-of-truth and the Go field name needs to match it. +If the "json" tag contains "omitempty", then the Go field must be a reference type. +If the "url" tag contains "omitempty", then the Go field must be a value type (unless it is a timestamp).`, + Run: func(pass *analysis.Pass) (any, error) { + return run(pass, f.allowedTagNames, f.allowedTagTypes) + }, + }, + }, nil +} + +// GetLoadMode returns the load mode for the StructFieldPlugin. +func (f *StructFieldPlugin) GetLoadMode() string { + return register.LoadModeSyntax +} + +func run(pass *analysis.Pass, allowedTagNames, allowedTagTypes map[string]bool) (any, error) { + for _, file := range pass.Files { + ast.Inspect(file, func(n ast.Node) bool { + if n == nil { + return false + } + + t, ok := n.(*ast.TypeSpec) + if !ok { + return true + } + structType, ok := t.Type.(*ast.StructType) + if !ok { + return true + } + + // Check only exported + if !ast.IsExported(t.Name.Name) { + return true + } + + for _, field := range structType.Fields.List { + if field.Tag == nil || len(field.Names) == 0 { + continue + } + + processStructField(t.Name.Name, field, pass, allowedTagNames, allowedTagTypes) + } + + return true + }) + } + return nil, nil +} + +func processStructField(structName string, field *ast.Field, pass *analysis.Pass, allowedTagNames, allowedTagTypes map[string]bool) { + goField := field.Names[0] + tagValue := strings.Trim(field.Tag.Value, "`") + structTag := reflect.StructTag(tagValue) + + processTag(structName, goField, field, structTag, "json", pass, allowedTagNames, allowedTagTypes) + processTag(structName, goField, field, structTag, "url", pass, allowedTagNames, allowedTagTypes) +} + +func processTag(structName string, goField *ast.Ident, field *ast.Field, structTag reflect.StructTag, tagType string, pass *analysis.Pass, allowedTagNames, allowedTagTypes map[string]bool) { + tagName, ok := structTag.Lookup(tagType) + if !ok || tagName == "-" { + return + } + + hasOmitEmpty := strings.Contains(tagName, ",omitempty") + hasOmitZero := strings.Contains(tagName, ",omitzero") + + if hasOmitEmpty || hasOmitZero { + checkGoFieldType(structName, goField.Name, tagType, field, field.Pos(), pass, allowedTagTypes, hasOmitEmpty, hasOmitZero) + tagName = strings.ReplaceAll(tagName, ",omitzero", "") + tagName = strings.ReplaceAll(tagName, ",omitempty", "") + } + if tagType == "url" { + tagName = strings.ReplaceAll(tagName, ",comma", "") + } + checkGoFieldName(structName, goField.Name, tagType, tagName, goField.Pos(), pass, allowedTagNames) +} + +func checkAndReportInvalidTypesForOmitzero(structName, tagType, goFieldName string, fieldType ast.Expr, tokenPos token.Pos, pass *analysis.Pass) bool { + switch ft := fieldType.(type) { + case *ast.StarExpr: + // Check for *[]T where T is builtin - should be []T + if arrType, ok := ft.X.(*ast.ArrayType); ok { + if ident, ok := arrType.Elt.(*ast.Ident); ok && isBuiltinType(ident.Name) { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "[]"+ident.Name, structName) + } else if starExpr, ok := arrType.Elt.(*ast.StarExpr); ok { + // Check for *[]*T - should be []*T + if ident, ok := starExpr.X.(*ast.Ident); ok { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "[]*"+ident.Name, structName) + } + } else { + checkStructArrayType(structName, goFieldName, arrType, tokenPos, pass) + } + return true + } + // Check for *int and other pointers to builtin types + if ident, ok := ft.X.(*ast.Ident); ok { + if isBuiltinType(ident.Name) { + const msg = `the %q field in struct %q uses "omitzero" with a primitive type; remove "omitzero" and use only "omitempty" for pointer primitive types"` + pass.Reportf(tokenPos, msg, goFieldName, structName) + return true + } + } + // Check for *map - should be map + if _, ok := ft.X.(*ast.MapType); ok { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, exprToString(ft.X), structName) + return true + } + return true + case *ast.MapType: + return true + case *ast.ArrayType: + if tagType == "url" { + const msg = "the %q field in struct %q uses unsupported omitzero tag for URL tags" + pass.Reportf(tokenPos, msg, goFieldName, structName) + return true + } + checkStructArrayType(structName, goFieldName, ft, tokenPos, pass) + return true + case *ast.Ident: + if obj := pass.TypesInfo.ObjectOf(ft); obj != nil { + switch obj.Type().Underlying().(type) { + case *types.Struct: + // For Struct - should be *Struct + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "*"+ft.Name, structName) + return true + case *types.Basic: + if tagType == "url" { + const msg = "the %q field in struct %q uses unsupported omitzero tag for URL tags" + pass.Reportf(tokenPos, msg, goFieldName, structName) + return true + } + // For Builtin - should not to be used with omitzero + const msg = `the %q field in struct %q uses "omitzero" with a primitive type; remove "omitzero", as it is only allowed with structs, maps, and slices` + pass.Reportf(tokenPos, msg, goFieldName, structName) + return true + } + } + case *ast.SelectorExpr: + return true + default: + log.Fatalf("unhandled type: %T", ft) + } + return false +} + +func checkGoFieldName(structName, goFieldName, tagType, tagName string, tokenPos token.Pos, pass *analysis.Pass, allowedNames map[string]bool) { + fullName := structName + "." + goFieldName + if allowedNames[fullName] { + return + } + + want, alternate := tagNameToPascal(tagName) + if goFieldName != want && goFieldName != alternate { + const msg = "change Go field name %q to %q for %v tag %q in struct %q" + pass.Reportf(tokenPos, msg, goFieldName, want, tagType, tagName, structName) + } +} + +func checkGoFieldType(structName, goFieldName, tagType string, field *ast.Field, tokenPos token.Pos, pass *analysis.Pass, allowedTypes map[string]bool, omitempty, omitzero bool) { + if allowedTypes[structName+"."+goFieldName] { + return + } + switch { + case omitzero: + skipOmitzero := checkAndReportInvalidTypesForOmitzero(structName, tagType, goFieldName, field.Type, tokenPos, pass) + if !skipOmitzero { + const msg = `the %q field in struct %q uses "omitzero"; remove "omitzero", as it is only allowed with structs, maps, and slices` + pass.Reportf(tokenPos, msg, goFieldName, structName) + } + + case omitempty: + if newFieldType, ok := checkAndReportInvalidTypes(structName, tagType, goFieldName, field.Type, tokenPos, pass); !ok { + const msg = `change the %q field type to %q in the struct %q because its %v tag uses "omitempty"` + pass.Reportf(tokenPos, msg, goFieldName, newFieldType, structName, tagType) + } + } +} + +func checkAndReportInvalidTypes(structName, tagType, goFieldName string, fieldType ast.Expr, tokenPos token.Pos, pass *analysis.Pass) (newFieldType string, ok bool) { + switch ft := fieldType.(type) { + case *ast.StarExpr: + // Check for *[]T where T is builtin - should be []T + if arrType, ok := ft.X.(*ast.ArrayType); ok { + if ident, ok := arrType.Elt.(*ast.Ident); ok && isBuiltinType(ident.Name) { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "[]"+ident.Name, structName) + } else if starExpr, ok := arrType.Elt.(*ast.StarExpr); ok { + // Check for *[]*T - should be []*T + if ident, ok := starExpr.X.(*ast.Ident); ok { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "[]*"+ident.Name, structName) + } + } else { + checkStructArrayType(structName, goFieldName, arrType, tokenPos, pass) + } + } + // Check for *map - should be map + if _, ok := ft.X.(*ast.MapType); ok { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, exprToString(ft.X), structName) + } + if ident, ok := ft.X.(*ast.Ident); ok && tagType == "url" && isBuiltinType(ident.Name) { + // Remove the pointer for primitives in url tags + return ident.Name, false + } + return "", true + case *ast.MapType: + return "", true + case *ast.ArrayType: + checkStructArrayType(structName, goFieldName, ft, tokenPos, pass) + return "", true + case *ast.SelectorExpr: + // Check for json.RawMessage + if ident, ok := ft.X.(*ast.Ident); ok && ident.Name == "json" && ft.Sel.Name == "RawMessage" { + return "", true + } + case *ast.Ident: + // Check for `any` type + if ft.Name == "any" { + return "", true + } + if tagType == "url" && isBuiltinType(ft.Name) { + return "", true + } + default: + log.Fatalf("unhandled type: %T", ft) + } + + newFieldType = "*" + exprToString(fieldType) + return newFieldType, false +} + +func checkStructArrayType(structName, goFieldName string, arrType *ast.ArrayType, tokenPos token.Pos, pass *analysis.Pass) { + if starExpr, ok := arrType.Elt.(*ast.StarExpr); ok { + if ident, ok := starExpr.X.(*ast.Ident); ok && isBuiltinType(ident.Name) { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "[]"+ident.Name, structName) + } + return + } + + if ident, ok := arrType.Elt.(*ast.Ident); ok && ident.Obj != nil { + if _, ok := ident.Obj.Decl.(*ast.TypeSpec).Type.(*ast.StructType); ok { + const msg = "change the %q field type to %q in the struct %q" + pass.Reportf(tokenPos, msg, goFieldName, "[]*"+ident.Name, structName) + } + } +} + +func isBuiltinType(typeName string) bool { + return types.Universe.Lookup(typeName) != nil +} + +func exprToString(e ast.Expr) string { + switch t := e.(type) { + case *ast.Ident: + return t.Name + case *ast.SelectorExpr: + return exprToString(t.X) + "." + t.Sel.Name + case *ast.MapType: + return "map[" + exprToString(t.Key) + "]" + exprToString(t.Value) + default: + return fmt.Sprintf("%T", e) + } +} + +func splitTag(jsonTagName string) []string { + jsonTagName = strings.TrimPrefix(jsonTagName, "$") + + if strings.Contains(jsonTagName, "_") { + return strings.Split(jsonTagName, "_") + } + + if strings.Contains(jsonTagName, "-") { + return strings.Split(jsonTagName, "-") + } + + if strings.ToLower(jsonTagName) == jsonTagName { // single word + return []string{jsonTagName} + } + + s := camelCaseRE.ReplaceAllString(jsonTagName, "$1 $2") + parts := strings.Fields(s) + for i, part := range parts { + parts[i] = strings.ToLower(part) + } + + return parts +} + +var camelCaseRE = regexp.MustCompile(`([a-z0-9])([A-Z])`) + +func tagNameToPascal(tagName string) (want, alternate string) { + parts := splitTag(tagName) + alt := make([]string, len(parts)) + for i, part := range parts { + alt[i] = part + if part == "" { + continue + } + upper := strings.ToUpper(part) + if initialisms[upper] { + parts[i] = upper + alt[i] = upper + } else if specialCase, ok := specialCases[upper]; ok { + parts[i] = specialCase + alt[i] = specialCase + } else if possibleAlternate, ok := possibleAlternates[upper]; ok { + parts[i] = possibleAlternate + alt[i] = strings.ToUpper(part[:1]) + part[1:] + } else { + parts[i] = strings.ToUpper(part[:1]) + part[1:] + alt[i] = parts[i] + } + } + return strings.Join(parts, ""), strings.Join(alt, "") +} + +// Common Go initialisms that should be all caps. +var initialisms = map[string]bool{ + "AI": true, "API": true, "ASCII": true, "AWS": true, + "CAA": true, "CAS": true, "CLI": true, "CNAME": true, "CPU": true, + "CSS": true, "CWE": true, "CVE": true, "CVSS": true, + "DN": true, "DNS": true, + "EOF": true, "EPSS": true, + "GB": true, "GHSA": true, "GPG": true, "GUID": true, + "HTML": true, "HTTP": true, "HTTPS": true, + "ID": true, "IDE": true, "IDP": true, "IP": true, "JIT": true, + "JSON": true, + "OIDC": true, + "LDAP": true, "LFS": true, "LHS": true, "LOC": true, + "MCP": true, "MD5": true, "MS": true, "MX": true, + "NPM": true, "NTP": true, "NVD": true, + "OID": true, "OS": true, + "PEM": true, "PR": true, "QPS": true, + "RAM": true, "RHS": true, "RPC": true, + "SAML": true, "SAS": true, "SBOM": true, "SCIM": true, + "SHA": true, "SHA1": true, "SHA256": true, + "SKU": true, "SLA": true, "SMTP": true, "SNMP": true, + "SPDX": true, "SPDXID": true, "SQL": true, "SSH": true, + "SSL": true, "SSO": true, "SVN": true, + "TCP": true, "TFVC": true, "TLS": true, "TTL": true, + "UDP": true, "UI": true, "UID": true, "UUID": true, + "URI": true, "URL": true, "UTF8": true, + "VCF": true, "VCS": true, "VM": true, + "XML": true, "XMPP": true, "XSRF": true, "XSS": true, +} + +var specialCases = map[string]string{ + "CPUS": "CPUs", + "CWES": "CWEs", + "JFROG": "JFrog", + "GRAPHQL": "GraphQL", + "HREF": "HRef", + "IDS": "IDs", + "IPS": "IPs", + "OAUTH": "OAuth", + "OPENAPI": "OpenAPI", + "URLS": "URLs", +} + +var possibleAlternates = map[string]string{ + "ORGANIZATION": "Org", + "ORGANIZATIONS": "Orgs", + "REPOSITORY": "Repo", + "REPOSITORIES": "Repos", +} diff --git a/tools/structfield/structfield_test.go b/tools/structfield/structfield_test.go new file mode 100644 index 00000000000..a192782313a --- /dev/null +++ b/tools/structfield/structfield_test.go @@ -0,0 +1,29 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package structfield + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestRun(t *testing.T) { + t.Parallel() + testdata := analysistest.TestData() + plugin, _ := New(map[string]any{ + "allowed-tag-names": []any{ + "JSONFieldName.Query", + "URLFieldName.Query", + }, + "allowed-tag-types": []any{ + "JSONFieldType.Exception", + "URLFieldType.Exception", + }, + }) + analyzers, _ := plugin.BuildAnalyzers() + analysistest.Run(t, testdata, analyzers[0], "has-warnings", "no-warnings") +} diff --git a/tools/structfield/testdata/src/has-warnings/main.go b/tools/structfield/testdata/src/has-warnings/main.go new file mode 100644 index 00000000000..06eca6a3f13 --- /dev/null +++ b/tools/structfield/testdata/src/has-warnings/main.go @@ -0,0 +1,57 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +type JSONFieldName struct { + GitHubThing string `json:"github_thing"` // want `change Go field name "GitHubThing" to "GithubThing" for json tag "github_thing" in struct "JSONFieldName"` + Id *string `json:"id,omitempty"` // want `change Go field name "Id" to "ID" for json tag "id" in struct "JSONFieldName"` + strings *string `json:"strings,omitempty"` // want `change Go field name "strings" to "Strings" for json tag "strings" in struct "JSONFieldName"` + camelcaseexample *int `json:"camelCaseExample,omitempty"` // want `change Go field name "camelcaseexample" to "CamelCaseExample" for json tag "camelCaseExample" in struct "JSONFieldName"` + DollarRef string `json:"$ref"` // want `change Go field name "DollarRef" to "Ref" for json tag "\$ref" in struct "JSONFieldName"` +} + +type JSONFieldType struct { + String string `json:"string,omitempty"` // want `change the "String" field type to "\*string" in the struct "JSONFieldType" because its json tag uses "omitempty"` + SliceOfStringPointers []*string `json:"slice_of_string_pointers,omitempty"` // want `change the "SliceOfStringPointers" field type to "\[\]string" in the struct "JSONFieldType"` + PointerToSliceOfStrings *[]string `json:"pointer_to_slice_of_strings,omitempty"` // want `change the "PointerToSliceOfStrings" field type to "\[\]string" in the struct "JSONFieldType"` + SliceOfStructs []Struct `json:"slice_of_structs,omitempty"` // want `change the "SliceOfStructs" field type to "\[\]\*Struct" in the struct "JSONFieldType"` + PointerToSliceOfStructs *[]Struct `json:"pointer_to_slice_of_structs,omitempty"` // want `change the "PointerToSliceOfStructs" field type to "\[\]\*Struct" in the struct "JSONFieldType"` + PointerToSliceOfPointerStructs *[]*Struct `json:"pointer_to_slice_of_pointer_structs,omitempty"` // want `change the "PointerToSliceOfPointerStructs" field type to "\[\]\*Struct" in the struct "JSONFieldType"` + PointerToMap *map[string]string `json:"pointer_to_map,omitempty"` // want `change the "PointerToMap" field type to "map\[string\]string" in the struct "JSONFieldType"` + SliceOfInts []*int `json:"slice_of_ints,omitempty"` // want `change the "SliceOfInts" field type to "\[\]int" in the struct "JSONFieldType"` + + Count int `json:"count,omitzero"` // want `the "Count" field in struct "JSONFieldType" uses "omitzero" with a primitive type; remove "omitzero", as it is only allowed with structs, maps, and slices` + Size *int `json:"size,omitzero"` // want `the "Size" field in struct "JSONFieldType" uses "omitzero" with a primitive type; remove "omitzero" and use only "omitempty" for pointer primitive types` + PointerToSliceOfStringsZero *[]string `json:"pointer_to_slice_of_strings_zero,omitzero"` // want `change the "PointerToSliceOfStringsZero" field type to "\[\]string" in the struct "JSONFieldType"` + PointerToSliceOfStructsZero *[]Struct `json:"pointer_to_slice_of_structs_zero,omitzero"` // want `change the "PointerToSliceOfStructsZero" field type to "\[\]\*Struct" in the struct "JSONFieldType"` + PointerToSliceOfPointerStructsZero *[]*Struct `json:"pointer_to_slice_of_pointer_structs_zero,omitzero"` // want `change the "PointerToSliceOfPointerStructsZero" field type to "\[\]\*Struct" in the struct "JSONFieldType"` + PointerSliceInt *[]int `json:"pointer_slice_int,omitzero"` // want `change the "PointerSliceInt" field type to "\[\]int" in the struct "JSONFieldType"` + AnyZero any `json:"any_zero,omitzero"` // want `the "AnyZero" field in struct "JSONFieldType" uses "omitzero"; remove "omitzero", as it is only allowed with structs, maps, and slices` + StringPointerSlice []*string `json:"string_pointer_slice,omitzero"` // want `change the "StringPointerSlice" field type to "\[\]string" in the struct "JSONFieldType"` + PointerToMapZero *map[string]string `json:"pointer__to_map_zero,omitzero"` // want `change the "PointerToMapZero" field type to "map\[string\]string" in the struct "JSONFieldType"` + SliceOfStructsZero []Struct `json:"slice_of_structs_zero,omitzero"` // want `change the "SliceOfStructsZero" field type to "\[\]\*Struct" in the struct "JSONFieldType"` + StructZero Struct `json:"struct_zero,omitzero"` // want `change the "StructZero" field type to "\*Struct" in the struct "JSONFieldType"` + + AnyBoth any `json:"any_both,omitempty,omitzero"` // want `the "AnyBoth" field in struct "JSONFieldType" uses "omitzero"; remove "omitzero", as it is only allowed with structs, maps, and slices` + NonPointerStructBoth Struct `json:"non_pointer_struct_both,omitempty,omitzero"` // want `change the "NonPointerStructBoth" field type to "\*Struct" in the struct "JSONFieldType"` + PointerStringBoth *string `json:"pointer_string_both,omitempty,omitzero"` // want `the "PointerStringBoth" field in struct "JSONFieldType" uses "omitzero" with a primitive type; remove "omitzero" and use only "omitempty" for pointer primitive types` + StructZeroBoth Struct `json:"struct_zero_both,omitempty,omitzero"` // want `change the "StructZeroBoth" field type to "\*Struct" in the struct "JSONFieldType"` +} + +type Struct struct{} + +type URLFieldName struct { + GitHubThing string `url:"github_thing"` // want `change Go field name "GitHubThing" to "GithubThing" for url tag "github_thing" in struct "URLFieldName"` +} + +type URLFieldType struct { + Page *string `url:"page,omitempty"` // want `change the "Page" field type to "string" in the struct "URLFieldType" because its url tag uses "omitempty"` + PerPage *int `url:"per_page,omitempty"` // want `change the "PerPage" field type to "int" in the struct "URLFieldType" because its url tag uses "omitempty"` + Participating *bool `url:"participating,omitempty"` // want `change the "Participating" field type to "bool" in the struct "URLFieldType" because its url tag uses "omitempty"` + + PerPageZeros []int `url:"per_page_zeros,omitzero"` // want `the "PerPageZeros" field in struct "URLFieldType" uses unsupported omitzero tag for URL tags` + PerPageBoth int `url:"per_page_both,omitempty,omitzero"` // want `the "PerPageBoth" field in struct "URLFieldType" uses unsupported omitzero tag for URL tags` +} diff --git a/tools/structfield/testdata/src/no-warnings/main.go b/tools/structfield/testdata/src/no-warnings/main.go new file mode 100644 index 00000000000..3b2a61a0389 --- /dev/null +++ b/tools/structfield/testdata/src/no-warnings/main.go @@ -0,0 +1,57 @@ +// Copyright 2025 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "encoding/json" + "time" +) + +type JSONFieldName struct { + GithubThing string `json:"github_thing"` + ID *string `json:"id,omitempty"` + Strings *string `json:"strings,omitempty"` + Ref *string `json:"$ref,omitempty"` + Query string `json:"q"` +} + +type JSONFieldType struct { + WithoutTag string + + ID *string `json:"id,omitempty"` + HookAttributes map[string]string `json:"hook_attributes,omitempty"` + Inputs json.RawMessage `json:"inputs,omitempty"` + Exception string `json:"exception,omitempty"` + Value any `json:"value,omitempty"` + SliceOfPointerStructs []*Struct `json:"slice_of_pointer_structs,omitempty"` + + SliceOfStrings []string `json:"slice_of_strings,omitzero"` + MapOfStringToInt map[string]int `json:"map_of_string_to_int,omitzero"` + PointerStructField *Struct `json:"pointer_struct_field,omitzero"` + SliceOfPointerStructsZero []*Struct `json:"slice_of_pointer_structs_zero,omitzero"` + + SliceOfStringsBoth []string `json:"slice_of_strings_both,omitzero,omitempty"` + MapOfStringToIntBoth map[string]int `json:"map_of_string_to_int_both,omitzero,omitempty"` + SliceOfPointerStructsBoth []*Struct `json:"slice_of_pointer_structs_both,omitzero,omitempty"` + StructFieldBoth *Struct `json:"struct_field_both,omitzero,omitempty"` +} + +type URLFieldName struct { + ID string `url:"id,omitempty"` + Query string `url:"q"` +} + +type URLFieldType struct { + Page string `url:"page,omitempty"` + PerPage int `url:"per_page,omitempty"` + Labels []string `url:"labels,omitempty,comma"` + Since *time.Time `url:"since,omitempty"` + Since2 time.Time `url:"since2,omitzero"` + Fields []int64 `url:"fields,omitempty,comma"` + Exception string `url:"exception,omitempty"` +} + +type Struct struct{}